From f122504784e8e38be7d7df87af3f2a7044a7f14c Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sat, 25 Feb 2012 04:16:42 +0000 Subject: Support LV2 atom sequence ports alongside old event ports. git-svn-id: svn://localhost/ardour2/branches/3.0@11517 d708f5d6-7413-0410-9779-e7cbd77b26cf --- libs/ardour/ardour/buffer_set.h | 16 +- libs/ardour/ardour/lv2_event_buffer.h | 81 ---- libs/ardour/ardour/lv2_plugin.h | 14 +- libs/ardour/buffer_set.cc | 57 ++- libs/ardour/lv2/lv2plug.in/ns/ext/atom/atom.h | 259 ++++++++++++ libs/ardour/lv2/lv2plug.in/ns/ext/atom/forge.h | 544 +++++++++++++++++++++++++ libs/ardour/lv2/lv2plug.in/ns/ext/atom/util.h | 424 +++++++++++++++++++ libs/ardour/lv2_evbuf.c | 264 ++++++++++++ libs/ardour/lv2_evbuf.h | 161 ++++++++ libs/ardour/lv2_event_buffer.cc | 196 --------- libs/ardour/lv2_plugin.cc | 97 +++-- libs/ardour/wscript | 3 +- 12 files changed, 1772 insertions(+), 344 deletions(-) delete mode 100644 libs/ardour/ardour/lv2_event_buffer.h create mode 100644 libs/ardour/lv2/lv2plug.in/ns/ext/atom/atom.h create mode 100644 libs/ardour/lv2/lv2plug.in/ns/ext/atom/forge.h create mode 100644 libs/ardour/lv2/lv2plug.in/ns/ext/atom/util.h create mode 100644 libs/ardour/lv2_evbuf.c create mode 100644 libs/ardour/lv2_evbuf.h delete mode 100644 libs/ardour/lv2_event_buffer.cc (limited to 'libs') diff --git a/libs/ardour/ardour/buffer_set.h b/libs/ardour/ardour/buffer_set.h index 4db1c34557..5049eda70a 100644 --- a/libs/ardour/ardour/buffer_set.h +++ b/libs/ardour/ardour/buffer_set.h @@ -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 > LV2Buffers; + typedef std::vector< std::pair > 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 index 089681a730..0000000000 --- a/libs/ardour/ardour/lv2_event_buffer.h +++ /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__ diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h index 874baef533..77ad8ead24 100644 --- a/libs/ardour/ardour/lv2_plugin.h +++ b/libs/ardour/ardour/lv2_plugin.h @@ -98,7 +98,10 @@ class LV2Plugin : public ARDOUR::Plugin boost::shared_ptr 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; diff --git a/libs/ardour/buffer_set.cc b/libs/ardour/buffer_set.cc index caf6d7e8d2..bf114b95d5 100644 --- a/libs/ardour/buffer_set.cc +++ b/libs/ardour/buffer_set.cc @@ -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 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 index 0000000000..9bce7ce7aa --- /dev/null +++ b/libs/ardour/lv2/lv2plug.in/ns/ext/atom/atom.h @@ -0,0 +1,259 @@ +/* + Copyright 2008-2012 David Robillard + + 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 + . +*/ + +#ifndef LV2_ATOM_H +#define LV2_ATOM_H + +#include +#include + +#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.: +
+   | Event 1 (size 6)                              | Event 2
+   |       |       |       |       |       |       |       |       |
+   | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
+   |FRAMES |SUBFRMS|TYPE   |SIZE   |DATADATADATAPAD|FRAMES |SUBFRMS|...
+   
+*/ +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 index 0000000000..b9dd51ca28 --- /dev/null +++ b/libs/ardour/lv2/lv2plug.in/ns/ext/atom/forge.h @@ -0,0 +1,544 @@ +/* + Copyright 2008-2012 David Robillard + + 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 + +#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 +#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 index 0000000000..6b46a676f8 --- /dev/null +++ b/libs/ardour/lv2/lv2plug.in/ns/ext/atom/util.h @@ -0,0 +1,424 @@ +/* + Copyright 2008-2012 David Robillard + + 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 +#include +#include + +#include "lv2/lv2plug.in/ns/ext/atom/atom.h" + +#ifdef __cplusplus +extern "C" { +#else +# include +#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 index 0000000000..7877e07950 --- /dev/null +++ b/libs/ardour/lv2_evbuf.c @@ -0,0 +1,264 @@ +/* + Copyright 2008-2012 David Robillard + + 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 +#include +#include +#include + +#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 index 0000000000..b2caa12e28 --- /dev/null +++ b/libs/ardour/lv2_evbuf.h @@ -0,0 +1,161 @@ +/* + Copyright 2008-2012 David Robillard + + 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 +#include + +#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 index f1e5e4d0d1..0000000000 --- a/libs/ardour/lv2_event_buffer.cc +++ /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 -#include -#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(_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 - diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc index bc3880e86c..eae8f1633e 100644 --- a/libs/ardour/lv2_plugin.cc +++ b/libs/ardour/lv2_plugin.cc @@ -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 +#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 #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 diff --git a/libs/ardour/wscript b/libs/ardour/wscript index dccf53735f..21936bc278 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -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'] -- cgit v1.2.3