Support LV2 atom sequence ports alongside old event ports.

git-svn-id: svn://localhost/ardour2/branches/3.0@11517 d708f5d6-7413-0410-9779-e7cbd77b26cf
This commit is contained in:
David Robillard 2012-02-25 04:16:42 +00:00
parent 4f96a1006b
commit f122504784
12 changed files with 1772 additions and 344 deletions

View file

@ -37,15 +37,16 @@ struct _VstMidiEvent;
typedef struct _VstMidiEvent VstMidiEvent;
#endif
#ifdef LV2_SUPPORT
typedef struct LV2_Evbuf_Impl LV2_Evbuf;
#endif
namespace ARDOUR {
class Buffer;
class AudioBuffer;
class MidiBuffer;
class PortSet;
#ifdef LV2_SUPPORT
class LV2EventBuffer;
#endif
/** A set of buffers of various types.
*
@ -113,8 +114,11 @@ public:
#ifdef LV2_SUPPORT
/** Get a MIDI buffer translated into an LV2 MIDI buffer for use with plugins.
* The index here corresponds directly to MIDI buffer numbers (i.e. the index
* passed to get_midi), translation back and forth will happen as needed */
LV2EventBuffer& get_lv2_midi(bool input, size_t i);
* passed to get_midi), translation back and forth will happen as needed.
* If atom_type is 0 the returned buffer will be in the old event API
* format. Otherwise, atom_type must be the URID for atom:Sequence.
*/
LV2_Evbuf* get_lv2_midi(bool input, size_t i, uint32_t atom_type);
/** Flush modified LV2 event output buffers back to Ardour buffers */
void flush_lv2_midi(bool input, size_t i);
@ -175,7 +179,7 @@ private:
#ifdef LV2_SUPPORT
/// LV2 MIDI buffers (for conversion to/from MIDI buffers)
typedef std::vector< std::pair<bool, LV2EventBuffer*> > LV2Buffers;
typedef std::vector< std::pair<bool, LV2_Evbuf*> > LV2Buffers;
LV2Buffers _lv2_buffers;
#endif

View file

