Support LV2 atom sequence ports alongside old event ports.
authorDavid Robillard <d@drobilla.net>
Sat, 25 Feb 2012 04:16:42 +0000 (04:16 +0000)
committerDavid Robillard <d@drobilla.net>
Sat, 25 Feb 2012 04:16:42 +0000 (04:16 +0000)
git-svn-id: svn://localhost/ardour2/branches/3.0@11517 d708f5d6-7413-0410-9779-e7cbd77b26cf

12 files changed:
libs/ardour/ardour/buffer_set.h
libs/ardour/ardour/lv2_event_buffer.h [deleted file]
libs/ardour/ardour/lv2_plugin.h
libs/ardour/buffer_set.cc
libs/ardour/lv2/lv2plug.in/ns/ext/atom/atom.h [new file with mode: 0644]
libs/ardour/lv2/lv2plug.in/ns/ext/atom/forge.h [new file with mode: 0644]
libs/ardour/lv2/lv2plug.in/ns/ext/atom/util.h [new file with mode: 0644]
libs/ardour/lv2_evbuf.c [new file with mode: 0644]
libs/ardour/lv2_evbuf.h [new file with mode: 0644]
libs/ardour/lv2_event_buffer.cc [deleted file]
libs/ardour/lv2_plugin.cc
libs/ardour/wscript

index 4db1c34557832c37fb31aad91561e3c3724f521f..5049eda70a36f149f76d8394ad396b816cb1e38e 100644 (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
 
diff --git a/libs/ardour/ardour/lv2_event_buffer.h b/libs/ardour/ardour/lv2_event_buffer.h
deleted file mode 100644 (file)
index 089681a..0000000
+++ /dev/null
@@ -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__
index 874baef533298c00b3af5868f794a7cebfd7eb9c..77ad8ead24308f9650f6be62cfa39f7e6c6a45b1 100644 (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;
index caf6d7e8d2e8fcd8b8570bae203f24b54e5b94a8..bf114b95d5d9e0ce9d121670e1f1034355d6dc16 100644 (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) {
diff --git a/libs/ardour/lv2/lv2plug.in/ns/ext/atom/atom.h b/libs/ardour/lv2/lv2plug.in/ns/ext/atom/atom.h
new file mode 100644 (file)
index 0000000..9bce7ce
--- /dev/null
@@ -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 */
diff --git a/libs/ardour/lv2/lv2plug.in/ns/ext/atom/forge.h b/libs/ardour/lv2/lv2plug.in/ns/ext/atom/forge.h
new file mode 100644 (file)
index 0000000..b9dd51c
--- /dev/null
@@ -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 */
diff --git a/libs/ardour/lv2/lv2plug.in/ns/ext/atom/util.h b/libs/ardour/lv2/lv2plug.in/ns/ext/atom/util.h
new file mode 100644 (file)
index 0000000..6b46a67
--- /dev/null
@@ -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 */
diff --git a/libs/ardour/lv2_evbuf.c b/libs/ardour/lv2_evbuf.c
new file mode 100644 (file)
index 0000000..7877e07
--- /dev/null
@@ -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;
+}
diff --git a/libs/ardour/lv2_evbuf.h b/libs/ardour/lv2_evbuf.h
new file mode 100644 (file)
index 0000000..b2caa12
--- /dev/null
@@ -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 */
diff --git a/libs/ardour/lv2_event_buffer.cc b/libs/ardour/lv2_event_buffer.cc
deleted file mode 100644 (file)
index f1e5e4d..0000000
+++ /dev/null
@@ -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
-
index bc3880e86ccc4d6d9ac4abbd488a22e8b09d2136..eae8f1633ea376bed8cb531ee09f82c0fe521f95 100644 (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"
 
 
 #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
index dccf53735f6eccd41d4162993a4baa4512c44b83..21936bc27811103aaefa2585e0e69104acbf5a25 100644 (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']