diff --git a/modules/juce_audio_plugin_client/LV2/includes/atom-util.h b/modules/juce_audio_plugin_client/LV2/includes/atom-util.h new file mode 100644 index 0000000000..7af96de80c --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/atom-util.h @@ -0,0 +1,460 @@ +/* + Copyright 2008-2014 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. +*/ + +/** + @defgroup util Utilities + @ingroup atom + @{ +*/ + +#ifndef LV2_ATOM_UTIL_H +#define LV2_ATOM_UTIL_H + +#include +#include +#include + +#include "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 + 7U) & (~7U); +} + +/** Return the total size of `atom`, including the header. */ +static inline uint32_t +lv2_atom_total_size(const LV2_Atom* atom) +{ + return (uint32_t)sizeof(LV2_Atom) + atom->size; +} + +/** Return true iff `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 `a` is equal to `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 + @{ +*/ + +/** Get an iterator pointing to the first event in a Sequence body. */ +static inline const LV2_Atom_Event* +lv2_atom_sequence_begin(const LV2_Atom_Sequence_Body* body) +{ + return (const LV2_Atom_Event*)(body + 1); +} + +/** Get an iterator pointing to the end of a Sequence body. */ +static inline const LV2_Atom_Event* +lv2_atom_sequence_end(const LV2_Atom_Sequence_Body* body, uint32_t size) +{ + return (const LV2_Atom_Event*)((const uint8_t*)body + lv2_atom_pad_size(size)); +} + +/** Get an iterator pointing to the end of a Sequence body. */ +static inline LV2_Atom_Event* +lv2_atom_sequence_end2(LV2_Atom_Sequence_Body* body, uint32_t size) +{ + return (LV2_Atom_Event*)((uint8_t*)body + lv2_atom_pad_size(size)); +} + +/** Return true iff `i` has reached the end of `body`. */ +static inline bool +lv2_atom_sequence_is_end(const LV2_Atom_Sequence_Body* body, + uint32_t size, + const LV2_Atom_Event* i) +{ + return (const uint8_t*)i >= ((const uint8_t*)body + size); +} + +/** Return an iterator to the element following `i`. */ +static inline const LV2_Atom_Event* +lv2_atom_sequence_next(const LV2_Atom_Event* i) +{ + return (const LV2_Atom_Event*)((const uint8_t*)i + + sizeof(LV2_Atom_Event) + + lv2_atom_pad_size(i->body.size)); +} + +/** + A macro for iterating over all events in a Sequence. + @param seq 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_ATOM_SEQUENCE_FOREACH(sequence, ev) { + // Do something with ev (an LV2_Atom_Event*) here... + } + @endcode +*/ +#define LV2_ATOM_SEQUENCE_FOREACH(seq, iter) \ + for (const LV2_Atom_Event* (iter) = lv2_atom_sequence_begin(&(seq)->body); \ + !lv2_atom_sequence_is_end(&(seq)->body, (seq)->atom.size, (iter)); \ + (iter) = lv2_atom_sequence_next(iter)) + +/** Like LV2_ATOM_SEQUENCE_FOREACH but for a headerless sequence body. */ +#define LV2_ATOM_SEQUENCE_BODY_FOREACH(body, size, iter) \ + for (const LV2_Atom_Event* (iter) = lv2_atom_sequence_begin(body); \ + !lv2_atom_sequence_is_end(body, size, (iter)); \ + (iter) = lv2_atom_sequence_next(iter)) + +/** + @} + @name Sequence Utilities + @{ +*/ + +/** + Clear all events from `sequence`. + + This simply resets the size field, the other fields are left untouched. +*/ +static inline void +lv2_atom_sequence_clear(LV2_Atom_Sequence* seq) +{ + seq->atom.size = sizeof(LV2_Atom_Sequence_Body); +} + +/** + Append an event at the end of `sequence`. + + @param seq Sequence to append to. + @param capacity Total capacity of the sequence atom + (e.g. as set by the host for sequence output ports). + @param event Event to write. + + @return A pointer to the newly written event in `seq`, + or NULL on failure (insufficient space). +*/ +static inline LV2_Atom_Event* +lv2_atom_sequence_append_event(LV2_Atom_Sequence* seq, + uint32_t capacity, + const LV2_Atom_Event* event) +{ + const uint32_t total_size = (uint32_t)sizeof(*event) + event->body.size; + if (capacity - seq->atom.size < total_size) { + return NULL; + } + + LV2_Atom_Event* e = lv2_atom_sequence_end2(&seq->body, seq->atom.size); + memcpy(e, event, total_size); + + seq->atom.size += lv2_atom_pad_size(total_size); + + return e; +} + +/** + @} + @name Tuple Iterator + @{ +*/ + +/** Get an iterator pointing to the first element in `tup`. */ +static inline const LV2_Atom* +lv2_atom_tuple_begin(const LV2_Atom_Tuple* tup) +{ + return (const LV2_Atom*)(LV2_ATOM_BODY_CONST(tup)); +} + +/** Return true iff `i` has reached the end of `body`. */ +static inline bool +lv2_atom_tuple_is_end(const void* body, uint32_t size, const LV2_Atom* i) +{ + return (const uint8_t*)i >= ((const uint8_t*)body + size); +} + +/** Return an iterator to the element following `i`. */ +static inline const LV2_Atom* +lv2_atom_tuple_next(const LV2_Atom* i) +{ + return (const LV2_Atom*)( + (const uint8_t*)i + sizeof(LV2_Atom) + lv2_atom_pad_size(i->size)); +} + +/** + 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_ATOMO_TUPLE_FOREACH(tuple, elem) { + // Do something with elem (an LV2_Atom*) here... + } + @endcode +*/ +#define LV2_ATOM_TUPLE_FOREACH(tuple, iter) \ + for (const LV2_Atom* (iter) = lv2_atom_tuple_begin(tuple); \ + !lv2_atom_tuple_is_end(LV2_ATOM_BODY_CONST(tuple), (tuple)->size, (iter)); \ + (iter) = lv2_atom_tuple_next(iter)) + +/** Like LV2_ATOM_TUPLE_FOREACH but for a headerless tuple body. */ +#define LV2_ATOM_TUPLE_BODY_FOREACH(body, size, iter) \ + for (const LV2_Atom* (iter) = (const LV2_Atom*)body; \ + !lv2_atom_tuple_is_end(body, size, (iter)); \ + (iter) = lv2_atom_tuple_next(iter)) + +/** + @} + @name Object Iterator + @{ +*/ + +/** Return a pointer to the first property in `body`. */ +static inline const LV2_Atom_Property_Body* +lv2_atom_object_begin(const LV2_Atom_Object_Body* body) +{ + return (const LV2_Atom_Property_Body*)(body + 1); +} + +/** Return true iff `i` has reached the end of `obj`. */ +static inline bool +lv2_atom_object_is_end(const LV2_Atom_Object_Body* body, + uint32_t size, + const LV2_Atom_Property_Body* i) +{ + return (const uint8_t*)i >= ((const uint8_t*)body + size); +} + +/** Return an iterator to the property following `i`. */ +static inline const LV2_Atom_Property_Body* +lv2_atom_object_next(const LV2_Atom_Property_Body* i) +{ + const LV2_Atom* const value = (const LV2_Atom*)( + (const uint8_t*)i + 2 * sizeof(uint32_t)); + return (const LV2_Atom_Property_Body*)( + (const uint8_t*)i + lv2_atom_pad_size( + (uint32_t)sizeof(LV2_Atom_Property_Body) + value->size)); +} + +/** + A macro for iterating over all properties of an Object. + @param obj 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_ATOM_OBJECT_FOREACH(object, i) { + // Do something with prop (an LV2_Atom_Property_Body*) here... + } + @endcode +*/ +#define LV2_ATOM_OBJECT_FOREACH(obj, iter) \ + for (const LV2_Atom_Property_Body* (iter) = lv2_atom_object_begin(&(obj)->body); \ + !lv2_atom_object_is_end(&(obj)->body, (obj)->atom.size, (iter)); \ + (iter) = lv2_atom_object_next(iter)) + +/** Like LV2_ATOM_OBJECT_FOREACH but for a headerless object body. */ +#define LV2_ATOM_OBJECT_BODY_FOREACH(body, size, iter) \ + for (const LV2_Atom_Property_Body* (iter) = lv2_atom_object_begin(body); \ + !lv2_atom_object_is_end(body, size, (iter)); \ + (iter) = lv2_atom_object_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_ATOM_OBJECT_QUERY_END = { 0, NULL }; + +/** + Get an object's values for various keys. + + The value pointer of each item in `query` will be set to the location of + the corresponding value in `object`. Every value pointer in `query` MUST + be initialised to NULL. This function reads `object` in a single linear + sweep. By allocating `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_ATOM_OBJECT_QUERY_END + }; + lv2_atom_object_query(obj, q); + // name and age are now set to the appropriate values in obj, or NULL. + @endcode +*/ +static inline int +lv2_atom_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_ATOM_OBJECT_FOREACH(object, prop) { + 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; +} + +/** + Body only version of lv2_atom_object_get(). +*/ +static inline int +lv2_atom_object_body_get(uint32_t size, const LV2_Atom_Object_Body* body, ...) +{ + int matches = 0; + int n_queries = 0; + + /* Count number of keys so we can short-circuit when done */ + va_list args; + va_start(args, body); + for (n_queries = 0; va_arg(args, uint32_t); ++n_queries) { + if (!va_arg(args, const LV2_Atom**)) { + return -1; + } + } + va_end(args); + + LV2_ATOM_OBJECT_BODY_FOREACH(body, size, prop) { + va_start(args, body); + 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; +} + +/** + Variable argument version of lv2_atom_object_query(). + + 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_atom_object_get(obj, + uris.name_key, &name, + uris.age_key, &age, + 0); + @endcode +*/ +static inline int +lv2_atom_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_ATOM_OBJECT_FOREACH(object, prop) { + 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/modules/juce_audio_plugin_client/LV2/includes/atom.h b/modules/juce_audio_plugin_client/LV2/includes/atom.h new file mode 100644 index 0000000000..1af831c861 --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/atom.h @@ -0,0 +1,254 @@ +/* + Copyright 2008-2014 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. +*/ + +/** + @defgroup atom Atom + + A generic value container and several data types, see + for details. + + @{ +*/ + +#ifndef LV2_ATOM_H +#define LV2_ATOM_H + +#include +#include + +#define LV2_ATOM_URI "http://lv2plug.in/ns/ext/atom" +#define LV2_ATOM_PREFIX LV2_ATOM_URI "#" + +#define LV2_ATOM__Atom LV2_ATOM_PREFIX "Atom" +#define LV2_ATOM__AtomPort LV2_ATOM_PREFIX "AtomPort" +#define LV2_ATOM__Blank LV2_ATOM_PREFIX "Blank" +#define LV2_ATOM__Bool LV2_ATOM_PREFIX "Bool" +#define LV2_ATOM__Chunk LV2_ATOM_PREFIX "Chunk" +#define LV2_ATOM__Double LV2_ATOM_PREFIX "Double" +#define LV2_ATOM__Event LV2_ATOM_PREFIX "Event" +#define LV2_ATOM__Float LV2_ATOM_PREFIX "Float" +#define LV2_ATOM__Int LV2_ATOM_PREFIX "Int" +#define LV2_ATOM__Literal LV2_ATOM_PREFIX "Literal" +#define LV2_ATOM__Long LV2_ATOM_PREFIX "Long" +#define LV2_ATOM__Number LV2_ATOM_PREFIX "Number" +#define LV2_ATOM__Object LV2_ATOM_PREFIX "Object" +#define LV2_ATOM__Path LV2_ATOM_PREFIX "Path" +#define LV2_ATOM__Property LV2_ATOM_PREFIX "Property" +#define LV2_ATOM__Resource LV2_ATOM_PREFIX "Resource" +#define LV2_ATOM__Sequence LV2_ATOM_PREFIX "Sequence" +#define LV2_ATOM__Sound LV2_ATOM_PREFIX "Sound" +#define LV2_ATOM__String LV2_ATOM_PREFIX "String" +#define LV2_ATOM__Tuple LV2_ATOM_PREFIX "Tuple" +#define LV2_ATOM__URI LV2_ATOM_PREFIX "URI" +#define LV2_ATOM__URID LV2_ATOM_PREFIX "URID" +#define LV2_ATOM__Vector LV2_ATOM_PREFIX "Vector" +#define LV2_ATOM__atomTransfer LV2_ATOM_PREFIX "atomTransfer" +#define LV2_ATOM__beatTime LV2_ATOM_PREFIX "beatTime" +#define LV2_ATOM__bufferType LV2_ATOM_PREFIX "bufferType" +#define LV2_ATOM__childType LV2_ATOM_PREFIX "childType" +#define LV2_ATOM__eventTransfer LV2_ATOM_PREFIX "eventTransfer" +#define LV2_ATOM__frameTime LV2_ATOM_PREFIX "frameTime" +#define LV2_ATOM__supports LV2_ATOM_PREFIX "supports" +#define LV2_ATOM__timeUnit LV2_ATOM_PREFIX "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) \ + ((uint8_t*)(atom) + sizeof(type)) + +/** + Const version of LV2_ATOM_CONTENTS. +*/ +#define LV2_ATOM_CONTENTS_CONST(type, atom) \ + ((const 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) + +/** + Const version of LV2_ATOM_BODY. +*/ +#define LV2_ATOM_BODY_CONST(atom) LV2_ATOM_CONTENTS_CONST(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:Int or atom:Bool. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + int32_t body; /**< Integer value. */ +} LV2_Atom_Int; + +/** An atom:Long. May be cast to LV2_Atom. */ +typedef struct { + LV2_Atom atom; /**< Atom header. */ + int64_t body; /**< Integer value. */ +} LV2_Atom_Long; + +/** 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_Int 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 child_size; /**< The size of each element in the vector. */ + uint32_t child_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, or 0 for 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 audio frames. + + 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_Sequence_Body body; /**< Body. */ +} LV2_Atom_Sequence; + +/** + @} +*/ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_ATOM_H */ diff --git a/modules/juce_audio_plugin_client/LV2/includes/buf-size.h b/modules/juce_audio_plugin_client/LV2/includes/buf-size.h new file mode 100644 index 0000000000..cff152be2b --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/buf-size.h @@ -0,0 +1,44 @@ +/* + Copyright 2007-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_BUF_SIZE_H +#define LV2_BUF_SIZE_H + +/** + @defgroup buf-size Buffer Size + + Access to, and restrictions on, buffer sizes; see + for details. + + @{ +*/ + +#define LV2_BUF_SIZE_URI "http://lv2plug.in/ns/ext/buf-size" +#define LV2_BUF_SIZE_PREFIX LV2_BUF_SIZE_URI "#" + +#define LV2_BUF_SIZE__boundedBlockLength LV2_BUF_SIZE_PREFIX "boundedBlockLength" +#define LV2_BUF_SIZE__fixedBlockLength LV2_BUF_SIZE_PREFIX "fixedBlockLength" +#define LV2_BUF_SIZE__maxBlockLength LV2_BUF_SIZE_PREFIX "maxBlockLength" +#define LV2_BUF_SIZE__minBlockLength LV2_BUF_SIZE_PREFIX "minBlockLength" +#define LV2_BUF_SIZE__nominalBlockLength LV2_BUF_SIZE_PREFIX "nominalBlockLength" +#define LV2_BUF_SIZE__powerOf2BlockLength LV2_BUF_SIZE_PREFIX "powerOf2BlockLength" +#define LV2_BUF_SIZE__sequenceSize LV2_BUF_SIZE_PREFIX "sequenceSize" + +/** + @} +*/ + +#endif /* LV2_BUF_SIZE_H */ diff --git a/modules/juce_audio_plugin_client/LV2/includes/instance-access.h b/modules/juce_audio_plugin_client/LV2/includes/instance-access.h new file mode 100644 index 0000000000..cc84d65033 --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/instance-access.h @@ -0,0 +1,36 @@ +/* + LV2 Instance Access Extension + 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. +*/ + +/** + @defgroup instance-access Instance Access + + Access to the LV2_Handle of a plugin for UIs; see + for details. + + @{ +*/ + +#ifndef LV2_INSTANCE_ACCESS_H +#define LV2_INSTANCE_ACCESS_H + +#define LV2_INSTANCE_ACCESS_URI "http://lv2plug.in/ns/ext/instance-access" + +#endif /* LV2_INSTANCE_ACCESS_H */ + +/** + @} +*/ diff --git a/modules/juce_audio_plugin_client/LV2/includes/lv2.h b/modules/juce_audio_plugin_client/LV2/includes/lv2.h new file mode 100644 index 0000000000..2ab3186f4f --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/lv2.h @@ -0,0 +1,474 @@ +/* + LV2 - An audio plugin interface specification. + Copyright 2006-2012 Steve Harris, David Robillard. + + Based on LADSPA, Copyright 2000-2002 Richard W.E. Furse, + Paul Barton-Davis, Stefan Westerfeld. + + 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. +*/ + +/** + @defgroup lv2core LV2 Core + + Core LV2 specification, see for details. + + @{ +*/ + +#ifndef LV2_H_INCLUDED +#define LV2_H_INCLUDED + +#include + +#define LV2_CORE_URI "http://lv2plug.in/ns/lv2core" +#define LV2_CORE_PREFIX LV2_CORE_URI "#" + +#define LV2_CORE__AllpassPlugin LV2_CORE_PREFIX "AllpassPlugin" +#define LV2_CORE__AmplifierPlugin LV2_CORE_PREFIX "AmplifierPlugin" +#define LV2_CORE__AnalyserPlugin LV2_CORE_PREFIX "AnalyserPlugin" +#define LV2_CORE__AudioPort LV2_CORE_PREFIX "AudioPort" +#define LV2_CORE__BandpassPlugin LV2_CORE_PREFIX "BandpassPlugin" +#define LV2_CORE__CVPort LV2_CORE_PREFIX "CVPort" +#define LV2_CORE__ChorusPlugin LV2_CORE_PREFIX "ChorusPlugin" +#define LV2_CORE__CombPlugin LV2_CORE_PREFIX "CombPlugin" +#define LV2_CORE__CompressorPlugin LV2_CORE_PREFIX "CompressorPlugin" +#define LV2_CORE__ConstantPlugin LV2_CORE_PREFIX "ConstantPlugin" +#define LV2_CORE__ControlPort LV2_CORE_PREFIX "ControlPort" +#define LV2_CORE__ConverterPlugin LV2_CORE_PREFIX "ConverterPlugin" +#define LV2_CORE__DelayPlugin LV2_CORE_PREFIX "DelayPlugin" +#define LV2_CORE__DistortionPlugin LV2_CORE_PREFIX "DistortionPlugin" +#define LV2_CORE__DynamicsPlugin LV2_CORE_PREFIX "DynamicsPlugin" +#define LV2_CORE__EQPlugin LV2_CORE_PREFIX "EQPlugin" +#define LV2_CORE__EnvelopePlugin LV2_CORE_PREFIX "EnvelopePlugin" +#define LV2_CORE__ExpanderPlugin LV2_CORE_PREFIX "ExpanderPlugin" +#define LV2_CORE__ExtensionData LV2_CORE_PREFIX "ExtensionData" +#define LV2_CORE__Feature LV2_CORE_PREFIX "Feature" +#define LV2_CORE__FilterPlugin LV2_CORE_PREFIX "FilterPlugin" +#define LV2_CORE__FlangerPlugin LV2_CORE_PREFIX "FlangerPlugin" +#define LV2_CORE__FunctionPlugin LV2_CORE_PREFIX "FunctionPlugin" +#define LV2_CORE__GatePlugin LV2_CORE_PREFIX "GatePlugin" +#define LV2_CORE__GeneratorPlugin LV2_CORE_PREFIX "GeneratorPlugin" +#define LV2_CORE__HighpassPlugin LV2_CORE_PREFIX "HighpassPlugin" +#define LV2_CORE__InputPort LV2_CORE_PREFIX "InputPort" +#define LV2_CORE__InstrumentPlugin LV2_CORE_PREFIX "InstrumentPlugin" +#define LV2_CORE__LimiterPlugin LV2_CORE_PREFIX "LimiterPlugin" +#define LV2_CORE__LowpassPlugin LV2_CORE_PREFIX "LowpassPlugin" +#define LV2_CORE__MixerPlugin LV2_CORE_PREFIX "MixerPlugin" +#define LV2_CORE__ModulatorPlugin LV2_CORE_PREFIX "ModulatorPlugin" +#define LV2_CORE__MultiEQPlugin LV2_CORE_PREFIX "MultiEQPlugin" +#define LV2_CORE__OscillatorPlugin LV2_CORE_PREFIX "OscillatorPlugin" +#define LV2_CORE__OutputPort LV2_CORE_PREFIX "OutputPort" +#define LV2_CORE__ParaEQPlugin LV2_CORE_PREFIX "ParaEQPlugin" +#define LV2_CORE__PhaserPlugin LV2_CORE_PREFIX "PhaserPlugin" +#define LV2_CORE__PitchPlugin LV2_CORE_PREFIX "PitchPlugin" +#define LV2_CORE__Plugin LV2_CORE_PREFIX "Plugin" +#define LV2_CORE__PluginBase LV2_CORE_PREFIX "PluginBase" +#define LV2_CORE__Point LV2_CORE_PREFIX "Point" +#define LV2_CORE__Port LV2_CORE_PREFIX "Port" +#define LV2_CORE__PortProperty LV2_CORE_PREFIX "PortProperty" +#define LV2_CORE__Resource LV2_CORE_PREFIX "Resource" +#define LV2_CORE__ReverbPlugin LV2_CORE_PREFIX "ReverbPlugin" +#define LV2_CORE__ScalePoint LV2_CORE_PREFIX "ScalePoint" +#define LV2_CORE__SimulatorPlugin LV2_CORE_PREFIX "SimulatorPlugin" +#define LV2_CORE__SpatialPlugin LV2_CORE_PREFIX "SpatialPlugin" +#define LV2_CORE__Specification LV2_CORE_PREFIX "Specification" +#define LV2_CORE__SpectralPlugin LV2_CORE_PREFIX "SpectralPlugin" +#define LV2_CORE__UtilityPlugin LV2_CORE_PREFIX "UtilityPlugin" +#define LV2_CORE__WaveshaperPlugin LV2_CORE_PREFIX "WaveshaperPlugin" +#define LV2_CORE__appliesTo LV2_CORE_PREFIX "appliesTo" +#define LV2_CORE__binary LV2_CORE_PREFIX "binary" +#define LV2_CORE__connectionOptional LV2_CORE_PREFIX "connectionOptional" +#define LV2_CORE__control LV2_CORE_PREFIX "control" +#define LV2_CORE__default LV2_CORE_PREFIX "default" +#define LV2_CORE__designation LV2_CORE_PREFIX "designation" +#define LV2_CORE__documentation LV2_CORE_PREFIX "documentation" +#define LV2_CORE__enumeration LV2_CORE_PREFIX "enumeration" +#define LV2_CORE__extensionData LV2_CORE_PREFIX "extensionData" +#define LV2_CORE__freeWheeling LV2_CORE_PREFIX "freeWheeling" +#define LV2_CORE__hardRTCapable LV2_CORE_PREFIX "hardRTCapable" +#define LV2_CORE__inPlaceBroken LV2_CORE_PREFIX "inPlaceBroken" +#define LV2_CORE__index LV2_CORE_PREFIX "index" +#define LV2_CORE__integer LV2_CORE_PREFIX "integer" +#define LV2_CORE__isLive LV2_CORE_PREFIX "isLive" +#define LV2_CORE__latency LV2_CORE_PREFIX "latency" +#define LV2_CORE__maximum LV2_CORE_PREFIX "maximum" +#define LV2_CORE__microVersion LV2_CORE_PREFIX "microVersion" +#define LV2_CORE__minimum LV2_CORE_PREFIX "minimum" +#define LV2_CORE__minorVersion LV2_CORE_PREFIX "minorVersion" +#define LV2_CORE__name LV2_CORE_PREFIX "name" +#define LV2_CORE__optionalFeature LV2_CORE_PREFIX "optionalFeature" +#define LV2_CORE__port LV2_CORE_PREFIX "port" +#define LV2_CORE__portProperty LV2_CORE_PREFIX "portProperty" +#define LV2_CORE__project LV2_CORE_PREFIX "project" +#define LV2_CORE__prototype LV2_CORE_PREFIX "prototype" +#define LV2_CORE__reportsLatency LV2_CORE_PREFIX "reportsLatency" +#define LV2_CORE__requiredFeature LV2_CORE_PREFIX "requiredFeature" +#define LV2_CORE__sampleRate LV2_CORE_PREFIX "sampleRate" +#define LV2_CORE__scalePoint LV2_CORE_PREFIX "scalePoint" +#define LV2_CORE__symbol LV2_CORE_PREFIX "symbol" +#define LV2_CORE__toggled LV2_CORE_PREFIX "toggled" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Plugin Instance Handle. + + This is a handle for one particular instance of a plugin. It is valid to + compare to NULL (or 0 for C++) but otherwise the host MUST NOT attempt to + interpret it. +*/ +typedef void * LV2_Handle; + +/** + Feature. + + Features allow hosts to make additional functionality available to plugins + without requiring modification to the LV2 API. Extensions may define new + features and specify the `URI` and `data` to be used if necessary. + Some features, such as lv2:isLive, do not require the host to pass data. +*/ +typedef struct _LV2_Feature { + /** + A globally unique, case-sensitive identifier (URI) for this feature. + + This MUST be a valid URI string as defined by RFC 3986. + */ + const char * URI; + + /** + Pointer to arbitrary data. + + The format of this data is defined by the extension which describes the + feature with the given `URI`. + */ + void * data; +} LV2_Feature; + +/** + Plugin Descriptor. + + This structure provides the core functions necessary to instantiate and use + a plugin. +*/ +typedef struct _LV2_Descriptor { + /** + A globally unique, case-sensitive identifier for this plugin. + + This MUST be a valid URI string as defined by RFC 3986. All plugins with + the same URI MUST be compatible to some degree, see + http://lv2plug.in/ns/lv2core for details. + */ + const char * URI; + + /** + Instantiate the plugin. + + Note that instance initialisation should generally occur in activate() + rather than here. If a host calls instantiate(), it MUST call cleanup() + at some point in the future. + + @param descriptor Descriptor of the plugin to instantiate. + + @param sample_rate Sample rate, in Hz, for the new plugin instance. + + @param bundle_path Path to the LV2 bundle which contains this plugin + binary. It MUST include the trailing directory separator (e.g. '/') so + that simply appending a filename will yield the path to that file in the + bundle. + + @param features A NULL terminated array of LV2_Feature structs which + represent the features the host supports. Plugins may refuse to + instantiate if required features are not found here. However, hosts MUST + NOT use this as a discovery mechanism: instead, use the RDF data to + determine which features are required and do not attempt to instantiate + unsupported plugins at all. This parameter MUST NOT be NULL, i.e. a host + that supports no features MUST pass a single element array containing + NULL. + + @return A handle for the new plugin instance, or NULL if instantiation + has failed. + */ + LV2_Handle (*instantiate)(const struct _LV2_Descriptor * descriptor, + double sample_rate, + const char * bundle_path, + const LV2_Feature *const * features); + + /** + Connect a port on a plugin instance to a memory location. + + Plugin writers should be aware that the host may elect to use the same + buffer for more than one port and even use the same buffer for both + input and output (see lv2:inPlaceBroken in lv2.ttl). + + If the plugin has the feature lv2:hardRTCapable then there are various + things that the plugin MUST NOT do within the connect_port() function; + see lv2core.ttl for details. + + connect_port() MUST be called at least once for each port before run() + is called, unless that port is lv2:connectionOptional. The plugin must + pay careful attention to the block size passed to run() since the block + allocated may only just be large enough to contain the data, and is not + guaranteed to remain constant between run() calls. + + connect_port() may be called more than once for a plugin instance to + allow the host to change the buffers that the plugin is reading or + writing. These calls may be made before or after activate() or + deactivate() calls. + + @param instance Plugin instance containing the port. + + @param port Index of the port to connect. The host MUST NOT try to + connect a port index that is not defined in the plugin's RDF data. If + it does, the plugin's behaviour is undefined (a crash is likely). + + @param data_location Pointer to data of the type defined by the port + type in the plugin's RDF data (e.g. an array of float for an + lv2:AudioPort). This pointer must be stored by the plugin instance and + used to read/write data when run() is called. Data present at the time + of the connect_port() call MUST NOT be considered meaningful. + */ + void (*connect_port)(LV2_Handle instance, + uint32_t port, + void * data_location); + + /** + Initialise a plugin instance and activate it for use. + + This is separated from instantiate() to aid real-time support and so + that hosts can reinitialise a plugin instance by calling deactivate() + and then activate(). In this case the plugin instance MUST reset all + state information dependent on the history of the plugin instance except + for any data locations provided by connect_port(). If there is nothing + for activate() to do then this field may be NULL. + + When present, hosts MUST call this function once before run() is called + for the first time. This call SHOULD be made as close to the run() call + as possible and indicates to real-time plugins that they are now live, + however plugins MUST NOT rely on a prompt call to run() after + activate(). + + The host MUST NOT call activate() again until deactivate() has been + called first. If a host calls activate(), it MUST call deactivate() at + some point in the future. Note that connect_port() may be called before + or after activate(). + */ + void (*activate)(LV2_Handle instance); + + /** + Run a plugin instance for a block. + + Note that if an activate() function exists then it must be called before + run(). If deactivate() is called for a plugin instance then run() may + not be called until activate() has been called again. + + If the plugin has the feature lv2:hardRTCapable then there are various + things that the plugin MUST NOT do within the run() function (see + lv2core.ttl for details). + + As a special case, when `sample_count` is 0, the plugin should update + any output ports that represent a single instant in time (e.g. control + ports, but not audio ports). This is particularly useful for latent + plugins, which should update their latency output port so hosts can + pre-roll plugins to compute latency. Plugins MUST NOT crash when + `sample_count` is 0. + + @param instance Instance to be run. + + @param sample_count The block size (in samples) for which the plugin + instance must run. + */ + void (*run)(LV2_Handle instance, + uint32_t sample_count); + + /** + Deactivate a plugin instance (counterpart to activate()). + + Hosts MUST deactivate all activated instances after they have been run() + for the last time. This call SHOULD be made as close to the last run() + call as possible and indicates to real-time plugins that they are no + longer live, however plugins MUST NOT rely on prompt deactivation. If + there is nothing for deactivate() to do then this field may be NULL + + Deactivation is not similar to pausing since the plugin instance will be + reinitialised by activate(). However, deactivate() itself MUST NOT fully + reset plugin state. For example, the host may deactivate a plugin, then + store its state (using some extension to do so). + + Hosts MUST NOT call deactivate() unless activate() was previously + called. Note that connect_port() may be called before or after + deactivate(). + */ + void (*deactivate)(LV2_Handle instance); + + /** + Clean up a plugin instance (counterpart to instantiate()). + + Once an instance of a plugin has been finished with it must be deleted + using this function. The instance handle passed ceases to be valid after + this call. + + If activate() was called for a plugin instance then a corresponding call + to deactivate() MUST be made before cleanup() is called. Hosts MUST NOT + call cleanup() unless instantiate() was previously called. + */ + void (*cleanup)(LV2_Handle instance); + + /** + Return additional plugin data defined by some extenion. + + A typical use of this facility is to return a struct containing function + pointers to extend the LV2_Descriptor API. + + The actual type and meaning of the returned object MUST be specified + precisely by the extension. This function MUST return NULL for any + unsupported URI. If a plugin does not support any extension data, this + field may be NULL. + + The host is never responsible for freeing the returned value. + */ + const void * (*extension_data)(const char * uri); +} LV2_Descriptor; + +/** + Helper macro needed for LV2_SYMBOL_EXPORT when using C++. +*/ +#ifdef __cplusplus +# define LV2_SYMBOL_EXTERN extern "C" +#else +# define LV2_SYMBOL_EXTERN +#endif + +/** + Put this (LV2_SYMBOL_EXPORT) before any functions that are to be loaded + by the host as a symbol from the dynamic library. +*/ +#ifdef _WIN32 +# define LV2_SYMBOL_EXPORT LV2_SYMBOL_EXTERN __declspec(dllexport) +#else +# define LV2_SYMBOL_EXPORT LV2_SYMBOL_EXTERN __attribute__((visibility("default"))) +#endif + +/** + Prototype for plugin accessor function. + + Plugins are discovered by hosts using RDF data (not by loading libraries). + See http://lv2plug.in for details on the discovery process, though most + hosts should use an existing library to implement this functionality. + + This is the simple plugin discovery API, suitable for most statically + defined plugins. Advanced plugins that need access to their bundle during + discovery can use lv2_lib_descriptor() instead. Plugin libraries MUST + include a function called "lv2_descriptor" or "lv2_lib_descriptor" with + C-style linkage, but SHOULD provide "lv2_descriptor" wherever possible. + + When it is time to load a plugin (designated by its URI), the host loads the + plugin's library, gets the lv2_descriptor() function from it, and uses this + function to find the LV2_Descriptor for the desired plugin. Plugins are + accessed by index using values from 0 upwards. This function MUST return + NULL for out of range indices, so the host can enumerate plugins by + increasing `index` until NULL is returned. + + Note that `index` has no meaning, hosts MUST NOT depend on it remaining + consistent between loads of the plugin library. +*/ +LV2_SYMBOL_EXPORT +const LV2_Descriptor * lv2_descriptor(uint32_t index); + +/** + Type of the lv2_descriptor() function in a library (old discovery API). +*/ +typedef const LV2_Descriptor * +(*LV2_Descriptor_Function)(uint32_t index); + +/** + Handle for a library descriptor. +*/ +typedef void* LV2_Lib_Handle; + +/** + Descriptor for a plugin library. + + To access a plugin library, the host creates an LV2_Lib_Descriptor via the + lv2_lib_descriptor() function in the shared object. +*/ +typedef struct { + /** + Opaque library data which must be passed as the first parameter to all + the methods of this struct. + */ + LV2_Lib_Handle handle; + + /** + The total size of this struct. This allows for this struct to be + expanded in the future if necessary. This MUST be set by the library to + sizeof(LV2_Lib_Descriptor). The host MUST NOT access any fields of this + struct beyond get_plugin() unless this field indicates they are present. + */ + uint32_t size; + + /** + Destroy this library descriptor and free all related resources. + */ + void (*cleanup)(LV2_Lib_Handle handle); + + /** + Plugin accessor. + + Plugins are accessed by index using values from 0 upwards. Out of range + indices MUST result in this function returning NULL, so the host can + enumerate plugins by increasing `index` until NULL is returned. + */ + const LV2_Descriptor * (*get_plugin)(LV2_Lib_Handle handle, + uint32_t index); +} LV2_Lib_Descriptor; + +/** + Prototype for library accessor function. + + This is the more advanced discovery API, which allows plugin libraries to + access their bundles during discovery, which makes it possible for plugins to + be dynamically defined by files in their bundle. This API also has an + explicit cleanup function, removing any need for non-portable shared library + destructors. Simple plugins that do not require these features may use + lv2_descriptor() instead. + + This is the entry point for a plugin library. Hosts load this symbol from + the library and call this function to obtain a library descriptor which can + be used to access all the contained plugins. The returned object must not + be destroyed (using LV2_Lib_Descriptor::cleanup()) until all plugins loaded + from that library have been destroyed. +*/ +LV2_SYMBOL_EXPORT +const LV2_Lib_Descriptor * +lv2_lib_descriptor(const char * bundle_path, + const LV2_Feature *const * features); + +/** + Type of the lv2_lib_descriptor() function in an LV2 library. +*/ +typedef const LV2_Lib_Descriptor * +(*LV2_Lib_Descriptor_Function)(const char * bundle_path, + const LV2_Feature *const * features); + +#ifdef __cplusplus +} +#endif + +#endif /* LV2_H_INCLUDED */ + +/** + @} +*/ diff --git a/modules/juce_audio_plugin_client/LV2/includes/lv2_external_ui.h b/modules/juce_audio_plugin_client/LV2/includes/lv2_external_ui.h new file mode 100755 index 0000000000..789cd3fff1 --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/lv2_external_ui.h @@ -0,0 +1,109 @@ +/* + LV2 External UI extension + This work is in public domain. + + This file 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. + + If you have questions, contact Filipe Coelho (aka falkTX) + or ask in #lad channel, FreeNode IRC network. +*/ + +/** + @file lv2_external_ui.h + C header for the LV2 External UI extension . +*/ + +#ifndef LV2_EXTERNAL_UI_H +#define LV2_EXTERNAL_UI_H + +#include "ui.h" + +#define LV2_EXTERNAL_UI_URI "http://kxstudio.sf.net/ns/lv2ext/external-ui" +#define LV2_EXTERNAL_UI_PREFIX LV2_EXTERNAL_UI_URI "#" + +#define LV2_EXTERNAL_UI__Host LV2_EXTERNAL_UI_PREFIX "Host" +#define LV2_EXTERNAL_UI__Widget LV2_EXTERNAL_UI_PREFIX "Widget" + +/** This extension used to be defined by a lv2plug.in URI */ +#define LV2_EXTERNAL_UI_DEPRECATED_URI "http://lv2plug.in/ns/extensions/ui#external" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * When LV2_EXTERNAL_UI__Widget UI is instantiated, the returned + * LV2UI_Widget handle must be cast to pointer to LV2_External_UI_Widget. + * UI is created in invisible state. + */ +typedef struct _LV2_External_UI_Widget { + /** + * Host calls this function regulary. UI library implementing the + * callback may do IPC or redraw the UI. + * + * @param _this_ the UI context + */ + void (*run)(struct _LV2_External_UI_Widget * _this_); + + /** + * Host calls this function to make the plugin UI visible. + * + * @param _this_ the UI context + */ + void (*show)(struct _LV2_External_UI_Widget * _this_); + + /** + * Host calls this function to make the plugin UI invisible again. + * + * @param _this_ the UI context + */ + void (*hide)(struct _LV2_External_UI_Widget * _this_); + +} LV2_External_UI_Widget; + +#define LV2_EXTERNAL_UI_RUN(ptr) (ptr)->run(ptr) +#define LV2_EXTERNAL_UI_SHOW(ptr) (ptr)->show(ptr) +#define LV2_EXTERNAL_UI_HIDE(ptr) (ptr)->hide(ptr) + +/** + * On UI instantiation, host must supply LV2_EXTERNAL_UI__Host feature. + * LV2_Feature::data must be pointer to LV2_External_UI_Host. + */ +typedef struct _LV2_External_UI_Host { + /** + * Callback that plugin UI will call when UI (GUI window) is closed by user. + * This callback will be called during execution of LV2_External_UI_Widget::run() + * (i.e. not from background thread). + * + * After this callback is called, UI is defunct. Host must call LV2UI_Descriptor::cleanup(). + * If host wants to make the UI visible again, the UI must be reinstantiated. + * + * @note When using the depreated URI LV2_EXTERNAL_UI_DEPRECATED_URI, + * some hosts will not call LV2UI_Descriptor::cleanup() as they should, + * and may call show() again without re-initialization. + * + * @param controller Host context associated with plugin UI, as + * supplied to LV2UI_Descriptor::instantiate(). + */ + void (*ui_closed)(LV2UI_Controller controller); + + /** + * Optional (may be NULL) "user friendly" identifier which the UI + * may display to allow a user to easily associate this particular + * UI instance with the correct plugin instance as it is represented + * by the host (e.g. "track 1" or "channel 4"). + * + * If supplied by host, the string will be referenced only during + * LV2UI_Descriptor::instantiate() + */ + const char * plugin_human_id; + +} LV2_External_UI_Host; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_EXTERNAL_UI_H */ diff --git a/modules/juce_audio_plugin_client/LV2/includes/lv2_programs.h b/modules/juce_audio_plugin_client/LV2/includes/lv2_programs.h new file mode 100644 index 0000000000..01e287718d --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/lv2_programs.h @@ -0,0 +1,174 @@ +/* + LV2 Programs Extension + Copyright 2012 Filipe Coelho + + 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 lv2_programs.h + C header for the LV2 programs extension . +*/ + +#ifndef LV2_PROGRAMS_H +#define LV2_PROGRAMS_H + +#include "lv2.h" +#include "ui.h" + +#define LV2_PROGRAMS_URI "http://kxstudio.sf.net/ns/lv2ext/programs" +#define LV2_PROGRAMS_PREFIX LV2_PROGRAMS_URI "#" + +#define LV2_PROGRAMS__Host LV2_PROGRAMS_PREFIX "Host" +#define LV2_PROGRAMS__Interface LV2_PROGRAMS_PREFIX "Interface" +#define LV2_PROGRAMS__UIInterface LV2_PROGRAMS_PREFIX "UIInterface" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* LV2_Programs_Handle; + +typedef struct _LV2_Program_Descriptor { + + /** Bank number for this program. Note that this extension does not + support MIDI-style separation of bank LSB and MSB values. There is + no restriction on the set of available banks: the numbers do not + need to be contiguous, there does not need to be a bank 0, etc. */ + uint32_t bank; + + /** Program number (unique within its bank) for this program. There is + no restriction on the set of available programs: the numbers do not + need to be contiguous, there does not need to be a program 0, etc. */ + uint32_t program; + + /** Name of the program. */ + const char * name; + +} LV2_Program_Descriptor; + +/** + Programs extension, plugin data. + + When the plugin's extension_data is called with argument LV2_PROGRAMS__Interface, + the plugin MUST return an LV2_Programs_Instance structure, which remains valid + for the lifetime of the plugin. +*/ +typedef struct _LV2_Programs_Interface { + /** + * get_program() + * + * This member is a function pointer that provides a description + * of a program (named preset sound) available on this plugin. + * + * The index argument is an index into the plugin's list of + * programs, not a program number as represented by the Program + * field of the LV2_Program_Descriptor. (This distinction is + * needed to support plugins that use non-contiguous program or + * bank numbers.) + * + * This function returns a LV2_Program_Descriptor pointer that is + * guaranteed to be valid only until the next call to get_program + * or deactivate, on the same plugin instance. This function must + * return NULL if passed an index argument out of range, so that + * the host can use it to query the number of programs as well as + * their properties. + */ + const LV2_Program_Descriptor *(*get_program)(LV2_Handle handle, + uint32_t index); + + /** + * select_program() + * + * This member is a function pointer that selects a new program + * for this plugin. The program change should take effect + * immediately at the start of the next run() call. (This + * means that a host providing the capability of changing programs + * between any two notes on a track must vary the block size so as + * to place the program change at the right place. A host that + * wanted to avoid this would probably just instantiate a plugin + * for each program.) + * + * Plugins should ignore a select_program() call with an invalid + * bank or program. + * + * A plugin is not required to select any particular default + * program on activate(): it's the host's duty to set a program + * explicitly. + * + * A plugin is permitted to re-write the values of its input + * control ports when select_program is called. The host should + * re-read the input control port values and update its own + * records appropriately. (This is the only circumstance in which + * a LV2 plugin is allowed to modify its own control-input ports.) + */ + void (*select_program)(LV2_Handle handle, + uint32_t bank, + uint32_t program); + +} LV2_Programs_Interface; + +/** + Programs extension, UI data. + + When the UI's extension_data is called with argument LV2_PROGRAMS__UIInterface, + the UI MUST return an LV2_Programs_UI_Interface structure, which remains valid + for the lifetime of the UI. +*/ +typedef struct _LV2_Programs_UI_Interface { + /** + * select_program() + * + * This is exactly the same as select_program in LV2_Programs_Instance, + * but this struct relates to the UI instead of the plugin. + * + * When called, UIs should update their state to match the selected program. + */ + void (*select_program)(LV2UI_Handle handle, + uint32_t bank, + uint32_t program); + +} LV2_Programs_UI_Interface; + +/** + Feature data for LV2_PROGRAMS__Host. +*/ +typedef struct _LV2_Programs_Host { + /** + * Opaque host data. + */ + LV2_Programs_Handle handle; + + /** + * program_changed() + * + * Tell the host to reload a plugin's program. + * Parameter handle MUST be the 'handle' member of this struct. + * Parameter index is program index to change. + * When index is -1, host should reload all the programs. + * + * The plugin MUST NEVER call this function on a RT context or during run(). + * + * NOTE: This call is to inform the host about a program's bank, program or name change. + * It DOES NOT change the current selected program. + */ + void (*program_changed)(LV2_Programs_Handle handle, + int32_t index); + +} LV2_Programs_Host; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_PROGRAMS_H */ diff --git a/modules/juce_audio_plugin_client/LV2/includes/midi.h b/modules/juce_audio_plugin_client/LV2/includes/midi.h new file mode 100644 index 0000000000..3c8d23e806 --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/midi.h @@ -0,0 +1,232 @@ +/* + Copyright 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. +*/ + +/** + @defgroup midi MIDI + + Definitions of standard MIDI messages, see + for details. +*/ + +#ifndef LV2_MIDI_H +#define LV2_MIDI_H + +#include + +#ifdef __cplusplus +extern "C" { +#else +# include +#endif + +#define LV2_MIDI_URI "http://lv2plug.in/ns/ext/midi" +#define LV2_MIDI_PREFIX LV2_MIDI_URI "#" + +#define LV2_MIDI__ActiveSense LV2_MIDI_PREFIX "ActiveSense" +#define LV2_MIDI__Aftertouch LV2_MIDI_PREFIX "Aftertouch" +#define LV2_MIDI__Bender LV2_MIDI_PREFIX "Bender" +#define LV2_MIDI__ChannelPressure LV2_MIDI_PREFIX "ChannelPressure" +#define LV2_MIDI__Chunk LV2_MIDI_PREFIX "Chunk" +#define LV2_MIDI__Clock LV2_MIDI_PREFIX "Clock" +#define LV2_MIDI__Continue LV2_MIDI_PREFIX "Continue" +#define LV2_MIDI__Controller LV2_MIDI_PREFIX "Controller" +#define LV2_MIDI__MidiEvent LV2_MIDI_PREFIX "MidiEvent" +#define LV2_MIDI__NoteOff LV2_MIDI_PREFIX "NoteOff" +#define LV2_MIDI__NoteOn LV2_MIDI_PREFIX "NoteOn" +#define LV2_MIDI__ProgramChange LV2_MIDI_PREFIX "ProgramChange" +#define LV2_MIDI__QuarterFrame LV2_MIDI_PREFIX "QuarterFrame" +#define LV2_MIDI__Reset LV2_MIDI_PREFIX "Reset" +#define LV2_MIDI__SongPosition LV2_MIDI_PREFIX "SongPosition" +#define LV2_MIDI__SongSelect LV2_MIDI_PREFIX "SongSelect" +#define LV2_MIDI__Start LV2_MIDI_PREFIX "Start" +#define LV2_MIDI__Stop LV2_MIDI_PREFIX "Stop" +#define LV2_MIDI__SystemCommon LV2_MIDI_PREFIX "SystemCommon" +#define LV2_MIDI__SystemExclusive LV2_MIDI_PREFIX "SystemExclusive" +#define LV2_MIDI__SystemMessage LV2_MIDI_PREFIX "SystemMessage" +#define LV2_MIDI__SystemRealtime LV2_MIDI_PREFIX "SystemRealtime" +#define LV2_MIDI__Tick LV2_MIDI_PREFIX "Tick" +#define LV2_MIDI__TuneRequest LV2_MIDI_PREFIX "TuneRequest" +#define LV2_MIDI__VoiceMessage LV2_MIDI_PREFIX "VoiceMessage" +#define LV2_MIDI__benderValue LV2_MIDI_PREFIX "benderValue" +#define LV2_MIDI__binding LV2_MIDI_PREFIX "binding" +#define LV2_MIDI__byteNumber LV2_MIDI_PREFIX "byteNumber" +#define LV2_MIDI__channel LV2_MIDI_PREFIX "channel" +#define LV2_MIDI__chunk LV2_MIDI_PREFIX "chunk" +#define LV2_MIDI__controllerNumber LV2_MIDI_PREFIX "controllerNumber" +#define LV2_MIDI__controllerValue LV2_MIDI_PREFIX "controllerValue" +#define LV2_MIDI__noteNumber LV2_MIDI_PREFIX "noteNumber" +#define LV2_MIDI__pressure LV2_MIDI_PREFIX "pressure" +#define LV2_MIDI__programNumber LV2_MIDI_PREFIX "programNumber" +#define LV2_MIDI__property LV2_MIDI_PREFIX "property" +#define LV2_MIDI__songNumber LV2_MIDI_PREFIX "songNumber" +#define LV2_MIDI__songPosition LV2_MIDI_PREFIX "songPosition" +#define LV2_MIDI__status LV2_MIDI_PREFIX "status" +#define LV2_MIDI__statusMask LV2_MIDI_PREFIX "statusMask" +#define LV2_MIDI__velocity LV2_MIDI_PREFIX "velocity" + +/** + MIDI Message Type. + + This includes both voice messages (which have a channel) and system messages + (which do not), as well as a sentinel value for invalid messages. To get + the type of a message suitable for use in a switch statement, use + lv2_midi_get_type() on the status byte. +*/ +typedef enum { + LV2_MIDI_MSG_INVALID = 0, /**< Invalid Message */ + LV2_MIDI_MSG_NOTE_OFF = 0x80, /**< Note Off */ + LV2_MIDI_MSG_NOTE_ON = 0x90, /**< Note On */ + LV2_MIDI_MSG_NOTE_PRESSURE = 0xA0, /**< Note Pressure */ + LV2_MIDI_MSG_CONTROLLER = 0xB0, /**< Controller */ + LV2_MIDI_MSG_PGM_CHANGE = 0xC0, /**< Program Change */ + LV2_MIDI_MSG_CHANNEL_PRESSURE = 0xD0, /**< Channel Pressure */ + LV2_MIDI_MSG_BENDER = 0xE0, /**< Pitch Bender */ + LV2_MIDI_MSG_SYSTEM_EXCLUSIVE = 0xF0, /**< System Exclusive Begin */ + LV2_MIDI_MSG_MTC_QUARTER = 0xF1, /**< MTC Quarter Frame */ + LV2_MIDI_MSG_SONG_POS = 0xF2, /**< Song Position */ + LV2_MIDI_MSG_SONG_SELECT = 0xF3, /**< Song Select */ + LV2_MIDI_MSG_TUNE_REQUEST = 0xF6, /**< Tune Request */ + LV2_MIDI_MSG_CLOCK = 0xF8, /**< Clock */ + LV2_MIDI_MSG_START = 0xFA, /**< Start */ + LV2_MIDI_MSG_CONTINUE = 0xFB, /**< Continue */ + LV2_MIDI_MSG_STOP = 0xFC, /**< Stop */ + LV2_MIDI_MSG_ACTIVE_SENSE = 0xFE, /**< Active Sensing */ + LV2_MIDI_MSG_RESET = 0xFF /**< Reset */ +} LV2_Midi_Message_Type; + +/** + Standard MIDI Controller Numbers. +*/ +typedef enum { + LV2_MIDI_CTL_MSB_BANK = 0x00, /**< Bank Selection */ + LV2_MIDI_CTL_MSB_MODWHEEL = 0x01, /**< Modulation */ + LV2_MIDI_CTL_MSB_BREATH = 0x02, /**< Breath */ + LV2_MIDI_CTL_MSB_FOOT = 0x04, /**< Foot */ + LV2_MIDI_CTL_MSB_PORTAMENTO_TIME = 0x05, /**< Portamento Time */ + LV2_MIDI_CTL_MSB_DATA_ENTRY = 0x06, /**< Data Entry */ + LV2_MIDI_CTL_MSB_MAIN_VOLUME = 0x07, /**< Main Volume */ + LV2_MIDI_CTL_MSB_BALANCE = 0x08, /**< Balance */ + LV2_MIDI_CTL_MSB_PAN = 0x0A, /**< Panpot */ + LV2_MIDI_CTL_MSB_EXPRESSION = 0x0B, /**< Expression */ + LV2_MIDI_CTL_MSB_EFFECT1 = 0x0C, /**< Effect1 */ + LV2_MIDI_CTL_MSB_EFFECT2 = 0x0D, /**< Effect2 */ + LV2_MIDI_CTL_MSB_GENERAL_PURPOSE1 = 0x10, /**< General Purpose 1 */ + LV2_MIDI_CTL_MSB_GENERAL_PURPOSE2 = 0x11, /**< General Purpose 2 */ + LV2_MIDI_CTL_MSB_GENERAL_PURPOSE3 = 0x12, /**< General Purpose 3 */ + LV2_MIDI_CTL_MSB_GENERAL_PURPOSE4 = 0x13, /**< General Purpose 4 */ + LV2_MIDI_CTL_LSB_BANK = 0x20, /**< Bank Selection */ + LV2_MIDI_CTL_LSB_MODWHEEL = 0x21, /**< Modulation */ + LV2_MIDI_CTL_LSB_BREATH = 0x22, /**< Breath */ + LV2_MIDI_CTL_LSB_FOOT = 0x24, /**< Foot */ + LV2_MIDI_CTL_LSB_PORTAMENTO_TIME = 0x25, /**< Portamento Time */ + LV2_MIDI_CTL_LSB_DATA_ENTRY = 0x26, /**< Data Entry */ + LV2_MIDI_CTL_LSB_MAIN_VOLUME = 0x27, /**< Main Volume */ + LV2_MIDI_CTL_LSB_BALANCE = 0x28, /**< Balance */ + LV2_MIDI_CTL_LSB_PAN = 0x2A, /**< Panpot */ + LV2_MIDI_CTL_LSB_EXPRESSION = 0x2B, /**< Expression */ + LV2_MIDI_CTL_LSB_EFFECT1 = 0x2C, /**< Effect1 */ + LV2_MIDI_CTL_LSB_EFFECT2 = 0x2D, /**< Effect2 */ + LV2_MIDI_CTL_LSB_GENERAL_PURPOSE1 = 0x30, /**< General Purpose 1 */ + LV2_MIDI_CTL_LSB_GENERAL_PURPOSE2 = 0x31, /**< General Purpose 2 */ + LV2_MIDI_CTL_LSB_GENERAL_PURPOSE3 = 0x32, /**< General Purpose 3 */ + LV2_MIDI_CTL_LSB_GENERAL_PURPOSE4 = 0x33, /**< General Purpose 4 */ + LV2_MIDI_CTL_SUSTAIN = 0x40, /**< Sustain Pedal */ + LV2_MIDI_CTL_PORTAMENTO = 0x41, /**< Portamento */ + LV2_MIDI_CTL_SOSTENUTO = 0x42, /**< Sostenuto */ + LV2_MIDI_CTL_SOFT_PEDAL = 0x43, /**< Soft Pedal */ + LV2_MIDI_CTL_LEGATO_FOOTSWITCH = 0x44, /**< Legato Foot Switch */ + LV2_MIDI_CTL_HOLD2 = 0x45, /**< Hold2 */ + LV2_MIDI_CTL_SC1_SOUND_VARIATION = 0x46, /**< SC1 Sound Variation */ + LV2_MIDI_CTL_SC2_TIMBRE = 0x47, /**< SC2 Timbre */ + LV2_MIDI_CTL_SC3_RELEASE_TIME = 0x48, /**< SC3 Release Time */ + LV2_MIDI_CTL_SC4_ATTACK_TIME = 0x49, /**< SC4 Attack Time */ + LV2_MIDI_CTL_SC5_BRIGHTNESS = 0x4A, /**< SC5 Brightness */ + LV2_MIDI_CTL_SC6 = 0x4B, /**< SC6 */ + LV2_MIDI_CTL_SC7 = 0x4C, /**< SC7 */ + LV2_MIDI_CTL_SC8 = 0x4D, /**< SC8 */ + LV2_MIDI_CTL_SC9 = 0x4E, /**< SC9 */ + LV2_MIDI_CTL_SC10 = 0x4F, /**< SC10 */ + LV2_MIDI_CTL_GENERAL_PURPOSE5 = 0x50, /**< General Purpose 5 */ + LV2_MIDI_CTL_GENERAL_PURPOSE6 = 0x51, /**< General Purpose 6 */ + LV2_MIDI_CTL_GENERAL_PURPOSE7 = 0x52, /**< General Purpose 7 */ + LV2_MIDI_CTL_GENERAL_PURPOSE8 = 0x53, /**< General Purpose 8 */ + LV2_MIDI_CTL_PORTAMENTO_CONTROL = 0x54, /**< Portamento Control */ + LV2_MIDI_CTL_E1_REVERB_DEPTH = 0x5B, /**< E1 Reverb Depth */ + LV2_MIDI_CTL_E2_TREMOLO_DEPTH = 0x5C, /**< E2 Tremolo Depth */ + LV2_MIDI_CTL_E3_CHORUS_DEPTH = 0x5D, /**< E3 Chorus Depth */ + LV2_MIDI_CTL_E4_DETUNE_DEPTH = 0x5E, /**< E4 Detune Depth */ + LV2_MIDI_CTL_E5_PHASER_DEPTH = 0x5F, /**< E5 Phaser Depth */ + LV2_MIDI_CTL_DATA_INCREMENT = 0x60, /**< Data Increment */ + LV2_MIDI_CTL_DATA_DECREMENT = 0x61, /**< Data Decrement */ + LV2_MIDI_CTL_NRPN_LSB = 0x62, /**< Non-registered Parameter Number */ + LV2_MIDI_CTL_NRPN_MSB = 0x63, /**< Non-registered Parameter Number */ + LV2_MIDI_CTL_RPN_LSB = 0x64, /**< Registered Parameter Number */ + LV2_MIDI_CTL_RPN_MSB = 0x65, /**< Registered Parameter Number */ + LV2_MIDI_CTL_ALL_SOUNDS_OFF = 0x78, /**< All Sounds Off */ + LV2_MIDI_CTL_RESET_CONTROLLERS = 0x79, /**< Reset Controllers */ + LV2_MIDI_CTL_LOCAL_CONTROL_SWITCH = 0x7A, /**< Local Control Switch */ + LV2_MIDI_CTL_ALL_NOTES_OFF = 0x7B, /**< All Notes Off */ + LV2_MIDI_CTL_OMNI_OFF = 0x7C, /**< Omni Off */ + LV2_MIDI_CTL_OMNI_ON = 0x7D, /**< Omni On */ + LV2_MIDI_CTL_MONO1 = 0x7E, /**< Mono1 */ + LV2_MIDI_CTL_MONO2 = 0x7F /**< Mono2 */ +} LV2_Midi_Controller; + +/** + Return true iff `msg` is a MIDI voice message (which has a channel). +*/ +static inline bool +lv2_midi_is_voice_message(const uint8_t* msg) { + return msg[0] >= 0x80 && msg[0] < 0xF0; +} + +/** + Return true iff `msg` is a MIDI system message (which has no channel). +*/ +static inline bool +lv2_midi_is_system_message(const uint8_t* msg) { + switch (msg[0]) { + case 0xF4: case 0xF5: case 0xF7: case 0xF9: case 0xFD: + return false; + default: + return (msg[0] & 0xF0) == 0xF0; + } +} + +/** + Return the type of a MIDI message. + @param msg Pointer to the start (status byte) of a MIDI message. +*/ +static inline LV2_Midi_Message_Type +lv2_midi_message_type(const uint8_t* msg) { + if (lv2_midi_is_voice_message(msg)) { + return (LV2_Midi_Message_Type)(msg[0] & 0xF0); + } else if (lv2_midi_is_system_message(msg)) { + return (LV2_Midi_Message_Type)msg[0]; + } else { + return LV2_MIDI_MSG_INVALID; + } +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_MIDI_H */ + +/** + @} +*/ diff --git a/modules/juce_audio_plugin_client/LV2/includes/options.h b/modules/juce_audio_plugin_client/LV2/includes/options.h new file mode 100644 index 0000000000..887fdc940a --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/options.h @@ -0,0 +1,145 @@ +/* + Copyright 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. +*/ + +/** + @defgroup options Options + + Instantiation time options, see for + details. + + @{ +*/ + +#ifndef LV2_OPTIONS_H +#define LV2_OPTIONS_H + +#include + +#include "lv2.h" +#include "urid.h" + +#define LV2_OPTIONS_URI "http://lv2plug.in/ns/ext/options" +#define LV2_OPTIONS_PREFIX LV2_OPTIONS_URI "#" + +#define LV2_OPTIONS__Option LV2_OPTIONS_PREFIX "Option" +#define LV2_OPTIONS__interface LV2_OPTIONS_PREFIX "interface" +#define LV2_OPTIONS__options LV2_OPTIONS_PREFIX "options" +#define LV2_OPTIONS__requiredOption LV2_OPTIONS_PREFIX "requiredOption" +#define LV2_OPTIONS__supportedOption LV2_OPTIONS_PREFIX "supportedOption" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + The context of an Option, which defines the subject it applies to. +*/ +typedef enum { + /** + This option applies to the instance itself. The subject must be + ignored. + */ + LV2_OPTIONS_INSTANCE, + + /** + This option applies to some named resource. The subject is a URI mapped + to an integer (a LV2_URID, like the key) + */ + LV2_OPTIONS_RESOURCE, + + /** + This option applies to some blank node. The subject is a blank node + identifier, which is valid only within the current local scope. + */ + LV2_OPTIONS_BLANK, + + /** + This option applies to a port on the instance. The subject is the + port's index. + */ + LV2_OPTIONS_PORT +} LV2_Options_Context; + +/** + An option. + + This is a property with a subject, also known as a triple or statement. + + This struct is useful anywhere a statement needs to be passed where no + memory ownership issues are present (since the value is a const pointer). + + Options can be passed to an instance via the feature LV2_OPTIONS__options + with data pointed to an array of options terminated by a zeroed option, or + accessed/manipulated using LV2_Options_Interface. +*/ +typedef struct _LV2_Options_Option { + LV2_Options_Context context; /**< Context (type of subject). */ + uint32_t subject; /**< Subject. */ + LV2_URID key; /**< Key (property). */ + uint32_t size; /**< Size of value in bytes. */ + LV2_URID type; /**< Type of value (datatype). */ + const void* value; /**< Pointer to value (object). */ +} LV2_Options_Option; + +/** A status code for option functions. */ +typedef enum { + LV2_OPTIONS_SUCCESS = 0, /**< Completed successfully. */ + LV2_OPTIONS_ERR_UNKNOWN = 1, /**< Unknown error. */ + LV2_OPTIONS_ERR_BAD_SUBJECT = 1 << 1, /**< Invalid/unsupported subject. */ + LV2_OPTIONS_ERR_BAD_KEY = 1 << 2, /**< Invalid/unsupported key. */ + LV2_OPTIONS_ERR_BAD_VALUE = 1 << 3 /**< Invalid/unsupported value. */ +} LV2_Options_Status; + +/** + Interface for dynamically setting options (LV2_OPTIONS__interface). +*/ +typedef struct _LV2_Options_Interface { + /** + Get the given options. + + Each element of the passed options array MUST have type, subject, and + key set. All other fields (size, type, value) MUST be initialised to + zero, and are set to the option value if such an option is found. + + This function is in the "instantiation" LV2 threading class, so no other + instance functions may be called concurrently. + + @return Bitwise OR of LV2_Options_Status values. + */ + uint32_t (*get)(LV2_Handle instance, + LV2_Options_Option* options); + + /** + Set the given options. + + This function is in the "instantiation" LV2 threading class, so no other + instance functions may be called concurrently. + + @return Bitwise OR of LV2_Options_Status values. + */ + uint32_t (*set)(LV2_Handle instance, + const LV2_Options_Option* options); +} LV2_Options_Interface; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_OPTIONS_H */ + +/** + @} +*/ diff --git a/modules/juce_audio_plugin_client/LV2/includes/port-props.h b/modules/juce_audio_plugin_client/LV2/includes/port-props.h new file mode 100644 index 0000000000..32e110ae82 --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/port-props.h @@ -0,0 +1,48 @@ +/* + Copyright 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. +*/ + +/** + @defgroup port-props Port Properties + + Various port properties. + + @{ +*/ + +#ifndef LV2_PORT_PROPS_H +#define LV2_PORT_PROPS_H + +#define LV2_PORT_PROPS_URI "http://lv2plug.in/ns/ext/port-props" +#define LV2_PORT_PROPS_PREFIX LV2_PORT_PROPS_URI "#" + +#define LV2_PORT_PROPS__causesArtifacts LV2_PORT_PROPS_PREFIX "causesArtifacts" +#define LV2_PORT_PROPS__continuousCV LV2_PORT_PROPS_PREFIX "continuousCV" +#define LV2_PORT_PROPS__discreteCV LV2_PORT_PROPS_PREFIX "discreteCV" +#define LV2_PORT_PROPS__displayPriority LV2_PORT_PROPS_PREFIX "displayPriority" +#define LV2_PORT_PROPS__expensive LV2_PORT_PROPS_PREFIX "expensive" +#define LV2_PORT_PROPS__hasStrictBounds LV2_PORT_PROPS_PREFIX "hasStrictBounds" +#define LV2_PORT_PROPS__logarithmic LV2_PORT_PROPS_PREFIX "logarithmic" +#define LV2_PORT_PROPS__notAutomatic LV2_PORT_PROPS_PREFIX "notAutomatic" +#define LV2_PORT_PROPS__notOnGUI LV2_PORT_PROPS_PREFIX "notOnGUI" +#define LV2_PORT_PROPS__rangeSteps LV2_PORT_PROPS_PREFIX "rangeSteps" +#define LV2_PORT_PROPS__supportsStrictBounds LV2_PORT_PROPS_PREFIX "supportsStrictBounds" +#define LV2_PORT_PROPS__trigger LV2_PORT_PROPS_PREFIX "trigger" + +#endif /* LV2_PORT_PROPS_H */ + +/** + @} +*/ diff --git a/modules/juce_audio_plugin_client/LV2/includes/presets.h b/modules/juce_audio_plugin_client/LV2/includes/presets.h new file mode 100644 index 0000000000..22d91a6931 --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/presets.h @@ -0,0 +1,39 @@ +/* + Copyright 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. +*/ + +/** + @defgroup presets Presets + + Presets for plugins, see for details. + + @{ +*/ + +#ifndef LV2_PRESETS_H +#define LV2_PRESETS_H + +#define LV2_PRESETS_URI "http://lv2plug.in/ns/ext/presets" +#define LV2_PRESETS_PREFIX LV2_PRESETS_URI "#" + +#define LV2_PRESETS__Preset LV2_PRESETS_PREFIX "Preset" +#define LV2_PRESETS__preset LV2_PRESETS_PREFIX "preset" +#define LV2_PRESETS__value LV2_PRESETS_PREFIX "value" + +#endif /* LV2_PRESETS_H */ + +/** + @} +*/ diff --git a/modules/juce_audio_plugin_client/LV2/includes/state.h b/modules/juce_audio_plugin_client/LV2/includes/state.h new file mode 100644 index 0000000000..100c64b64d --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/state.h @@ -0,0 +1,360 @@ +/* + Copyright 2010-2012 David Robillard + Copyright 2010 Leonard Ritter + + 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. +*/ + +/** + @defgroup state State + + An interface for LV2 plugins to save and restore state, see + for details. + + @{ +*/ + +#ifndef LV2_STATE_H +#define LV2_STATE_H + +#include +#include + +#include "lv2.h" + +#define LV2_STATE_URI "http://lv2plug.in/ns/ext/state" +#define LV2_STATE_PREFIX LV2_STATE_URI "#" + +#define LV2_STATE__State LV2_STATE_PREFIX "State" +#define LV2_STATE__interface LV2_STATE_PREFIX "interface" +#define LV2_STATE__loadDefaultState LV2_STATE_PREFIX "loadDefaultState" +#define LV2_STATE__makePath LV2_STATE_PREFIX "makePath" +#define LV2_STATE__mapPath LV2_STATE_PREFIX "mapPath" +#define LV2_STATE__state LV2_STATE_PREFIX "state" + +#ifdef __cplusplus +extern "C" { +#else +# include +#endif + +typedef void* LV2_State_Handle; +typedef void* LV2_State_Map_Path_Handle; +typedef void* LV2_State_Make_Path_Handle; + +/** + Flags describing value characteristics. + + These flags are used along with the value's type URI to determine how to + (de-)serialise the value data, or whether it is even possible to do so. +*/ +typedef enum { + /** + Plain Old Data. + + Values with this flag contain no pointers or references to other areas + of memory. It is safe to copy POD values with a simple memcpy and store + them for the duration of the process. A POD value is not necessarily + safe to trasmit between processes or machines (e.g. filenames are POD), + see LV2_STATE_IS_PORTABLE for details. + + Implementations MUST NOT attempt to copy or serialise a non-POD value if + they do not understand its type (and thus know how to correctly do so). + */ + LV2_STATE_IS_POD = 1, + + /** + Portable (architecture independent) data. + + Values with this flag are in a format that is usable on any + architecture. A portable value saved on one machine can be restored on + another machine regardless of architecture. The format of portable + values MUST NOT depend on architecture-specific properties like + endianness or alignment. Portable values MUST NOT contain filenames. + */ + LV2_STATE_IS_PORTABLE = 1 << 1, + + /** + Native data. + + This flag is used by the host to indicate that the saved data is only + going to be used locally in the currently running process (e.g. for + instance duplication or snapshots), so the plugin should use the most + efficient representation possible and not worry about serialisation + and portability. + */ + LV2_STATE_IS_NATIVE = 1 << 2 +} LV2_State_Flags; + +/** A status code for state functions. */ +typedef enum { + LV2_STATE_SUCCESS = 0, /**< Completed successfully. */ + LV2_STATE_ERR_UNKNOWN = 1, /**< Unknown error. */ + LV2_STATE_ERR_BAD_TYPE = 2, /**< Failed due to unsupported type. */ + LV2_STATE_ERR_BAD_FLAGS = 3, /**< Failed due to unsupported flags. */ + LV2_STATE_ERR_NO_FEATURE = 4, /**< Failed due to missing features. */ + LV2_STATE_ERR_NO_PROPERTY = 5 /**< Failed due to missing property. */ +} LV2_State_Status; + +/** + A host-provided function to store a property. + @param handle Must be the handle passed to LV2_State_Interface.save(). + @param key The key to store `value` under (URID). + @param value Pointer to the value to be stored. + @param size The size of `value` in bytes. + @param type The type of `value` (URID). + @param flags LV2_State_Flags for `value`. + @return 0 on success, otherwise a non-zero error code. + + The host passes a callback of this type to LV2_State_Interface.save(). This + callback is called repeatedly by the plugin to store all the properties that + describe its current state. + + DO NOT INVENT NONSENSE URI SCHEMES FOR THE KEY. Best is to use keys from + existing vocabularies. If nothing appropriate is available, use http URIs + that point to somewhere you can host documents so documentation can be made + resolvable (e.g. a child of the plugin or project URI). If this is not + possible, invent a URN scheme, e.g. urn:myproj:whatever. The plugin MUST + NOT pass an invalid URI key. + + The host MAY fail to store a property for whatever reason, but SHOULD + store any property that is LV2_STATE_IS_POD and LV2_STATE_IS_PORTABLE. + Implementations SHOULD use the types from the LV2 Atom extension + (http://lv2plug.in/ns/ext/atom) wherever possible. The plugin SHOULD + attempt to fall-back and avoid the error if possible. + + Note that `size` MUST be > 0, and `value` MUST point to a valid region of + memory `size` bytes long (this is required to make restore unambiguous). + + The plugin MUST NOT attempt to use this function outside of the + LV2_State_Interface.restore() context. +*/ +typedef LV2_State_Status (*LV2_State_Store_Function)( + LV2_State_Handle handle, + uint32_t key, + const void* value, + size_t size, + uint32_t type, + uint32_t flags); + +/** + A host-provided function to retrieve a property. + @param handle Must be the handle passed to LV2_State_Interface.restore(). + @param key The key of the property to retrieve (URID). + @param size (Output) If non-NULL, set to the size of the restored value. + @param type (Output) If non-NULL, set to the type of the restored value. + @param flags (Output) If non-NULL, set to the flags for the restored value. + @return A pointer to the restored value (object), or NULL if no value + has been stored under `key`. + + A callback of this type is passed by the host to + LV2_State_Interface.restore(). This callback is called repeatedly by the + plugin to retrieve any properties it requires to restore its state. + + The returned value MUST remain valid until LV2_State_Interface.restore() + returns. The plugin MUST NOT attempt to use this function, or any value + returned from it, outside of the LV2_State_Interface.restore() context. +*/ +typedef const void* (*LV2_State_Retrieve_Function)( + LV2_State_Handle handle, + uint32_t key, + size_t* size, + uint32_t* type, + uint32_t* flags); + +/** + LV2 Plugin State Interface. + + When the plugin's extension_data is called with argument + LV2_STATE__interface, the plugin MUST return an LV2_State_Interface + structure, which remains valid for the lifetime of the plugin. + + The host can use the contained function pointers to save and restore the + state of a plugin instance at any time, provided the threading restrictions + of the functions are met. + + Stored data is only guaranteed to be compatible between instances of plugins + with the same URI (i.e. if a change to a plugin would cause a fatal error + when restoring state saved by a previous version of that plugin, the plugin + URI MUST change just as it must when ports change incompatibly). Plugin + authors should consider this possibility, and always store sensible data + with meaningful types to avoid such problems in the future. +*/ +typedef struct _LV2_State_Interface { + /** + Save plugin state using a host-provided `store` callback. + + @param instance The instance handle of the plugin. + @param store The host-provided store callback. + @param handle An opaque pointer to host data which MUST be passed as the + handle parameter to `store` if it is called. + @param flags Flags describing desired properties of this save. These + flags may be used to determine the most appropriate values to store. + @param features Extensible parameter for passing any additional + features to be used for this save. + + The plugin is expected to store everything necessary to completely + restore its state later. Plugins SHOULD store simple POD data whenever + possible, and consider the possibility of state being restored much + later on a different machine. + + The `handle` pointer and `store` function MUST NOT be used + beyond the scope of save(). + + This function has its own special threading class: it may not be called + concurrently with any "Instantiation" function, but it may be called + concurrently with functions in any other class, unless the definition of + that class prohibits it (e.g. it may not be called concurrently with a + "Discovery" function, but it may be called concurrently with an "Audio" + function. The plugin is responsible for any locking or lock-free + techniques necessary to make this possible. + + Note that in the simple case where state is only modified by restore(), + there are no synchronization issues since save() is never called + concurrently with restore() (though run() may read it during a save). + + Plugins that dynamically modify state while running, however, must take + care to do so in such a way that a concurrent call to save() will save a + consistent representation of plugin state for a single instant in time. + */ + LV2_State_Status (*save)(LV2_Handle instance, + LV2_State_Store_Function store, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features); + + /** + Restore plugin state using a host-provided `retrieve` callback. + + @param instance The instance handle of the plugin. + @param retrieve The host-provided retrieve callback. + @param handle An opaque pointer to host data which MUST be passed as the + handle parameter to `retrieve` if it is called. + @param flags Currently unused. + @param features Extensible parameter for passing any additional + features to be used for this restore. + + The plugin MAY assume a restored value was set by a previous call to + LV2_State_Interface.save() by a plugin with the same URI. + + The plugin MUST gracefully fall back to a default value when a value can + not be retrieved. This allows the host to reset the plugin state with + an empty map. + + The `handle` pointer and `store` function MUST NOT be used + beyond the scope of restore(). + + This function is in the "Instantiation" threading class as defined by + LV2. This means it MUST NOT be called concurrently with any other + function on the same plugin instance. + */ + LV2_State_Status (*restore)(LV2_Handle instance, + LV2_State_Retrieve_Function retrieve, + LV2_State_Handle handle, + uint32_t flags, + const LV2_Feature *const * features); +} LV2_State_Interface; + +/** + Feature data for state:mapPath (@ref LV2_STATE__mapPath). +*/ +typedef struct { + /** + Opaque host data. + */ + LV2_State_Map_Path_Handle handle; + + /** + Map an absolute path to an abstract path for use in plugin state. + @param handle MUST be the `handle` member of this struct. + @param absolute_path The absolute path of a file. + @return An abstract path suitable for use in plugin state. + + The plugin MUST use this function to map any paths that will be stored + in plugin state. The returned value is an abstract path which MAY not + be an actual file system path; absolute_path() MUST be used to map + it to an actual path in order to use the file. + + Plugins MUST NOT make any assumptions about abstract paths except that + they can be mapped back to the absolute path of the "same" file (though + not necessarily the same original path) using absolute_path(). + + This function may only be called within the context of + LV2_State_Interface methods. The caller is responsible for freeing the + returned value with free(). + */ + char* (*abstract_path)(LV2_State_Map_Path_Handle handle, + const char* absolute_path); + + /** + Map an abstract path from plugin state to an absolute path. + @param handle MUST be the `handle` member of this struct. + @param abstract_path An abstract path (e.g. a path from plugin state). + @return An absolute file system path. + + The plugin MUST use this function in order to actually open or otherwise + use any paths loaded from plugin state. + + This function may only be called within the context of + LV2_State_Interface methods. The caller is responsible for freeing the + returned value with free(). + */ + char* (*absolute_path)(LV2_State_Map_Path_Handle handle, + const char* abstract_path); +} LV2_State_Map_Path; + +/** + Feature data for state:makePath (@ref LV2_STATE__makePath). +*/ +typedef struct { + /** + Opaque host data. + */ + LV2_State_Make_Path_Handle handle; + + /** + Return a path the plugin may use to create a new file. + @param handle MUST be the `handle` member of this struct. + @param path The path of the new file within a namespace unique to this + plugin instance. + @return The absolute path to use for the new file. + + This function can be used by plugins to create files and directories, + either at state saving time (if this feature is passed to + LV2_State_Interface.save()) or any time (if this feature is passed to + LV2_Descriptor.instantiate()). + + The host MUST do whatever is necessary for the plugin to be able to + create a file at the returned path (e.g. using fopen), including + creating any leading directories. + + If this function is passed to LV2_Descriptor.instantiate(), it may be + called from any non-realtime context. If it is passed to + LV2_State_Interface.save(), it may only be called within the dynamic + scope of that function call. + + The caller is responsible for freeing the returned value with free(). + */ + char* (*path)(LV2_State_Make_Path_Handle handle, + const char* path); +} LV2_State_Make_Path; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_STATE_H */ + +/** + @} +*/ diff --git a/modules/juce_audio_plugin_client/LV2/includes/time.h b/modules/juce_audio_plugin_client/LV2/includes/time.h new file mode 100644 index 0000000000..14c930bf42 --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/time.h @@ -0,0 +1,52 @@ +/* + Copyright 2011 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. +*/ + +/** + @defgroup time Time + + Properties for describing time, see for + details. + + Note the time extension is purely data, this header merely defines URIs for + convenience. + + @{ +*/ + +#ifndef LV2_TIME_H +#define LV2_TIME_H + +#define LV2_TIME_URI "http://lv2plug.in/ns/ext/time" + +#define LV2_TIME__Time LV2_TIME_URI "#Time" +#define LV2_TIME__Position LV2_TIME_URI "#Position" +#define LV2_TIME__Rate LV2_TIME_URI "#Rate" +#define LV2_TIME__position LV2_TIME_URI "#position" +#define LV2_TIME__barBeat LV2_TIME_URI "#barBeat" +#define LV2_TIME__bar LV2_TIME_URI "#bar" +#define LV2_TIME__beat LV2_TIME_URI "#beat" +#define LV2_TIME__beatUnit LV2_TIME_URI "#beatUnit" +#define LV2_TIME__beatsPerBar LV2_TIME_URI "#beatsPerBar" +#define LV2_TIME__beatsPerMinute LV2_TIME_URI "#beatsPerMinute" +#define LV2_TIME__frame LV2_TIME_URI "#frame" +#define LV2_TIME__framesPerSecond LV2_TIME_URI "#framesPerSecond" +#define LV2_TIME__speed LV2_TIME_URI "#speed" + +/** + @} +*/ + +#endif /* LV2_TIME_H */ diff --git a/modules/juce_audio_plugin_client/LV2/includes/ui.h b/modules/juce_audio_plugin_client/LV2/includes/ui.h new file mode 100644 index 0000000000..aeb9631f56 --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/ui.h @@ -0,0 +1,441 @@ +/* + LV2 UI Extension + Copyright 2009-2014 David Robillard + Copyright 2006-2011 Lars Luthman + + 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. +*/ + +/** + @defgroup ui User Interfaces + + User interfaces of any type for plugins, + for details. + + @{ +*/ + +#ifndef LV2_UI_H +#define LV2_UI_H + +#include + +#include "lv2.h" + +#define LV2_UI_URI "http://lv2plug.in/ns/extensions/ui" +#define LV2_UI_PREFIX LV2_UI_URI "#" + +#define LV2_UI__CocoaUI LV2_UI_PREFIX "CocoaUI" +#define LV2_UI__Gtk3UI LV2_UI_PREFIX "Gtk3UI" +#define LV2_UI__GtkUI LV2_UI_PREFIX "GtkUI" +#define LV2_UI__PortNotification LV2_UI_PREFIX "PortNotification" +#define LV2_UI__Qt4UI LV2_UI_PREFIX "Qt4UI" +#define LV2_UI__Qt5UI LV2_UI_PREFIX "Qt5UI" +#define LV2_UI__UI LV2_UI_PREFIX "UI" +#define LV2_UI__WindowsUI LV2_UI_PREFIX "WindowsUI" +#define LV2_UI__X11UI LV2_UI_PREFIX "X11UI" +#define LV2_UI__binary LV2_UI_PREFIX "binary" +#define LV2_UI__fixedSize LV2_UI_PREFIX "fixedSize" +#define LV2_UI__idleInterface LV2_UI_PREFIX "idleInterface" +#define LV2_UI__noUserResize LV2_UI_PREFIX "noUserResize" +#define LV2_UI__notifyType LV2_UI_PREFIX "notifyType" +#define LV2_UI__parent LV2_UI_PREFIX "parent" +#define LV2_UI__plugin LV2_UI_PREFIX "plugin" +#define LV2_UI__portIndex LV2_UI_PREFIX "portIndex" +#define LV2_UI__portMap LV2_UI_PREFIX "portMap" +#define LV2_UI__portNotification LV2_UI_PREFIX "portNotification" +#define LV2_UI__portSubscribe LV2_UI_PREFIX "portSubscribe" +#define LV2_UI__resize LV2_UI_PREFIX "resize" +#define LV2_UI__showInterface LV2_UI_PREFIX "showInterface" +#define LV2_UI__touch LV2_UI_PREFIX "touch" +#define LV2_UI__ui LV2_UI_PREFIX "ui" +#define LV2_UI__updateRate LV2_UI_PREFIX "updateRate" +#define LV2_UI__windowTitle LV2_UI_PREFIX "windowTitle" + +/** + The index returned by LV2UI_Port_Map::port_index() for unknown ports. +*/ +#define LV2UI_INVALID_PORT_INDEX ((uint32_t)-1) + +#ifdef __cplusplus +extern "C" { +#else +# include +#endif + +/** + A pointer to some widget or other type of UI handle. + + The actual type is defined by the type of the UI. +*/ +typedef void* LV2UI_Widget; + +/** + A pointer to UI instance internals. + + The host may compare this to NULL, but otherwise MUST NOT interpret it. +*/ +typedef void* LV2UI_Handle; + +/** + A pointer to a controller provided by the host. + + The UI may compare this to NULL, but otherwise MUST NOT interpret it. +*/ +typedef void* LV2UI_Controller; + +/** + A pointer to opaque data for a feature. +*/ +typedef void* LV2UI_Feature_Handle; + +/** + A host-provided function that sends data to a plugin's input ports. + + @param controller The opaque controller pointer passed to + LV2UI_Descriptor::instantiate(). + + @param port_index Index of the port to update. + + @param buffer Buffer containing `buffer_size` bytes of data. + + @param buffer_size Size of `buffer` in bytes. + + @param port_protocol Either 0 or the URID for a ui:PortProtocol. If 0, the + protocol is implicitly ui:floatProtocol, the port MUST be an lv2:ControlPort + input, `buffer` MUST point to a single float value, and `buffer_size` MUST + be sizeof(float). The UI SHOULD NOT use a protocol not supported by the + host, but the host MUST gracefully ignore any protocol it does not + understand. +*/ +typedef void (*LV2UI_Write_Function)(LV2UI_Controller controller, + uint32_t port_index, + uint32_t buffer_size, + uint32_t port_protocol, + const void* buffer); + +/** + A plugin UI. + + A pointer to an object of this type is returned by the lv2ui_descriptor() + function. +*/ +typedef struct _LV2UI_Descriptor { + /** + The URI for this UI (not for the plugin it controls). + */ + const char* URI; + + /** + Create a new UI and return a handle to it. This function works + similarly to LV2_Descriptor::instantiate(). + + @param descriptor The descriptor for the UI to instantiate. + + @param plugin_uri The URI of the plugin that this UI will control. + + @param bundle_path The path to the bundle containing this UI, including + the trailing directory separator. + + @param write_function A function that the UI can use to send data to the + plugin's input ports. + + @param controller A handle for the UI instance to be passed as the + first parameter of UI methods. + + @param widget (output) widget pointer. The UI points this at its main + widget, which has the type defined by the UI type in the data file. + + @param features An array of LV2_Feature pointers. The host must pass + all feature URIs that it and the UI supports and any additional data, as + in LV2_Descriptor::instantiate(). Note that UI features and plugin + features are not necessarily the same. + + */ + LV2UI_Handle (*instantiate)(const struct _LV2UI_Descriptor* descriptor, + const char* plugin_uri, + const char* bundle_path, + LV2UI_Write_Function write_function, + LV2UI_Controller controller, + LV2UI_Widget* widget, + const LV2_Feature* const* features); + + + /** + Destroy the UI. The host must not try to access the widget after + calling this function. + */ + void (*cleanup)(LV2UI_Handle ui); + + /** + Tell the UI that something interesting has happened at a plugin port. + + What is "interesting" and how it is written to `buffer` is defined by + `format`, which has the same meaning as in LV2UI_Write_Function(). + Format 0 is a special case for lv2:ControlPort, where this function + should be called when the port value changes (but not necessarily for + every change), `buffer_size` must be sizeof(float), and `buffer` + points to a single IEEE-754 float. + + By default, the host should only call this function for lv2:ControlPort + inputs. However, the UI can request updates for other ports statically + with ui:portNotification or dynamicaly with ui:portSubscribe. + + The UI MUST NOT retain any reference to `buffer` after this function + returns, it is only valid for the duration of the call. + + This member may be NULL if the UI is not interested in any port events. + */ + void (*port_event)(LV2UI_Handle ui, + uint32_t port_index, + uint32_t buffer_size, + uint32_t format, + const void* buffer); + + /** + Return a data structure associated with an extension URI, typically an + interface struct with additional function pointers + + This member may be set to NULL if the UI is not interested in supporting + any extensions. This is similar to LV2_Descriptor::extension_data(). + + */ + const void* (*extension_data)(const char* uri); +} LV2UI_Descriptor; + +/** + Feature/interface for resizable UIs (LV2_UI__resize). + + This structure is used in two ways: as a feature passed by the host via + LV2UI_Descriptor::instantiate(), or as an interface provided by a UI via + LV2UI_Descriptor::extension_data()). +*/ +typedef struct _LV2UI_Resize { + /** + Pointer to opaque data which must be passed to ui_resize(). + */ + LV2UI_Feature_Handle handle; + + /** + Request/advertise a size change. + + When provided by the host, the UI may call this function to inform the + host about the size of the UI. + + When provided by the UI, the host may call this function to notify the + UI that it should change its size accordingly. + + @return 0 on success. + */ + int (*ui_resize)(LV2UI_Feature_Handle handle, int width, int height); +} LV2UI_Resize; + +/** + Feature to map port symbols to UIs. + + This can be used by the UI to get the index for a port with the given + symbol. This makes it possible to implement and distribute a UI separately + from the plugin (since symbol, unlike index, is a stable port identifier). +*/ +typedef struct _LV2UI_Port_Map { + /** + Pointer to opaque data which must be passed to port_index(). + */ + LV2UI_Feature_Handle handle; + + /** + Get the index for the port with the given `symbol`. + + @return The index of the port, or LV2UI_INVALID_PORT_INDEX if no such + port is found. + */ + uint32_t (*port_index)(LV2UI_Feature_Handle handle, const char* symbol); +} LV2UI_Port_Map; + +/** + Feature to subscribe to port updates (LV2_UI__portSubscribe). +*/ +typedef struct _LV2UI_Port_Subscribe { + /** + Pointer to opaque data which must be passed to subscribe() and + unsubscribe(). + */ + LV2UI_Feature_Handle handle; + + /** + Subscribe to updates for a port. + + This means that the host will call the UI's port_event() function when + the port value changes (as defined by protocol). + + Calling this function with the same `port_index` and `port_protocol` + as an already active subscription has no effect. + + @param handle The handle field of this struct. + @param port_index The index of the port. + @param port_protocol The URID of the ui:PortProtocol. + @param features Features for this subscription. + @return 0 on success. + */ + uint32_t (*subscribe)(LV2UI_Feature_Handle handle, + uint32_t port_index, + uint32_t port_protocol, + const LV2_Feature* const* features); + + /** + Unsubscribe from updates for a port. + + This means that the host will cease calling calling port_event() when + the port value changes. + + Calling this function with a `port_index` and `port_protocol` that + does not refer to an active port subscription has no effect. + + @param handle The handle field of this struct. + @param port_index The index of the port. + @param port_protocol The URID of the ui:PortProtocol. + @param features Features for this subscription. + @return 0 on success. + */ + uint32_t (*unsubscribe)(LV2UI_Feature_Handle handle, + uint32_t port_index, + uint32_t port_protocol, + const LV2_Feature* const* features); +} LV2UI_Port_Subscribe; + +/** + A feature to notify the host that the user has grabbed a UI control. +*/ +typedef struct _LV2UI_Touch { + /** + Pointer to opaque data which must be passed to ui_resize(). + */ + LV2UI_Feature_Handle handle; + + /** + Notify the host that a control has been grabbed or released. + + The host should cease automating the port or otherwise manipulating the + port value until the control has been ungrabbed. + + @param handle The handle field of this struct. + @param port_index The index of the port associated with the control. + @param grabbed If true, the control has been grabbed, otherwise the + control has been released. + */ + void (*touch)(LV2UI_Feature_Handle handle, + uint32_t port_index, + bool grabbed); +} LV2UI_Touch; + +/** + UI Idle Interface (LV2_UI__idleInterface) + + UIs can provide this interface to have an idle() callback called by the host + rapidly to update the UI. +*/ +typedef struct _LV2UI_Idle_Interface { + /** + Run a single iteration of the UI's idle loop. + + This will be called rapidly in the UI thread at a rate appropriate + for a toolkit main loop. There are no precise timing guarantees, but + the host should attempt to call idle() at a high enough rate for smooth + animation, at least 30Hz. + + @return non-zero if the UI has been closed, in which case the host + should stop calling idle(), and can either completely destroy the UI, or + re-show it and resume calling idle(). + */ + int (*idle)(LV2UI_Handle ui); +} LV2UI_Idle_Interface; + +/** + UI Show Interface (LV2_UI__showInterface) + + UIs can provide this interface to show and hide a window, which allows them + to function in hosts unable to embed their widget. This allows any UI to + provide a fallback for embedding that works in any host. + + If used: + - The host MUST use LV2UI_Idle_Interface to drive the UI. + - The UI MUST return non-zero from LV2UI_Idle_Interface::idle() when it has been closed. + - If idle() returns non-zero, the host MUST call hide() and stop calling + idle(). It MAY later call show() then resume calling idle(). +*/ +typedef struct _LV2UI_Show_Interface { + /** + Show a window for this UI. + + The window title MAY have been passed by the host to + LV2UI_Descriptor::instantiate() as an LV2_Options_Option with key + LV2_UI__windowTitle. + + @return 0 on success, or anything else to stop being called. + */ + int (*show)(LV2UI_Handle ui); + + /** + Hide the window for this UI. + + @return 0 on success, or anything else to stop being called. + */ + int (*hide)(LV2UI_Handle ui); +} LV2UI_Show_Interface; + +/** + Peak data for a slice of time, the update format for ui:peakProtocol. +*/ +typedef struct _LV2UI_Peak_Data { + /** + The start of the measurement period. This is just a running counter + that is only meaningful in comparison to previous values and must not be + interpreted as an absolute time. + */ + uint32_t period_start; + + /** + The size of the measurement period, in the same units as period_start. + */ + uint32_t period_size; + + /** + The peak value for the measurement period. This should be the maximal + value for abs(sample) over all the samples in the period. + */ + float peak; +} LV2UI_Peak_Data; + +/** + Prototype for UI accessor function. + + This is the entry point to a UI library, which works in the same way as + lv2_descriptor() but for UIs rather than plugins. +*/ +LV2_SYMBOL_EXPORT +const LV2UI_Descriptor* lv2ui_descriptor(uint32_t index); + +/** + The type of the lv2ui_descriptor() function. +*/ +typedef const LV2UI_Descriptor* (*LV2UI_DescriptorFunction)(uint32_t index); + +#ifdef __cplusplus +} +#endif + +#endif /* LV2_UI_H */ + +/** + @} +*/ diff --git a/modules/juce_audio_plugin_client/LV2/includes/units.h b/modules/juce_audio_plugin_client/LV2/includes/units.h new file mode 100644 index 0000000000..e61af2a188 --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/units.h @@ -0,0 +1,69 @@ +/* + Copyright 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. +*/ + +/** + @defgroup units Units + + Units for LV2 values, see for + details. + + @{ +*/ + +#ifndef LV2_UNITS_H +#define LV2_UNITS_H + +#define LV2_UNITS_URI "http://lv2plug.in/ns/extensions/units" +#define LV2_UNITS_PREFIX LV2_UNITS_URI "#" + +#define LV2_UNITS__Conversion LV2_UNITS_PREFIX "Conversion" +#define LV2_UNITS__Unit LV2_UNITS_PREFIX "Unit" +#define LV2_UNITS__bar LV2_UNITS_PREFIX "bar" +#define LV2_UNITS__beat LV2_UNITS_PREFIX "beat" +#define LV2_UNITS__bpm LV2_UNITS_PREFIX "bpm" +#define LV2_UNITS__cent LV2_UNITS_PREFIX "cent" +#define LV2_UNITS__cm LV2_UNITS_PREFIX "cm" +#define LV2_UNITS__coef LV2_UNITS_PREFIX "coef" +#define LV2_UNITS__conversion LV2_UNITS_PREFIX "conversion" +#define LV2_UNITS__db LV2_UNITS_PREFIX "db" +#define LV2_UNITS__degree LV2_UNITS_PREFIX "degree" +#define LV2_UNITS__frame LV2_UNITS_PREFIX "frame" +#define LV2_UNITS__hz LV2_UNITS_PREFIX "hz" +#define LV2_UNITS__inch LV2_UNITS_PREFIX "inch" +#define LV2_UNITS__khz LV2_UNITS_PREFIX "khz" +#define LV2_UNITS__km LV2_UNITS_PREFIX "km" +#define LV2_UNITS__m LV2_UNITS_PREFIX "m" +#define LV2_UNITS__mhz LV2_UNITS_PREFIX "mhz" +#define LV2_UNITS__midiNote LV2_UNITS_PREFIX "midiNote" +#define LV2_UNITS__mile LV2_UNITS_PREFIX "mile" +#define LV2_UNITS__min LV2_UNITS_PREFIX "min" +#define LV2_UNITS__mm LV2_UNITS_PREFIX "mm" +#define LV2_UNITS__ms LV2_UNITS_PREFIX "ms" +#define LV2_UNITS__name LV2_UNITS_PREFIX "name" +#define LV2_UNITS__oct LV2_UNITS_PREFIX "oct" +#define LV2_UNITS__pc LV2_UNITS_PREFIX "pc" +#define LV2_UNITS__prefixConversion LV2_UNITS_PREFIX "prefixConversion" +#define LV2_UNITS__render LV2_UNITS_PREFIX "render" +#define LV2_UNITS__s LV2_UNITS_PREFIX "s" +#define LV2_UNITS__semitone12TET LV2_UNITS_PREFIX "semitone12TET" +#define LV2_UNITS__symbol LV2_UNITS_PREFIX "symbol" +#define LV2_UNITS__unit LV2_UNITS_PREFIX "unit" + +#endif /* LV2_UNITS_H */ + +/** + @} +*/ diff --git a/modules/juce_audio_plugin_client/LV2/includes/urid.h b/modules/juce_audio_plugin_client/LV2/includes/urid.h new file mode 100644 index 0000000000..ae1f113f4f --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/includes/urid.h @@ -0,0 +1,137 @@ +/* + Copyright 2008-2012 David Robillard + Copyright 2011 Gabriel M. Beddingfield + + 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. +*/ + +/** + @defgroup urid URID + + Features for mapping URIs to and from integers, see + for details. + + @{ +*/ + +#ifndef LV2_URID_H +#define LV2_URID_H + +#define LV2_URID_URI "http://lv2plug.in/ns/ext/urid" +#define LV2_URID_PREFIX LV2_URID_URI "#" + +#define LV2_URID__map LV2_URID_PREFIX "map" +#define LV2_URID__unmap LV2_URID_PREFIX "unmap" + +/* Legacy defines */ +#define LV2_URID_MAP_URI LV2_URID__map +#define LV2_URID_UNMAP_URI LV2_URID__unmap + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + Opaque pointer to host data for LV2_URID_Map. +*/ +typedef void* LV2_URID_Map_Handle; + +/** + Opaque pointer to host data for LV2_URID_Unmap. +*/ +typedef void* LV2_URID_Unmap_Handle; + +/** + URI mapped to an integer. +*/ +typedef uint32_t LV2_URID; + +/** + URID Map Feature (LV2_URID__map) +*/ +typedef struct _LV2_URID_Map { + /** + Opaque pointer to host data. + + This MUST be passed to map_uri() whenever it is called. + Otherwise, it must not be interpreted in any way. + */ + LV2_URID_Map_Handle handle; + + /** + Get the numeric ID of a URI. + + If the ID does not already exist, it will be created. + + This function is referentially transparent; any number of calls with the + same arguments is guaranteed to return the same value over the life of a + plugin instance. Note, however, that several URIs MAY resolve to the + same ID if the host considers those URIs equivalent. + + This function is not necessarily very fast or RT-safe: plugins SHOULD + cache any IDs they might need in performance critical situations. + + The return value 0 is reserved and indicates that an ID for that URI + could not be created for whatever reason. However, hosts SHOULD NOT + return 0 from this function in non-exceptional circumstances (i.e. the + URI map SHOULD be dynamic). + + @param handle Must be the callback_data member of this struct. + @param uri The URI to be mapped to an integer ID. + */ + LV2_URID (*map)(LV2_URID_Map_Handle handle, + const char* uri); +} LV2_URID_Map; + +/** + URI Unmap Feature (LV2_URID__unmap) +*/ +typedef struct _LV2_URID_Unmap { + /** + Opaque pointer to host data. + + This MUST be passed to unmap() whenever it is called. + Otherwise, it must not be interpreted in any way. + */ + LV2_URID_Unmap_Handle handle; + + /** + Get the URI for a previously mapped numeric ID. + + Returns NULL if `urid` is not yet mapped. Otherwise, the corresponding + URI is returned in a canonical form. This MAY not be the exact same + string that was originally passed to LV2_URID_Map::map(), but it MUST be + an identical URI according to the URI syntax specification (RFC3986). A + non-NULL return for a given `urid` will always be the same for the life + of the plugin. Plugins that intend to perform string comparison on + unmapped URIs SHOULD first canonicalise URI strings with a call to + map_uri() followed by a call to unmap_uri(). + + @param handle Must be the callback_data member of this struct. + @param urid The ID to be mapped back to the URI string. + */ + const char* (*unmap)(LV2_URID_Unmap_Handle handle, + LV2_URID urid); +} LV2_URID_Unmap; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* LV2_URID_H */ + +/** + @} +*/ diff --git a/modules/juce_audio_plugin_client/LV2/juce_LV2_Wrapper.cpp b/modules/juce_audio_plugin_client/LV2/juce_LV2_Wrapper.cpp new file mode 100644 index 0000000000..5496751356 --- /dev/null +++ b/modules/juce_audio_plugin_client/LV2/juce_LV2_Wrapper.cpp @@ -0,0 +1,2172 @@ +/* + ============================================================================== + + Juce LV2 Wrapper + + ============================================================================== +*/ + +// Your project must contain an AppConfig.h file with your project-specific settings in it, +// and your header search path must make it accessible to the module's files. +#include "AppConfig.h" + +#include "../utility/juce_CheckSettingMacros.h" +#include "../../juce_core/system/juce_TargetPlatform.h" // for JUCE_LINUX + + +#if JucePlugin_Build_LV2 + +/** Plugin requires processing with a fixed/constant block size */ +#ifndef JucePlugin_WantsLV2FixedBlockSize + #define JucePlugin_WantsLV2FixedBlockSize 0 +#endif + +/** Enable latency port */ +#ifndef JucePlugin_WantsLV2Latency + #define JucePlugin_WantsLV2Latency 1 +#endif + +/** Use non-parameter states */ +#ifndef JucePlugin_WantsLV2State + #define JucePlugin_WantsLV2State 1 +#endif + +/** States are strings, needs custom get/setStateInformationString */ +#ifndef JucePlugin_WantsLV2StateString + #define JucePlugin_WantsLV2StateString 0 +#endif + +/** Export presets */ +#ifndef JucePlugin_WantsLV2Presets + #define JucePlugin_WantsLV2Presets 1 +#endif + +/** Request time position */ +#ifndef JucePlugin_WantsLV2TimePos + #define JucePlugin_WantsLV2TimePos 1 +#endif + +/** Using string states require enabling states first */ +#if JucePlugin_WantsLV2StateString && ! JucePlugin_WantsLV2State + #undef JucePlugin_WantsLV2State + #define JucePlugin_WantsLV2State 1 +#endif + +#if JUCE_LINUX && ! JUCE_AUDIOPROCESSOR_NO_GUI + #include + #undef KeyPress +#endif + +#include +#include + +// LV2 includes.. +#include "includes/lv2.h" +#include "includes/atom.h" +#include "includes/atom-util.h" +#include "includes/buf-size.h" +#include "includes/instance-access.h" +#include "includes/midi.h" +#include "includes/options.h" +#include "includes/port-props.h" +#include "includes/presets.h" +#include "includes/state.h" +#include "includes/time.h" +#include "includes/ui.h" +#include "includes/urid.h" +#include "includes/lv2_external_ui.h" +#include "includes/lv2_programs.h" + +#include "../utility/juce_IncludeModuleHeaders.h" + +#if JUCE_LINUX && ! JUCE_AUDIOPROCESSOR_NO_GUI +namespace juce +{ + extern Display* display; +} +#endif + +#define JUCE_LV2_STATE_STRING_URI "urn:juce:stateString" +#define JUCE_LV2_STATE_BINARY_URI "urn:juce:stateBinary" + +//============================================================================== +// Various helper functions for creating the ttl files + +#if JUCE_MAC + #define PLUGIN_EXT ".dylib" +#elif JUCE_LINUX + #define PLUGIN_EXT ".so" +#elif JUCE_WINDOWS + #define PLUGIN_EXT ".dll" +#endif + +/** Returns plugin type, defined in AppConfig.h or JucePluginCharacteristics.h */ +const String getPluginType() +{ + String pluginType; +#ifdef JucePlugin_LV2Category + pluginType = "lv2:" JucePlugin_LV2Category; + pluginType += ", "; +#elif JucePlugin_IsSynth + pluginType = "lv2:InstrumentPlugin, "; +#endif + pluginType += "lv2:Plugin"; + return pluginType; +} + +/** Returns plugin URI */ +static const String& getPluginURI() +{ + // JucePlugin_LV2URI might be defined as a function (eg. allowing dynamic URIs based on filename) + static const String pluginURI(JucePlugin_LV2URI); + return pluginURI; +} + +static Array usedSymbols; + +/** Converts a parameter name to an LV2 compatible symbol. */ +const String nameToSymbol (const String& name, const uint32 portIndex) +{ + String symbol, trimmedName = name.trimStart().trimEnd().toLowerCase(); + + if (trimmedName.isEmpty()) + { + symbol += "lv2_port_"; + symbol += String(portIndex+1); + } + else + { + for (int i=0; i < trimmedName.length(); ++i) + { + const juce_wchar c = trimmedName[i]; + if (i == 0 && std::isdigit(c)) + symbol += "_"; + else if (std::isalpha(c) || std::isdigit(c)) + symbol += c; + else + symbol += "_"; + } + } + + // Do not allow identical symbols + if (usedSymbols.contains(symbol)) + { + int offset = 2; + String offsetStr = "_2"; + symbol += offsetStr; + + while (usedSymbols.contains(symbol)) + { + offset += 1; + String newOffsetStr = "_" + String(offset); + symbol = symbol.replace(offsetStr, newOffsetStr); + offsetStr = newOffsetStr; + } + } + usedSymbols.add(symbol); + + return symbol; +} + +/** Prevents NaN or out of 0.0<->1.0 bounds parameter values. */ +float safeParamValue (float value) +{ + if (std::isnan(value)) + value = 0.0f; + else if (value < 0.0f) + value = 0.0f; + else if (value > 1.0f) + value = 1.0f; + return value; +} + +/** Create the manifest.ttl file contents */ +const String makeManifestFile (AudioProcessor* const filter, const String& binary) +{ + const String& pluginURI(getPluginURI()); + String text; + + // Header + text += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; + text += "@prefix pset: <" LV2_PRESETS_PREFIX "> .\n"; + text += "@prefix rdfs: .\n"; + text += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; + text += "\n"; + + // Plugin + text += "<" + pluginURI + ">\n"; + text += " a lv2:Plugin ;\n"; + text += " lv2:binary <" + binary + PLUGIN_EXT "> ;\n"; + text += " rdfs:seeAlso <" + binary + ".ttl> .\n"; + text += "\n"; + +#if ! JUCE_AUDIOPROCESSOR_NO_GUI + // UIs + if (filter->hasEditor()) + { + text += "<" + pluginURI + "#ExternalUI>\n"; + text += " a <" LV2_EXTERNAL_UI__Widget "> ;\n"; + text += " ui:binary <" + binary + PLUGIN_EXT "> ;\n"; + text += " lv2:requiredFeature <" LV2_INSTANCE_ACCESS_URI "> ;\n"; + text += " lv2:extensionData <" LV2_PROGRAMS__UIInterface "> .\n"; + text += "\n"; + + text += "<" + pluginURI + "#ParentUI>\n"; + #if JUCE_MAC + text += " a ui:CocoaUI ;\n"; + #elif JUCE_LINUX + text += " a ui:X11UI ;\n"; + #elif JUCE_WINDOWS + text += " a ui:WindowsUI ;\n"; + #endif + text += " ui:binary <" + binary + PLUGIN_EXT "> ;\n"; + text += " lv2:requiredFeature <" LV2_INSTANCE_ACCESS_URI "> ;\n"; + text += " lv2:optionalFeature ui:noUserResize ;\n"; + text += " lv2:extensionData <" LV2_PROGRAMS__UIInterface "> .\n"; + text += "\n"; + } +#endif + +#if JucePlugin_WantsLV2Presets + const String presetSeparator(pluginURI.contains("#") ? ":" : "#"); + + // Presets + for (int i = 0; i < filter->getNumPrograms(); ++i) + { + text += "<" + pluginURI + presetSeparator + "preset" + String::formatted("%03i", i+1) + ">\n"; + text += " a pset:Preset ;\n"; + text += " lv2:appliesTo <" + pluginURI + "> ;\n"; + text += " rdfs:label \"" + filter->getProgramName(i) + "\" ;\n"; + text += " rdfs:seeAlso .\n"; + text += "\n"; + } +#endif + + return text; +} + +/** Create the -plugin-.ttl file contents */ +const String makePluginFile (AudioProcessor* const filter, const int maxNumInputChannels, const int maxNumOutputChannels) +{ + const String& pluginURI(getPluginURI()); + String text; + + // Header + text += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; + text += "@prefix doap: .\n"; + text += "@prefix foaf: .\n"; + text += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; + text += "@prefix rdfs: .\n"; + text += "@prefix ui: <" LV2_UI_PREFIX "> .\n"; + text += "\n"; + + // Plugin + text += "<" + pluginURI + ">\n"; + text += " a " + getPluginType() + " ;\n"; + text += " lv2:requiredFeature <" LV2_BUF_SIZE__boundedBlockLength "> ,\n"; +#if JucePlugin_WantsLV2FixedBlockSize + text += " <" LV2_BUF_SIZE__fixedBlockLength "> ,\n"; +#endif + text += " <" LV2_URID__map "> ;\n"; + text += " lv2:extensionData <" LV2_OPTIONS__interface "> ,\n"; +#if JucePlugin_WantsLV2State + text += " <" LV2_STATE__interface "> ,\n"; +#endif + text += " <" LV2_PROGRAMS__Interface "> ;\n"; + text += "\n"; + +#if ! JUCE_AUDIOPROCESSOR_NO_GUI + // UIs + if (filter->hasEditor()) + { + text += " ui:ui <" + pluginURI + "#ExternalUI> ,\n"; + text += " <" + pluginURI + "#ParentUI> ;\n"; + text += "\n"; + } +#endif + + uint32 portIndex = 0; + +#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos) + // MIDI input + text += " lv2:port [\n"; + text += " a lv2:InputPort, atom:AtomPort ;\n"; + text += " atom:bufferType atom:Sequence ;\n"; + #if JucePlugin_WantsMidiInput + text += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n"; + #endif + #if JucePlugin_WantsLV2TimePos + text += " atom:supports <" LV2_TIME__Position "> ;\n"; + #endif + text += " lv2:index " + String(portIndex++) + " ;\n"; + text += " lv2:symbol \"lv2_events_in\" ;\n"; + text += " lv2:name \"Events Input\" ;\n"; + text += " lv2:designation lv2:control ;\n"; + #if ! JucePlugin_IsSynth + text += " lv2:portProperty lv2:connectionOptional ;\n"; + #endif + text += " ] ;\n"; + text += "\n"; +#endif + +#if JucePlugin_ProducesMidiOutput + // MIDI output + text += " lv2:port [\n"; + text += " a lv2:OutputPort, atom:AtomPort ;\n"; + text += " atom:bufferType atom:Sequence ;\n"; + text += " atom:supports <" LV2_MIDI__MidiEvent "> ;\n"; + text += " lv2:index " + String(portIndex++) + " ;\n"; + text += " lv2:symbol \"lv2_midi_out\" ;\n"; + text += " lv2:name \"MIDI Output\" ;\n"; + text += " ] ;\n"; + text += "\n"; +#endif + + // Freewheel port + text += " lv2:port [\n"; + text += " a lv2:InputPort, lv2:ControlPort ;\n"; + text += " lv2:index " + String(portIndex++) + " ;\n"; + text += " lv2:symbol \"lv2_freewheel\" ;\n"; + text += " lv2:name \"Freewheel\" ;\n"; + text += " lv2:default 0.0 ;\n"; + text += " lv2:minimum 0.0 ;\n"; + text += " lv2:maximum 1.0 ;\n"; + text += " lv2:designation <" LV2_CORE__freeWheeling "> ;\n"; + text += " lv2:portProperty lv2:toggled, <" LV2_PORT_PROPS__notOnGUI "> ;\n"; + text += " ] ;\n"; + text += "\n"; + +#if JucePlugin_WantsLV2Latency + // Latency port + text += " lv2:port [\n"; + text += " a lv2:OutputPort, lv2:ControlPort ;\n"; + text += " lv2:index " + String(portIndex++) + " ;\n"; + text += " lv2:symbol \"lv2_latency\" ;\n"; + text += " lv2:name \"Latency\" ;\n"; + text += " lv2:designation <" LV2_CORE__latency "> ;\n"; + text += " lv2:portProperty lv2:reportsLatency, lv2:integer ;\n"; + text += " ] ;\n"; + text += "\n"; +#endif + + // Audio inputs + for (int i=0; i < maxNumInputChannels; ++i) + { + if (i == 0) + text += " lv2:port [\n"; + else + text += " [\n"; + + text += " a lv2:InputPort, lv2:AudioPort ;\n"; + text += " lv2:index " + String(portIndex++) + " ;\n"; + text += " lv2:symbol \"lv2_audio_in_" + String(i+1) + "\" ;\n"; + text += " lv2:name \"Audio Input " + String(i+1) + "\" ;\n"; + + if (i+1 == maxNumInputChannels) + text += " ] ;\n\n"; + else + text += " ] ,\n"; + } + + // Audio outputs + for (int i=0; i < maxNumOutputChannels; ++i) + { + if (i == 0) + text += " lv2:port [\n"; + else + text += " [\n"; + + text += " a lv2:OutputPort, lv2:AudioPort ;\n"; + text += " lv2:index " + String(portIndex++) + " ;\n"; + text += " lv2:symbol \"lv2_audio_out_" + String(i+1) + "\" ;\n"; + text += " lv2:name \"Audio Output " + String(i+1) + "\" ;\n"; + + if (i+1 == maxNumOutputChannels) + text += " ] ;\n\n"; + else + text += " ] ,\n"; + } + + // Parameters + for (int i=0; i < filter->getNumParameters(); ++i) + { + if (i == 0) + text += " lv2:port [\n"; + else + text += " [\n"; + + text += " a lv2:InputPort, lv2:ControlPort ;\n"; + text += " lv2:index " + String(portIndex++) + " ;\n"; + text += " lv2:symbol \"" + nameToSymbol(filter->getParameterName(i), i) + "\" ;\n"; + + if (filter->getParameterName(i).isNotEmpty()) + text += " lv2:name \"" + filter->getParameterName(i) + "\" ;\n"; + else + text += " lv2:name \"Port " + String(i+1) + "\" ;\n"; + + text += " lv2:default " + String::formatted("%f", safeParamValue(filter->getParameter(i))) + " ;\n"; + text += " lv2:minimum 0.0 ;\n"; + text += " lv2:maximum 1.0 ;\n"; + + if (! filter->isParameterAutomatable(i)) + text += " lv2:portProperty <" LV2_PORT_PROPS__expensive "> ;\n"; + + if (i+1 == filter->getNumParameters()) + text += " ] ;\n\n"; + else + text += " ] ,\n"; + } + + text += " doap:name \"" + filter->getName() + "\" ;\n"; + text += " doap:maintainer [ foaf:name \"" JucePlugin_Manufacturer "\" ] .\n"; + + return text; +} + +/** Create the presets.ttl file contents */ +const String makePresetsFile (AudioProcessor* const filter) +{ + const String& pluginURI(getPluginURI()); + String text; + + // Header + text += "@prefix atom: <" LV2_ATOM_PREFIX "> .\n"; + text += "@prefix lv2: <" LV2_CORE_PREFIX "> .\n"; + text += "@prefix pset: <" LV2_PRESETS_PREFIX "> .\n"; + text += "@prefix rdf: .\n"; + text += "@prefix rdfs: .\n"; + text += "@prefix state: <" LV2_STATE_PREFIX "> .\n"; + text += "@prefix xsd: .\n"; + text += "\n"; + + // Presets + const int numPrograms = filter->getNumPrograms(); + const String presetSeparator(pluginURI.contains("#") ? ":" : "#"); + + for (int i = 0; i < numPrograms; ++i) + { + std::cout << "\nSaving preset " << i+1 << "/" << numPrograms+1 << "..."; + std::cout.flush(); + + String preset; + + // Label + filter->setCurrentProgram(i); + preset += "<" + pluginURI + presetSeparator + "preset" + String::formatted("%03i", i+1) + "> a pset:Preset ;\n"; + + // State +#if JucePlugin_WantsLV2State + preset += " state:state [\n"; + #if JucePlugin_WantsLV2StateString + preset += " <" JUCE_LV2_STATE_STRING_URI ">\n"; + preset += "\"\"\"\n"; + preset += filter->getStateInformationString().replace("\r\n","\n"); + preset += "\"\"\"\n"; + #else + MemoryBlock chunkMemory; + filter->getCurrentProgramStateInformation(chunkMemory); + const String chunkString(Base64::toBase64(chunkMemory.getData(), chunkMemory.getSize())); + + preset += " <" JUCE_LV2_STATE_BINARY_URI "> [\n"; + preset += " a atom:Chunk ;\n"; + preset += " rdf:value \"" + chunkString + "\"^^xsd:base64Binary ;\n"; + preset += " ] ;\n"; + #endif + if (filter->getNumParameters() == 0) + { + preset += " ] .\n\n"; + continue; + } + + preset += " ] ;\n\n"; +#endif + + // Port values + usedSymbols.clear(); + + for (int j=0; j < filter->getNumParameters(); ++j) + { + if (j == 0) + preset += " lv2:port [\n"; + else + preset += " [\n"; + + preset += " lv2:symbol \"" + nameToSymbol(filter->getParameterName(j), j) + "\" ;\n"; + preset += " pset:value " + String::formatted("%f", safeParamValue(filter->getParameter(j))) + " ;\n"; + + if (j+1 == filter->getNumParameters()) + preset += " ] "; + else + preset += " ] ,\n"; + } + preset += ".\n\n"; + + text += preset; + } + + return text; +} + +/** Creates manifest.ttl, plugin.ttl and presets.ttl files */ +void createLv2Files(const char* basename) +{ + const ScopedJuceInitialiser_GUI juceInitialiser; + ScopedPointer filter (createPluginFilterOfType (AudioProcessor::wrapperType_LV2)); + + String binary(basename); + String binaryTTL(binary + ".ttl"); + + std::cout << "Writing manifest.ttl..."; std::cout.flush(); + std::fstream manifest("manifest.ttl", std::ios::out); + manifest << makeManifestFile(filter, binary) << std::endl; + manifest.close(); + std::cout << " done!" << std::endl; + + std::cout << "Writing " << binary << ".ttl..."; std::cout.flush(); + std::fstream plugin(binaryTTL.toUTF8(), std::ios::out); + plugin << makePluginFile(filter, JucePlugin_MaxNumInputChannels, JucePlugin_MaxNumOutputChannels) << std::endl; + plugin.close(); + std::cout << " done!" << std::endl; + +#if JucePlugin_WantsLV2Presets + std::cout << "Writing presets.ttl..."; std::cout.flush(); + std::fstream presets("presets.ttl", std::ios::out); + presets << makePresetsFile(filter) << std::endl; + presets.close(); + std::cout << " done!" << std::endl; +#endif +} + +//============================================================================== +#if JUCE_LINUX + +class SharedMessageThread : public Thread +{ +public: + SharedMessageThread() + : Thread ("Lv2MessageThread"), + initialised (false) + { + startThread (7); + + while (! initialised) + sleep (1); + } + + ~SharedMessageThread() + { + MessageManager::getInstance()->stopDispatchLoop(); + waitForThreadToExit (5000); + } + + void run() override + { + const ScopedJuceInitialiser_GUI juceInitialiser; + + MessageManager::getInstance()->setCurrentThreadAsMessageThread(); + initialised = true; + + MessageManager::getInstance()->runDispatchLoop(); + } + +private: + volatile bool initialised; +}; +#endif + +#if ! JUCE_AUDIOPROCESSOR_NO_GUI +//============================================================================== +/** + Lightweight DocumentWindow subclass for external ui +*/ +class JuceLv2ExternalUIWindow : public DocumentWindow +{ +public: + /** Creates a Document Window wrapper */ + JuceLv2ExternalUIWindow (AudioProcessorEditor* editor, const String& title) : + DocumentWindow (title, Colours::white, DocumentWindow::minimiseButton | DocumentWindow::closeButton, false), + closed (false), + lastPos (0, 0) + { + setOpaque (true); + setContentNonOwned (editor, true); + setSize (editor->getWidth(), editor->getHeight()); + setUsingNativeTitleBar (true); + } + + /** Close button handler */ + void closeButtonPressed() + { + saveLastPos(); + removeFromDesktop(); + closed = true; + } + + void saveLastPos() + { + lastPos = getScreenPosition(); + } + + void restoreLastPos() + { + setTopLeftPosition (lastPos.getX(), lastPos.getY()); + } + + Point getLastPos() + { + return lastPos; + } + + bool isClosed() + { + return closed; + } + + void reset() + { + closed = false; + } + +private: + bool closed; + Point lastPos; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ExternalUIWindow); +}; + +//============================================================================== +/** + Juce LV2 External UI handle +*/ +class JuceLv2ExternalUIWrapper : public LV2_External_UI_Widget +{ +public: + JuceLv2ExternalUIWrapper (AudioProcessorEditor* editor, const String& title) + : window (editor, title) + { + // external UI calls + run = doRun; + show = doShow; + hide = doHide; + } + + ~JuceLv2ExternalUIWrapper() + { + if (window.isOnDesktop()) + window.removeFromDesktop(); + } + + void close() + { + window.closeButtonPressed(); + } + + bool isClosed() + { + return window.isClosed(); + } + + void reset(const String& title) + { + window.reset(); + window.setName(title); + } + + void repaint() + { + window.repaint(); + } + + Point getScreenPosition() + { + if (window.isClosed()) + return window.getLastPos(); + else + return window.getScreenPosition(); + } + + void setScreenPos (int x, int y) + { + if (! window.isClosed()) + window.setTopLeftPosition(x, y); + } + + //============================================================================== + static void doRun (LV2_External_UI_Widget* _this_) + { + const MessageManagerLock mmLock; + JuceLv2ExternalUIWrapper* self = (JuceLv2ExternalUIWrapper*) _this_; + + if (! self->isClosed()) + self->window.repaint(); + } + + static void doShow (LV2_External_UI_Widget* _this_) + { + const MessageManagerLock mmLock; + JuceLv2ExternalUIWrapper* self = (JuceLv2ExternalUIWrapper*) _this_; + + if (! self->isClosed()) + { + if (! self->window.isOnDesktop()) + self->window.addToDesktop(); + + self->window.restoreLastPos(); + self->window.setVisible(true); + } + } + + static void doHide (LV2_External_UI_Widget* _this_) + { + const MessageManagerLock mmLock; + JuceLv2ExternalUIWrapper* self = (JuceLv2ExternalUIWrapper*) _this_; + + if (! self->isClosed()) + { + self->window.saveLastPos(); + self->window.setVisible(false); + } + } + +private: + JuceLv2ExternalUIWindow window; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ExternalUIWrapper); +}; + +//============================================================================== +/** + Juce LV2 Parent UI container, listens for resize events and passes them to ui-resize +*/ +class JuceLv2ParentContainer : public Component +{ +public: + JuceLv2ParentContainer (AudioProcessorEditor* editor, const LV2UI_Resize* uiResize_) + : uiResize(uiResize_) + { + setOpaque (true); + editor->setOpaque (true); + setBounds (editor->getBounds()); + + editor->setTopLeftPosition (0, 0); + addAndMakeVisible (editor); + } + + ~JuceLv2ParentContainer() + { + } + + void paint (Graphics&) {} + void paintOverChildren (Graphics&) {} + + void childBoundsChanged (Component* child) + { + const int cw = child->getWidth(); + const int ch = child->getHeight(); + +#if JUCE_LINUX + XResizeWindow (display, (Window) getWindowHandle(), cw, ch); +#else + setSize (cw, ch); +#endif + + if (uiResize != nullptr) + uiResize->ui_resize (uiResize->handle, cw, ch); + } + + void reset (const LV2UI_Resize* uiResize_) + { + uiResize = uiResize_; + + if (uiResize != nullptr) + uiResize->ui_resize (uiResize->handle, getWidth(), getHeight()); + } + +private: + //============================================================================== + const LV2UI_Resize* uiResize; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2ParentContainer); +}; + +//============================================================================== +/** + Juce LV2 UI handle +*/ +class JuceLv2UIWrapper : public AudioProcessorListener, + public Timer +{ +public: + JuceLv2UIWrapper (AudioProcessor* filter_, LV2UI_Write_Function writeFunction_, LV2UI_Controller controller_, + LV2UI_Widget* widget, const LV2_Feature* const* features, bool isExternal_) + : filter (filter_), + writeFunction (writeFunction_), + controller (controller_), + isExternal (isExternal_), + controlPortOffset (0), + lastProgramCount (0), + uiTouch (nullptr), + programsHost (nullptr), + externalUIHost (nullptr), + lastExternalUIPos (-1, -1), + uiResize (nullptr) + { + jassert (filter != nullptr); + + filter->addListener(this); + + if (filter->hasEditor()) + { + editor = filter->createEditorIfNeeded(); + + if (editor == nullptr) + { + *widget = nullptr; + return; + } + } + + for (int i = 0; features[i] != nullptr; ++i) + { + if (strcmp(features[i]->URI, LV2_UI__touch) == 0) + uiTouch = (const LV2UI_Touch*)features[i]->data; + + else if (strcmp(features[i]->URI, LV2_PROGRAMS__Host) == 0) + programsHost = (const LV2_Programs_Host*)features[i]->data; + } + + if (isExternal) + { + resetExternalUI (features); + + if (externalUIHost != nullptr) + { + String title (filter->getName()); + + if (externalUIHost->plugin_human_id != nullptr) + title = externalUIHost->plugin_human_id; + + externalUI = new JuceLv2ExternalUIWrapper (editor, title); + *widget = externalUI; + startTimer (100); + } + else + { + *widget = nullptr; + } + } + else + { + resetParentUI (features); + + if (parentContainer != nullptr) + *widget = parentContainer->getWindowHandle(); + else + *widget = nullptr; + } + +#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos) + controlPortOffset += 1; +#endif +#if JucePlugin_ProducesMidiOutput + controlPortOffset += 1; +#endif + controlPortOffset += 1; // freewheel +#if JucePlugin_WantsLV2Latency + controlPortOffset += 1; +#endif + controlPortOffset += JucePlugin_MaxNumInputChannels; + controlPortOffset += JucePlugin_MaxNumOutputChannels; + + lastProgramCount = filter->getNumPrograms(); + } + + ~JuceLv2UIWrapper() + { + PopupMenu::dismissAllActiveMenus(); + + filter->removeListener(this); + + parentContainer = nullptr; + externalUI = nullptr; + externalUIHost = nullptr; + + if (editor != nullptr) + { + filter->editorBeingDeleted (editor); + editor = nullptr; + } + } + + //============================================================================== + // LV2 core calls + + void lv2Cleanup() + { + const MessageManagerLock mmLock; + + if (isExternal) + { + if (isTimerRunning()) + stopTimer(); + + externalUIHost = nullptr; + + if (externalUI != nullptr) + { + lastExternalUIPos = externalUI->getScreenPosition(); + externalUI->close(); + } + } + else + { + if (parentContainer) + { + parentContainer->setVisible (false); + if (parentContainer->isOnDesktop()) + parentContainer->removeFromDesktop(); + } + } + } + + //============================================================================== + // Juce calls + + void audioProcessorParameterChanged (AudioProcessor*, int index, float newValue) + { + if (writeFunction != nullptr && controller != nullptr) + writeFunction (controller, index + controlPortOffset, sizeof (float), 0, &newValue); + } + + void audioProcessorChanged (AudioProcessor*) + { + if (filter != nullptr && programsHost != nullptr) + { + if (filter->getNumPrograms() != lastProgramCount) + { + programsHost->program_changed (programsHost->handle, -1); + lastProgramCount = filter->getNumPrograms(); + } + else + programsHost->program_changed (programsHost->handle, filter->getCurrentProgram()); + } + } + + void audioProcessorParameterChangeGestureBegin (AudioProcessor*, int parameterIndex) + { + if (uiTouch != nullptr) + uiTouch->touch (uiTouch->handle, parameterIndex + controlPortOffset, true); + } + + void audioProcessorParameterChangeGestureEnd (AudioProcessor*, int parameterIndex) + { + if (uiTouch != nullptr) + uiTouch->touch (uiTouch->handle, parameterIndex + controlPortOffset, false); + } + + void timerCallback() + { + if (externalUI != nullptr && externalUI->isClosed()) + { + if (externalUIHost != nullptr) + externalUIHost->ui_closed (controller); + + if (isTimerRunning()) + stopTimer(); + } + } + + //============================================================================== + void resetIfNeeded (LV2UI_Write_Function writeFunction_, LV2UI_Controller controller_, LV2UI_Widget* widget, + const LV2_Feature* const* features) + { + writeFunction = writeFunction_; + controller = controller_; + uiTouch = nullptr; + programsHost = nullptr; + + for (int i = 0; features[i] != nullptr; ++i) + { + if (strcmp(features[i]->URI, LV2_UI__touch) == 0) + uiTouch = (const LV2UI_Touch*)features[i]->data; + + else if (strcmp(features[i]->URI, LV2_PROGRAMS__Host) == 0) + programsHost = (const LV2_Programs_Host*)features[i]->data; + } + + if (isExternal) + { + resetExternalUI (features); + *widget = externalUI; + } + else + { + resetParentUI (features); + *widget = parentContainer->getWindowHandle(); + } + } + + void repaint() + { + const MessageManagerLock mmLock; + + if (editor != nullptr) + editor->repaint(); + + if (parentContainer != nullptr) + parentContainer->repaint(); + + if (externalUI != nullptr) + externalUI->repaint(); + } + +private: + AudioProcessor* const filter; + ScopedPointer editor; + + LV2UI_Write_Function writeFunction; + LV2UI_Controller controller; + const bool isExternal; + + uint32 controlPortOffset; + int lastProgramCount; + + const LV2UI_Touch* uiTouch; + const LV2_Programs_Host* programsHost; + + ScopedPointer externalUI; + const LV2_External_UI_Host* externalUIHost; + Point lastExternalUIPos; + + ScopedPointer parentContainer; + const LV2UI_Resize* uiResize; + + //============================================================================== + void resetExternalUI (const LV2_Feature* const* features) + { + externalUIHost = nullptr; + + for (int i = 0; features[i] != nullptr; ++i) + { + if (strcmp(features[i]->URI, LV2_EXTERNAL_UI__Host) == 0) + { + externalUIHost = (const LV2_External_UI_Host*)features[i]->data; + break; + } + } + + if (externalUI != nullptr) + { + String title(filter->getName()); + + if (externalUIHost->plugin_human_id != nullptr) + title = externalUIHost->plugin_human_id; + + if (lastExternalUIPos.getX() != -1 && lastExternalUIPos.getY() != -1) + externalUI->setScreenPos(lastExternalUIPos.getX(), lastExternalUIPos.getY()); + + externalUI->reset(title); + startTimer (100); + } + } + + void resetParentUI (const LV2_Feature* const* features) + { + void* parent = nullptr; + uiResize = nullptr; + + for (int i = 0; features[i] != nullptr; ++i) + { + if (strcmp(features[i]->URI, LV2_UI__parent) == 0) + parent = features[i]->data; + + else if (strcmp(features[i]->URI, LV2_UI__resize) == 0) + uiResize = (const LV2UI_Resize*)features[i]->data; + } + + if (parent != nullptr) + { + if (parentContainer == nullptr) + parentContainer = new JuceLv2ParentContainer (editor, uiResize); + + parentContainer->setVisible (false); + + if (parentContainer->isOnDesktop()) + parentContainer->removeFromDesktop(); + + parentContainer->addToDesktop (0, parent); + +#if JUCE_LINUX + Window hostWindow = (Window) parent; + Window editorWnd = (Window) parentContainer->getWindowHandle(); + XReparentWindow (display, editorWnd, hostWindow, 0, 0); +#endif + + parentContainer->reset (uiResize); + parentContainer->setVisible (true); + } + } + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2UIWrapper) +}; + +#endif /* JUCE_AUDIOPROCESSOR_NO_GUI */ + +//============================================================================== +/** + Juce LV2 handle +*/ +class JuceLv2Wrapper : public AudioPlayHead +{ +public: + //============================================================================== + JuceLv2Wrapper (double sampleRate_, const LV2_Feature* const* features) + : numInChans (JucePlugin_MaxNumInputChannels), + numOutChans (JucePlugin_MaxNumOutputChannels), + bufferSize (2048), + sampleRate (sampleRate_), + uridMap (nullptr), + uridAtomBlank (0), + uridAtomObject (0), + uridAtomDouble (0), + uridAtomFloat (0), + uridAtomInt (0), + uridAtomLong (0), + uridAtomSequence (0), + uridMidiEvent (0), + uridTimePos (0), + uridTimeBar (0), + uridTimeBarBeat (0), + uridTimeBeatsPerBar (0), + uridTimeBeatsPerMinute (0), + uridTimeBeatUnit (0), + uridTimeFrame (0), + uridTimeSpeed (0), + usingNominalBlockLength (false) + { + { + const MessageManagerLock mmLock; + filter = createPluginFilterOfType (AudioProcessor::wrapperType_LV2); + } + jassert (filter != nullptr); + + filter->setPlayConfigDetails (numInChans, numOutChans, 0, 0); + filter->setPlayHead (this); + +#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos) + portEventsIn = nullptr; +#endif +#if JucePlugin_ProducesMidiOutput + portMidiOut = nullptr; +#endif + + portFreewheel = nullptr; + +#if JucePlugin_WantsLV2Latency + portLatency = nullptr; +#endif + + for (int i=0; i < numInChans; ++i) + portAudioIns[i] = nullptr; + for (int i=0; i < numOutChans; ++i) + portAudioOuts[i] = nullptr; + + portControls.insertMultiple (0, nullptr, filter->getNumParameters()); + + for (int i=0; i < filter->getNumParameters(); ++i) + lastControlValues.add (filter->getParameter(i)); + + curPosInfo.resetToDefault(); + + // we need URID_Map first + for (int i=0; features[i] != nullptr; ++i) + { + if (strcmp(features[i]->URI, LV2_URID__map) == 0) + { + uridMap = (const LV2_URID_Map*)features[i]->data; + break; + } + } + + // we require uridMap to work properly (it's set as required feature) + jassert (uridMap != nullptr); + + if (uridMap != nullptr) + { + uridAtomBlank = uridMap->map(uridMap->handle, LV2_ATOM__Blank); + uridAtomObject = uridMap->map(uridMap->handle, LV2_ATOM__Object); + uridAtomDouble = uridMap->map(uridMap->handle, LV2_ATOM__Double); + uridAtomFloat = uridMap->map(uridMap->handle, LV2_ATOM__Float); + uridAtomInt = uridMap->map(uridMap->handle, LV2_ATOM__Int); + uridAtomLong = uridMap->map(uridMap->handle, LV2_ATOM__Long); + uridAtomSequence = uridMap->map(uridMap->handle, LV2_ATOM__Sequence); + uridMidiEvent = uridMap->map(uridMap->handle, LV2_MIDI__MidiEvent); + uridTimePos = uridMap->map(uridMap->handle, LV2_TIME__Position); + uridTimeBar = uridMap->map(uridMap->handle, LV2_TIME__bar); + uridTimeBarBeat = uridMap->map(uridMap->handle, LV2_TIME__barBeat); + uridTimeBeatsPerBar = uridMap->map(uridMap->handle, LV2_TIME__beatsPerBar); + uridTimeBeatsPerMinute = uridMap->map(uridMap->handle, LV2_TIME__beatsPerMinute); + uridTimeBeatUnit = uridMap->map(uridMap->handle, LV2_TIME__beatUnit); + uridTimeFrame = uridMap->map(uridMap->handle, LV2_TIME__frame); + uridTimeSpeed = uridMap->map(uridMap->handle, LV2_TIME__speed); + + for (int i=0; features[i] != nullptr; ++i) + { + if (strcmp(features[i]->URI, LV2_OPTIONS__options) == 0) + { + const LV2_Options_Option* options = (const LV2_Options_Option*)features[i]->data; + + for (int j=0; options[j].key != 0; ++j) + { + if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__nominalBlockLength)) + { + if (options[j].type == uridAtomInt) + { + bufferSize = *(int*)options[j].value; + usingNominalBlockLength = true; + } + else + { + std::cerr << "Host provides nominalBlockLength but has wrong value type" << std::endl; + } + break; + } + + if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength)) + { + if (options[j].type == uridAtomInt) + bufferSize = *(int*)options[j].value; + else + std::cerr << "Host provides maxBlockLength but has wrong value type" << std::endl; + + // no break, continue in case host supports nominalBlockLength + } + } + break; + } + } + } + + progDesc.bank = 0; + progDesc.program = 0; + progDesc.name = nullptr; + } + + ~JuceLv2Wrapper () + { + const MessageManagerLock mmLock; + +#if ! JUCE_AUDIOPROCESSOR_NO_GUI + ui = nullptr; +#endif + filter = nullptr; + + if (progDesc.name != nullptr) + free((void*)progDesc.name); + + portControls.clear(); + lastControlValues.clear(); + } + + //============================================================================== + // LV2 core calls + + void lv2ConnectPort (uint32 portId, void* dataLocation) + { + uint32 index = 0; + +#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos) + if (portId == index++) + { + portEventsIn = (LV2_Atom_Sequence*)dataLocation; + return; + } +#endif + +#if JucePlugin_ProducesMidiOutput + if (portId == index++) + { + portMidiOut = (LV2_Atom_Sequence*)dataLocation; + return; + } +#endif + + if (portId == index++) + { + portFreewheel = (float*)dataLocation; + return; + } + +#if JucePlugin_WantsLV2Latency + if (portId == index++) + { + portLatency = (float*)dataLocation; + return; + } +#endif + + for (int i=0; i < numInChans; ++i) + { + if (portId == index++) + { + portAudioIns[i] = (float*)dataLocation; + return; + } + } + + for (int i=0; i < numOutChans; ++i) + { + if (portId == index++) + { + portAudioOuts[i] = (float*)dataLocation; + return; + } + } + + for (int i=0; i < filter->getNumParameters(); ++i) + { + if (portId == index++) + { + portControls.set(i, (float*)dataLocation); + return; + } + } + } + + void lv2Activate() + { + jassert (filter != nullptr); + + filter->prepareToPlay (sampleRate, bufferSize); + filter->setPlayConfigDetails (numInChans, numOutChans, sampleRate, bufferSize); + + channels.calloc (numInChans + numOutChans); + +#if (JucePlugin_WantsMidiInput || JucePlugin_ProducesMidiOutput) + midiEvents.ensureSize (2048); + midiEvents.clear(); +#endif + } + + void lv2Deactivate() + { + jassert (filter != nullptr); + + filter->releaseResources(); + + channels.free(); + } + + void lv2Run (uint32 sampleCount) + { + jassert (filter != nullptr); + +#if JucePlugin_WantsLV2Latency + if (portLatency != nullptr) + *portLatency = filter->getLatencySamples(); +#endif + + if (portFreewheel != nullptr) + filter->setNonRealtime (*portFreewheel >= 0.5f); + + if (sampleCount == 0) + { + /** + LV2 pre-roll + Hosts might use this to force plugins to update its output control ports. + (plugins can only access port locations during run) */ + return; + } + + // Check for updated parameters + { + float curValue; + + for (int i = 0; i < portControls.size(); ++i) + { + if (portControls[i] != nullptr) + { + curValue = *portControls[i]; + + if (lastControlValues[i] != curValue) + { + filter->setParameter (i, curValue); + lastControlValues.setUnchecked (i, curValue); + } + } + } + } + + { + const ScopedLock sl (filter->getCallbackLock()); + + if (filter->isSuspended() && false) + { + for (int i = 0; i < numOutChans; ++i) + zeromem (portAudioOuts[i], sizeof (float) * sampleCount); + } + else + { + int i; + for (i = 0; i < numOutChans; ++i) + { + channels[i] = portAudioOuts[i]; + + if (i < numInChans && portAudioIns[i] != portAudioOuts[i]) + FloatVectorOperations::copy (portAudioOuts [i], portAudioIns[i], sampleCount); + } + + for (; i < numInChans; ++i) + channels [i] = portAudioIns[i]; + +#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos) + if (portEventsIn != nullptr) + { + midiEvents.clear(); + + LV2_ATOM_SEQUENCE_FOREACH(portEventsIn, iter) + { + const LV2_Atom_Event* event = (const LV2_Atom_Event*)iter; + + if (event == nullptr) + continue; + if (event->time.frames >= sampleCount) + break; + + #if JucePlugin_WantsMidiInput + if (event->body.type == uridMidiEvent) + { + const uint8* data = (const uint8*)(event + 1); + midiEvents.addEvent(data, event->body.size, event->time.frames); + continue; + } + #endif + #if JucePlugin_WantsLV2TimePos + if (event->body.type == uridAtomBlank || event->body.type == uridAtomObject) + { + const LV2_Atom_Object* obj = (LV2_Atom_Object*)&event->body; + + if (obj->body.otype != uridTimePos) + continue; + + LV2_Atom* bar = nullptr; + LV2_Atom* barBeat = nullptr; + LV2_Atom* beatUnit = nullptr; + LV2_Atom* beatsPerBar = nullptr; + LV2_Atom* beatsPerMinute = nullptr; + LV2_Atom* frame = nullptr; + LV2_Atom* speed = nullptr; + + lv2_atom_object_get (obj, + uridTimeBar, &bar, + uridTimeBarBeat, &barBeat, + uridTimeBeatUnit, &beatUnit, + uridTimeBeatsPerBar, &beatsPerBar, + uridTimeBeatsPerMinute, &beatsPerMinute, + uridTimeFrame, &frame, + uridTimeSpeed, &speed, + nullptr); + + // need to handle this first as other values depend on it + if (speed != nullptr) + { + /**/ if (speed->type == uridAtomDouble) + lastPositionData.speed = ((LV2_Atom_Double*)speed)->body; + else if (speed->type == uridAtomFloat) + lastPositionData.speed = ((LV2_Atom_Float*)speed)->body; + else if (speed->type == uridAtomInt) + lastPositionData.speed = ((LV2_Atom_Int*)speed)->body; + else if (speed->type == uridAtomLong) + lastPositionData.speed = ((LV2_Atom_Long*)speed)->body; + + curPosInfo.isPlaying = lastPositionData.speed != 0.0; + } + + if (bar != nullptr) + { + /**/ if (bar->type == uridAtomDouble) + lastPositionData.bar = ((LV2_Atom_Double*)bar)->body; + else if (bar->type == uridAtomFloat) + lastPositionData.bar = ((LV2_Atom_Float*)bar)->body; + else if (bar->type == uridAtomInt) + lastPositionData.bar = ((LV2_Atom_Int*)bar)->body; + else if (bar->type == uridAtomLong) + lastPositionData.bar = ((LV2_Atom_Long*)bar)->body; + } + + if (barBeat != nullptr) + { + /**/ if (barBeat->type == uridAtomDouble) + lastPositionData.barBeat = ((LV2_Atom_Double*)barBeat)->body; + else if (barBeat->type == uridAtomFloat) + lastPositionData.barBeat = ((LV2_Atom_Float*)barBeat)->body; + else if (barBeat->type == uridAtomInt) + lastPositionData.barBeat = ((LV2_Atom_Int*)barBeat)->body; + else if (barBeat->type == uridAtomLong) + lastPositionData.barBeat = ((LV2_Atom_Long*)barBeat)->body; + } + + if (beatUnit != nullptr) + { + /**/ if (beatUnit->type == uridAtomDouble) + lastPositionData.beatUnit = ((LV2_Atom_Double*)beatUnit)->body; + else if (beatUnit->type == uridAtomFloat) + lastPositionData.beatUnit = ((LV2_Atom_Float*)beatUnit)->body; + else if (beatUnit->type == uridAtomInt) + lastPositionData.beatUnit = ((LV2_Atom_Int*)beatUnit)->body; + else if (beatUnit->type == uridAtomLong) + lastPositionData.beatUnit = ((LV2_Atom_Long*)beatUnit)->body; + + if (lastPositionData.beatUnit > 0) + curPosInfo.timeSigDenominator = lastPositionData.beatUnit; + } + + if (beatsPerBar != nullptr) + { + /**/ if (beatsPerBar->type == uridAtomDouble) + lastPositionData.beatsPerBar = ((LV2_Atom_Double*)beatsPerBar)->body; + else if (beatsPerBar->type == uridAtomFloat) + lastPositionData.beatsPerBar = ((LV2_Atom_Float*)beatsPerBar)->body; + else if (beatsPerBar->type == uridAtomInt) + lastPositionData.beatsPerBar = ((LV2_Atom_Int*)beatsPerBar)->body; + else if (beatsPerBar->type == uridAtomLong) + lastPositionData.beatsPerBar = ((LV2_Atom_Long*)beatsPerBar)->body; + + if (lastPositionData.beatsPerBar > 0.0f) + curPosInfo.timeSigNumerator = lastPositionData.beatsPerBar; + } + + if (beatsPerMinute != nullptr) + { + /**/ if (beatsPerMinute->type == uridAtomDouble) + lastPositionData.beatsPerMinute = ((LV2_Atom_Double*)beatsPerMinute)->body; + else if (beatsPerMinute->type == uridAtomFloat) + lastPositionData.beatsPerMinute = ((LV2_Atom_Float*)beatsPerMinute)->body; + else if (beatsPerMinute->type == uridAtomInt) + lastPositionData.beatsPerMinute = ((LV2_Atom_Int*)beatsPerMinute)->body; + else if (beatsPerMinute->type == uridAtomLong) + lastPositionData.beatsPerMinute = ((LV2_Atom_Long*)beatsPerMinute)->body; + + if (lastPositionData.beatsPerMinute > 0.0f) + { + curPosInfo.bpm = lastPositionData.beatsPerMinute; + + if (lastPositionData.speed != 0) + curPosInfo.bpm *= std::abs(lastPositionData.speed); + } + } + + if (frame != nullptr) + { + /**/ if (frame->type == uridAtomDouble) + lastPositionData.frame = ((LV2_Atom_Double*)frame)->body; + else if (frame->type == uridAtomFloat) + lastPositionData.frame = ((LV2_Atom_Float*)frame)->body; + else if (frame->type == uridAtomInt) + lastPositionData.frame = ((LV2_Atom_Int*)frame)->body; + else if (frame->type == uridAtomLong) + lastPositionData.frame = ((LV2_Atom_Long*)frame)->body; + + if (lastPositionData.frame >= 0) + { + curPosInfo.timeInSamples = lastPositionData.frame; + curPosInfo.timeInSeconds = double(curPosInfo.timeInSamples)/sampleRate; + } + } + + if (lastPositionData.bar >= 0 && lastPositionData.beatsPerBar > 0.0f) + { + curPosInfo.ppqPositionOfLastBarStart = lastPositionData.bar * lastPositionData.beatsPerBar; + + if (lastPositionData.barBeat >= 0.0f) + curPosInfo.ppqPosition = curPosInfo.ppqPositionOfLastBarStart + lastPositionData.barBeat; + } + + lastPositionData.extraValid = (lastPositionData.beatsPerMinute > 0.0 && + lastPositionData.beatUnit > 0 && + lastPositionData.beatsPerBar > 0.0f); + } + #endif + } + } +#endif + { + AudioSampleBuffer chans (channels, jmax (numInChans, numOutChans), sampleCount); + filter->processBlock (chans, midiEvents); + } + } + } + +#if JucePlugin_WantsLV2TimePos + // update timePos for next callback + if (lastPositionData.speed != 0.0) + { + if (lastPositionData.speed > 0.0) + { + // playing forwards + lastPositionData.frame += sampleCount; + } + else + { + // playing backwards + lastPositionData.frame -= sampleCount; + + if (lastPositionData.frame < 0) + lastPositionData.frame = 0; + } + + curPosInfo.timeInSamples = lastPositionData.frame; + curPosInfo.timeInSeconds = double(curPosInfo.timeInSamples)/sampleRate; + + if (lastPositionData.extraValid) + { + const double beatsPerMinute = lastPositionData.beatsPerMinute * lastPositionData.speed; + const double framesPerBeat = 60.0 * sampleRate / beatsPerMinute; + const double addedBarBeats = double(sampleCount) / framesPerBeat; + + if (lastPositionData.bar >= 0 && lastPositionData.barBeat >= 0.0f) + { + lastPositionData.bar += std::floor((lastPositionData.barBeat+addedBarBeats)/ + lastPositionData.beatsPerBar); + lastPositionData.barBeat = std::fmod(lastPositionData.barBeat+addedBarBeats, + lastPositionData.beatsPerBar); + + if (lastPositionData.bar < 0) + lastPositionData.bar = 0; + + curPosInfo.ppqPositionOfLastBarStart = lastPositionData.bar * lastPositionData.beatsPerBar; + curPosInfo.ppqPosition = curPosInfo.ppqPositionOfLastBarStart + lastPositionData.barBeat; + } + + curPosInfo.bpm = std::abs(beatsPerMinute); + } + } +#endif + +#if JucePlugin_ProducesMidiOutput + if (portMidiOut != nullptr) + { + const uint32_t capacity = portMidiOut->atom.size; + + portMidiOut->atom.size = sizeof(LV2_Atom_Sequence_Body); + portMidiOut->atom.type = uridAtomSequence; + portMidiOut->body.unit = 0; + portMidiOut->body.pad = 0; + + if (! midiEvents.isEmpty()) + { + const uint8* midiEventData; + int midiEventSize, midiEventPosition; + MidiBuffer::Iterator i (midiEvents); + + uint32_t size, offset = 0; + LV2_Atom_Event* aev; + + while (i.getNextEvent (midiEventData, midiEventSize, midiEventPosition)) + { + jassert (midiEventPosition >= 0 && midiEventPosition < (int)sampleCount); + + if (sizeof(LV2_Atom_Event) + midiEventSize > capacity - offset) + break; + + aev = (LV2_Atom_Event*)((char*)LV2_ATOM_CONTENTS(LV2_Atom_Sequence, portMidiOut) + offset); + aev->time.frames = midiEventPosition; + aev->body.type = uridMidiEvent; + aev->body.size = midiEventSize; + memcpy(LV2_ATOM_BODY(&aev->body), midiEventData, midiEventSize); + + size = lv2_atom_pad_size(sizeof(LV2_Atom_Event) + midiEventSize); + offset += size; + portMidiOut->atom.size += size; + } + + midiEvents.clear(); + } + } else +#endif + if (! midiEvents.isEmpty()) + { + midiEvents.clear(); + } + } + + //============================================================================== + // LV2 extended calls + + uint32_t lv2GetOptions (LV2_Options_Option* options) + { + // currently unused + ignoreUnused(options); + + return LV2_OPTIONS_SUCCESS; + } + + uint32_t lv2SetOptions (const LV2_Options_Option* options) + { + for (int j=0; options[j].key != 0; ++j) + { + if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__nominalBlockLength)) + { + if (options[j].type == uridAtomInt) + bufferSize = *(int*)options[j].value; + else + std::cerr << "Host changed nominalBlockLength but with wrong value type" << std::endl; + } + else if (options[j].key == uridMap->map(uridMap->handle, LV2_BUF_SIZE__maxBlockLength) && ! usingNominalBlockLength) + { + if (options[j].type == uridAtomInt) + bufferSize = *(int*)options[j].value; + else + std::cerr << "Host changed maxBlockLength but with wrong value type" << std::endl; + } + else if (options[j].key == uridMap->map(uridMap->handle, LV2_CORE__sampleRate)) + { + if (options[j].type == uridAtomDouble) + sampleRate = *(double*)options[j].value; + else + std::cerr << "Host changed sampleRate but with wrong value type" << std::endl; + } + } + + return LV2_OPTIONS_SUCCESS; + } + + const LV2_Program_Descriptor* lv2GetProgram (uint32_t index) + { + jassert (filter != nullptr); + + if (progDesc.name != nullptr) + { + free((void*)progDesc.name); + progDesc.name = nullptr; + } + + if ((int)index < filter->getNumPrograms()) + { + progDesc.bank = index / 128; + progDesc.program = index % 128; + progDesc.name = strdup(filter->getProgramName(index).toUTF8()); + return &progDesc; + } + + return nullptr; + } + + void lv2SelectProgram (uint32_t bank, uint32_t program) + { + jassert (filter != nullptr); + + int realProgram = bank * 128 + program; + + if (realProgram < filter->getNumPrograms()) + { + filter->setCurrentProgram(realProgram); + + // update input control ports now + for (int i = 0; i < portControls.size(); ++i) + { + float value = filter->getParameter(i); + + if (portControls[i] != nullptr) + *portControls[i] = value; + + lastControlValues.set(i, value); + } + } + } + + LV2_State_Status lv2SaveState (LV2_State_Store_Function store, LV2_State_Handle stateHandle) + { + jassert (filter != nullptr); + +#if JucePlugin_WantsLV2StateString + String stateData(filter->getStateInformationString().replace("\r\n","\n")); + CharPointer_UTF8 charData(stateData.toUTF8()); + + store (stateHandle, + uridMap->map(uridMap->handle, JUCE_LV2_STATE_STRING_URI), + charData.getAddress(), + charData.sizeInBytes(), + uridMap->map(uridMap->handle, LV2_ATOM__String), + LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE); +#else + MemoryBlock chunkMemory; + filter->getCurrentProgramStateInformation (chunkMemory); + + store (stateHandle, + uridMap->map(uridMap->handle, JUCE_LV2_STATE_BINARY_URI), + chunkMemory.getData(), + chunkMemory.getSize(), + uridMap->map(uridMap->handle, LV2_ATOM__Chunk), + LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE); +#endif + + return LV2_STATE_SUCCESS; + } + + LV2_State_Status lv2RestoreState (LV2_State_Retrieve_Function retrieve, LV2_State_Handle stateHandle, uint32_t flags) + { + jassert (filter != nullptr); + + size_t size = 0; + uint32 type = 0; + const void* data = retrieve (stateHandle, +#if JucePlugin_WantsLV2StateString + uridMap->map(uridMap->handle, JUCE_LV2_STATE_STRING_URI), +#else + uridMap->map(uridMap->handle, JUCE_LV2_STATE_BINARY_URI), +#endif + &size, &type, &flags); + + if (data == nullptr || size == 0 || type == 0) + return LV2_STATE_ERR_UNKNOWN; + +#if JucePlugin_WantsLV2StateString + if (type == uridMap->map (uridMap->handle, LV2_ATOM__String)) + { + String stateData (CharPointer_UTF8(static_cast(data))); + filter->setStateInformationString (stateData); + + #if ! JUCE_AUDIOPROCESSOR_NO_GUI + if (ui != nullptr) + ui->repaint(); + #endif + + return LV2_STATE_SUCCESS; + } +#else + if (type == uridMap->map (uridMap->handle, LV2_ATOM__Chunk)) + { + filter->setCurrentProgramStateInformation (data, size); + + #if ! JUCE_AUDIOPROCESSOR_NO_GUI + if (ui != nullptr) + ui->repaint(); + #endif + + return LV2_STATE_SUCCESS; + } +#endif + + return LV2_STATE_ERR_BAD_TYPE; + } + + //============================================================================== + // Juce calls + + bool getCurrentPosition (AudioPlayHead::CurrentPositionInfo& info) + { +#if JucePlugin_WantsLV2TimePos + info = curPosInfo; + return true; +#else + ignoreUnused(info); + return false; +#endif + } + +#if ! JUCE_AUDIOPROCESSOR_NO_GUI + //============================================================================== + JuceLv2UIWrapper* getUI (LV2UI_Write_Function writeFunction, LV2UI_Controller controller, LV2UI_Widget* widget, + const LV2_Feature* const* features, bool isExternal) + { + const MessageManagerLock mmLock; + + if (ui != nullptr) + ui->resetIfNeeded (writeFunction, controller, widget, features); + else + ui = new JuceLv2UIWrapper (filter, writeFunction, controller, widget, features, isExternal); + + return ui; + } +#endif + +private: +#if JUCE_LINUX + SharedResourcePointer msgThread; +#else + SharedResourcePointer sharedJuceGUI; +#endif + + ScopedPointer filter; +#if ! JUCE_AUDIOPROCESSOR_NO_GUI + ScopedPointer ui; +#endif + HeapBlock channels; + MidiBuffer midiEvents; + int numInChans, numOutChans; + +#if (JucePlugin_WantsMidiInput || JucePlugin_WantsLV2TimePos) + LV2_Atom_Sequence* portEventsIn; +#endif +#if JucePlugin_ProducesMidiOutput + LV2_Atom_Sequence* portMidiOut; +#endif + float* portFreewheel; +#if JucePlugin_WantsLV2Latency + float* portLatency; +#endif + float* portAudioIns[JucePlugin_MaxNumInputChannels]; + float* portAudioOuts[JucePlugin_MaxNumOutputChannels]; + Array portControls; + + uint32 bufferSize; + double sampleRate; + Array lastControlValues; + AudioPlayHead::CurrentPositionInfo curPosInfo; + + struct Lv2PositionData { + int64_t bar; + float barBeat; + uint32_t beatUnit; + float beatsPerBar; + float beatsPerMinute; + int64_t frame; + double speed; + bool extraValid; + + Lv2PositionData() + : bar(-1), + barBeat(-1.0f), + beatUnit(0), + beatsPerBar(0.0f), + beatsPerMinute(0.0f), + frame(-1), + speed(0.0), + extraValid(false) {} + }; + Lv2PositionData lastPositionData; + + const LV2_URID_Map* uridMap; + LV2_URID uridAtomBlank; + LV2_URID uridAtomObject; + LV2_URID uridAtomDouble; + LV2_URID uridAtomFloat; + LV2_URID uridAtomInt; + LV2_URID uridAtomLong; + LV2_URID uridAtomSequence; + LV2_URID uridMidiEvent; + LV2_URID uridTimePos; + LV2_URID uridTimeBar; + LV2_URID uridTimeBarBeat; + LV2_URID uridTimeBeatsPerBar; // timeSigNumerator + LV2_URID uridTimeBeatsPerMinute; // bpm + LV2_URID uridTimeBeatUnit; // timeSigDenominator + LV2_URID uridTimeFrame; // timeInSamples + LV2_URID uridTimeSpeed; + + bool usingNominalBlockLength; // if false use maxBlockLength + + LV2_Program_Descriptor progDesc; + + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceLv2Wrapper) +}; + +//============================================================================== +// LV2 descriptor functions + +static LV2_Handle juceLV2_Instantiate (const LV2_Descriptor*, double sampleRate, const char*, const LV2_Feature* const* features) +{ + return new JuceLv2Wrapper (sampleRate, features); +} + +#define handlePtr ((JuceLv2Wrapper*)handle) + +static void juceLV2_ConnectPort (LV2_Handle handle, uint32 port, void* dataLocation) +{ + handlePtr->lv2ConnectPort (port, dataLocation); +} + +static void juceLV2_Activate (LV2_Handle handle) +{ + handlePtr->lv2Activate(); +} + +static void juceLV2_Run( LV2_Handle handle, uint32 sampleCount) +{ + handlePtr->lv2Run (sampleCount); +} + +static void juceLV2_Deactivate (LV2_Handle handle) +{ + handlePtr->lv2Deactivate(); +} + +static void juceLV2_Cleanup (LV2_Handle handle) +{ + delete handlePtr; +} + +//============================================================================== +// LV2 extended functions + +static uint32_t juceLV2_getOptions (LV2_Handle handle, LV2_Options_Option* options) +{ + return handlePtr->lv2GetOptions(options); +} + +static uint32_t juceLV2_setOptions (LV2_Handle handle, const LV2_Options_Option* options) +{ + return handlePtr->lv2SetOptions(options); +} + +static const LV2_Program_Descriptor* juceLV2_getProgram (LV2_Handle handle, uint32_t index) +{ + return handlePtr->lv2GetProgram(index); +} + +static void juceLV2_selectProgram (LV2_Handle handle, uint32_t bank, uint32_t program) +{ + handlePtr->lv2SelectProgram(bank, program); +} + +static LV2_State_Status juceLV2_SaveState (LV2_Handle handle, LV2_State_Store_Function store, LV2_State_Handle stateHandle, + uint32_t, const LV2_Feature* const*) +{ + return handlePtr->lv2SaveState(store, stateHandle); +} + +static LV2_State_Status juceLV2_RestoreState (LV2_Handle handle, LV2_State_Retrieve_Function retrieve, LV2_State_Handle stateHandle, + uint32_t flags, const LV2_Feature* const*) +{ + return handlePtr->lv2RestoreState(retrieve, stateHandle, flags); +} + +#undef handlePtr + +static const void* juceLV2_ExtensionData (const char* uri) +{ + static const LV2_Options_Interface options = { juceLV2_getOptions, juceLV2_setOptions }; + static const LV2_Programs_Interface programs = { juceLV2_getProgram, juceLV2_selectProgram }; + static const LV2_State_Interface state = { juceLV2_SaveState, juceLV2_RestoreState }; + + if (strcmp(uri, LV2_OPTIONS__interface) == 0) + return &options; + if (strcmp(uri, LV2_PROGRAMS__Interface) == 0) + return &programs; + if (strcmp(uri, LV2_STATE__interface) == 0) + return &state; + + return nullptr; +} + +#if ! JUCE_AUDIOPROCESSOR_NO_GUI +//============================================================================== +// LV2 UI descriptor functions + +static LV2UI_Handle juceLV2UI_Instantiate (LV2UI_Write_Function writeFunction, LV2UI_Controller controller, + LV2UI_Widget* widget, const LV2_Feature* const* features, bool isExternal) +{ + for (int i = 0; features[i] != nullptr; ++i) + { + if (strcmp(features[i]->URI, LV2_INSTANCE_ACCESS_URI) == 0 && features[i]->data != nullptr) + { + JuceLv2Wrapper* wrapper = (JuceLv2Wrapper*)features[i]->data; + return wrapper->getUI(writeFunction, controller, widget, features, isExternal); + } + } + + std::cerr << "Host does not support instance-access, cannot use UI" << std::endl; + return nullptr; +} + +static LV2UI_Handle juceLV2UI_InstantiateExternal (const LV2UI_Descriptor*, const char*, const char*, LV2UI_Write_Function writeFunction, + LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features) +{ + return juceLV2UI_Instantiate(writeFunction, controller, widget, features, true); +} + +static LV2UI_Handle juceLV2UI_InstantiateParent (const LV2UI_Descriptor*, const char*, const char*, LV2UI_Write_Function writeFunction, + LV2UI_Controller controller, LV2UI_Widget* widget, const LV2_Feature* const* features) +{ + return juceLV2UI_Instantiate(writeFunction, controller, widget, features, false); +} + +static void juceLV2UI_Cleanup (LV2UI_Handle handle) +{ + ((JuceLv2UIWrapper*)handle)->lv2Cleanup(); +} +#endif + +//============================================================================== +// static LV2 Descriptor objects + +static const LV2_Descriptor JuceLv2Plugin = { + strdup(getPluginURI().toRawUTF8()), + juceLV2_Instantiate, + juceLV2_ConnectPort, + juceLV2_Activate, + juceLV2_Run, + juceLV2_Deactivate, + juceLV2_Cleanup, + juceLV2_ExtensionData +}; + +#if ! JUCE_AUDIOPROCESSOR_NO_GUI +static const LV2UI_Descriptor JuceLv2UI_External = { + strdup(String(getPluginURI() + "#ExternalUI").toRawUTF8()), + juceLV2UI_InstantiateExternal, + juceLV2UI_Cleanup, + nullptr, + nullptr +}; + +static const LV2UI_Descriptor JuceLv2UI_Parent = { + strdup(String(getPluginURI() + "#ParentUI").toRawUTF8()), + juceLV2UI_InstantiateParent, + juceLV2UI_Cleanup, + nullptr, + nullptr +}; +#endif + +static const struct DescriptorCleanup { + DescriptorCleanup() {} + ~DescriptorCleanup() + { + free((void*)JuceLv2Plugin.URI); +#if ! JUCE_AUDIOPROCESSOR_NO_GUI + free((void*)JuceLv2UI_External.URI); + free((void*)JuceLv2UI_Parent.URI); +#endif + } +} _descCleanup; + +#if JUCE_WINDOWS + #define JUCE_EXPORTED_FUNCTION extern "C" __declspec (dllexport) +#else + #define JUCE_EXPORTED_FUNCTION extern "C" __attribute__ ((visibility("default"))) +#endif + +//============================================================================== +// startup code.. + +JUCE_EXPORTED_FUNCTION void lv2_generate_ttl (const char* basename); +JUCE_EXPORTED_FUNCTION void lv2_generate_ttl (const char* basename) +{ + createLv2Files (basename); +} + +JUCE_EXPORTED_FUNCTION const LV2_Descriptor* lv2_descriptor (uint32 index); +JUCE_EXPORTED_FUNCTION const LV2_Descriptor* lv2_descriptor (uint32 index) +{ + return (index == 0) ? &JuceLv2Plugin : nullptr; +} + +#if ! JUCE_AUDIOPROCESSOR_NO_GUI +JUCE_EXPORTED_FUNCTION const LV2UI_Descriptor* lv2ui_descriptor (uint32 index); +JUCE_EXPORTED_FUNCTION const LV2UI_Descriptor* lv2ui_descriptor (uint32 index) +{ + switch (index) + { + case 0: + return &JuceLv2UI_External; + case 1: + return &JuceLv2UI_Parent; + default: + return nullptr; + } +} +#endif + +#endif diff --git a/modules/juce_audio_plugin_client/juce_audio_plugin_client_LV2.cpp b/modules/juce_audio_plugin_client/juce_audio_plugin_client_LV2.cpp new file mode 100644 index 0000000000..dc507e1257 --- /dev/null +++ b/modules/juce_audio_plugin_client/juce_audio_plugin_client_LV2.cpp @@ -0,0 +1,27 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2017 - ROLI Ltd. + + JUCE is an open source library subject to commercial or open-source + licensing. + + By using JUCE, you agree to the terms of both the JUCE 5 End-User License + Agreement and JUCE 5 Privacy Policy (both updated and effective as of the + 27th April 2017). + + End User License Agreement: www.juce.com/juce-5-licence + Privacy Policy: www.juce.com/juce-5-privacy-policy + + Or: You may also use this code under the terms of the GPL v3 (see + www.gnu.org/licenses). + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +#include "LV2/juce_LV2_Wrapper.cpp" diff --git a/modules/juce_audio_processors/processors/juce_AudioProcessor.h b/modules/juce_audio_processors/processors/juce_AudioProcessor.h index abde506275..8c4a141bce 100644 --- a/modules/juce_audio_processors/processors/juce_AudioProcessor.h +++ b/modules/juce_audio_processors/processors/juce_AudioProcessor.h @@ -1307,6 +1307,7 @@ public: wrapperType_AudioUnitv3, wrapperType_RTAS, wrapperType_AAX, + wrapperType_LV2, wrapperType_Standalone };