@ -1,81 +0,0 @@
/*
Copyright (C) 2009 Paul Davis
Author: David Robillard
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __ardour_lv2_event_buffer_h__
#define __ardour_lv2_event_buffer_h__
#include "lv2/lv2plug.in/ns/ext/event/event.h"
#include "lv2/lv2plug.in/ns/ext/event/event-helpers.h"
namespace ARDOUR {
class LV2EventBuffer {
public:
LV2EventBuffer(size_t capacity);
~LV2EventBuffer();
inline LV2_Event_Buffer* data() { return _data; }
inline const LV2_Event_Buffer* data() const { return _data; }
inline void rewind() const { lv2_event_begin(&_iter, _data); }
inline void reset() {
_latest_frames = 0;
_latest_subframes = 0;
_data->event_count = 0;
_data->size = 0;
rewind();
}
inline size_t event_count() const { return _data->event_count; }
inline uint32_t capacity() const { return _data->capacity; }
inline uint32_t size() const { return _data->size; }
inline uint32_t latest_frames() const { return _latest_frames; }
inline uint32_t latest_subframes() const { return _latest_subframes; }
bool increment() const;
bool is_valid() const;
bool get_event(uint32_t* frames,
uint32_t* subframes,
uint16_t* type,
uint16_t* size,
uint8_t** data) const;
bool append(uint32_t frames,
uint32_t subframes,
uint16_t type,
uint16_t size,
const uint8_t* data);
bool append(const LV2_Event_Buffer* buf);
private:
LV2_Event_Buffer* _data; ///< Contents
mutable LV2_Event_Iterator _iter; ///< Iterator into _data
uint32_t _latest_frames; ///< Latest time of all events (frames)
uint32_t _latest_subframes; ///< Latest time of all events (subframes)
};
} // namespace ARDOUR
#endif // __ardour_lv2_event_buffer_h__

View file

@ -98,7 +98,10 @@ class LV2Plugin : public ARDOUR::Plugin
boost::shared_ptr<Plugin::ScalePoints>
get_scale_points(uint32_t port_index) const;
static uint32_t midi_event_type () { return _midi_event_type; }
/// Return the URID of midi:MidiEvent
static uint32_t midi_event_type (bool event_api) {
return event_api ? _midi_event_type_ev : _midi_event_type;
}
void set_insert_info(const PluginInsert* insert);
@ -125,9 +128,10 @@ class LV2Plugin : public ARDOUR::Plugin
typedef enum {
PORT_INPUT = 1,
PORT_OUTPUT = 1 << 1,
PORT_EVENT = 1 << 2,
PORT_AUDIO = 1 << 3,
PORT_CONTROL = 1 << 4
PORT_AUDIO = 1 << 2,
PORT_CONTROL = 1 << 3,
PORT_EVENT = 1 << 4,
PORT_MESSAGE = 1 << 5
} PortFlag;
typedef unsigned PortFlags;
@ -150,7 +154,9 @@ class LV2Plugin : public ARDOUR::Plugin
bool _has_state_interface;
static URIMap _uri_map;
static uint32_t _midi_event_type_ev;
static uint32_t _midi_event_type;
static uint32_t _sequence_type;
static uint32_t _state_path_type;
const std::string plugin_dir () const;

View file

@ -36,7 +36,7 @@
#include "ardour/audioengine.h"
#ifdef LV2_SUPPORT
#include "ardour/lv2_plugin.h"
#include "ardour/lv2_event_buffer.h"
#include "lv2_evbuf.h"
#endif
#if defined WINDOWS_VST_SUPPORT || defined LXVST_SUPPORT
#include "ardour/vestige/aeffectx.h"
@ -189,7 +189,9 @@ BufferSet::ensure_buffers(DataType type, size_t num_buffers, size_t buffer_capac
// in both directions (input & output, out-of-place)
if (type == DataType::MIDI && _lv2_buffers.size() < _buffers[type].size() * 2 + 1) {
while (_lv2_buffers.size() < _buffers[type].size() * 2) {
_lv2_buffers.push_back(std::make_pair(false, new LV2EventBuffer(buffer_capacity)));
_lv2_buffers.push_back(
std::make_pair(false,
lv2_evbuf_new(buffer_capacity, LV2_EVBUF_EVENT, 0)));
}
}
#endif
@ -248,50 +250,61 @@ BufferSet::get(DataType type, size_t i) const
#ifdef LV2_SUPPORT
LV2EventBuffer&
BufferSet::get_lv2_midi(bool input, size_t i)
LV2_Evbuf*
BufferSet::get_lv2_midi(bool input, size_t i, uint32_t atom_type)
{
assert (count().get(DataType::MIDI) > i);
assert(count().get(DataType::MIDI) > i);
MidiBuffer& mbuf = get_midi(i);
LV2Buffers::value_type b = _lv2_buffers.at(i * 2 + (input ? 0 : 1));
LV2EventBuffer* ebuf = b.second;
MidiBuffer& mbuf = get_midi(i);
LV2Buffers::value_type b = _lv2_buffers.at(i * 2 + (input ? 0 : 1));
LV2_Evbuf* evbuf = b.second;
lv2_evbuf_set_type(evbuf,
atom_type ? LV2_EVBUF_ATOM : LV2_EVBUF_EVENT,
atom_type);
ebuf->reset();
lv2_evbuf_reset(evbuf);
if (input) {
DEBUG_TRACE (PBD::DEBUG::LV2, string_compose ("%1 bytes of MIDI waiting @ %2\n", mbuf.size(), (void*) mbuf.data()));
DEBUG_TRACE(PBD::DEBUG::LV2,
string_compose("%1 bytes of MIDI waiting @ %2\n",
mbuf.size(), (void*) mbuf.data()));
LV2_Evbuf_Iterator i = lv2_evbuf_begin(evbuf);
const uint32_t type = LV2Plugin::midi_event_type(atom_type == 0);
for (MidiBuffer::iterator e = mbuf.begin(); e != mbuf.end(); ++e) {
const Evoral::MIDIEvent<framepos_t> ev(*e, false);
uint32_t type = LV2Plugin::midi_event_type();
#ifndef NDEBUG
DEBUG_TRACE (PBD::DEBUG::LV2, string_compose ("\tMIDI event of size %1 @ %2\n", ev.size(), ev.time()));
DEBUG_TRACE(PBD::DEBUG::LV2,
string_compose("\tMIDI event of size %1 @ %2\n",
ev.size(), ev.time()));
for (uint16_t x = 0; x < ev.size(); ++x) {
std::stringstream ss;
ss << "\t\tByte[" << x << "] = " << std::hex << (int) ev.buffer()[x] << std::dec << std::endl;
ss << "\t\tev[" << x << "] = " << std::hex << (int) ev.buffer()[x] << std::dec << std::endl;
DEBUG_TRACE (PBD::DEBUG::LV2, ss.str());
}
#endif
ebuf->append(ev.time(), 0, type, ev.size(), ev.buffer());
lv2_evbuf_write(&i, ev.time(), 0, type, ev.size(), ev.buffer());
}
}
return *ebuf;
return evbuf;
}
void
BufferSet::flush_lv2_midi(bool input, size_t i)
{
MidiBuffer& mbuf = get_midi(i);
LV2Buffers::value_type b = _lv2_buffers.at(i * 2 + (input ? 0 : 1));
LV2EventBuffer* ebuf = b.second;
MidiBuffer& mbuf = get_midi(i);
LV2Buffers::value_type b = _lv2_buffers.at(i * 2 + (input ? 0 : 1));
LV2_Evbuf* evbuf = b.second;
mbuf.silence(0, 0);
for (ebuf->rewind(); ebuf->is_valid(); ebuf->increment()) {
for (LV2_Evbuf_Iterator i = lv2_evbuf_begin(evbuf);
lv2_evbuf_is_valid(i);
i = lv2_evbuf_next(i)) {
uint32_t frames;
uint32_t subframes;
uint16_t type;
uint16_t size;
uint32_t type;
uint32_t size;
uint8_t* data;
ebuf->get_event(&frames, &subframes, &type, &size, &data);
lv2_evbuf_get(i, &frames, &subframes, &type, &size, &data);
#ifndef NDEBUG
DEBUG_TRACE (PBD::DEBUG::LV2, string_compose ("(FLUSH) MIDI event of size %1\n", size));
for (uint16_t x = 0; x < size; ++x) {

View file

@ -0,0 +1,259 @@
/*
Copyright 2008-2012 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
@file atom.h C header for the LV2 Atom extension
<http://lv2plug.in/ns/ext/atom>.
*/
#ifndef LV2_ATOM_H
#define LV2_ATOM_H
#include <stdint.h>
#include <stddef.h>
#define LV2_ATOM_URI "http://lv2plug.in/ns/ext/atom"
#define LV2_ATOM__Atom LV2_ATOM_URI "#Atom"
#define LV2_ATOM__AtomPort LV2_ATOM_URI "#AtomPort"
#define LV2_ATOM__AudioFrames LV2_ATOM_URI "#AudioFrames"
#define LV2_ATOM__Beats LV2_ATOM_URI "#Beats"
#define LV2_ATOM__Blank LV2_ATOM_URI "#Blank"
#define LV2_ATOM__Bool LV2_ATOM_URI "#Bool"
#define LV2_ATOM__Double LV2_ATOM_URI "#Double"
#define LV2_ATOM__Event LV2_ATOM_URI "#Event"
#define LV2_ATOM__Float LV2_ATOM_URI "#Float"
#define LV2_ATOM__Int32 LV2_ATOM_URI "#Int32"
#define LV2_ATOM__Int64 LV2_ATOM_URI "#Int64"
#define LV2_ATOM__Literal LV2_ATOM_URI "#Literal"
#define LV2_ATOM__MessagePort LV2_ATOM_URI "#MessagePort"
#define LV2_ATOM__Number LV2_ATOM_URI "#Number"
#define LV2_ATOM__Object LV2_ATOM_URI "#Object"
#define LV2_ATOM__Path LV2_ATOM_URI "#Path"
#define LV2_ATOM__Property LV2_ATOM_URI "#Property"
#define LV2_ATOM__Resource LV2_ATOM_URI "#Resource"
#define LV2_ATOM__Sequence LV2_ATOM_URI "#Sequence"
#define LV2_ATOM__String LV2_ATOM_URI "#String"
#define LV2_ATOM__TimeUnit LV2_ATOM_URI "#TimeUnit"
#define LV2_ATOM__Tuple LV2_ATOM_URI "#Tuple"
#define LV2_ATOM__URI LV2_ATOM_URI "#URI"
#define LV2_ATOM__URID LV2_ATOM_URI "#URID"
#define LV2_ATOM__ValuePort LV2_ATOM_URI "#ValuePort"
#define LV2_ATOM__Vector LV2_ATOM_URI "#Vector"
#define LV2_ATOM__beatTime LV2_ATOM_URI "#beatTime"
#define LV2_ATOM__bufferType LV2_ATOM_URI "#bufferType"
#define LV2_ATOM__eventTransfer LV2_ATOM_URI "#eventTransfer"
#define LV2_ATOM__frameTime LV2_ATOM_URI "#frameTime"
#define LV2_ATOM__supports LV2_ATOM_URI "#supports"
#define LV2_ATOM__timeUnit LV2_ATOM_URI "#timeUnit"
#define LV2_ATOM_REFERENCE_TYPE 0
#ifdef __cplusplus
extern "C" {
#endif
/** This expression will fail to compile if double does not fit in 64 bits. */
typedef char lv2_atom_assert_double_fits_in_64_bits[
((sizeof(double) <= sizeof(uint64_t)) * 2) - 1];
/**
Return a pointer to the contents of an Atom. The "contents" of an atom
is the data past the complete type-specific header.
@param type The type of the atom, e.g. LV2_Atom_String.
@param atom A variable-sized atom.
*/
#define LV2_ATOM_CONTENTS(type, atom) \
((void*)((uint8_t*)(atom) + sizeof(type)))
/**
Return a pointer to the body of an Atom. The "body" of an atom is the
data just past the LV2_Atom head (i.e. the same offset for all types).
*/
#define LV2_ATOM_BODY(atom) LV2_ATOM_CONTENTS(LV2_Atom, atom)
/** The header of an atom:Atom. */
typedef struct {
uint32_t size; /**< Size in bytes, not including type and size. */
uint32_t type; /**< Type of this atom (mapped URI). */
} LV2_Atom;
/** An atom:Int32 or atom:Bool. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
int32_t body; /**< Integer value. */
} LV2_Atom_Int32;
/** An atom:Int64. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
int64_t body; /**< Integer value. */
} LV2_Atom_Int64;
/** An atom:Float. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
float body; /**< Floating point value. */
} LV2_Atom_Float;
/** An atom:Double. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
double body; /**< Floating point 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 body; /**< URID. */
} LV2_Atom_URID;
/** An atom:String. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
/* Contents (a null-terminated UTF-8 string) follow here. */
} LV2_Atom_String;
/** The body of an atom:Literal. */
typedef struct {
uint32_t datatype; /**< Datatype URID. */
uint32_t lang; /**< Language URID. */
/* Contents (a null-terminated UTF-8 string) follow here. */
} LV2_Atom_Literal_Body;
/** An atom:Literal. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
LV2_Atom_Literal_Body body; /**< Body. */
} LV2_Atom_Literal;
/** An atom:Tuple. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
/* Contents (a series of complete atoms) follow here. */
} LV2_Atom_Tuple;
/** The body of an atom:Vector. */
typedef struct {
uint32_t elem_count; /**< The number of elements in the vector */
uint32_t elem_type; /**< The type of each element in the vector */
/* Contents (a series of packed atom bodies) follow here. */
} LV2_Atom_Vector_Body;
/** An atom:Vector. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
LV2_Atom_Vector_Body body; /**< Body. */
} LV2_Atom_Vector;
/** The body of an atom:Property (e.g. in an atom:Object). */
typedef struct {
uint32_t key; /**< Key (predicate) (mapped URI). */
uint32_t context; /**< Context URID (may be, and generally is, 0). */
LV2_Atom value; /**< Value atom header. */
/* Value atom body follows here. */
} LV2_Atom_Property_Body;
/** An atom:Property. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
LV2_Atom_Property_Body body; /**< Body. */
} LV2_Atom_Property;
/** The body of an atom:Object. May be cast to LV2_Atom. */
typedef struct {
uint32_t id; /**< URID (atom:Resource) or blank ID (atom:Blank). */
uint32_t otype; /**< Type URID (same as rdf:type, for fast dispatch). */
/* Contents (a series of property bodies) follow here. */
} LV2_Atom_Object_Body;
/** An atom:Object. May be cast to LV2_Atom. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
LV2_Atom_Object_Body body; /**< Body. */
} LV2_Atom_Object;
/** The header of an atom:Event. Note this type is NOT an LV2_Atom. */
typedef struct {
/** Time stamp. Which type is valid is determined by context. */
union {
int64_t frames; /**< 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;
/**
The body of an atom:Sequence (a sequence of events).
The unit field is either a URID that described an appropriate time stamp
type, or may be 0 where a default stamp type is known. For
LV2_Descriptor::run(), the default stamp type is atom:AudioFrames, i.e.
LV2_Atom_Audio_Time.
The contents of a sequence is a series of LV2_Atom_Event, each aligned
to 64-bits, e.g.:
<pre>
| Event 1 (size 6) | Event 2
| | | | | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|FRAMES |SUBFRMS|TYPE |SIZE |DATADATADATAPAD|FRAMES |SUBFRMS|...
</pre>
*/
typedef struct {
uint32_t unit; /**< URID of unit of event time stamps. */
uint32_t pad; /**< Currently unused. */
/* Contents (a series of events) follow here. */
} LV2_Atom_Sequence_Body;
/** An atom:Sequence. */
typedef struct {
LV2_Atom atom; /**< Atom header. */
LV2_Atom_Literal_Body body; /**< Body. */
} LV2_Atom_Sequence;
/**
The contents of an atom:AtomPort buffer.
This contains a pointer to an Atom, which is the data to be
processed/written, as well as additional metadata. This struct may be
augmented in the future to add more metadata fields as they become
necessary. The initial version of this struct contains data, size, and
capacity. Implementations MUST check that any other fields they wish to use
are actually present by comparing the size with the offset of that field,
e.g.:
@code
if (offsetof(LV2_Atom_Port_Buffer, field) < buf->size) {
do_stuff_with(buf->field);
}
@endcode
*/
typedef struct {
LV2_Atom* data; /** Pointer to data. */
uint32_t size; /** Total size of this struct. */
uint32_t capacity; /** Available space for data body. */
} LV2_Atom_Port_Buffer;
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV2_ATOM_H */

View file

@ -0,0 +1,544 @@
/*
Copyright 2008-2012 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
@file forge.h An API for constructing LV2 atoms.
This file provides 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.
*/
#ifndef LV2_ATOM_FORGE_H
#define LV2_ATOM_FORGE_H
#include <assert.h>
#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
#include "lv2/lv2plug.in/ns/ext/atom/util.h"
#include "lv2/lv2plug.in/ns/ext/urid/urid.h"
#ifdef __cplusplus
extern "C" {
#else
# include <stdbool.h>
#endif
/** Handle for LV2_Atom_Forge_Sink. */
typedef void* LV2_Atom_Forge_Sink_Handle;
/** Sink function for writing output. See lv2_atom_forge_set_sink(). */
typedef void* (*LV2_Atom_Forge_Sink)(LV2_Atom_Forge_Sink_Handle handle,
const void* buf,
uint32_t size);
/** 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* atom;
} 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_Sink_Handle handle;
LV2_Atom_Forge_Frame* stack;
LV2_URID Blank;
LV2_URID Bool;
LV2_URID Double;
LV2_URID Float;
LV2_URID Int32;
LV2_URID Int64;
LV2_URID Literal;
LV2_URID Path;
LV2_URID Property;
LV2_URID Resource;
LV2_URID Sequence;
LV2_URID String;
LV2_URID Tuple;
LV2_URID URI;
LV2_URID URID;
LV2_URID Vector;
} LV2_Atom_Forge;
/**
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*
lv2_atom_forge_push(LV2_Atom_Forge* forge,
LV2_Atom_Forge_Frame* frame,
LV2_Atom* atom)
{
frame->parent = forge->stack;
frame->atom = atom;
forge->stack = frame;
return atom;
}
/** 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)
{
assert(frame == forge->stack);
forge->stack = frame->parent;
}
/** Set the output buffer where @p forge will write atoms. */
static inline void
lv2_atom_forge_set_buffer(LV2_Atom_Forge* forge, uint8_t* buf, size_t size)
{
forge->buf = buf;
forge->size = size;
forge->offset = 0;
forge->sink = NULL;
forge->handle = NULL;
}
/**
Set the sink function where @p forge will write output.
The return value of forge functions is a pointer to the written data, which
is used for updating parent sizes. To enable this, the sink function must
return a valid pointer to a contiguous LV2_Atom header. 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. When using a ringbuffer,
the returned pointers may not point to a complete atom (including body).
The user must take care to only use these return values in a way compatible
with the sink used.
*/
static inline void
lv2_atom_forge_set_sink(LV2_Atom_Forge* forge,
LV2_Atom_Forge_Sink sink,
LV2_Atom_Forge_Sink_Handle handle)
{
forge->buf = NULL;
forge->size = forge->offset = 0;
forge->sink = sink;
forge->handle = handle;
}
/**
Initialise @p forge.
URIs will be mapped using @p map and stored, a reference to @p 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->stack = NULL;
forge->Blank = map->map(map->handle, LV2_ATOM_URI "#Blank");
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->Path = map->map(map->handle, LV2_ATOM_URI "#Path");
forge->Property = map->map(map->handle, LV2_ATOM_URI "#Property");
forge->Resource = map->map(map->handle, LV2_ATOM_URI "#Resource");
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->URI = map->map(map->handle, LV2_ATOM_URI "#URI");
forge->URID = map->map(map->handle, LV2_ATOM_URI "#URID");
forge->Vector = map->map(map->handle, LV2_ATOM_URI "#Vector");
}
/**
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 void*
lv2_atom_forge_raw(LV2_Atom_Forge* forge, const void* data, uint32_t size)
{
uint8_t* out = NULL;
if (forge->sink) {
out = forge->sink(forge->handle, data, size);
} else {
out = forge->buf + forge->offset;
if (forge->offset + size > forge->size) {
return NULL;
}
forge->offset += size;
memcpy(out, data, size);
}
if (out) {
for (LV2_Atom_Forge_Frame* f = forge->stack; f; f = f->parent) {
f->atom->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 void*
lv2_atom_forge_write(LV2_Atom_Forge* forge, const void* data, uint32_t size)
{
void* out = lv2_atom_forge_raw(forge, data, size);
if (out) {
lv2_atom_forge_pad(forge, size);
}
return out;
}
/** Write an atom:Atom header. */
static inline LV2_Atom*
lv2_atom_forge_atom(LV2_Atom_Forge* forge, uint32_t size, uint32_t type)
{
const LV2_Atom a = { size, type };
return (LV2_Atom*)lv2_atom_forge_raw(forge, &a, sizeof(a));
}
/** Write an atom:Int32. */
static inline LV2_Atom_Int32*
lv2_atom_forge_int32(LV2_Atom_Forge* forge, int32_t val)
{
const LV2_Atom_Int32 a = { { sizeof(val), forge->Int32 }, val };
return (LV2_Atom_Int32*)lv2_atom_forge_write(forge, &a, sizeof(a));
}
/** Write an atom:Int64. */
static inline LV2_Atom_Int64*
lv2_atom_forge_int64(LV2_Atom_Forge* forge, int64_t val)
{
const LV2_Atom_Int64 a = { { sizeof(val), forge->Int64 }, val };
return (LV2_Atom_Int64*)lv2_atom_forge_write(forge, &a, sizeof(a));
}
/** Write an atom:Float. */
static inline LV2_Atom_Float*
lv2_atom_forge_float(LV2_Atom_Forge* forge, float val)
{
const LV2_Atom_Float a = { { sizeof(val), forge->Float }, val };
return (LV2_Atom_Float*)lv2_atom_forge_write(forge, &a, sizeof(a));
}
/** Write an atom:Double. */
static inline LV2_Atom_Double*
lv2_atom_forge_double(LV2_Atom_Forge* forge, double val)
{
const LV2_Atom_Double a = { { sizeof(val), forge->Double }, val };
return (LV2_Atom_Double*)lv2_atom_forge_write(
forge, &a, sizeof(a));
}
/** Write an atom:Bool. */
static inline LV2_Atom_Bool*
lv2_atom_forge_bool(LV2_Atom_Forge* forge, bool val)
{
const LV2_Atom_Bool a = { { sizeof(val), forge->Bool }, val };
return (LV2_Atom_Bool*)lv2_atom_forge_write(forge, &a, sizeof(a));
}
/** Write an atom:URID. */
static inline LV2_Atom_URID*
lv2_atom_forge_urid(LV2_Atom_Forge* forge, LV2_URID id)
{
const LV2_Atom_URID a = { { sizeof(id), forge->URID }, id };
return (LV2_Atom_URID*)lv2_atom_forge_write(forge, &a, sizeof(a));
}
/** Write a string body. Used internally. */
static inline uint8_t*
lv2_atom_forge_string_body(LV2_Atom_Forge* forge,
const uint8_t* str,
uint32_t len)
{
uint8_t* out = NULL;
if ( (out = lv2_atom_forge_raw(forge, str, len))
&& (out = lv2_atom_forge_raw(forge, "", 1))) {
lv2_atom_forge_pad(forge, len + 1);
}
return out;
}
/** Write an atom compatible with atom:String. Used internally. */
static inline LV2_Atom_String*
lv2_atom_forge_typed_string(LV2_Atom_Forge* forge,
uint32_t type,
const uint8_t* str,
uint32_t len)
{
const LV2_Atom_String a = { { len + 1, type } };
LV2_Atom_String* out = (LV2_Atom_String*)
lv2_atom_forge_raw(forge, &a, sizeof(a));
if (out) {
if (!lv2_atom_forge_string_body(forge, str, len)) {
out->atom.size = out->atom.type = 0;
out = NULL;
}
}
return out;
}
/** Write an atom:String. Note that @p str need not be NULL terminated. */
static inline LV2_Atom_String*
lv2_atom_forge_string(LV2_Atom_Forge* forge, const uint8_t* str, uint32_t len)
{
return lv2_atom_forge_typed_string(forge, forge->String, str, len);
}
/**
Write an atom:URI. Note that @p 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_String*
lv2_atom_forge_uri(LV2_Atom_Forge* forge, const uint8_t* uri, uint32_t len)
{
return lv2_atom_forge_typed_string(forge, forge->URI, uri, len);
}
/** Write an atom:Path. Note that @p path need not be NULL terminated. */
static inline LV2_Atom_String*
lv2_atom_forge_path(LV2_Atom_Forge* forge, const uint8_t* path, uint32_t len)
{
return lv2_atom_forge_typed_string(forge, forge->Path, path, len);
}
/** Write an atom:Literal. */
static inline LV2_Atom_Literal*
lv2_atom_forge_literal(LV2_Atom_Forge* forge,
const uint8_t* str,
uint32_t len,
uint32_t datatype,
uint32_t lang)
{
const LV2_Atom_Literal a = {
{ sizeof(LV2_Atom_Literal) - sizeof(LV2_Atom) + len + 1,
forge->Literal },
{ datatype,
lang }
};
LV2_Atom_Literal* out = (LV2_Atom_Literal*)
lv2_atom_forge_raw(forge, &a, sizeof(a));
if (out) {
if (!lv2_atom_forge_string_body(forge, str, len)) {
out->atom.size = out->atom.type = 0;
out = NULL;
}
}
return out;
}
/** Write an atom:Vector header, but not the vector body. */
static inline LV2_Atom_Vector*
lv2_atom_forge_vector_head(LV2_Atom_Forge* forge,
uint32_t elem_count,
uint32_t elem_type,
uint32_t elem_size)
{
const uint32_t size = sizeof(LV2_Atom_Vector) + (elem_size * elem_count);
const LV2_Atom_Vector a = {
{ size - sizeof(LV2_Atom), forge->Vector },
{ elem_count, elem_type }
};
return (LV2_Atom_Vector*)lv2_atom_forge_write(forge, &a, sizeof(a));
}
/** Write a complete atom:Vector. */
static inline LV2_Atom_Vector*
lv2_atom_forge_vector(LV2_Atom_Forge* forge,
uint32_t elem_count,
uint32_t elem_type,
uint32_t elem_size,
void* elems)
{
LV2_Atom_Vector* out = lv2_atom_forge_vector_head(
forge, elem_count, elem_type, elem_size);
if (out) {
lv2_atom_forge_write(forge, elems, elem_size * elem_count);
}
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_Tuple*
lv2_atom_forge_tuple(LV2_Atom_Forge* forge, LV2_Atom_Forge_Frame* frame)
{
const LV2_Atom_Tuple a = { { 0, forge->Tuple } };
LV2_Atom* atom = lv2_atom_forge_write(forge, &a, sizeof(a));
return (LV2_Atom_Tuple*)lv2_atom_forge_push(forge, frame, atom);
}
/**
Write the header of an atom:Resource.
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");
// Write object header
LV2_Atom_Forge_Frame frame;
LV2_Atom* obj = (LV2_Atom*)lv2_atom_forge_resource(forge, &frame, 1, eg_Cat);
// Write property: eg:name = "Hobbes"
lv2_atom_forge_property_head(forge, eg_name, 0);
lv2_atom_forge_string(forge, "Hobbes", strlen("Hobbes"));
// Finish object
lv2_atom_forge_pop(forge, &frame);
@endcode
*/
static inline LV2_Atom_Object*
lv2_atom_forge_resource(LV2_Atom_Forge* forge,
LV2_Atom_Forge_Frame* frame,
LV2_URID id,
LV2_URID otype)
{
const LV2_Atom_Object a = {
{ sizeof(LV2_Atom_Object) - sizeof(LV2_Atom), forge->Resource },
{ id, otype }
};
LV2_Atom* atom = (LV2_Atom*)lv2_atom_forge_write(forge, &a, sizeof(a));
return (LV2_Atom_Object*)lv2_atom_forge_push(forge, frame, atom);
}
/**
The same as lv2_atom_forge_resource(), but for object:Blank.
*/
static inline LV2_Atom_Object*
lv2_atom_forge_blank(LV2_Atom_Forge* forge,
LV2_Atom_Forge_Frame* frame,
uint32_t id,
LV2_URID otype)
{
const LV2_Atom_Object a = {
{ sizeof(LV2_Atom_Object) - sizeof(LV2_Atom), forge->Blank },
{ id, otype }
};
LV2_Atom* atom = (LV2_Atom*)lv2_atom_forge_write(forge, &a, sizeof(a));
return (LV2_Atom_Object*)lv2_atom_forge_push(forge, frame, atom);
}
/**
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_URID key,
LV2_URID context)
{
const LV2_Atom_Property_Body a = { key, context, { 0, 0 } };
return (LV2_Atom_Property_Body*)lv2_atom_forge_write(
forge, &a, 2 * sizeof(uint32_t));
}
/**
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_Forge_Frame* frame,
uint32_t unit)
{
const LV2_Atom_Sequence a = {
{ sizeof(LV2_Atom_Sequence) - sizeof(LV2_Atom), forge->Sequence },
{ unit, 0 }
};
LV2_Atom* atom = (LV2_Atom*)lv2_atom_forge_write(forge, &a, sizeof(a));
return (LV2_Atom_Sequence*)lv2_atom_forge_push(forge, frame, atom);
}
/**
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 @p parent parameter. Note the returned LV2_Event is NOT an Atom.
*/
static inline int64_t*
lv2_atom_forge_frame_time(LV2_Atom_Forge* forge, int64_t frames)
{
return (int64_t*)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, passing
the same @p parent parameter. Note the returned LV2_Event is NOT an Atom.
*/
static inline double*
lv2_atom_forge_beat_time(LV2_Atom_Forge* forge, double beats)
{
return (double*)lv2_atom_forge_write(forge, &beats, sizeof(beats));
}
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV2_ATOM_FORGE_H */

View file

@ -0,0 +1,424 @@
/*
Copyright 2008-2012 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/**
@file util.h Helper functions for the LV2 Atom extension.
Note these functions are all static inline, do not take their address.
This header is non-normative, it is provided for convenience.
*/
#ifndef LV2_ATOM_UTIL_H
#define LV2_ATOM_UTIL_H
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
#ifdef __cplusplus
extern "C" {
#else
# include <stdbool.h>
#endif
/** Pad a size to 64 bits. */
static inline uint32_t
lv2_atom_pad_size(uint32_t size)
{
return (size + 7) & (~7);
}
/** Return the total size of @p atom, including the header. */
static inline uint32_t
lv2_atom_total_size(const LV2_Atom* atom)
{
return sizeof(LV2_Atom) + atom->size;
}
/** Return true iff @p atom is null. */
static inline bool
lv2_atom_is_null(const LV2_Atom* atom)
{
return !atom || (atom->type == 0 && atom->size == 0);
}
/** Return true iff @p a is equal to @p b. */
static inline bool
lv2_atom_equals(const LV2_Atom* a, const LV2_Atom* b)
{
return (a == b) || ((a->type == b->type) &&
(a->size == b->size) &&
!memcmp(a + 1, b + 1, 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 a Sequence body. */
static inline LV2_Atom_Sequence_Iter
lv2_sequence_body_begin(const LV2_Atom_Sequence_Body* body)
{
return (LV2_Atom_Sequence_Iter)(body + 1);
}
/** Get an iterator pointing to the first element in a Sequence. */
static inline LV2_Atom_Sequence_Iter
lv2_sequence_begin(const LV2_Atom_Sequence* seq)
{
return (LV2_Atom_Sequence_Iter)(seq + 1);
}
/** Return true iff @p i has reached the end of @p body. */
static inline bool
lv2_sequence_body_is_end(const LV2_Atom_Sequence_Body* body,
uint32_t size,
LV2_Atom_Sequence_Iter i)
{
return (uint8_t*)i >= ((uint8_t*)body + size);
}
/** Return true iff @p i has reached the end of @p seq. */
static inline bool
lv2_sequence_is_end(const LV2_Atom_Sequence* seq, LV2_Atom_Sequence_Iter i)
{
return (uint8_t*)i >= ((uint8_t*)seq + sizeof(LV2_Atom) + seq->atom.size);
}
/** Return an iterator to the element following @p i. */
static inline LV2_Atom_Sequence_Iter
lv2_sequence_iter_next(const LV2_Atom_Sequence_Iter i)
{
return (LV2_Atom_Sequence_Iter)((uint8_t*)i
+ sizeof(LV2_Atom_Event)
+ lv2_atom_pad_size(i->body.size));
}
/** Return the element pointed to by @p i. */
static inline LV2_Atom_Event*
lv2_sequence_iter_get(LV2_Atom_Sequence_Iter i)
{
return (LV2_Atom_Event*)i;
}
/**
A macro for iterating over all events in a Sequence.
@param sequence The sequence to iterate over
@param iter The name of the iterator
This macro is used similarly to a for loop (which it expands to), e.g.:
@code
LV2_SEQUENCE_FOREACH(sequence, i) {
LV2_Atom_Event* ev = lv2_sequence_iter_get(i);
// Do something with ev here...
}
@endcode
*/
#define LV2_SEQUENCE_FOREACH(sequence, iter) \
for (LV2_Atom_Sequence_Iter (iter) = lv2_sequence_begin(sequence); \
!lv2_sequence_is_end(sequence, (iter)); \
(iter) = lv2_sequence_iter_next(iter))
/** A version of LV2_SEQUENCE_FOREACH for when only the body is available. */
#define LV2_SEQUENCE_BODY_FOREACH(body, size, iter) \
for (LV2_Atom_Sequence_Iter (iter) = lv2_sequence_body_begin(body); \
!lv2_sequence_body_is_end(body, size, (iter)); \
(iter) = lv2_sequence_iter_next(iter))
/**
@}
@name Tuple Iterator
@{
*/
/** An iterator over the elements of an LV2_Atom_Tuple. */
typedef LV2_Atom* LV2_Atom_Tuple_Iter;
/** Get an iterator pointing to the first element in @p tup. */
static inline LV2_Atom_Tuple_Iter
lv2_tuple_begin(const LV2_Atom_Tuple* tup)
{
return (LV2_Atom_Tuple_Iter)(LV2_ATOM_BODY(tup));
}
/** Return true iff @p i has reached the end of @p body. */
static inline bool
lv2_atom_tuple_body_is_end(const void* body,
uint32_t size,
LV2_Atom_Tuple_Iter i)
{
return (uint8_t*)i >= ((uint8_t*)body + size);
}
/** Return true iff @p i has reached the end of @p tup. */
static inline bool
lv2_tuple_is_end(const LV2_Atom_Tuple* tup, LV2_Atom_Tuple_Iter i)
{
return lv2_atom_tuple_body_is_end(LV2_ATOM_BODY(tup), tup->atom.size, i);
}
/** 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* elem = lv2_tuple_iter_get(i);
// Do something with elem 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))
/** A version of LV2_TUPLE_FOREACH for when only the body is available. */
#define LV2_TUPLE_BODY_FOREACH(body, size, iter) \
for (LV2_Atom_Tuple_Iter (iter) = (LV2_Atom_Tuple_Iter)body; \
!lv2_atom_tuple_body_is_end(body, size, (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;
static inline LV2_Atom_Object_Iter
lv2_object_body_begin(const LV2_Atom_Object_Body* body)
{
return (LV2_Atom_Object_Iter)(body + 1);
}
/** 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)(obj + 1);
}
static inline bool
lv2_atom_object_body_is_end(const LV2_Atom_Object_Body* body,
uint32_t size,
LV2_Atom_Object_Iter i)
{
return (uint8_t*)i >= ((uint8_t*)body + size);
}
/** 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 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_Body* 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))
/** A version of LV2_OBJECT_FOREACH for when only the body is available. */
#define LV2_OBJECT_BODY_FOREACH(body, size, iter) \
for (LV2_Atom_Object_Iter (iter) = lv2_object_body_begin(body); \
!lv2_atom_object_body_is_end(body, size, (iter)); \
(iter) = lv2_object_iter_next(iter))
/**
@}
@name Object Query
@{
*/
/** A single entry in an Object query. */
typedef struct {
uint32_t key; /**< Key to query (input set by user) */
const LV2_Atom** value; /**< Found value (output set by query function) */
} LV2_Atom_Object_Query;
static const LV2_Atom_Object_Query LV2_OBJECT_QUERY_END = { 0, NULL };
/**
Get an object's values for various keys.
The value pointer of each item in @p query will be set to the location of
the corresponding value in @p object. Every value pointer in @p query MUST
be initialised to NULL. This function reads @p object in a single linear
sweep. By allocating @p query on the stack, objects can be "queried"
quickly without allocating any memory. This function is realtime safe.
This function can only do "flat" queries, it is not smart enough to match
variables in nested objects.
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_query(obj, q);
// name and age are now set to the appropriate values in obj, or NULL.
@endcode
*/
static inline int
lv2_object_query(const LV2_Atom_Object* object, LV2_Atom_Object_Query* query)
{
int matches = 0;
int n_queries = 0;
/* Count number of query keys so we can short-circuit when done */
for (LV2_Atom_Object_Query* q = query; q->key; ++q) {
++n_queries;
}
LV2_OBJECT_FOREACH(object, o) {
const LV2_Atom_Property_Body* prop = lv2_object_iter_get(o);
for (LV2_Atom_Object_Query* q = query; q->key; ++q) {
if (q->key == prop->key && !*q->value) {
*q->value = &prop->value;
if (++matches == n_queries) {
return matches;
}
break;
}
}
}
return matches;
}
/**
Variable argument version of lv2_object_get().
This is nicer-looking in code, but a bit more error-prone since it is not
type safe and the argument list must be terminated.
The arguments should be a series of uint32_t key and const LV2_Atom** value
pairs, terminated by a zero key. The value pointers MUST be initialized to
NULL. For example:
@code
const LV2_Atom* name = NULL;
const LV2_Atom* age = NULL;
lv2_object_get(obj,
uris.name_key, &name,
uris.age_key, &age,
0);
@endcode
*/
static inline int
lv2_object_get(const LV2_Atom_Object* object, ...)
{
int matches = 0;
int n_queries = 0;
/* Count number of keys so we can short-circuit when done */
va_list args;
va_start(args, object);
for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) {
if (!va_arg(args, const LV2_Atom**)) {
return -1;
}
}
va_end(args);
LV2_OBJECT_FOREACH(object, o) {
const LV2_Atom_Property_Body* prop = lv2_object_iter_get(o);
va_start(args, object);
for (int i = 0; i < n_queries; ++i) {
uint32_t qkey = va_arg(args, uint32_t);
const LV2_Atom** qval = va_arg(args, const LV2_Atom**);
if (qkey == prop->key && !*qval) {
*qval = &prop->value;
if (++matches == n_queries) {
return matches;
}
break;
}
}
va_end(args);
}
return matches;
}
/**
@}
*/
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LV2_ATOM_UTIL_H */

264
libs/ardour/lv2_evbuf.c Normal file
View file

@ -0,0 +1,264 @@
/*
Copyright 2008-2012 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
#include "lv2/lv2plug.in/ns/ext/event/event.h"
#include "lv2_evbuf.h"
struct LV2_Evbuf_Impl {
LV2_Evbuf_Type type;
uint32_t capacity;
union {
LV2_Event_Buffer event;
LV2_Atom_Port_Buffer atom;
} buf;
};
static inline uint32_t
lv2_evbuf_pad_size(uint32_t size)
{
return (size + 7) & (~7);
}
LV2_Evbuf*
lv2_evbuf_new(uint32_t capacity, LV2_Evbuf_Type type, uint32_t atom_type)
{
// FIXME: memory must be 64-bit aligned
LV2_Evbuf* evbuf = (LV2_Evbuf*)malloc(
sizeof(LV2_Evbuf) + sizeof(LV2_Atom_Sequence) + capacity);
evbuf->capacity = capacity;
lv2_evbuf_set_type(evbuf, type, atom_type);
lv2_evbuf_reset(evbuf);
return evbuf;
}
void
lv2_evbuf_free(LV2_Evbuf* evbuf)
{
free(evbuf);
}
void
lv2_evbuf_set_type(LV2_Evbuf* evbuf, LV2_Evbuf_Type type, uint32_t atom_type)
{
evbuf->type = type;
switch (type) {
case LV2_EVBUF_EVENT:
evbuf->buf.event.data = (uint8_t*)(evbuf + 1);
evbuf->buf.event.capacity = evbuf->capacity;
break;
case LV2_EVBUF_ATOM:
evbuf->buf.atom.data = (LV2_Atom*)(evbuf + 1);
evbuf->buf.atom.size = sizeof(LV2_Atom_Port_Buffer);
evbuf->buf.atom.capacity = evbuf->capacity;
evbuf->buf.atom.data->type = atom_type;
evbuf->buf.atom.data->size = 0;
break;
}
lv2_evbuf_reset(evbuf);
}
void
lv2_evbuf_reset(LV2_Evbuf* evbuf)
{
switch (evbuf->type) {
case LV2_EVBUF_EVENT:
evbuf->buf.event.header_size = sizeof(LV2_Event_Buffer);
evbuf->buf.event.stamp_type = LV2_EVENT_AUDIO_STAMP;
evbuf->buf.event.event_count = 0;
evbuf->buf.event.size = 0;
break;
case LV2_EVBUF_ATOM:
evbuf->buf.atom.data->size = 0;
}
}
uint32_t
lv2_evbuf_get_size(LV2_Evbuf* evbuf)
{
switch (evbuf->type) {
case LV2_EVBUF_EVENT:
return evbuf->buf.event.size;
case LV2_EVBUF_ATOM:
return evbuf->buf.atom.data->size;
}
return 0;
}
void*
lv2_evbuf_get_buffer(LV2_Evbuf* evbuf)
{
switch (evbuf->type) {
case LV2_EVBUF_EVENT:
return &evbuf->buf.event;
case LV2_EVBUF_ATOM:
return &evbuf->buf.atom;
}
return NULL;
}
LV2_Evbuf_Iterator
lv2_evbuf_begin(LV2_Evbuf* evbuf)
{
LV2_Evbuf_Iterator iter = { evbuf, 0 };
return iter;
}
LV2_Evbuf_Iterator
lv2_evbuf_end(LV2_Evbuf* evbuf)
{
const size_t size = lv2_evbuf_get_size(evbuf);
const LV2_Evbuf_Iterator iter = { evbuf, lv2_evbuf_pad_size(size) };
return iter;
}
bool
lv2_evbuf_is_valid(LV2_Evbuf_Iterator iter)
{
return iter.offset < lv2_evbuf_get_size(iter.evbuf);
}
LV2_Evbuf_Iterator
lv2_evbuf_next(LV2_Evbuf_Iterator iter)
{
if (!lv2_evbuf_is_valid(iter)) {
return iter;
}
LV2_Evbuf* evbuf = iter.evbuf;
uint32_t offset = iter.offset;
uint32_t size;
switch (evbuf->type) {
case LV2_EVBUF_EVENT:
size = ((LV2_Event*)(evbuf->buf.event.data + offset))->size;
offset += lv2_evbuf_pad_size(sizeof(LV2_Event) + size);
break;
case LV2_EVBUF_ATOM:
size = ((LV2_Atom_Event*)
((char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, evbuf->buf.atom.data)
+ offset))->body.size;
offset += lv2_evbuf_pad_size(sizeof(LV2_Atom_Event) + size);
break;
}
LV2_Evbuf_Iterator next = { evbuf, offset };
return next;
}
bool
lv2_evbuf_get(LV2_Evbuf_Iterator iter,
uint32_t* frames,
uint32_t* subframes,
uint32_t* type,
uint32_t* size,
uint8_t** data)
{
*frames = *subframes = *type = *size = 0;
*data = NULL;
if (!lv2_evbuf_is_valid(iter)) {
return false;
}
LV2_Event_Buffer* ebuf;
LV2_Event* ev;
LV2_Atom_Port_Buffer* abuf;
LV2_Atom_Event* aev;
switch (iter.evbuf->type) {
case LV2_EVBUF_EVENT:
ebuf = &iter.evbuf->buf.event;
ev = (LV2_Event*)ebuf->data + iter.offset;
*frames = ev->frames;
*subframes = ev->subframes;
*type = ev->type;
*size = ev->size;
*data = (uint8_t*)ev + sizeof(LV2_Event);
break;
case LV2_EVBUF_ATOM:
abuf = &iter.evbuf->buf.atom;
aev = (LV2_Atom_Event*)(
(char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, abuf->data)
+ iter.offset);
*frames = aev->time.frames;
*subframes = 0;
*type = aev->body.type;
*size = aev->body.size;
*data = LV2_ATOM_BODY(&aev->body);
break;
}
return true;
}
bool
lv2_evbuf_write(LV2_Evbuf_Iterator* iter,
uint32_t frames,
uint32_t subframes,
uint32_t type,
uint32_t size,
const uint8_t* data)
{
LV2_Event_Buffer* ebuf;
LV2_Event* ev;
LV2_Atom_Port_Buffer* abuf;
LV2_Atom_Event* aev;
switch (iter->evbuf->type) {
case LV2_EVBUF_EVENT:
ebuf = &iter->evbuf->buf.event;
if (ebuf->capacity - ebuf->size < sizeof(LV2_Event) + size) {
return false;
}
ev = (LV2_Event*)(ebuf->data + iter->offset);
ev->frames = frames;
ev->subframes = subframes;
ev->type = type;
ev->size = size;
memcpy((uint8_t*)ev + sizeof(LV2_Event), data, size);
size = lv2_evbuf_pad_size(sizeof(LV2_Event) + size);
ebuf->size += size;
ebuf->event_count += 1;
iter->offset += size;
break;
case LV2_EVBUF_ATOM:
abuf = &iter->evbuf->buf.atom;
if (abuf->capacity - abuf->data->size < sizeof(LV2_Atom_Event) + size) {
return false;
}
aev = (LV2_Atom_Event*)(
(char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, abuf->data)
+ iter->offset);
aev->time.frames = frames;
aev->body.type = type;
aev->body.size = size;
memcpy(LV2_ATOM_BODY(&aev->body), data, size);
size = lv2_evbuf_pad_size(sizeof(LV2_Atom_Event) + size);
abuf->data->size += size;
iter->offset += size;
break;
}
return true;
}

161
libs/ardour/lv2_evbuf.h Normal file
View file

@ -0,0 +1,161 @@
/*
Copyright 2008-2012 David Robillard <http://drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LV2_EVBUF_H
#define LV2_EVBUF_H
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
Format of actual buffer.
*/
typedef enum {
/**
An (old) ev:EventBuffer (LV2_Event_Buffer).
*/
LV2_EVBUF_EVENT,
/**
A (new) atom:Sequence (LV2_Atom_Sequence).
*/
LV2_EVBUF_ATOM
} LV2_Evbuf_Type;
/**
An abstract/opaque LV2 event buffer.
*/
typedef struct LV2_Evbuf_Impl LV2_Evbuf;
/**
An iterator over an LV2_Evbuf.
*/
typedef struct {
LV2_Evbuf* evbuf;
uint32_t offset;
} LV2_Evbuf_Iterator;
/**
Allocate a new, empty event buffer.
The URID for atom:Sequence must be passed for atom_Sequence if type is
LV2_EVBUF_ATOM.
*/
LV2_Evbuf*
lv2_evbuf_new(uint32_t capacity, LV2_Evbuf_Type type, uint32_t atom_type);
/**
Free an event buffer allocated with lv2_evbuf_new.
*/
void
lv2_evbuf_free(LV2_Evbuf* evbuf);
/**
Change the type of an existing event buffer. This will clear and reset the
buffer, it is not possible to change the type and preserve the buffer
contents since the formats differ. The URID for atom:Sequence must be
passed for atom_Sequence if type is LV2_EVBUF_ATOM.
*/
void
lv2_evbuf_set_type(LV2_Evbuf* evbuf, LV2_Evbuf_Type type, uint32_t atom_type);
/**
Clear and initialize an existing event buffer.
The contents of buf are ignored entirely and overwritten, except capacity
which is unmodified.
*/
void
lv2_evbuf_reset(LV2_Evbuf* evbuf);
/**
Return the total padded size of the events stored in the buffer.
*/
uint32_t
lv2_evbuf_get_size(LV2_Evbuf* evbuf);
/**
Return the actual buffer implementation.
The format of the buffer returned depends on the buffer type.
*/
void*
lv2_evbuf_get_buffer(LV2_Evbuf* evbuf);
/**
Return an iterator to the start of @p buf.
*/
LV2_Evbuf_Iterator
lv2_evbuf_begin(LV2_Evbuf* evbuf);
/**
Return an iterator to the end of @a buf.
*/
LV2_Evbuf_Iterator
lv2_evbuf_end(LV2_Evbuf* evbuf);
/**
Check if @p iter is valid.
@return True if @p iter is valid, otherwise false (past end of buffer)
*/
bool
lv2_evbuf_is_valid(LV2_Evbuf_Iterator iter);
/**
Advance @p iter forward one event.
@p iter must be valid.
@return True if @p iter is valid, otherwise false (reached end of buffer)
*/
LV2_Evbuf_Iterator
lv2_evbuf_next(LV2_Evbuf_Iterator iter);
/**
Dereference an event iterator (i.e. get the event currently pointed to).
@p iter must be valid.
@p type Set to the type of the event.
@p size Set to the size of the event.
@p data Set to the contents of the event.
@return True on success.
*/
bool
lv2_evbuf_get(LV2_Evbuf_Iterator iter,
uint32_t* frames,
uint32_t* subframes,
uint32_t* type,
uint32_t* size,
uint8_t** data);
/**
Write an event at @p iter.
The event (if any) pointed to by @p iter will be overwritten, and @p iter
incremented to point to the following event (i.e. several calls to this
function can be done in sequence without twiddling iter in-between).
@return True if event was written, otherwise false (buffer is full).
*/
bool
lv2_evbuf_write(LV2_Evbuf_Iterator* iter,
uint32_t frames,
uint32_t subframes,
uint32_t type,
uint32_t size,
const uint8_t* data);
#ifdef __cplusplus
}
#endif
#endif /* LV2_EVBUF_H */

View file

@ -1,196 +0,0 @@
/*
Copyright (C) 2009 Paul Davis
Author: David Robillard
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdint.h>
#include <iostream>
#include "lv2/lv2plug.in/ns/ext/event/event.h"
#include "lv2/lv2plug.in/ns/ext/event/event-helpers.h"
#include "ardour/lv2_event_buffer.h"
using namespace std;
namespace ARDOUR {
/** Allocate a new event buffer.
* \a capacity is in bytes (not number of events).
*/
LV2EventBuffer::LV2EventBuffer(size_t capacity)
: _latest_frames(0)
, _latest_subframes(0)
{
if (capacity > UINT32_MAX) {
cerr << "Event buffer size " << capacity << " too large, aborting." << endl;
throw std::bad_alloc();
}
if (capacity == 0) {
cerr << "ERROR: LV2 event buffer of size 0 created." << endl;
capacity = 1024;
}
#ifdef NO_POSIX_MEMALIGN
_data = (LV2_Event_Buffer*)malloc(sizeof(LV2_Event_Buffer) + capacity);
int ret = (_data != NULL) ? 0 : -1;
#else
int ret = posix_memalign((void**)&_data, 16, sizeof(LV2_Event_Buffer) + capacity);
#endif
if (ret != 0) {
cerr << "Failed to allocate event buffer. Aborting." << endl;
exit(EXIT_FAILURE);
}
_data->event_count = 0;
_data->capacity = (uint32_t)capacity;
_data->size = 0;
_data->data = reinterpret_cast<uint8_t*>(_data + 1);
reset();
}
LV2EventBuffer::~LV2EventBuffer()
{
free(_data);
}
/** Increment the read position by one event.
*
* \return true if increment was successful, or false if end of buffer reached.
*/
bool
LV2EventBuffer::increment() const
{
if (lv2_event_is_valid(&_iter)) {
lv2_event_increment(&_iter);
return true;
} else {
return false;
}
}
/** \return true iff the cursor is valid (ie get_event is safe)
*/
bool
LV2EventBuffer::is_valid() const
{
return lv2_event_is_valid(&_iter);
}
/** Read an event from the current position in the buffer
*
* \return true if read was successful, or false if end of buffer reached
*/
bool
LV2EventBuffer::get_event(uint32_t* frames,
uint32_t* subframes,
uint16_t* type,
uint16_t* size,
uint8_t** data) const
{
if (lv2_event_is_valid(&_iter)) {
LV2_Event* ev = lv2_event_get(&_iter, data);
*frames = ev->frames;
*subframes = ev->subframes;
*type = ev->type;
*size = ev->size;
return true;
} else {
return false;
}
}
/** Append an event to the buffer.
*
* \a timestamp must be >= the latest event in the buffer.
*
* \return true on success
*/
bool
LV2EventBuffer::append(uint32_t frames,
uint32_t subframes,
uint16_t type,
uint16_t size,
const uint8_t* data)
{
#ifndef NDEBUG
if (lv2_event_is_valid(&_iter)) {
LV2_Event* last_event = lv2_event_get(&_iter, NULL);
assert(last_event->frames < frames
|| (last_event->frames == frames && last_event->subframes <= subframes));
}
#endif
/*cout << "Appending event type " << type << ", size " << size
<< " @ " << frames << "." << subframes << endl;
cout << "Buffer capacity " << _data->capacity << ", size " << _data->size << endl;*/
if (!lv2_event_write(&_iter, frames, subframes, type, size, data)) {
cerr << "ERROR: Failed to write event." << endl;
return false;
} else {
_latest_frames = frames;
_latest_subframes = subframes;
return true;
}
}
/** Append a buffer of events to the buffer.
*
* \a timestamp must be >= the latest event in the buffer.
*
* \return true on success
*/
bool
LV2EventBuffer::append(const LV2_Event_Buffer* /*buf*/)
{
uint8_t** data = NULL;
bool ret = true;
LV2_Event_Iterator iter;
for (lv2_event_begin(&iter, _data); lv2_event_is_valid(&iter); lv2_event_increment(&iter)) {
LV2_Event* ev = lv2_event_get(&iter, data);
#ifndef NDEBUG
assert((ev->frames > _latest_frames)
|| (ev->frames == _latest_frames
&& ev->subframes >= _latest_subframes));
#endif
if (!(ret = append(ev->frames, ev->subframes, ev->type, ev->size, *data))) {
cerr << "ERROR: Failed to write event." << endl;
break;
}
_latest_frames = ev->frames;
_latest_subframes = ev->subframes;
}
return ret;
}
} // namespace ARDOUR

View file

@ -41,7 +41,6 @@
#include "ardour/audio_buffer.h"
#include "ardour/audioengine.h"
#include "ardour/debug.h"
#include "ardour/lv2_event_buffer.h"
#include "ardour/lv2_plugin.h"
#include "ardour/session.h"
@ -50,8 +49,11 @@
#include <lilv/lilv.h>
#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
#include "lv2/lv2plug.in/ns/ext/state/state.h"
#include "rdff.h"
#include "lv2_evbuf.h"
#ifdef HAVE_SUIL
#include <suil/suil.h>
#endif
@ -66,11 +68,16 @@ using namespace ARDOUR;
using namespace PBD;
URIMap LV2Plugin::_uri_map;
uint32_t LV2Plugin::_midi_event_type_ev = _uri_map.uri_to_id(
"http://lv2plug.in/ns/ext/event",
"http://lv2plug.in/ns/ext/midi#MidiEvent");
uint32_t LV2Plugin::_midi_event_type = _uri_map.uri_to_id(
"http://lv2plug.in/ns/ext/event",
"http://lv2plug.in/ns/ext/midi#MidiEvent");
NULL,
"http://lv2plug.in/ns/ext/midi#MidiEvent");
uint32_t LV2Plugin::_sequence_type = _uri_map.uri_to_id(
NULL, LV2_ATOM__Sequence);
uint32_t LV2Plugin::_state_path_type = _uri_map.uri_to_id(
NULL, LV2_STATE_PATH_URI);
NULL, LV2_STATE_PATH_URI);
class LV2World : boost::noncopyable {
public:
@ -84,6 +91,9 @@ public:
LilvNode* control_class; ///< Control port
LilvNode* event_class; ///< Event port
LilvNode* midi_class; ///< MIDI event
LilvNode* message_port_class;
LilvNode* buffer_type;
LilvNode* sequence_class;
LilvNode* in_place_broken;
LilvNode* integer;
LilvNode* toggled;
@ -220,6 +230,13 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate)
flags |= PORT_AUDIO;
} else if (lilv_port_is_a(_impl->plugin, port, _world.event_class)) {
flags |= PORT_EVENT;
} else if (lilv_port_is_a(_impl->plugin, port, _world.message_port_class)) {
LilvNodes* buffer_types = lilv_port_get_value(
_impl->plugin, port, _world.buffer_type);
if (lilv_nodes_contains(buffer_types, _world.sequence_class)) {
flags |= PORT_MESSAGE;
}
lilv_nodes_free(buffer_types);
} else {
error << string_compose(
"LV2: \"%1\" port %2 has no known data type",
@ -953,37 +970,41 @@ LV2Plugin::connect_and_run(BufferSet& bufs,
uint32_t midi_out_index = 0;
bool valid;
for (uint32_t port_index = 0; port_index < num_ports; ++port_index) {
void* buf = NULL;
uint32_t index = 0;
if (parameter_is_audio(port_index)) {
if (parameter_is_input(port_index)) {
void* buf = NULL;
uint32_t index = 0;
PortFlags flags = _port_flags[port_index];
if (flags & PORT_AUDIO) {
if (flags & PORT_INPUT) {
index = in_map.get(DataType::AUDIO, audio_in_index++, &valid);
buf = (valid)
? bufs.get_audio(index).data(offset)
: silent_bufs.get_audio(0).data(offset);
} else if (parameter_is_output(port_index)) {
} else {
index = out_map.get(DataType::AUDIO, audio_out_index++, &valid);
buf = (valid)
? bufs.get_audio(index).data(offset)
: scratch_bufs.get_audio(0).data(offset);
}
} else if (parameter_is_event(port_index)) {
} else if (flags & (PORT_EVENT|PORT_MESSAGE)) {
/* FIXME: The checks here for bufs.count().n_midi() > index shouldn't
be necessary, but the mapping is illegal in some cases. Ideally
that should be fixed, but this is easier...
*/
if (parameter_is_input(port_index)) {
const uint32_t atom_type = (flags & PORT_MESSAGE) ? _sequence_type : 0;
if (flags & PORT_INPUT) {
index = in_map.get(DataType::MIDI, midi_in_index++, &valid);
buf = (valid && bufs.count().n_midi() > index)
? bufs.get_lv2_midi(true, index).data()
: silent_bufs.get_lv2_midi(true, 0).data();
} else if (parameter_is_output(port_index)) {
? lv2_evbuf_get_buffer(bufs.get_lv2_midi(true, index, atom_type))
: lv2_evbuf_get_buffer(silent_bufs.get_lv2_midi(true, 0, atom_type));
} else {
index = out_map.get(DataType::MIDI, midi_out_index++, &valid);
buf = (valid && bufs.count().n_midi() > index)
? bufs.get_lv2_midi(false, index).data()
: scratch_bufs.get_lv2_midi(true, 0).data();
? lv2_evbuf_get_buffer(bufs.get_lv2_midi(false, index, atom_type))
: lv2_evbuf_get_buffer(scratch_bufs.get_lv2_midi(false, 0, atom_type));
}
} // else port is optional (or we shouldn't have made it this far)
} else {
continue; // Control port, leave buffer alone
}
lilv_instance_connect_port(_impl->instance, port_index, buf);
}
@ -1138,19 +1159,22 @@ LV2World::LV2World()
: world(lilv_world_new())
{
lilv_world_load_all(world);
input_class = lilv_new_uri(world, LILV_URI_INPUT_PORT);
output_class = lilv_new_uri(world, LILV_URI_OUTPUT_PORT);
control_class = lilv_new_uri(world, LILV_URI_CONTROL_PORT);
audio_class = lilv_new_uri(world, LILV_URI_AUDIO_PORT);
event_class = lilv_new_uri(world, LILV_URI_EVENT_PORT);
midi_class = lilv_new_uri(world, LILV_URI_MIDI_EVENT);
in_place_broken = lilv_new_uri(world, LILV_NS_LV2 "inPlaceBroken");
integer = lilv_new_uri(world, LILV_NS_LV2 "integer");
toggled = lilv_new_uri(world, LILV_NS_LV2 "toggled");
srate = lilv_new_uri(world, LILV_NS_LV2 "sampleRate");
gtk_gui = lilv_new_uri(world, NS_UI "GtkUI");
external_gui = lilv_new_uri(world, NS_UI "external");
logarithmic = lilv_new_uri(world, "http://lv2plug.in/ns/dev/extportinfo#logarithmic");
input_class = lilv_new_uri(world, LILV_URI_INPUT_PORT);
output_class = lilv_new_uri(world, LILV_URI_OUTPUT_PORT);
control_class = lilv_new_uri(world, LILV_URI_CONTROL_PORT);
audio_class = lilv_new_uri(world, LILV_URI_AUDIO_PORT);
event_class = lilv_new_uri(world, LILV_URI_EVENT_PORT);
midi_class = lilv_new_uri(world, LILV_URI_MIDI_EVENT);
message_port_class = lilv_new_uri(world, LV2_ATOM__MessagePort);
buffer_type = lilv_new_uri(world, LV2_ATOM__bufferType);
sequence_class = lilv_new_uri(world, LV2_ATOM__Sequence);
in_place_broken = lilv_new_uri(world, LILV_NS_LV2 "inPlaceBroken");
integer = lilv_new_uri(world, LILV_NS_LV2 "integer");
toggled = lilv_new_uri(world, LILV_NS_LV2 "toggled");
srate = lilv_new_uri(world, LILV_NS_LV2 "sampleRate");
gtk_gui = lilv_new_uri(world, NS_UI "GtkUI");
external_gui = lilv_new_uri(world, NS_UI "external");
logarithmic = lilv_new_uri(world, "http://lv2plug.in/ns/dev/extportinfo#logarithmic");
}
LV2World::~LV2World()
@ -1161,6 +1185,9 @@ LV2World::~LV2World()
lilv_node_free(audio_class);
lilv_node_free(event_class);
lilv_node_free(midi_class);
lilv_node_free(message_port_class);
lilv_node_free(buffer_type);
lilv_node_free(sequence_class);
lilv_node_free(in_place_broken);
}
@ -1230,14 +1257,18 @@ LV2PluginInfo::discover()
p, _world.input_class, _world.audio_class, NULL));
info->n_inputs.set_midi(
lilv_plugin_get_num_ports_of_class(
p, _world.input_class, _world.event_class, NULL));
p, _world.input_class, _world.event_class, NULL)
+ lilv_plugin_get_num_ports_of_class(
p, _world.input_class, _world.message_port_class, NULL));
info->n_outputs.set_audio(
lilv_plugin_get_num_ports_of_class(
p, _world.output_class, _world.audio_class, NULL));
info->n_outputs.set_midi(
lilv_plugin_get_num_ports_of_class(
p, _world.output_class, _world.event_class, NULL));
p, _world.output_class, _world.event_class, NULL)
+ lilv_plugin_get_num_ports_of_class(
p, _world.output_class, _world.message_port_class, NULL));
info->unique_id = lilv_node_as_uri(lilv_plugin_get_uri(p));
info->index = 0; // Meaningless for LV2

View file

@ -379,8 +379,7 @@ def build(bld):
#obj.add_objects = 'default/libs/surfaces/control_protocol/smpte_1.o'
if bld.is_defined('HAVE_LILV') :
obj.source += [ 'lv2_plugin.cc', 'lv2_event_buffer.cc',
'uri_map.cc', 'rdff.c' ]
obj.source += ['lv2_plugin.cc', 'lv2_evbuf.c', 'uri_map.cc']
obj.uselib += ['LILV']
if bld.is_defined('HAVE_SUIL'):
obj.uselib += ['SUIL']