diff options
-rw-r--r-- | osc.lv2/CMakeLists.txt | 16 | ||||
-rw-r--r-- | osc.lv2/lv2-osc.doap.ttl | 2 | ||||
-rw-r--r-- | osc.lv2/lv2_osc.h | 704 | ||||
-rw-r--r-- | osc.lv2/manifest.ttl | 2 | ||||
-rw-r--r-- | osc.lv2/osc.lv2/forge.h | 471 | ||||
-rw-r--r-- | osc.lv2/osc.lv2/osc.h | 187 | ||||
-rw-r--r-- | osc.lv2/osc.lv2/reader.h | 570 | ||||
-rw-r--r-- | osc.lv2/osc.lv2/util.h | 349 | ||||
-rw-r--r-- | osc.lv2/osc.lv2/writer.h | 551 | ||||
-rw-r--r-- | osc.lv2/osc.ttl | 79 | ||||
-rw-r--r-- | osc.lv2/osc_test.c | 423 |
11 files changed, 2579 insertions, 775 deletions
diff --git a/osc.lv2/CMakeLists.txt b/osc.lv2/CMakeLists.txt new file mode 100644 index 0000000..64ce73b --- /dev/null +++ b/osc.lv2/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 2.8) + +project(osc.lv2) + +include_directories(${PROJECT_SOURCE_DIR}) + +set(CMAKE_C_FLAGS "-std=gnu11 -Wextra -Wno-unused-parameter -ffast-math -fvisibility=hidden ${CMAKE_C_FLAGS}") +set(CMAKE_C_FLAGS "-Wshadow -Wimplicit-function-declaration -Wredundant-decls -Wmissing-prototypes -Wstrict-prototypes ${CMAKE_C_FLAGS}") + +include(CTest) + +if(${BUILD_TESTING}) + add_executable(osc_test + osc_test.c) + add_test(NAME API-Test COMMAND osc_test) +endif() diff --git a/osc.lv2/lv2-osc.doap.ttl b/osc.lv2/lv2-osc.doap.ttl index 15add6d..ef74f92 100644 --- a/osc.lv2/lv2-osc.doap.ttl +++ b/osc.lv2/lv2-osc.doap.ttl @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch) +# Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) # # This is free software: you can redistribute it and/or modify # it under the terms of the Artistic License 2.0 as published by diff --git a/osc.lv2/lv2_osc.h b/osc.lv2/lv2_osc.h deleted file mode 100644 index 057f212..0000000 --- a/osc.lv2/lv2_osc.h +++ /dev/null @@ -1,704 +0,0 @@ -/* - * Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch) - * - * This is free software: you can redistribute it and/or modify - * it under the terms of the Artistic License 2.0 as published by - * The Perl Foundation. - * - * This source is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * Artistic License 2.0 for more details. - * - * You should have received a copy of the Artistic License 2.0 - * along the source as a COPYING file. If not, obtain it from - * http://www.perlfoundation.org/artistic_license_2_0. - */ - -#ifndef _LV2_OSC_H_ -#define _LV2_OSC_H_ - -#include <math.h> // INFINITY - -#include <lv2/lv2plug.in/ns/lv2core/lv2.h> -#include <lv2/lv2plug.in/ns/ext/urid/urid.h> -#include <lv2/lv2plug.in/ns/ext/atom/atom.h> -#include <lv2/lv2plug.in/ns/ext/atom/forge.h> -#include <lv2/lv2plug.in/ns/ext/midi/midi.h> - -#define OSC_URI "http://open-music-kontrollers.ch/lv2/osc" -#define OSC_PREFIX OSC_URI "#" - -#define OSC__Event OSC_PREFIX "Event" // event -#define OSC__schedule OSC_PREFIX "schedule" // feature - -#define OSC__Bundle OSC_PREFIX "Bundle" // object otype -#define OSC__Message OSC_PREFIX "Message" // object otype -#define OSC__bundleTimestamp OSC_PREFIX "bundleTimestamp" // property key -#define OSC__bundleItems OSC_PREFIX "bundleItems" // property key -#define OSC__messagePath OSC_PREFIX "messagePath" // property key -#define OSC__messageFormat OSC_PREFIX "messageFormat" // property key -#define OSC__messageArguments OSC_PREFIX "messageArguments" // property key - -typedef void *osc_schedule_handle_t; -typedef struct _osc_schedule_t osc_schedule_t; -typedef struct _osc_forge_t osc_forge_t; - -typedef void (*osc_bundle_push_cb_t)(uint64_t timestamp, void *data); -typedef void (*osc_bundle_pop_cb_t)(void *data); -typedef void (*osc_message_cb_t)(const char *path, const char *fmt, - const LV2_Atom_Tuple *arguments, void *data); - -typedef double (*osc_schedule_osc2frames_t)(osc_schedule_handle_t handle, - uint64_t timestamp); -typedef uint64_t (*osc_schedule_frames2osc_t)(osc_schedule_handle_t handle, - double frames); - -struct _osc_schedule_t { - osc_schedule_osc2frames_t osc2frames; - osc_schedule_frames2osc_t frames2osc; - osc_schedule_handle_t handle; -}; - -struct _osc_forge_t { - LV2_URID OSC_Event; - - LV2_URID OSC_Bundle; - LV2_URID OSC_Message; - - LV2_URID OSC_bundleTimestamp; - LV2_URID OSC_bundleItems; - - LV2_URID OSC_messagePath; - LV2_URID OSC_messageFormat; - LV2_URID OSC_messageArguments; - - LV2_URID MIDI_MidiEvent; - - LV2_URID ATOM_Object; -}; - -static inline void -osc_forge_init(osc_forge_t *oforge, LV2_URID_Map *map) -{ - oforge->OSC_Event = map->map(map->handle, OSC__Event); - - oforge->OSC_Bundle = map->map(map->handle, OSC__Bundle); - oforge->OSC_Message = map->map(map->handle, OSC__Message); - - oforge->OSC_bundleTimestamp = map->map(map->handle, OSC__bundleTimestamp); - oforge->OSC_bundleItems = map->map(map->handle, OSC__bundleItems); - - oforge->OSC_messagePath = map->map(map->handle, OSC__messagePath); - oforge->OSC_messageFormat = map->map(map->handle, OSC__messageFormat); - oforge->OSC_messageArguments = map->map(map->handle, OSC__messageArguments); - - oforge->MIDI_MidiEvent = map->map(map->handle, LV2_MIDI__MidiEvent); - - oforge->ATOM_Object = map->map(map->handle, LV2_ATOM__Object); -} - -static inline int -osc_atom_is_bundle(osc_forge_t *oforge, const LV2_Atom_Object *obj) -{ - return (obj->atom.type == oforge->ATOM_Object) - && (obj->body.otype == oforge->OSC_Bundle); -} - -static inline void -osc_atom_bundle_unpack(osc_forge_t *oforge, const LV2_Atom_Object *obj, - const LV2_Atom_Long **timestamp, const LV2_Atom_Tuple **items) -{ - *timestamp = NULL; - *items = NULL; - - LV2_Atom_Object_Query q [] = { - { oforge->OSC_bundleTimestamp, (const LV2_Atom **)timestamp }, - { oforge->OSC_bundleItems, (const LV2_Atom **)items }, - LV2_ATOM_OBJECT_QUERY_END - }; - - lv2_atom_object_query(obj, q); -} - -static inline int -osc_atom_is_message(osc_forge_t *oforge, const LV2_Atom_Object *obj) -{ - return (obj->atom.type == oforge->ATOM_Object) - && (obj->body.otype == oforge->OSC_Message); -} - -static inline void -osc_atom_message_unpack(osc_forge_t *oforge, const LV2_Atom_Object *obj, - const LV2_Atom_String **path, const LV2_Atom_String **format, - const LV2_Atom_Tuple **arguments) -{ - *path = NULL; - *format = NULL; - *arguments = NULL; - - LV2_Atom_Object_Query q [] = { - { oforge->OSC_messagePath, (const LV2_Atom **)path }, - { oforge->OSC_messageFormat, (const LV2_Atom **)format }, - { oforge->OSC_messageArguments, (const LV2_Atom **)arguments }, - LV2_ATOM_OBJECT_QUERY_END - }; - - lv2_atom_object_query(obj, q); -} - -static inline void osc_atom_event_unroll(osc_forge_t *oforge, - const LV2_Atom_Object *obj, osc_bundle_push_cb_t bundle_push_cb, - osc_bundle_pop_cb_t bundle_pop_cb, osc_message_cb_t message_cb, void *data); - -static inline void -osc_atom_message_unroll(osc_forge_t *oforge, const LV2_Atom_Object *obj, - osc_message_cb_t message_cb, void *data) -{ - const LV2_Atom_String* path; - const LV2_Atom_String* fmt; - const LV2_Atom_Tuple* args; - - osc_atom_message_unpack(oforge, obj, &path, &fmt, &args); - - const char *path_str = path ? LV2_ATOM_BODY_CONST(path) : NULL; - const char *fmt_str = fmt ? LV2_ATOM_BODY_CONST(fmt) : NULL; - - if(message_cb) - message_cb(path_str, fmt_str, args, data); -} - -static inline void -osc_atom_bundle_unroll(osc_forge_t *oforge, const LV2_Atom_Object *obj, - osc_bundle_push_cb_t bundle_push_cb, osc_bundle_pop_cb_t bundle_pop_cb, - osc_message_cb_t message_cb, void *data) -{ - const LV2_Atom_Long* timestamp; - const LV2_Atom_Tuple* items; - - osc_atom_bundle_unpack(oforge, obj, ×tamp, &items); - - uint64_t timestamp_body = timestamp ? (uint64_t)timestamp->body : 1ULL; - - if(bundle_push_cb) - bundle_push_cb(timestamp_body, data); - - // iterate over tuple body - if(items) - { - for(const LV2_Atom *itr = lv2_atom_tuple_begin(items); - !lv2_atom_tuple_is_end(LV2_ATOM_BODY(items), items->atom.size, itr); - itr = lv2_atom_tuple_next(itr)) - { - osc_atom_event_unroll(oforge, (const LV2_Atom_Object *)itr, - bundle_push_cb, bundle_pop_cb, message_cb, data); - } - } - - if(bundle_pop_cb) - bundle_pop_cb(data); -} - -static inline void -osc_atom_event_unroll(osc_forge_t *oforge, const LV2_Atom_Object *obj, - osc_bundle_push_cb_t bundle_push_cb, osc_bundle_pop_cb_t bundle_pop_cb, - osc_message_cb_t message_cb, void *data) -{ - if(osc_atom_is_bundle(oforge, obj)) - { - osc_atom_bundle_unroll(oforge, obj, bundle_push_cb, bundle_pop_cb, - message_cb, data); - } - else if(osc_atom_is_message(oforge, obj)) - { - osc_atom_message_unroll(oforge, obj, message_cb, data); - } -} - -static inline LV2_Atom_Forge_Ref -osc_forge_bundle_push(osc_forge_t *oforge, LV2_Atom_Forge *forge, - LV2_Atom_Forge_Frame frame [2], uint64_t timestamp) -{ - if(!lv2_atom_forge_object(forge, &frame[0], 0, oforge->OSC_Bundle)) - return 0; - - if(!lv2_atom_forge_key(forge, oforge->OSC_bundleTimestamp)) - return 0; - if(!lv2_atom_forge_long(forge, timestamp)) - return 0; - - if(!lv2_atom_forge_key(forge, oforge->OSC_bundleItems)) - return 0; - - return lv2_atom_forge_tuple(forge, &frame[1]); -} - -static inline void -osc_forge_bundle_pop(osc_forge_t *oforge, LV2_Atom_Forge *forge, - LV2_Atom_Forge_Frame frame [2]) -{ - lv2_atom_forge_pop(forge, &frame[1]); - lv2_atom_forge_pop(forge, &frame[0]); -} - -static inline LV2_Atom_Forge_Ref -osc_forge_message_push(osc_forge_t *oforge, LV2_Atom_Forge *forge, - LV2_Atom_Forge_Frame frame [2], const char *path, const char *fmt) -{ - if(!lv2_atom_forge_object(forge, &frame[0], 0, oforge->OSC_Message)) - return 0; - - if(!lv2_atom_forge_key(forge, oforge->OSC_messagePath)) - return 0; - if(!lv2_atom_forge_string(forge, path, strlen(path))) - return 0; - - if(!lv2_atom_forge_key(forge, oforge->OSC_messageFormat)) - return 0; - if(!lv2_atom_forge_string(forge, fmt, strlen(fmt))) - return 0; - - if(!lv2_atom_forge_key(forge, oforge->OSC_messageArguments)) - return 0; - - return lv2_atom_forge_tuple(forge, &frame[1]); -} - -static inline void -osc_forge_message_pop(osc_forge_t *oforge, LV2_Atom_Forge *forge, - LV2_Atom_Forge_Frame frame [2]) -{ - lv2_atom_forge_pop(forge, &frame[1]); - lv2_atom_forge_pop(forge, &frame[0]); -} - -static inline LV2_Atom_Forge_Ref -osc_forge_int32(osc_forge_t *oforge, LV2_Atom_Forge *forge, int32_t i) -{ - return lv2_atom_forge_int(forge, i); -} - -static inline LV2_Atom_Forge_Ref -osc_forge_float(osc_forge_t *oforge, LV2_Atom_Forge *forge, float f) -{ - return lv2_atom_forge_float(forge, f); -} - -static inline LV2_Atom_Forge_Ref -osc_forge_string(osc_forge_t *oforge, LV2_Atom_Forge *forge, const char *s) -{ - return lv2_atom_forge_string(forge, s, strlen(s)); -} - -static inline LV2_Atom_Forge_Ref -osc_forge_symbol(osc_forge_t *oforge, LV2_Atom_Forge *forge, const char *s) -{ - return lv2_atom_forge_string(forge, s, strlen(s)); -} - -static inline LV2_Atom_Forge_Ref -osc_forge_blob(osc_forge_t *oforge, LV2_Atom_Forge *forge, uint32_t size, - const uint8_t *b) -{ - LV2_Atom_Forge_Ref ref; - if(!(ref = lv2_atom_forge_atom(forge, size, forge->Chunk))) - return 0; - if(!(ref = lv2_atom_forge_raw(forge, b, size))) - return 0; - lv2_atom_forge_pad(forge, size); - - return ref; -} - -static inline LV2_Atom_Forge_Ref -osc_forge_int64(osc_forge_t *oforge, LV2_Atom_Forge *forge, int64_t h) -{ - return lv2_atom_forge_long(forge, h); -} - -static inline LV2_Atom_Forge_Ref -osc_forge_double(osc_forge_t *oforge, LV2_Atom_Forge *forge, double d) -{ - return lv2_atom_forge_double(forge, d); -} - -static inline LV2_Atom_Forge_Ref -osc_forge_timestamp(osc_forge_t *oforge, LV2_Atom_Forge *forge, uint64_t t) -{ - return lv2_atom_forge_long(forge, t); -} - -static inline LV2_Atom_Forge_Ref -osc_forge_char(osc_forge_t *oforge, LV2_Atom_Forge *forge, char c) -{ - return lv2_atom_forge_int(forge, c); -} - -static inline LV2_Atom_Forge_Ref -osc_forge_midi(osc_forge_t *oforge, LV2_Atom_Forge *forge, uint32_t size, - const uint8_t *m) -{ - LV2_Atom_Forge_Ref ref; - if(!(ref = lv2_atom_forge_atom(forge, size, oforge->MIDI_MidiEvent))) - return 0; - if(!(ref = lv2_atom_forge_raw(forge, m, size))) - return 0; - lv2_atom_forge_pad(forge, size); - - return ref; -} - -static inline LV2_Atom_Forge_Ref -osc_forge_message_varlist(osc_forge_t *oforge, LV2_Atom_Forge *forge, - const char *path, const char *fmt, va_list args) -{ - LV2_Atom_Forge_Frame frame [2]; - LV2_Atom_Forge_Ref ref; - - if(!(ref = osc_forge_message_push(oforge, forge, frame, path, fmt))) - return 0; - - for(const char *type = fmt; *type; type++) - { - switch(*type) - { - case 'i': - { - if(!(ref =osc_forge_int32(oforge, forge, va_arg(args, int32_t)))) - return 0; - break; - } - case 'f': - { - if(!(ref = osc_forge_float(oforge, forge, (float)va_arg(args, double)))) - return 0; - break; - } - case 's': - { - if(!(ref = osc_forge_string(oforge, forge, va_arg(args, const char *)))) - return 0; - break; - } - case 'S': - { - if(!(ref = osc_forge_symbol(oforge, forge, va_arg(args, const char *)))) - return 0; - break; - } - case 'b': - { - uint32_t size = va_arg(args, uint32_t); - const uint8_t *b = va_arg(args, const uint8_t *); - if(!(ref = osc_forge_blob(oforge, forge, size, b))) - return 0; - break; - } - - case 'h': - { - if(!(ref = osc_forge_int64(oforge, forge, va_arg(args, int64_t)))) - return 0; - break; - } - case 'd': - { - if(!(ref = osc_forge_double(oforge, forge, va_arg(args, double)))) - return 0; - break; - } - case 't': - { - if(!(ref = osc_forge_timestamp(oforge, forge, va_arg(args, uint64_t)))) - return 0; - break; - } - - case 'c': - { - if(!(ref = osc_forge_char(oforge, forge, (char)va_arg(args, unsigned int)))) - return 0; - break; - } - case 'm': - { - int32_t size = va_arg(args, int32_t); - const uint8_t *m = va_arg(args, const uint8_t *); - if(!(ref = osc_forge_midi(oforge, forge, size, m))) - return 0; - break; - } - - case 'T': - case 'F': - case 'N': - case 'I': - { - break; - } - - default: // unknown argument type - { - return 0; - } - } - } - - osc_forge_message_pop(oforge, forge, frame); - - return ref; -} - -static inline LV2_Atom_Forge_Ref -osc_forge_message_vararg(osc_forge_t *oforge, LV2_Atom_Forge *forge, - const char *path, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - - LV2_Atom_Forge_Ref ref; - ref = osc_forge_message_varlist(oforge, forge, path, fmt, args); - - va_end(args); - - return ref; -} - -static inline const LV2_Atom * -osc_deforge_int32(osc_forge_t *oforge, LV2_Atom_Forge *forge, - const LV2_Atom *atom, int32_t *i) -{ - if(!atom || (atom->type != forge->Int) ) - return NULL; - - *i = ((const LV2_Atom_Int *)atom)->body; - - return lv2_atom_tuple_next(atom); -} - -static inline const LV2_Atom * -osc_deforge_float(osc_forge_t *oforge, LV2_Atom_Forge *forge, - const LV2_Atom *atom, float *f) -{ - if(!atom || (atom->type != forge->Float) ) - return NULL; - - *f = ((const LV2_Atom_Float *)atom)->body; - - return lv2_atom_tuple_next(atom); -} - -static inline const LV2_Atom * -osc_deforge_string(osc_forge_t *oforge, LV2_Atom_Forge *forge, - const LV2_Atom *atom, const char **s) -{ - if(!atom || (atom->type != forge->String) ) - return NULL; - - *s = LV2_ATOM_BODY_CONST(atom); - - return lv2_atom_tuple_next(atom); -} - -static inline const LV2_Atom * -osc_deforge_symbol(osc_forge_t *oforge, LV2_Atom_Forge *forge, - const LV2_Atom *atom, const char **s) -{ - if(!atom || (atom->type != forge->String) ) - return NULL; - - *s = LV2_ATOM_BODY_CONST(atom); - - return lv2_atom_tuple_next(atom); -} - -static inline const LV2_Atom * -osc_deforge_blob(osc_forge_t *oforge, LV2_Atom_Forge *forge, - const LV2_Atom *atom, uint32_t *size, const uint8_t **b) -{ - if(!atom || (atom->type != forge->Chunk) ) - return NULL; - - *size = atom->size; - *b = LV2_ATOM_BODY_CONST(atom); - - return lv2_atom_tuple_next(atom); -} - -static inline const LV2_Atom * -osc_deforge_int64(osc_forge_t *oforge, LV2_Atom_Forge *forge, - const LV2_Atom *atom, int64_t *h) -{ - if(!atom || (atom->type != forge->Long) ) - return NULL; - - *h = ((const LV2_Atom_Long *)atom)->body; - - return lv2_atom_tuple_next(atom); -} - -static inline const LV2_Atom * -osc_deforge_double(osc_forge_t *oforge, LV2_Atom_Forge *forge, - const LV2_Atom *atom, double *d) -{ - if(!atom || (atom->type != forge->Double) ) - return NULL; - - *d = ((const LV2_Atom_Double *)atom)->body; - - return lv2_atom_tuple_next(atom); -} - -static inline const LV2_Atom * -osc_deforge_timestamp(osc_forge_t *oforge, LV2_Atom_Forge *forge, - const LV2_Atom *atom, uint64_t *t) -{ - if(!atom || (atom->type != forge->Long) ) - return NULL; - - *t = ((const LV2_Atom_Long *)atom)->body; - - return lv2_atom_tuple_next(atom); -} - -static inline const LV2_Atom * -osc_deforge_char(osc_forge_t *oforge, LV2_Atom_Forge *forge, - const LV2_Atom *atom, char *c) -{ - if(!atom || (atom->type != forge->Int) ) - return NULL; - - *c = ((const LV2_Atom_Int *)atom)->body; - - return lv2_atom_tuple_next(atom); -} - -static inline const LV2_Atom * -osc_deforge_midi(osc_forge_t *oforge, LV2_Atom_Forge *forge, - const LV2_Atom *atom, uint32_t *size, const uint8_t **m) -{ - if(!atom || (atom->type != oforge->MIDI_MidiEvent) ) - return NULL; - - *size = atom->size; - *m = LV2_ATOM_BODY_CONST(atom); - - return lv2_atom_tuple_next(atom); -} - -static inline const LV2_Atom * -osc_deforge_message_varlist(osc_forge_t *oforge, LV2_Atom_Forge *forge, - const LV2_Atom *atom, const char *fmt, va_list args) -{ - for(const char *type = fmt; *type; type++) - { - switch(*type) - { - case 'i': - { - int32_t *i = va_arg(args, int32_t *); - if(!(atom = osc_deforge_int32(oforge, forge, atom, i))) - return NULL; - break; - } - case 'f': - { - float *f = va_arg(args, float *); - if(!(atom = osc_deforge_float(oforge, forge, atom, f))) - return NULL; - break; - } - case 's': - { - const char **s = va_arg(args, const char **); - if(!(atom = osc_deforge_string(oforge, forge, atom, s))) - return NULL; - break; - } - case 'S': - { - const char **s = va_arg(args, const char **); - if(!(atom = osc_deforge_symbol(oforge, forge, atom, s))) - return NULL; - break; - } - case 'b': - { - uint32_t *size = va_arg(args, uint32_t *); - const uint8_t **b = va_arg(args, const uint8_t **); - if(!(atom = osc_deforge_blob(oforge, forge, atom, size, b))) - return NULL; - break; - } - - case 'h': - { - int64_t *h = va_arg(args, int64_t *); - if(!(atom = osc_deforge_int64(oforge, forge, atom, h))) - return NULL; - break; - } - case 'd': - { - double *d = va_arg(args, double *); - if(!(atom = osc_deforge_double(oforge, forge, atom, d))) - return NULL; - break; - } - case 't': - { - uint64_t *t = va_arg(args, uint64_t *); - if(!(atom = osc_deforge_timestamp(oforge, forge, atom, t))) - return NULL; - break; - } - - case 'c': - { - char *c = va_arg(args, char *); - if(!(atom = osc_deforge_char(oforge, forge, atom, c))) - return NULL; - break; - } - case 'm': - { - uint32_t *size = va_arg(args, uint32_t *); - const uint8_t **m = va_arg(args, const uint8_t **); - if(!(atom = osc_deforge_midi(oforge, forge, atom, size, m))) - return NULL; - break; - } - - case 'T': - case 'F': - case 'N': - case 'I': - { - break; - } - - default: // unknown argument type - { - return NULL; - } - } - } - - return atom; -} - -static inline const LV2_Atom * -osc_deforge_message_vararg(osc_forge_t *oforge, LV2_Atom_Forge *forge, - const LV2_Atom *atom, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - - atom = osc_deforge_message_varlist(oforge, forge, atom, fmt, args); - - va_end(args); - - return atom; -} - -#endif // _LV2_OSC_H_ diff --git a/osc.lv2/manifest.ttl b/osc.lv2/manifest.ttl index 9f75ab3..a2bbaf8 100644 --- a/osc.lv2/manifest.ttl +++ b/osc.lv2/manifest.ttl @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch) +# Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) # # This is free software: you can redistribute it and/or modify # it under the terms of the Artistic License 2.0 as published by diff --git a/osc.lv2/osc.lv2/forge.h b/osc.lv2/osc.lv2/forge.h new file mode 100644 index 0000000..d46121f --- /dev/null +++ b/osc.lv2/osc.lv2/forge.h @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the Artistic License 2.0 as published by + * The Perl Foundation. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Artistic License 2.0 for more details. + * + * You should have received a copy of the Artistic License 2.0 + * along the source as a COPYING file. If not, obtain it from + * http://www.perlfoundation.org/artistic_license_2_0. + */ + +#ifndef LV2_OSC_FORGE_H +#define LV2_OSC_FORGE_H + +#include <osc.lv2/osc.h> +#include <osc.lv2/util.h> +#include <osc.lv2/reader.h> + +#include <lv2/lv2plug.in/ns/ext/atom/forge.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define lv2_osc_forge_int(forge, osc_urid, val) \ + lv2_atom_forge_int((forge), (val)) + +#define lv2_osc_forge_float(forge, osc_urid, val) \ + lv2_atom_forge_float((forge), (val)) + +#define lv2_osc_forge_string(forge, osc_urid, val, len) \ + lv2_atom_forge_string((forge), (val), (len)) + +#define lv2_osc_forge_long(forge, osc_urid, val) \ + lv2_atom_forge_long((forge), (val)) + +#define lv2_osc_forge_double(forge, osc_urid, val) \ + lv2_atom_forge_double((forge), (val)) + +#define lv2_osc_forge_true(forge, osc_urid) \ + lv2_atom_forge_bool((forge), 1) + +#define lv2_osc_forge_false(forge, osc_urid) \ + lv2_atom_forge_bool((forge), 0) + +#define lv2_osc_forge_nil(forge, osc_urid) \ + lv2_atom_forge_atom((forge), 0, 0) + +#define lv2_osc_forge_impulse(forge, osc_urid) \ + lv2_atom_forge_atom((forge), 0, (osc_urid)->OSC_Impulse) + +#define lv2_osc_forge_symbol(forge, osc_urid, val) \ + lv2_atom_forge_urid((forge), (val)) + +static inline LV2_Atom_Forge_Ref +lv2_osc_forge_chunk(LV2_Atom_Forge *forge, LV2_URID type, + const uint8_t *buf, uint32_t size) +{ + LV2_Atom_Forge_Ref ref; + + if( (ref = lv2_atom_forge_atom(forge, size, type)) + && (ref = lv2_atom_forge_raw(forge, buf, size)) ) + { + lv2_atom_forge_pad(forge, size); + return ref; + } + + return 0; +} + +static inline LV2_Atom_Forge_Ref +lv2_osc_forge_midi(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid, + const uint8_t *buf, uint32_t size) +{ + assert(size <= 3); + return lv2_osc_forge_chunk(forge, osc_urid->MIDI_MidiEvent, buf, size); +} + +static inline LV2_Atom_Forge_Ref +lv2_osc_forge_blob(LV2_Atom_Forge* forge, LV2_OSC_URID *osc_urid, + const uint8_t *buf, uint32_t size) +{ + return lv2_osc_forge_chunk(forge, osc_urid->ATOM_Chunk, buf, size); +} + +static inline LV2_Atom_Forge_Ref +lv2_osc_forge_char(LV2_Atom_Forge* forge, LV2_OSC_URID *osc_urid, + char val) +{ + return lv2_osc_forge_chunk(forge, osc_urid->OSC_Char, (const uint8_t *)&val, 1); +} + +static inline LV2_Atom_Forge_Ref +lv2_osc_forge_rgba(LV2_Atom_Forge* forge, LV2_OSC_URID *osc_urid, + uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + const uint8_t val [4] = {r, g, b, a}; + return lv2_osc_forge_chunk(forge, osc_urid->OSC_RGBA, val, 4); +} + +static inline LV2_Atom_Forge_Ref +lv2_osc_forge_timetag(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid, + const LV2_OSC_Timetag *timetag) +{ + LV2_Atom_Forge_Frame frame; + LV2_Atom_Forge_Ref ref; + + if( (ref = lv2_atom_forge_object(forge, &frame, 0, osc_urid->OSC_Timetag)) + && (ref = lv2_atom_forge_key(forge, osc_urid->OSC_timetagIntegral)) + && (ref = lv2_atom_forge_long(forge, timetag->integral)) + && (ref = lv2_atom_forge_key(forge, osc_urid->OSC_timetagFraction)) + && (ref = lv2_atom_forge_long(forge, timetag->fraction)) ) + { + lv2_atom_forge_pop(forge, &frame); + return ref; + } + + return 0; +} + +static inline LV2_Atom_Forge_Ref +lv2_osc_forge_bundle_head(LV2_Atom_Forge* forge, LV2_OSC_URID *osc_urid, + LV2_Atom_Forge_Frame frame [2], const LV2_OSC_Timetag *timetag) +{ + LV2_Atom_Forge_Ref ref; + + if( (ref = lv2_atom_forge_object(forge, &frame[0], 0, osc_urid->OSC_Bundle)) + && (ref = lv2_atom_forge_key(forge, osc_urid->OSC_bundleTimetag)) + && (ref = lv2_osc_forge_timetag(forge, osc_urid, timetag)) + && (ref = lv2_atom_forge_key(forge, osc_urid->OSC_bundleItems)) + && (ref = lv2_atom_forge_tuple(forge, &frame[1])) ) + { + return ref; + } + + return 0; +} + +/** + TODO +*/ +static inline LV2_Atom_Forge_Ref +lv2_osc_forge_message_head(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid, + LV2_Atom_Forge_Frame frame [2], const char *path) +{ + assert(path); + + LV2_Atom_Forge_Ref ref; + if( (ref = lv2_atom_forge_object(forge, &frame[0], 0, osc_urid->OSC_Message)) + && (ref = lv2_atom_forge_key(forge, osc_urid->OSC_messagePath)) + && (ref = lv2_atom_forge_string(forge, path, strlen(path))) + && (ref = lv2_atom_forge_key(forge, osc_urid->OSC_messageArguments)) + && (ref = lv2_atom_forge_tuple(forge, &frame[1])) ) + { + return ref; + } + + return 0; +} + +/** + TODO +*/ +static inline void +lv2_osc_forge_pop(LV2_Atom_Forge *forge, LV2_Atom_Forge_Frame frame [2]) +{ + lv2_atom_forge_pop(forge, &frame[1]); // a LV2_Atom_Tuple + lv2_atom_forge_pop(forge, &frame[0]); // a LV2_Atom_Object +} + +static inline LV2_Atom_Forge_Ref +lv2_osc_forge_message_varlist(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid, + const char *path, const char *fmt, va_list args) +{ + LV2_Atom_Forge_Frame frame [2]; + LV2_Atom_Forge_Ref ref; + + if(!lv2_osc_check_path(path) || !lv2_osc_check_fmt(fmt, 0)) + return 0; + if(!(ref = lv2_osc_forge_message_head(forge, osc_urid, frame, path))) + return 0; + + for(const char *type = fmt; *type; type++) + { + switch( (LV2_OSC_Type)*type) + { + case LV2_OSC_INT32: + { + if(!(ref = lv2_osc_forge_int(forge, osc_urid, va_arg(args, int32_t)))) + return 0; + break; + } + case LV2_OSC_FLOAT: + { + if(!(ref = lv2_osc_forge_float(forge, osc_urid, (float)va_arg(args, double)))) + return 0; + break; + } + case LV2_OSC_STRING: + { + const char *s = va_arg(args, const char *); + if(!s || !(ref = lv2_osc_forge_string(forge, osc_urid, s, strlen(s)))) + return 0; + break; + } + case LV2_OSC_BLOB: + { + const int32_t size = va_arg(args, int32_t); + const uint8_t *b = va_arg(args, const uint8_t *); + if(!b || !(ref = lv2_osc_forge_blob(forge, osc_urid, b, size))) + return 0; + break; + } + + case LV2_OSC_INT64: + { + if(!(ref = lv2_osc_forge_long(forge, osc_urid, va_arg(args, int64_t)))) + return 0; + break; + } + case LV2_OSC_DOUBLE: + { + if(!(ref = lv2_osc_forge_double(forge, osc_urid, va_arg(args, double)))) + return 0; + break; + } + case LV2_OSC_TIMETAG: + { + const LV2_OSC_Timetag timetag = { + .integral = va_arg(args, uint32_t), + .fraction = va_arg(args, uint32_t) + }; + if(!(ref = lv2_osc_forge_timetag(forge, osc_urid, &timetag))) + return 0; + break; + } + + case LV2_OSC_TRUE: + { + if(!(ref = lv2_osc_forge_true(forge, osc_urid))) + return 0; + break; + } + case LV2_OSC_FALSE: + { + if(!(ref = lv2_osc_forge_false(forge, osc_urid))) + return 0; + break; + } + case LV2_OSC_NIL: + { + if(!(ref = lv2_osc_forge_nil(forge, osc_urid))) + return 0; + break; + } + case LV2_OSC_IMPULSE: + { + if(!(ref = lv2_osc_forge_impulse(forge, osc_urid))) + return 0; + break; + } + + case LV2_OSC_SYMBOL: + { + if(!(ref = lv2_osc_forge_symbol(forge, osc_urid, va_arg(args, uint32_t)))) + return 0; + break; + } + case LV2_OSC_MIDI: + { + const int32_t size = va_arg(args, int32_t); + const uint8_t *m = va_arg(args, const uint8_t *); + if(!m || !(ref = lv2_osc_forge_midi(forge, osc_urid, m, size))) + return 0; + break; + } + case LV2_OSC_CHAR: + { + if(!(ref = lv2_osc_forge_char(forge, osc_urid, (char)va_arg(args, int)))) + return 0; + break; + } + case LV2_OSC_RGBA: + { + if(!(ref = lv2_osc_forge_rgba(forge, osc_urid, + (uint8_t)va_arg(args, unsigned), + (uint8_t)va_arg(args, unsigned), + (uint8_t)va_arg(args, unsigned), + (uint8_t)va_arg(args, unsigned)))) + return 0; + break; + } + } + } + + lv2_osc_forge_pop(forge, frame); + + return ref; +} + +static inline LV2_Atom_Forge_Ref +lv2_osc_forge_message_vararg(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid, + const char *path, const char *fmt, ...) +{ + LV2_Atom_Forge_Ref ref; + va_list args; + + va_start(args, fmt); + + ref = lv2_osc_forge_message_varlist(forge, osc_urid, path, fmt, args); + + va_end(args); + + return ref; +} + +static inline LV2_Atom_Forge_Ref +lv2_osc_forge_packet(LV2_Atom_Forge *forge, LV2_OSC_URID *osc_urid, + LV2_URID_Map *map, const uint8_t *buf, size_t size) +{ + LV2_OSC_Reader reader; + LV2_Atom_Forge_Frame frame [2]; + LV2_Atom_Forge_Ref ref; + + lv2_osc_reader_initialize(&reader, buf, size); + + if(lv2_osc_reader_is_bundle(&reader)) + { + LV2_OSC_Item *itm = OSC_READER_BUNDLE_BEGIN(&reader, size); + + if(itm && (ref = lv2_osc_forge_bundle_head(forge, osc_urid, frame, + LV2_OSC_TIMETAG_CREATE(itm->timetag)))) + { + OSC_READER_BUNDLE_ITERATE(&reader, itm) + { + if(!(ref = lv2_osc_forge_packet(forge, osc_urid, map, itm->body, itm->size))) + return 0; + } + + lv2_osc_forge_pop(forge, frame); + + return ref; + } + } + else if(lv2_osc_reader_is_message(&reader)) + { + LV2_OSC_Arg *arg = OSC_READER_MESSAGE_BEGIN(&reader, size); + + if(arg && (ref = lv2_osc_forge_message_head(forge, osc_urid, frame, arg->path))) + { + OSC_READER_MESSAGE_ITERATE(&reader, arg) + { + switch( (LV2_OSC_Type)*arg->type) + { + case LV2_OSC_INT32: + { + if(!(ref = lv2_osc_forge_int(forge, osc_urid, arg->i))) + return 0; + break; + } + case LV2_OSC_FLOAT: + { + if(!(ref = lv2_osc_forge_float(forge, osc_urid, arg->f))) + return 0; + break; + } + case LV2_OSC_STRING: + { + if(!(ref = lv2_osc_forge_string(forge, osc_urid, arg->s, arg->size - 1))) + return 0; + break; + } + case LV2_OSC_BLOB: + { + if(!(ref = lv2_osc_forge_blob(forge, osc_urid, arg->b, arg->size))) + return 0; + break; + } + + case LV2_OSC_INT64: + { + if(!(ref = lv2_osc_forge_long(forge, osc_urid, arg->h))) + return 0; + break; + } + case LV2_OSC_DOUBLE: + { + if(!(ref = lv2_osc_forge_double(forge, osc_urid, arg->d))) + return 0; + break; + } + case LV2_OSC_TIMETAG: + { + if(!(ref = lv2_osc_forge_timetag(forge, osc_urid, LV2_OSC_TIMETAG_CREATE(arg->t)))) + return 0; + break; + } + + case LV2_OSC_TRUE: + { + if(!(ref = lv2_osc_forge_true(forge, osc_urid))) + return 0; + break; + } + case LV2_OSC_FALSE: + { + if(!(ref = lv2_osc_forge_false(forge, osc_urid))) + return 0; + break; + } + case LV2_OSC_NIL: + { + if(!(ref = lv2_osc_forge_nil(forge, osc_urid))) + return 0; + break; + } + case LV2_OSC_IMPULSE: + { + if(!(ref = lv2_osc_forge_impulse(forge, osc_urid))) + return 0; + break; + } + + case LV2_OSC_SYMBOL: + { + if(!(ref = lv2_osc_forge_symbol(forge, osc_urid, + map->map(map->handle, arg->S)))) + return 0; + break; + } + case LV2_OSC_MIDI: + { + if(!(ref = lv2_osc_forge_midi(forge, osc_urid, &arg->b[1], arg->size - 1))) + return 0; + break; + } + case LV2_OSC_CHAR: + { + if(!(ref = lv2_osc_forge_char(forge, osc_urid, arg->c))) + return 0; + break; + } + case LV2_OSC_RGBA: + { + if(!(ref = lv2_osc_forge_rgba(forge, osc_urid, arg->R, arg->G, arg->B, arg->A))) + return 0; + break; + } + } + } + + lv2_osc_forge_pop(forge, frame); + + return ref; + } + } + + return 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // LV2_OSC_FORGE_H diff --git a/osc.lv2/osc.lv2/osc.h b/osc.lv2/osc.lv2/osc.h new file mode 100644 index 0000000..3b36a19 --- /dev/null +++ b/osc.lv2/osc.lv2/osc.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the Artistic License 2.0 as published by + * The Perl Foundation. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Artistic License 2.0 for more details. + * + * You should have received a copy of the Artistic License 2.0 + * along the source as a COPYING file. If not, obtain it from + * http://www.perlfoundation.org/artistic_license_2_0. + */ + +#ifndef LV2_OSC_H +#define LV2_OSC_H + +#include <stdint.h> + +#include <lv2/lv2plug.in/ns/ext/urid/urid.h> +#include <lv2/lv2plug.in/ns/ext/atom/atom.h> +#include <lv2/lv2plug.in/ns/ext/midi/midi.h> + +#define LV2_OSC_URI "http://open-music-kontrollers.ch/lv2/osc" +#define LV2_OSC_PREFIX LV2_OSC_URI "#" + +#define LV2_OSC__Event LV2_OSC_PREFIX "Event" // atom message type +#define LV2_OSC__schedule LV2_OSC_PREFIX "schedule" // feature + +#define LV2_OSC__Packet LV2_OSC_PREFIX "Packet" // atom object type + +#define LV2_OSC__Bundle LV2_OSC_PREFIX "Bundle" // atom object type +#define LV2_OSC__bundleTimetag LV2_OSC_PREFIX "bundleTimetag" // atom object property +#define LV2_OSC__bundleItems LV2_OSC_PREFIX "bundleItems" + +#define LV2_OSC__Message LV2_OSC_PREFIX "Message" // atom object type +#define LV2_OSC__messagePath LV2_OSC_PREFIX "messagePath" // atom object property +#define LV2_OSC__messageArguments LV2_OSC_PREFIX "messageArguments" // atom object property + +#define LV2_OSC__Timetag LV2_OSC_PREFIX "Timetag" // atom object type +#define LV2_OSC__timetagIntegral LV2_OSC_PREFIX "timetagIntegral" // atom object property +#define LV2_OSC__timetagFraction LV2_OSC_PREFIX "timetagFraction" // atom object property + +#define LV2_OSC__Impulse LV2_OSC_PREFIX "Impulse" // atom type +#define LV2_OSC__Char LV2_OSC_PREFIX "Char" // atom type +#define LV2_OSC__RGBA LV2_OSC_PREFIX "RGBA" // atom type + +#define LV2_OSC_PADDED_SIZE(size) ( ( (size_t)(size) + 3 ) & ( ~3 ) ) +#define LV2_OSC_IMMEDIATE 1ULL + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void *LV2_OSC_Schedule_Handle; + +typedef double (*LV2_OSC_Schedule_OSC2Frames)( + LV2_OSC_Schedule_Handle handle, + uint64_t timetag); + +typedef uint64_t (*LV2_OSC_Schedule_Frames2OSC)( + LV2_OSC_Schedule_Handle handle, + double frames); + +typedef struct _LV2_OSC_Schedule { + LV2_OSC_Schedule_Handle handle; + LV2_OSC_Schedule_OSC2Frames osc2frames; + LV2_OSC_Schedule_Frames2OSC frames2osc; +} LV2_OSC_Schedule; + +typedef enum LV2_OSC_Type { + LV2_OSC_INT32 = 'i', + LV2_OSC_FLOAT = 'f', + LV2_OSC_STRING = 's', + LV2_OSC_BLOB = 'b', + + LV2_OSC_TRUE = 'T', + LV2_OSC_FALSE = 'F', + LV2_OSC_NIL = 'N', + LV2_OSC_IMPULSE = 'I', + + LV2_OSC_INT64 = 'h', + LV2_OSC_DOUBLE = 'd', + LV2_OSC_TIMETAG = 't', + + LV2_OSC_SYMBOL = 'S', + LV2_OSC_CHAR = 'c', + LV2_OSC_MIDI = 'm', + LV2_OSC_RGBA = 'r' +} LV2_OSC_Type; + +union swap32_t { + uint32_t u; + + int32_t i; + float f; +}; + +union swap64_t { + uint64_t u; + + int64_t h; + uint64_t t; + double d; +}; + +typedef struct _LV2_OSC_Timetag { + uint32_t integral; + uint32_t fraction; +} LV2_OSC_Timetag; + +typedef struct _LV2_OSC_URID { + LV2_URID OSC_Packet; + + LV2_URID OSC_Bundle; + LV2_URID OSC_bundleTimetag; + LV2_URID OSC_bundleItems; + + LV2_URID OSC_Message; + LV2_URID OSC_messagePath; + LV2_URID OSC_messageArguments; + + LV2_URID OSC_Timetag; + LV2_URID OSC_timetagIntegral; + LV2_URID OSC_timetagFraction; + + LV2_URID OSC_Impulse; + LV2_URID OSC_Char; + LV2_URID OSC_RGBA; + + LV2_URID MIDI_MidiEvent; + + LV2_URID ATOM_Int; + LV2_URID ATOM_Long; + LV2_URID ATOM_String; + LV2_URID ATOM_Float; + LV2_URID ATOM_Double; + LV2_URID ATOM_URID; + LV2_URID ATOM_Bool; + LV2_URID ATOM_Tuple; + LV2_URID ATOM_Object; + LV2_URID ATOM_Chunk; +} LV2_OSC_URID; + +static inline void +lv2_osc_urid_init(LV2_OSC_URID *osc_urid, LV2_URID_Map *map) +{ + osc_urid->OSC_Packet = map->map(map->handle, LV2_OSC__Packet); + + osc_urid->OSC_Bundle = map->map(map->handle, LV2_OSC__Bundle); + osc_urid->OSC_bundleTimetag = map->map(map->handle, LV2_OSC__bundleTimetag); + osc_urid->OSC_bundleItems = map->map(map->handle, LV2_OSC__bundleItems); + + osc_urid->OSC_Message = map->map(map->handle, LV2_OSC__Message); + osc_urid->OSC_messagePath = map->map(map->handle, LV2_OSC__messagePath); + osc_urid->OSC_messageArguments = map->map(map->handle, LV2_OSC__messageArguments); + + osc_urid->OSC_Timetag = map->map(map->handle, LV2_OSC__Timetag); + osc_urid->OSC_timetagIntegral = map->map(map->handle, LV2_OSC__timetagIntegral); + osc_urid->OSC_timetagFraction = map->map(map->handle, LV2_OSC__timetagFraction); + + osc_urid->OSC_Impulse = map->map(map->handle, LV2_OSC__Impulse); + osc_urid->OSC_Char = map->map(map->handle, LV2_OSC__Char); + osc_urid->OSC_RGBA = map->map(map->handle, LV2_OSC__RGBA); + + osc_urid->MIDI_MidiEvent = map->map(map->handle, LV2_MIDI__MidiEvent); + + osc_urid->ATOM_Int = map->map(map->handle, LV2_ATOM__Int); + osc_urid->ATOM_Long = map->map(map->handle, LV2_ATOM__Long); + osc_urid->ATOM_String = map->map(map->handle, LV2_ATOM__String); + osc_urid->ATOM_Float = map->map(map->handle, LV2_ATOM__Float); + osc_urid->ATOM_Double = map->map(map->handle, LV2_ATOM__Double); + osc_urid->ATOM_URID = map->map(map->handle, LV2_ATOM__URID); + osc_urid->ATOM_Bool = map->map(map->handle, LV2_ATOM__Bool); + osc_urid->ATOM_Tuple = map->map(map->handle, LV2_ATOM__Tuple); + osc_urid->ATOM_Object = map->map(map->handle, LV2_ATOM__Object); + osc_urid->ATOM_Chunk = map->map(map->handle, LV2_ATOM__Chunk); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // LV2_OSC_H diff --git a/osc.lv2/osc.lv2/reader.h b/osc.lv2/osc.lv2/reader.h new file mode 100644 index 0000000..2f5def9 --- /dev/null +++ b/osc.lv2/osc.lv2/reader.h @@ -0,0 +1,570 @@ +/* + * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the Artistic License 2.0 as published by + * The Perl Foundation. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Artistic License 2.0 for more details. + * + * You should have received a copy of the Artistic License 2.0 + * along the source as a COPYING file. If not, obtain it from + * http://www.perlfoundation.org/artistic_license_2_0. + */ + +#ifndef LV2_OSC_READER_H +#define LV2_OSC_READER_H + +#include <stdbool.h> +#include <string.h> +#include <endian.h> + +#include <osc.lv2/osc.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _LV2_OSC_Reader LV2_OSC_Reader; +typedef struct _LV2_OSC_Item LV2_OSC_Item; +typedef struct _LV2_OSC_Arg LV2_OSC_Arg; + +struct _LV2_OSC_Reader { + const uint8_t *buf; + const uint8_t *ptr; + const uint8_t *end; +}; + +struct _LV2_OSC_Item { + int32_t size; + const uint8_t *body; + + uint64_t timetag; + const uint8_t *end; +}; + +struct _LV2_OSC_Arg { + const char *type; + int32_t size; + union { + int32_t i; + float f; + const char *s; + const uint8_t *b; + + int64_t h; + double d; + uint64_t t; + + const uint8_t *m; + const char *S; + char c; + struct { + uint8_t R; + uint8_t G; + uint8_t B; + uint8_t A; + }; // anonymous RGBA struct + }; + + const char *path; + const uint8_t *end; +}; + +static inline void +lv2_osc_reader_initialize(LV2_OSC_Reader *reader, const uint8_t *buf, size_t size) +{ + reader->buf = buf; + reader->ptr = buf; + reader->end = buf + size; +} + +static inline bool +lv2_osc_reader_overflow(LV2_OSC_Reader *reader, size_t size) +{ + return reader->ptr + size > reader->end; +} + +static inline bool +lv2_osc_reader_be32toh(LV2_OSC_Reader *reader, union swap32_t *s32) +{ + if(lv2_osc_reader_overflow(reader, 4)) + return false; + + s32->u = *(const uint32_t *)reader->ptr; + s32->u = be32toh(s32->u); + reader->ptr += 4; + + return true; +} + +static inline bool +lv2_osc_reader_be64toh(LV2_OSC_Reader *reader, union swap64_t *s64) +{ + if(lv2_osc_reader_overflow(reader, 8)) + return false; + + s64->u = *(const uint64_t *)reader->ptr; + s64->u = be64toh(s64->u); + reader->ptr += 8; + + return true; +} + +static inline bool +lv2_osc_reader_get_int32(LV2_OSC_Reader *reader, int32_t *i) +{ + union swap32_t s32; + if(!lv2_osc_reader_be32toh(reader, &s32)) + return false; + + *i = s32.i; + + return true; +} + +static inline bool +lv2_osc_reader_get_float(LV2_OSC_Reader *reader, float *f) +{ + union swap32_t s32; + if(!lv2_osc_reader_be32toh(reader, &s32)) + return false; + + *f = s32.f; + + return true; +} + +static inline bool +lv2_osc_reader_get_int64(LV2_OSC_Reader *reader, int64_t *h) +{ + union swap64_t s64; + if(!lv2_osc_reader_be64toh(reader, &s64)) + return false; + + *h = s64.h; + + return true; +} + +static inline bool +lv2_osc_reader_get_timetag(LV2_OSC_Reader *reader, uint64_t *t) +{ + union swap64_t s64; + if(!lv2_osc_reader_be64toh(reader, &s64)) + return false; + + *t = s64.u; + + return true; +} + +static inline bool +lv2_osc_reader_get_double(LV2_OSC_Reader *reader, double *d) +{ + union swap64_t s64; + if(!lv2_osc_reader_be64toh(reader, &s64)) + return false; + + *d = s64.d; + + return true; +} + +static inline bool +lv2_osc_reader_get_string(LV2_OSC_Reader *reader, const char **s) +{ + const char *str = (const char *)reader->ptr; + const size_t padded = LV2_OSC_PADDED_SIZE(strlen(str) + 1); + if(lv2_osc_reader_overflow(reader, padded )) + return false; + + *s = str; + reader->ptr += padded; + + return true; +} + +static inline bool +lv2_osc_reader_get_symbol(LV2_OSC_Reader *reader, const char **S) +{ + return lv2_osc_reader_get_string(reader, S); +} + +static inline bool +lv2_osc_reader_get_midi(LV2_OSC_Reader *reader, const uint8_t **m) +{ + if(lv2_osc_reader_overflow(reader, 4)) + return false; + + *m = reader->ptr; + reader->ptr += 4; + + return true; +} + +static inline bool +lv2_osc_reader_get_blob(LV2_OSC_Reader *reader, int32_t *len, const uint8_t **body) +{ + if(!lv2_osc_reader_get_int32(reader, len)) + return false; + + const size_t padded = LV2_OSC_PADDED_SIZE(*len); + if(lv2_osc_reader_overflow(reader, padded)) + return false; + + *body = reader->ptr; + reader->ptr += padded; + + return true; +} + +static inline bool +lv2_osc_reader_get_rgba(LV2_OSC_Reader *reader, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *a) +{ + if(lv2_osc_reader_overflow(reader, 4)) + return false; + + *r = reader->ptr[0]; + *g = reader->ptr[1]; + *b = reader->ptr[2]; + *a = reader->ptr[3]; + reader->ptr += 4; + + return true; +} + +static inline bool +lv2_osc_reader_get_char(LV2_OSC_Reader *reader, char *c) +{ + int32_t i; + if(!lv2_osc_reader_get_int32(reader, &i)) + return false; + + *c = i; + + return true; +} + +static inline LV2_OSC_Item * +lv2_osc_reader_item_raw(LV2_OSC_Reader *reader, LV2_OSC_Item *itm) +{ + if(!lv2_osc_reader_get_int32(reader, &itm->size)) + return NULL; + + if(lv2_osc_reader_overflow(reader, itm->size)) + return NULL; + + itm->body = reader->ptr; + + return itm; +} + +static inline LV2_OSC_Item * +lv2_osc_reader_item_begin(LV2_OSC_Reader *reader, LV2_OSC_Item *itm, size_t len) +{ + if(lv2_osc_reader_overflow(reader, len)) + return NULL; + + itm->end = reader->ptr + len; + + if(lv2_osc_reader_overflow(reader, 16)) + return NULL; + + if(strncmp((const char *)reader->ptr, "#bundle", 8)) + return NULL; + reader->ptr += 8; + + if(!lv2_osc_reader_get_timetag(reader, &itm->timetag)) + return NULL; + + return lv2_osc_reader_item_raw(reader, itm); +} + +static inline bool +lv2_osc_reader_item_is_end(LV2_OSC_Reader *reader, LV2_OSC_Item *itm) +{ + return reader->ptr > itm->end; +} + +static inline LV2_OSC_Item * +lv2_osc_reader_item_next(LV2_OSC_Reader *reader, LV2_OSC_Item *itm) +{ + reader->ptr += itm->size; + + return lv2_osc_reader_item_raw(reader, itm); +} + +#define OSC_READER_BUNDLE_BEGIN(reader, len) \ + lv2_osc_reader_item_begin( \ + (reader), \ + &(LV2_OSC_Item){ .size = 0, .body = NULL, .timetag = 1ULL, .end = NULL }, \ + len) + +#define OSC_READER_BUNDLE_ITERATE(reader, itm) \ + for(itm = itm; \ + itm && !lv2_osc_reader_item_is_end((reader), (itm)); \ + itm = lv2_osc_reader_item_next((reader), (itm))) + +#define OSC_READER_BUNDLE_FOREACH(reader, itm, len) \ + for(LV2_OSC_Item *(itm) = OSC_READER_BUNDLE_BEGIN((reader), (len)); \ + itm && !lv2_osc_reader_item_is_end((reader), (itm)); \ + itm = lv2_osc_reader_item_next((reader), (itm))) + +static inline LV2_OSC_Arg * +lv2_osc_reader_arg_raw(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg) +{ + switch( (LV2_OSC_Type)*arg->type) + { + case LV2_OSC_INT32: + { + if(!lv2_osc_reader_get_int32(reader, &arg->i)) + return NULL; + arg->size = 4; + + break; + } + case LV2_OSC_FLOAT: + { + if(!lv2_osc_reader_get_float(reader, &arg->f)) + return NULL; + arg->size = 4; + + break; + } + case LV2_OSC_STRING: + { + if(!lv2_osc_reader_get_string(reader, &arg->s)) + return NULL; + arg->size = strlen(arg->s) + 1; + + break; + } + case LV2_OSC_BLOB: + { + if(!lv2_osc_reader_get_blob(reader, &arg->size, &arg->b)) + return NULL; + //arg->size = arg->size; + + break; + } + + case LV2_OSC_TRUE: + case LV2_OSC_FALSE: + case LV2_OSC_NIL: + case LV2_OSC_IMPULSE: + break; + + case LV2_OSC_INT64: + { + if(!lv2_osc_reader_get_int64(reader, &arg->h)) + return NULL; + arg->size = 8; + + break; + } + case LV2_OSC_DOUBLE: + { + if(!lv2_osc_reader_get_double(reader, &arg->d)) + return NULL; + arg->size = 8; + + break; + } + case LV2_OSC_TIMETAG: + { + if(!lv2_osc_reader_get_timetag(reader, &arg->t)) + return NULL; + arg->size = 8; + + break; + } + + case LV2_OSC_MIDI: + { + if(!lv2_osc_reader_get_midi(reader, &arg->m)) + return NULL; + arg->size = 4; + + break; + } + case LV2_OSC_SYMBOL: + { + if(!lv2_osc_reader_get_symbol(reader, &arg->S)) + return NULL; + arg->size = strlen(arg->S) + 1; + + break; + } + case LV2_OSC_CHAR: + { + if(!lv2_osc_reader_get_char(reader, &arg->c)) + return NULL; + arg->size = 4; + + break; + } + case LV2_OSC_RGBA: + { + if(!lv2_osc_reader_get_rgba(reader, &arg->R, &arg->G, &arg->B, &arg->A)) + return NULL; + arg->size = 4; + + break; + } + } + + return arg; +} + +static inline LV2_OSC_Arg * +lv2_osc_reader_arg_begin(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg, size_t len) +{ + if(lv2_osc_reader_overflow(reader, len)) + return NULL; + + arg->end = reader->ptr + len; + + if(!lv2_osc_reader_get_string(reader, &arg->path)) //TODO check for validity + return NULL; + + if(!lv2_osc_reader_get_string(reader, &arg->type)) //TODO check for validity + return NULL; + + if(*arg->type != ',') + return NULL; + + arg->type++; // skip ',' + + return lv2_osc_reader_arg_raw(reader, arg); +} + +static inline bool +lv2_osc_reader_arg_is_end(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg) +{ + return (*arg->type == '\0') || (reader->ptr > arg->end); +} + +static inline LV2_OSC_Arg * +lv2_osc_reader_arg_next(LV2_OSC_Reader *reader, LV2_OSC_Arg *arg) +{ + arg->type++; + + return lv2_osc_reader_arg_raw(reader, arg); +} + +#define OSC_READER_MESSAGE_BEGIN(reader, len) \ + lv2_osc_reader_arg_begin( \ + (reader), \ + &(LV2_OSC_Arg){ .type = NULL, .size = 0, .path = NULL, .end = NULL }, \ + len) + +#define OSC_READER_MESSAGE_ITERATE(reader, arg) \ + for(arg = arg; \ + arg && !lv2_osc_reader_arg_is_end((reader), (arg)); \ + arg = lv2_osc_reader_arg_next((reader), (arg))) + +#define OSC_READER_MESSAGE_FOREACH(reader, arg, len) \ + for(LV2_OSC_Arg *(arg) = OSC_READER_MESSAGE_BEGIN((reader), (len)); \ + arg && !lv2_osc_reader_arg_is_end((reader), (arg)); \ + arg = lv2_osc_reader_arg_next((reader), (arg))) + +static inline bool +lv2_osc_reader_arg_varlist(LV2_OSC_Reader *reader, const char *fmt, va_list args) +{ + for(const char *type = fmt; *type; type++) + { + switch( (LV2_OSC_Type)*type) + { + case LV2_OSC_INT32: + if(!lv2_osc_reader_get_int32(reader, va_arg(args, int32_t *))) + return false; + break; + case LV2_OSC_FLOAT: + if(!lv2_osc_reader_get_float(reader, va_arg(args, float *))) + return false; + break; + case LV2_OSC_STRING: + if(!lv2_osc_reader_get_string(reader, va_arg(args, const char **))) + return false; + break; + case LV2_OSC_BLOB: + if(!lv2_osc_reader_get_blob(reader, va_arg(args, int32_t *), va_arg(args, const uint8_t **))) + return false; + break; + + case LV2_OSC_TRUE: + case LV2_OSC_FALSE: + case LV2_OSC_NIL: + case LV2_OSC_IMPULSE: + break; + + case LV2_OSC_INT64: + if(!lv2_osc_reader_get_int64(reader, va_arg(args, int64_t *))) + return false; + break; + case LV2_OSC_DOUBLE: + if(!lv2_osc_reader_get_double(reader, va_arg(args, double *))) + return false; + break; + case LV2_OSC_TIMETAG: + if(!lv2_osc_reader_get_timetag(reader, va_arg(args, uint64_t *))) + return false; + break; + + case LV2_OSC_MIDI: + if(!lv2_osc_reader_get_midi(reader, va_arg(args, const uint8_t **))) + return false; + break; + case LV2_OSC_SYMBOL: + if(!lv2_osc_reader_get_symbol(reader, va_arg(args, const char **))) + return false; + break; + case LV2_OSC_CHAR: + if(!lv2_osc_reader_get_char(reader, va_arg(args, char *))) + return false; + break; + case LV2_OSC_RGBA: + if(!lv2_osc_reader_get_rgba(reader, va_arg(args, uint8_t *), va_arg(args, uint8_t *), + va_arg(args, uint8_t *), va_arg(args, uint8_t *))) + return false; + break; + } + } + + return true; +} + +static inline bool +lv2_osc_reader_arg_vararg(LV2_OSC_Reader *reader, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + const bool res = lv2_osc_reader_arg_varlist(reader, fmt, args); + + va_end(args); + + return res; +} + +static inline bool +lv2_osc_reader_is_bundle(LV2_OSC_Reader *reader) +{ + return strncmp((const char *)reader->ptr, "#bundle", 8) == 0; +} + +static inline bool +lv2_osc_reader_is_message(LV2_OSC_Reader *reader) +{ + return reader->ptr[0] == '/'; //FIXME check path +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // LV2_OSC_READER_H diff --git a/osc.lv2/osc.lv2/util.h b/osc.lv2/osc.lv2/util.h new file mode 100644 index 0000000..d1cb762 --- /dev/null +++ b/osc.lv2/osc.lv2/util.h @@ -0,0 +1,349 @@ +/* + * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the Artistic License 2.0 as published by + * The Perl Foundation. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Artistic License 2.0 for more details. + * + * You should have received a copy of the Artistic License 2.0 + * along the source as a COPYING file. If not, obtain it from + * http://www.perlfoundation.org/artistic_license_2_0. + */ + +#ifndef LV2_OSC_UTIL_H +#define LV2_OSC_UTIL_H + +#include <assert.h> +#include <ctype.h> + +#include <osc.lv2/osc.h> + +#include <lv2/lv2plug.in/ns/ext/atom/util.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*LV2_OSC_Method)(const char *path, + const LV2_Atom_Tuple *arguments, void *data); + +// characters not allowed in OSC path string +static const char invalid_path_chars [] = { + ' ', '#', + '\0' +}; + +// allowed characters in OSC format string +static const char valid_format_chars [] = { + LV2_OSC_INT32, LV2_OSC_FLOAT, LV2_OSC_STRING, LV2_OSC_BLOB, + LV2_OSC_TRUE, LV2_OSC_FALSE, LV2_OSC_NIL, LV2_OSC_IMPULSE, + LV2_OSC_INT64, LV2_OSC_DOUBLE, LV2_OSC_TIMETAG, + LV2_OSC_SYMBOL, LV2_OSC_MIDI, + '\0' +}; + +/** + TODO +*/ +static inline bool +lv2_osc_check_path(const char *path) +{ + assert(path); + + if(path[0] != '/') + return false; + + for(const char *ptr=path+1; *ptr!='\0'; ptr++) + if( (isprint(*ptr) == 0) || (strchr(invalid_path_chars, *ptr) != NULL) ) + return false; + + return true; +} + +/** + TODO +*/ +static inline bool +lv2_osc_check_fmt(const char *format, int offset) +{ + assert(format); + + if(offset && (format[0] != ',') ) + return false; + + for(const char *ptr=format+offset; *ptr!='\0'; ptr++) + if(strchr(valid_format_chars, *ptr) == NULL) + return false; + + return true; +} + +/** + TODO +*/ +static inline uint64_t +lv2_osc_timetag_parse(const LV2_OSC_Timetag *timetag) +{ + return ((uint64_t)timetag->integral << 32) | timetag->fraction; +} + +/** + TODO +*/ +static inline LV2_OSC_Timetag * +lv2_osc_timetag_create(LV2_OSC_Timetag *timetag, uint64_t tt) +{ + timetag->integral = tt >> 32; + timetag->fraction = tt & 0xffffffff; + + return timetag; +} + +#define LV2_OSC_TIMETAG_CREATE(tt) \ + lv2_osc_timetag_create(&(LV2_OSC_Timetag){.integral = 0, .fraction = 0}, (tt)) + +/** + TODO +*/ +static inline bool +lv2_osc_is_packet_type(LV2_OSC_URID *osc_urid, LV2_URID type) +{ + return type == osc_urid->OSC_Packet; +} + +/** + TODO +*/ +static inline bool +lv2_osc_is_bundle_type(LV2_OSC_URID *osc_urid, LV2_URID type) +{ + return type == osc_urid->OSC_Bundle; +} + +/** + TODO +*/ +static inline bool +lv2_osc_is_message_type(LV2_OSC_URID *osc_urid, LV2_URID type) +{ + return type == osc_urid->OSC_Message; +} + +/** + TODO +*/ +static inline bool +lv2_osc_is_message_or_bundle_type(LV2_OSC_URID *osc_urid, LV2_URID type) +{ + return lv2_osc_is_message_type(osc_urid, type) + || lv2_osc_is_bundle_type(osc_urid, type); +} + +static inline LV2_OSC_Type +lv2_osc_argument_type(LV2_OSC_URID *osc_urid, const LV2_Atom *atom) +{ + const LV2_Atom_Object *obj = (const LV2_Atom_Object *)atom; + + if(atom->type == osc_urid->ATOM_Int) + return LV2_OSC_INT32; + else if(atom->type == osc_urid->ATOM_Float) + return LV2_OSC_FLOAT; + else if(atom->type == osc_urid->ATOM_String) + return LV2_OSC_STRING; + else if(atom->type == osc_urid->ATOM_Chunk) + return LV2_OSC_BLOB; + + else if(atom->type == osc_urid->ATOM_Long) + return LV2_OSC_INT64; + else if(atom->type == osc_urid->ATOM_Double) + return LV2_OSC_DOUBLE; + else if( (atom->type == osc_urid->ATOM_Object) && (obj->body.otype == osc_urid->OSC_Timetag) ) + return LV2_OSC_TIMETAG; + + else if(atom->type == osc_urid->ATOM_Bool) + { + if(((const LV2_Atom_Bool *)atom)->body) + return LV2_OSC_TRUE; + else + return LV2_OSC_FALSE; + } + else if( (atom->type == 0) && (atom->size == 0) ) + return LV2_OSC_NIL; + else if(atom->type == osc_urid->OSC_Impulse) + return LV2_OSC_IMPULSE; + + else if(atom->type == osc_urid->ATOM_URID) + return LV2_OSC_SYMBOL; + else if(atom->type == osc_urid->MIDI_MidiEvent) + return LV2_OSC_MIDI; + else if(atom->type == osc_urid->OSC_Char) + return LV2_OSC_CHAR; + else if(atom->type == osc_urid->OSC_RGBA) + return LV2_OSC_RGBA; + + return '\0'; +} + +/** + TODO +*/ +static inline void +lv2_osc_timetag_get(LV2_OSC_URID *osc_urid, const LV2_Atom_Object *obj, + LV2_OSC_Timetag *timetag) +{ + assert(timetag); + + const LV2_Atom_Long *integral = NULL; + const LV2_Atom_Long *fraction = NULL; + + lv2_atom_object_get(obj, + osc_urid->OSC_timetagIntegral, &integral, + osc_urid->OSC_timetagFraction, &fraction, + 0); + + if( integral && (integral->atom.type == osc_urid->ATOM_Long) + && fraction && (fraction->atom.type == osc_urid->ATOM_Long) ) + { + timetag->integral = integral->body; + timetag->fraction = fraction->body; + } + else + { + // set to immediate + timetag->integral = 0; + timetag->fraction = 1; + } +} + +/** + TODO +*/ +static inline bool +lv2_osc_bundle_body_get(LV2_OSC_URID *osc_urid, uint32_t size, const LV2_Atom_Object_Body *body, + const LV2_Atom_Object **timetag, const LV2_Atom_Tuple **items) +{ + assert(timetag && items); + + *timetag = NULL; + *items = NULL; + + lv2_atom_object_body_get(size, body, + osc_urid->OSC_bundleTimetag, timetag, + osc_urid->OSC_bundleItems, items, + 0); + + if(!*timetag || ((*timetag)->atom.type != osc_urid->ATOM_Object) || ((*timetag)->body.otype != osc_urid->OSC_Timetag)) + return false; + if(!*items || ((*items)->atom.type != osc_urid->ATOM_Tuple)) + return false; + + return true; +} + +/** + TODO +*/ +static inline bool +lv2_osc_bundle_get(LV2_OSC_URID *osc_urid, const LV2_Atom_Object *obj, + const LV2_Atom_Object **timetag, const LV2_Atom_Tuple **items) +{ + return lv2_osc_bundle_body_get(osc_urid, obj->atom.size, &obj->body, + timetag, items); +} + +/** + TODO +*/ +static inline bool +lv2_osc_message_body_get(LV2_OSC_URID *osc_urid, uint32_t size, const LV2_Atom_Object_Body *body, + const LV2_Atom_String **path, const LV2_Atom_Tuple **arguments) +{ + assert(path && arguments); + + *path = NULL; + *arguments = NULL; + + lv2_atom_object_body_get(size, body, + osc_urid->OSC_messagePath, path, + osc_urid->OSC_messageArguments, arguments, + 0); + + if(!*path || ((*path)->atom.type != osc_urid->ATOM_String)) + return false; + // message without arguments is valid + if( *arguments && ((*arguments)->atom.type != osc_urid->ATOM_Tuple)) + return false; + + return true; +} + +/** + TODO +*/ +static inline bool +lv2_osc_message_get(LV2_OSC_URID *osc_urid, const LV2_Atom_Object *obj, + const LV2_Atom_String **path, const LV2_Atom_Tuple **arguments) +{ + return lv2_osc_message_body_get(osc_urid, obj->atom.size, &obj->body, + path, arguments); +} + +static inline bool +lv2_osc_body_unroll(LV2_OSC_URID *osc_urid, uint32_t size, const LV2_Atom_Object_Body *body, + LV2_OSC_Method method, void *data) +{ + if(body->otype == osc_urid->OSC_Bundle) + { + const LV2_Atom_Object *timetag = NULL; + const LV2_Atom_Tuple *items = NULL; + + if(!lv2_osc_bundle_body_get(osc_urid, size, body, &timetag, &items)) + return false; + + LV2_OSC_Timetag tt; + lv2_osc_timetag_get(osc_urid, timetag, &tt); + + LV2_ATOM_TUPLE_FOREACH(items, atom) + { + const LV2_Atom_Object *obj= (const LV2_Atom_Object *)atom; + + if(!lv2_osc_body_unroll(osc_urid, obj->atom.size, &obj->body, method, data)) + return false; + } + + return true; + } + else if(body->otype == osc_urid->OSC_Message) + { + const LV2_Atom_String *path = NULL; + const LV2_Atom_Tuple *arguments = NULL; + + if(!lv2_osc_message_body_get(osc_urid, size, body, &path, &arguments)) + return false; + + if(method) + method(LV2_ATOM_BODY_CONST(path), arguments, data); + + return true; + } + + return false; +} + +static inline bool +lv2_osc_unroll(LV2_OSC_URID *osc_urid, const LV2_Atom_Object *obj, + LV2_OSC_Method method, void *data) +{ + return lv2_osc_body_unroll(osc_urid, obj->atom.size, &obj->body, method, data); +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // LV2_OSC_UTIL_H diff --git a/osc.lv2/osc.lv2/writer.h b/osc.lv2/osc.lv2/writer.h new file mode 100644 index 0000000..2cc89bb --- /dev/null +++ b/osc.lv2/osc.lv2/writer.h @@ -0,0 +1,551 @@ +/* + * Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the Artistic License 2.0 as published by + * The Perl Foundation. + * + * This source is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Artistic License 2.0 for more details. + * + * You should have received a copy of the Artistic License 2.0 + * along the source as a COPYING file. If not, obtain it from + * http://www.perlfoundation.org/artistic_license_2_0. + */ + +#ifndef LV2_OSC_WRITER_H +#define LV2_OSC_WRITER_H + +#include <stdbool.h> +#include <string.h> +#include <endian.h> + +#include <osc.lv2/osc.h> +#include <osc.lv2/util.h> + +#include <lv2/lv2plug.in/ns/ext/atom/util.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct _LV2_OSC_Writer LV2_OSC_Writer; +typedef struct _LV2_OSC_Writer_Frame LV2_OSC_Writer_Frame; + +struct _LV2_OSC_Writer { + uint8_t *buf; + uint8_t *ptr; + const uint8_t *end; +}; + +struct _LV2_OSC_Writer_Frame { + uint8_t *ref; +}; + +static inline void +lv2_osc_writer_initialize(LV2_OSC_Writer *writer, uint8_t *buf, size_t size) +{ + writer->buf = buf; + writer->ptr = buf; + writer->end = buf + size; +} + +static inline size_t +lv2_osc_writer_get_size(LV2_OSC_Writer *writer) +{ + if(writer->ptr > writer->buf) + return writer->ptr - writer->buf; + + return 0; +} + +static inline uint8_t * +lv2_osc_writer_finalize(LV2_OSC_Writer *writer, size_t *size) +{ + *size = lv2_osc_writer_get_size(writer); + + if(*size) + return writer->buf; + + return NULL; +} + +static inline bool +lv2_osc_writer_overflow(LV2_OSC_Writer *writer, size_t size) +{ + return writer->ptr + size >= writer->end; +} + +static inline bool +lv2_osc_writer_htobe32(LV2_OSC_Writer *writer, union swap32_t *s32) +{ + if(lv2_osc_writer_overflow(writer, 4)) + return false; + + s32->u = htobe32(s32->u); + *(uint32_t *)writer->ptr = s32->u; + writer->ptr += 4; + + return true; +} + +static inline bool +lv2_osc_writer_htobe64(LV2_OSC_Writer *writer, union swap64_t *s64) +{ + if(lv2_osc_writer_overflow(writer, 8)) + return false; + + s64->u = htobe64(s64->u); + *(uint64_t *)writer->ptr = s64->u; + writer->ptr += 8; + + return true; +} + +static inline bool +lv2_osc_writer_add_int32(LV2_OSC_Writer *writer, int32_t i) +{ + return lv2_osc_writer_htobe32(writer, &(union swap32_t){ .i = i }); +} + +static inline bool +lv2_osc_writer_add_float(LV2_OSC_Writer *writer, float f) +{ + return lv2_osc_writer_htobe32(writer, &(union swap32_t){ .f = f }); +} + +static inline bool +lv2_osc_writer_add_string(LV2_OSC_Writer *writer, const char *s) +{ + const size_t padded = LV2_OSC_PADDED_SIZE(strlen(s) + 1); + if(lv2_osc_writer_overflow(writer, padded)) + return false; + + strncpy((char *)writer->ptr, s, padded); + writer->ptr += padded; + + return true; +} + +static inline bool +lv2_osc_writer_add_symbol(LV2_OSC_Writer *writer, const char *S) +{ + return lv2_osc_writer_add_string(writer, S); +} + +static inline bool +lv2_osc_writer_add_int64(LV2_OSC_Writer *writer, int64_t h) +{ + return lv2_osc_writer_htobe64(writer, &(union swap64_t){ .h = h }); +} + +static inline bool +lv2_osc_writer_add_double(LV2_OSC_Writer *writer, double d) +{ + return lv2_osc_writer_htobe64(writer, &(union swap64_t){ .d = d }); +} + +static inline bool +lv2_osc_writer_add_timetag(LV2_OSC_Writer *writer, uint64_t u) +{ + return lv2_osc_writer_htobe64(writer, &(union swap64_t){ .u = u }); +} + +static inline bool +lv2_osc_writer_add_blob_inline(LV2_OSC_Writer *writer, int32_t len, uint8_t **body) +{ + const size_t len_padded = LV2_OSC_PADDED_SIZE(len); + const size_t size = 4 + len_padded; + if(lv2_osc_writer_overflow(writer, size)) + return false; + + if(!lv2_osc_writer_add_int32(writer, len)) + return false; + + *body = writer->ptr; + memset(&writer->ptr[len], 0x0, len_padded - len); + writer->ptr += len_padded; + + return true; +} + +static inline bool +lv2_osc_writer_add_blob(LV2_OSC_Writer *writer, int32_t len, const uint8_t *body) +{ + uint8_t *dst; + if(!lv2_osc_writer_add_blob_inline(writer, len, &dst)) + return false; + + memcpy(dst, body, len); + + return true; +} + +static inline bool +lv2_osc_writer_add_midi_inline(LV2_OSC_Writer *writer, int32_t len, uint8_t **m) +{ + if( (len > 4) || lv2_osc_writer_overflow(writer, 4)) + return false; + + *m = writer->ptr; + memset(&writer->ptr[len], 0x0, 4 - len); + writer->ptr += 4; + + return true; +} + +static inline bool +lv2_osc_writer_add_midi(LV2_OSC_Writer *writer, int32_t len, const uint8_t *m) +{ + uint8_t *dst; + if(!lv2_osc_writer_add_midi_inline(writer, len, &dst)) + return false; + + memcpy(dst, m, len); + + return true; +} + +static inline bool +lv2_osc_writer_add_rgba(LV2_OSC_Writer *writer, uint8_t r, uint8_t g, uint8_t b, uint8_t a) +{ + if(lv2_osc_writer_overflow(writer, 4)) + return false; + + writer->ptr[0] = r; + writer->ptr[1] = g; + writer->ptr[2] = b; + writer->ptr[3] = a; + writer->ptr += 4; + + return true; +} + +static inline bool +lv2_osc_writer_add_char(LV2_OSC_Writer *writer, char c) +{ + return lv2_osc_writer_add_int32(writer, (int32_t)c); +} + +static inline bool +lv2_osc_writer_push_bundle(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame, uint64_t t) +{ + if(lv2_osc_writer_overflow(writer, 16)) + return false; + + frame->ref = writer->ptr; + + strncpy((char *)writer->ptr, "#bundle", 8); + writer->ptr += 8; + + return lv2_osc_writer_add_timetag(writer, t); +} + +static inline bool +lv2_osc_writer_pop_bundle(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame) +{ + union swap32_t s32 = { .i = writer->ptr - frame->ref - 16}; + + if(s32.i <= 0) + { + writer->ptr = frame->ref; + return false; + } + + return true; +} + +static inline bool +lv2_osc_writer_push_item(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame) +{ + if(lv2_osc_writer_overflow(writer, 4)) + return false; + + frame->ref = writer->ptr; + writer->ptr += 4; + + return true; +} + +static inline bool +lv2_osc_writer_pop_item(LV2_OSC_Writer *writer, LV2_OSC_Writer_Frame *frame) +{ + union swap32_t s32 = { .i = writer->ptr - frame->ref - 4}; + + if(s32.i <= 0) + { + writer->ptr = frame->ref; + return false; + } + + s32.u = htobe32(s32.u); + *(uint32_t *)frame->ref = s32.u; + + return true; +} + +static inline bool +lv2_osc_writer_add_path(LV2_OSC_Writer *writer, const char *path) +{ + return lv2_osc_writer_add_string(writer, path); +} + +static inline bool +lv2_osc_writer_add_format(LV2_OSC_Writer *writer, const char *fmt) +{ + const size_t padded = LV2_OSC_PADDED_SIZE(strlen(fmt) + 2); + if(lv2_osc_writer_overflow(writer, padded)) + return false; + + *writer->ptr++ = ','; + strncpy((char *)writer->ptr, fmt, padded - 1); + writer->ptr += padded - 1; + + return true; +} + +static inline bool +lv2_osc_writer_arg_varlist(LV2_OSC_Writer *writer, const char *fmt, va_list args) +{ + for(const char *type = fmt; *type; type++) + { + switch( (LV2_OSC_Type)*type) + { + case LV2_OSC_INT32: + if(!lv2_osc_writer_add_int32(writer, va_arg(args, int32_t))) + return false; + break; + case LV2_OSC_FLOAT: + if(!lv2_osc_writer_add_float(writer, (float)va_arg(args, double))) + return false; + break; + case LV2_OSC_STRING: + if(!lv2_osc_writer_add_string(writer, va_arg(args, const char *))) + return false; + break; + case LV2_OSC_BLOB: + if(!lv2_osc_writer_add_blob(writer, va_arg(args, int32_t), va_arg(args, const uint8_t *))) + return false; + break; + + case LV2_OSC_TRUE: + case LV2_OSC_FALSE: + case LV2_OSC_NIL: + case LV2_OSC_IMPULSE: + break; + + case LV2_OSC_INT64: + if(!lv2_osc_writer_add_int64(writer, va_arg(args, int64_t))) + return false; + break; + case LV2_OSC_DOUBLE: + if(!lv2_osc_writer_add_double(writer, va_arg(args, double))) + return false; + break; + case LV2_OSC_TIMETAG: + if(!lv2_osc_writer_add_timetag(writer, va_arg(args, uint64_t))) + return false; + break; + + case LV2_OSC_MIDI: + if(!lv2_osc_writer_add_midi(writer, va_arg(args, int32_t), va_arg(args, const uint8_t *))) + return false; + break; + case LV2_OSC_SYMBOL: + if(!lv2_osc_writer_add_symbol(writer, va_arg(args, const char *))) + return false; + break; + case LV2_OSC_CHAR: + if(!lv2_osc_writer_add_char(writer, va_arg(args, int))) + return false; + break; + case LV2_OSC_RGBA: + if(!lv2_osc_writer_add_rgba(writer, va_arg(args, unsigned), va_arg(args, unsigned), + va_arg(args, unsigned), va_arg(args, unsigned))) + return false; + break; + } + } + + return true; +} + +static inline bool +lv2_osc_writer_arg_vararg(LV2_OSC_Writer *writer, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + const bool res = lv2_osc_writer_arg_varlist(writer, fmt, args); + + va_end(args); + + return res; +} + +static inline bool +lv2_osc_writer_message_varlist(LV2_OSC_Writer *writer, const char *path, const char *fmt, va_list args) +{ + if(!lv2_osc_writer_add_path(writer, path)) + return false; + + if(!lv2_osc_writer_add_format(writer, fmt)) + return false; + + return lv2_osc_writer_arg_varlist(writer, fmt, args); +} + +static inline bool +lv2_osc_writer_message_vararg(LV2_OSC_Writer *writer, const char *path, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + + const bool res = lv2_osc_writer_message_varlist(writer, path, fmt, args); + + va_end(args); + + return res; +} + +static inline bool +lv2_osc_writer_packet(LV2_OSC_Writer *writer, LV2_OSC_URID *osc_urid, + LV2_URID_Unmap *unmap, uint32_t size, const LV2_Atom_Object_Body *body) +{ + if(body->otype == osc_urid->OSC_Bundle) + { + const LV2_Atom_Object *timetag = NULL; + const LV2_Atom_Tuple *items = NULL; + + if(!lv2_osc_bundle_body_get(osc_urid, size, body, &timetag, &items)) + return false; + + LV2_OSC_Timetag tt; + LV2_OSC_Writer_Frame bndl; + + lv2_osc_timetag_get(osc_urid, timetag, &tt); + if(!lv2_osc_writer_push_bundle(writer, &bndl, lv2_osc_timetag_parse(&tt))) + return false; + + LV2_ATOM_TUPLE_FOREACH(items, atom) + { + const LV2_Atom_Object *obj= (const LV2_Atom_Object *)atom; + LV2_OSC_Writer_Frame itm; + + if( !lv2_osc_writer_push_item(writer, &itm) + || !lv2_osc_writer_packet(writer, osc_urid, unmap, obj->atom.size, &obj->body) + || !lv2_osc_writer_pop_item(writer, &itm) ) + { + return false; + } + } + + return lv2_osc_writer_pop_bundle(writer, &bndl); + } + else if(body->otype == osc_urid->OSC_Message) + { + const LV2_Atom_String *path = NULL; + const LV2_Atom_Tuple *arguments = NULL; + + if(lv2_osc_message_body_get(osc_urid, size, body, &path, &arguments)) + { + if(!lv2_osc_writer_add_path(writer, LV2_ATOM_BODY_CONST(path))) + return false; + + char fmt [128]; //TODO how big? + char *ptr = fmt; + LV2_ATOM_TUPLE_FOREACH(arguments, atom) + { + *ptr++ = lv2_osc_argument_type(osc_urid, atom); + } + *ptr = '\0'; + if(!lv2_osc_writer_add_format(writer, fmt)) + return false; + + LV2_ATOM_TUPLE_FOREACH(arguments, atom) + { + const LV2_Atom_Object *obj= (const LV2_Atom_Object *)atom; + + if(atom->type == osc_urid->ATOM_Int) + { + if(!lv2_osc_writer_add_int32(writer, ((const LV2_Atom_Int *)atom)->body)) + return false; + } + else if(atom->type == osc_urid->ATOM_Float) + { + if(!lv2_osc_writer_add_float(writer, ((const LV2_Atom_Float *)atom)->body)) + return false; + } + else if(atom->type == osc_urid->ATOM_String) + { + if(!lv2_osc_writer_add_string(writer, LV2_ATOM_BODY_CONST(atom))) + return false; + } + else if(atom->type == osc_urid->ATOM_Chunk) + { + if(!lv2_osc_writer_add_blob(writer, atom->size, LV2_ATOM_BODY_CONST(atom))) + return false; + } + + else if(atom->type == osc_urid->ATOM_Long) + { + if(!lv2_osc_writer_add_int64(writer, ((const LV2_Atom_Long *)atom)->body)) + return false; + } + else if(atom->type == osc_urid->ATOM_Double) + { + if(!lv2_osc_writer_add_double(writer, ((const LV2_Atom_Double *)atom)->body)) + return false; + } + else if( (atom->type == osc_urid->ATOM_Object) && (obj->body.otype == osc_urid->OSC_Timetag) ) + { + LV2_OSC_Timetag tt; + lv2_osc_timetag_get(osc_urid, obj, &tt); + if(!lv2_osc_writer_add_timetag(writer, lv2_osc_timetag_parse(&tt))) + return false; + } + + // there is nothing to do for: true, false, nil, impulse + + else if(atom->type == osc_urid->ATOM_URID) + { + const char *symbol = unmap->unmap(unmap->handle, ((const LV2_Atom_URID *)atom)->body); + if(!symbol || !lv2_osc_writer_add_symbol(writer, symbol)) + return false; + } + else if(atom->type == osc_urid->MIDI_MidiEvent) + { + uint8_t *m = NULL; + if(!lv2_osc_writer_add_midi_inline(writer, atom->size + 1, &m)) + return false; + m[0] = 0x0; // port + memcpy(&m[1], LV2_ATOM_BODY_CONST(atom), atom->size); + } + else if(atom->type == osc_urid->OSC_Char) + { + const char c = *(const char *)LV2_ATOM_BODY_CONST(atom); + if(!lv2_osc_writer_add_char(writer, c)) + return false; + } + else if(atom->type == osc_urid->OSC_RGBA) + { + const uint8_t *rgba = LV2_ATOM_BODY_CONST(atom); + if(!lv2_osc_writer_add_rgba(writer, rgba[0], rgba[1], rgba[2], rgba[3])) + return false; + } + } + } + + return true; + } + + return false; +} + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // LV2_OSC_WRITER_H diff --git a/osc.lv2/osc.ttl b/osc.lv2/osc.ttl index efa9e32..db4a048 100644 --- a/osc.lv2/osc.ttl +++ b/osc.lv2/osc.ttl @@ -1,4 +1,4 @@ -# Copyright (c) 2015 Hanspeter Portner (dev@open-music-kontrollers.ch) +# Copyright (c) 2015-2016 Hanspeter Portner (dev@open-music-kontrollers.ch) # # This is free software: you can redistribute it and/or modify # it under the terms of the Artistic License 2.0 as published by @@ -13,14 +13,12 @@ # along the source as a COPYING file. If not, obtain it from # http://www.perlfoundation.org/artistic_license_2_0. -@prefix atom: <http://lv2plug.in/ns/ext/atom#> . -@prefix lv2: <http://lv2plug.in/ns/lv2core#> . -@prefix midi: <http://lv2plug.in/ns/midi#> . -@prefix owl: <http://www.w3.org/2002/07/owl#> . -@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . +@prefix owl: <http://www.w3.org/2002/07/owl#> . @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . -@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . -@prefix osc: <http://open-music-kontrollers.ch/lv2/osc#> . +@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . +@prefix lv2: <http://lv2plug.in/ns/lv2core#> . +@prefix atom: <http://lv2plug.in/ns/ext/atom#> . +@prefix osc: <http://open-music-kontrollers.ch/lv2/osc#> . <http://open-music-kontrollers.ch/lv2/osc> a owl:Ontology ; @@ -37,65 +35,8 @@ osc:schedule a lv2:Feature . osc:Event - a rdfs:Class ; - rdfs:subClassOf atom:Object ; + a rdfs:Class , + rdfs:Datatype ; + rdfs:subClassOf atom:Atom ; + owl:onDatatype xsd:hexBinary ; rdfs:label "OSC Event (Bundle or Message)" . - -osc:Bundle - a rdfs:Class ; - rdfs:subClassOf osc:Event ; - rdfs:label "OSC Bundle" . - -osc:Message - a rdfs:Class ; - rdfs:subClassOf osc:Event ; - rdfs:label "OSC Message" . - -osc:bundleTimestamp - a rdf:Property , - owl:ObjectProperty , - owl:FunctionalProperty ; - rdfs:domain osc:Bundle ; - rdfs:range atom:Long ; - rdfs:label "OSC Bundle Timestamp" . - -osc:bundleItems - a rdf:Property , - owl:ObjectProperty , - owl:FunctionalProperty ; - rdfs:domain osc:Bundle ; - rdfs:range atom:Tuple ; - rdfs:label "OSC Bundle Items" ; - lv2:documentation """ - <p>Tuple of OSC Bundle Items (e.g. nested osc:Bundle's and/or - osc:Message's).</p> - """ . - -osc:messagePath - a rdf:Property , - owl:ObjectProperty , - owl:FunctionalProperty ; - rdfs:domain osc:Message ; - rdfs:range atom:String ; - rdfs:label "OSC Message Path" . - -osc:messageFormat - a rdf:Property , - owl:ObjectProperty , - owl:FunctionalProperty ; - rdfs:domain osc:Message ; - rdfs:range atom:String ; - rdfs:label "OSC Message Format" . - -osc:messageArguments - a rdf:Property , - owl:ObjectProperty , - owl:FunctionalProperty ; - rdfs:domain osc:Message ; - rdfs:range atom:Tuple ; - rdfs:label "OSC Message Arguments" ; - lv2:documentation """ - <p>Tuple of OSC Message Arguments (e.g. Atom:Int, Atom:Long, Atom:Float, - Atom:Double, Atom:String, Atom:Chunk, Atom:Bool, Atom:Blank, - MIDI:MidiEvent).</p> - """ . diff --git a/osc.lv2/osc_test.c b/osc.lv2/osc_test.c new file mode 100644 index 0000000..1280fc0 --- /dev/null +++ b/osc.lv2/osc_test.c @@ -0,0 +1,423 @@ +#include <assert.h> +#include <string.h> +#include <stdio.h> + +#include <osc.lv2/osc.h> +#include <osc.lv2/reader.h> +#include <osc.lv2/writer.h> +#include <osc.lv2/forge.h> + +#define BUF_SIZE 8192 +#define MAX_URIDS 512 + +typedef void (*test_t)(LV2_OSC_Writer *writer); +typedef struct _urid_t urid_t; +typedef struct _handle_t handle_t; + +struct _urid_t { + LV2_URID urid; + char *uri; +}; + +struct _handle_t { + urid_t urids [MAX_URIDS]; + LV2_URID urid; +}; + +static handle_t __handle; +static uint8_t buf0 [BUF_SIZE]; +static uint8_t buf1 [BUF_SIZE]; +static uint8_t buf2 [BUF_SIZE]; +static const LV2_Atom_Object *obj2= (const LV2_Atom_Object *)buf2; + +const uint8_t raw_0 [] = { + '/', 0x0, 0x0, 0x0, + ',', 0x0, 0x0, 0x0 +}; + +const uint8_t raw_1 [] = { + '/', 'p', 'i', 'n', + 'g', 0x0, 0x0, 0x0, + ',', 'i', 'f', 's', + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xc, + 0x40, 0x59, 0x99, 0x9a, + 'w', 'o', 'r', 'l', + 'd', 0x0, 0x0, 0x0 +}; + +const uint8_t raw_2 [] = { + '/', 'p', 'i', 'n', + 'g', 0x0, 0x0, 0x0, + ',', 'h', 'd', 'S', + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0xc, + 0x40, 0x0b, 0x33, 0x33, + 0x33, 0x33, 0x33, 0x33, + 'h', 't', 't', 'p', + ':', '/', '/', 'e', + 'x', 'a', 'm', 'p', + 'l', 'e', '.', 'c', + 'o', 'm', 0x0, 0x0 +}; + +const uint8_t raw_3 [] = { + '/', 'p', 'i', 'n', + 'g', 0x0, 0x0, 0x0, + ',', 'T', 'F', 'N', + 'I', 0x0, 0x0, 0x0 +}; + +const uint8_t raw_4 [] = { + '/', 'm', 'i', 'd', + 'i', 0x0, 0x0, 0x0, + ',', 'm', 0x0, 0x0, + 0x0, 0x90, 24, 0x7f +}; + +const uint8_t raw_5 [] = { + '/', 'b', 'l', 'o', + 'b', 0x0, 0x0, 0x0, + ',', 'b', 0x0, 0x0, + 0x0, 0x0, 0x0, 0x6, + 0x1, 0x2, 0x3, 0x4, + 0x5, 0x6, 0x0, 0x0 +}; + +const uint8_t raw_6 [] = { + '#', 'b', 'u', 'n', + 'd', 'l', 'e', 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x1, + + 0x0, 0x0, 0x0, 0x8, + '/', 0x0, 0x0, 0x0, + ',', 0x0, 0x0, 0x0 +}; + +const uint8_t raw_7 [] = { + '#', 'b', 'u', 'n', + 'd', 'l', 'e', 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x1, + + 0x0, 0x0, 0x0, 0x1c, + '#', 'b', 'u', 'n', + 'd', 'l', 'e', 0x0, + 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x1, + + 0x0, 0x0, 0x0, 0x8, + '/', 0x0, 0x0, 0x0, + ',', 0x0, 0x0, 0x0, + + 0x0, 0x0, 0x0, 0x8, + '/', 0x0, 0x0, 0x0, + ',', 0x0, 0x0, 0x0 +}; + +static LV2_URID +_map(LV2_URID_Map_Handle instance, const char *uri) +{ + handle_t *handle = instance; + + urid_t *itm; + for(itm=handle->urids; itm->urid; itm++) + { + if(!strcmp(itm->uri, uri)) + return itm->urid; + } + + assert(handle->urid + 1 < MAX_URIDS); + + // create new + itm->urid = ++handle->urid; + itm->uri = strdup(uri); + + return itm->urid; +} + +static const char * +_unmap(LV2_URID_Unmap_Handle instance, LV2_URID urid) +{ + handle_t *handle = instance; + + urid_t *itm; + for(itm=handle->urids; itm->urid; itm++) + { + if(itm->urid == urid) + return itm->uri; + } + + // not found + return NULL; +} + +static LV2_URID_Map map = { + .handle = &__handle, + .map = _map +}; + +static LV2_URID_Unmap unmap = { + .handle = &__handle, + .unmap = _unmap +}; + +static void +_dump(const uint8_t *src, const uint8_t *dst, size_t size) +{ + for(size_t i = 0; i < size; i++) + printf("%zu %02x %02x\n", i, src[i], dst[i]); + printf("\n"); +} + +static void +_clone(LV2_OSC_Reader *reader, LV2_OSC_Writer *writer, size_t size) +{ + if(lv2_osc_reader_is_bundle(reader)) + { + LV2_OSC_Item *itm = OSC_READER_BUNDLE_BEGIN(reader, size); + assert(itm); + + LV2_OSC_Writer_Frame frame_bndl; + assert(lv2_osc_writer_push_bundle(writer, &frame_bndl, itm->timetag)); + + OSC_READER_BUNDLE_ITERATE(reader, itm) + { + LV2_OSC_Reader reader2; + lv2_osc_reader_initialize(&reader2, itm->body, itm->size); + + LV2_OSC_Writer_Frame frame_itm; + assert(lv2_osc_writer_push_item(writer, &frame_itm)); + _clone(&reader2, writer, itm->size); + assert(lv2_osc_writer_pop_item(writer, &frame_itm)); + } + + assert(lv2_osc_writer_pop_bundle(writer, &frame_bndl)); + } + else if(lv2_osc_reader_is_message(reader)) + { + LV2_OSC_Arg *arg = OSC_READER_MESSAGE_BEGIN(reader, size); + assert(arg); + + assert(lv2_osc_writer_add_path(writer, arg->path)); + assert(lv2_osc_writer_add_format(writer, arg->type)); + + OSC_READER_MESSAGE_ITERATE(reader, arg) + { + switch((LV2_OSC_Type)*arg->type) + { + case LV2_OSC_INT32: + assert(lv2_osc_writer_add_int32(writer, arg->i)); + break; + case LV2_OSC_FLOAT: + assert(lv2_osc_writer_add_float(writer, arg->f)); + break; + case LV2_OSC_STRING: + assert(lv2_osc_writer_add_string(writer, arg->s)); + break; + case LV2_OSC_BLOB: + assert(lv2_osc_writer_add_blob(writer, arg->size, arg->b)); + break; + + case LV2_OSC_INT64: + assert(lv2_osc_writer_add_int64(writer, arg->h)); + break; + case LV2_OSC_DOUBLE: + assert(lv2_osc_writer_add_double(writer, arg->d)); + break; + case LV2_OSC_TIMETAG: + assert(lv2_osc_writer_add_timetag(writer, arg->t)); + break; + + case LV2_OSC_TRUE: + case LV2_OSC_FALSE: + case LV2_OSC_NIL: + case LV2_OSC_IMPULSE: + break; + + case LV2_OSC_MIDI: + assert(lv2_osc_writer_add_midi(writer, arg->size, arg->m)); + break; + case LV2_OSC_SYMBOL: + assert(lv2_osc_writer_add_symbol(writer, arg->S)); + break; + case LV2_OSC_CHAR: + assert(lv2_osc_writer_add_char(writer, arg->c)); + break; + case LV2_OSC_RGBA: + assert(lv2_osc_writer_add_rgba(writer, arg->R, arg->G, arg->B, arg->A)); + break; + } + } + } +} + +static void +_test_a(LV2_OSC_Writer *writer, const uint8_t *raw, size_t size) +{ + LV2_OSC_URID osc_urid; + lv2_osc_urid_init(&osc_urid, &map); + + // check writer against raw bytes + size_t len; + assert(lv2_osc_writer_finalize(writer, &len) == buf0); + assert(len == size); + assert(memcmp(raw, buf0, size) == 0); + + // check reader & writer + LV2_OSC_Reader reader; + lv2_osc_reader_initialize(&reader, buf0, size); + lv2_osc_writer_initialize(writer, buf1, BUF_SIZE); + _clone(&reader, writer, size); + + // check cloned against raw bytes + assert(lv2_osc_writer_finalize(writer, &len) == buf1); + assert(len == size); + assert(memcmp(raw, buf1, size) == 0); + + // check forge + LV2_Atom_Forge forge; + lv2_atom_forge_init(&forge, &map); + lv2_atom_forge_set_buffer(&forge, buf2, BUF_SIZE); + assert(lv2_osc_forge_packet(&forge, &osc_urid, &map, buf0, size)); + + // check deforge + lv2_osc_writer_initialize(writer, buf1, BUF_SIZE); + assert(lv2_osc_writer_packet(writer, &osc_urid, &unmap, obj2->atom.size, &obj2->body)); + + // check deforged against raw bytes + assert(lv2_osc_writer_finalize(writer, &len) == buf1); + assert(len == size); + assert(memcmp(raw, buf1, size) == 0); +} + +static void +test_0_a(LV2_OSC_Writer *writer) +{ + assert(lv2_osc_writer_message_vararg(writer, "/", "")); + _test_a(writer, raw_0, sizeof(raw_0)); +} + +static void +test_1_a(LV2_OSC_Writer *writer) +{ + assert(lv2_osc_writer_message_vararg(writer, "/ping", "ifs", + 12, 3.4f, "world")); + _test_a(writer, raw_1, sizeof(raw_1)); +} + +static void +test_2_a(LV2_OSC_Writer *writer) +{ + assert(lv2_osc_writer_message_vararg(writer, "/ping", "hdS", + 12, 3.4, "http://example.com")); + _test_a(writer, raw_2, sizeof(raw_2)); +} + +static void +test_3_a(LV2_OSC_Writer *writer) +{ + assert(lv2_osc_writer_message_vararg(writer, "/ping", "TFNI")); + _test_a(writer, raw_3, sizeof(raw_3)); +} + +static void +test_4_a(LV2_OSC_Writer *writer) +{ + uint8_t m [] = {0x00, 0x90, 24, 0x7f}; + assert(lv2_osc_writer_message_vararg(writer, "/midi", "m", 4, m)); + _test_a(writer, raw_4, sizeof(raw_4)); +} + +static void +test_5_a(LV2_OSC_Writer *writer) +{ + uint8_t b [] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6}; + assert(lv2_osc_writer_message_vararg(writer, "/blob", "b", 6, b)); + _test_a(writer, raw_5, sizeof(raw_5)); +} + +static void +test_6_a(LV2_OSC_Writer *writer) +{ + LV2_OSC_Writer_Frame frame_bndl, frame_itm; + + assert(lv2_osc_writer_push_bundle(writer, &frame_bndl, LV2_OSC_IMMEDIATE)); + { + assert(lv2_osc_writer_push_item(writer, &frame_itm)); + { + assert(lv2_osc_writer_message_vararg(writer, "/", "")); + } + assert(lv2_osc_writer_pop_item(writer, &frame_itm)); + } + assert(lv2_osc_writer_pop_bundle(writer, &frame_bndl)); + + _test_a(writer, raw_6, sizeof(raw_6)); +} + +static void +test_7_a(LV2_OSC_Writer *writer) +{ + LV2_OSC_Writer_Frame frame_bndl[2], frame_itm[2]; + + assert(lv2_osc_writer_push_bundle(writer, &frame_bndl[0], LV2_OSC_IMMEDIATE)); + { + assert(lv2_osc_writer_push_item(writer, &frame_itm[0])); + { + assert(lv2_osc_writer_push_bundle(writer, &frame_bndl[1], LV2_OSC_IMMEDIATE)); + { + assert(lv2_osc_writer_push_item(writer, &frame_itm[1])); + { + assert(lv2_osc_writer_message_vararg(writer, "/", "")); + } + assert(lv2_osc_writer_pop_item(writer, &frame_itm[1])); + } + assert(lv2_osc_writer_pop_bundle(writer, &frame_bndl[1])); + } + assert(lv2_osc_writer_pop_item(writer, &frame_itm[0])); + + assert(lv2_osc_writer_push_item(writer, &frame_itm[0])); + { + assert(lv2_osc_writer_message_vararg(writer, "/", "")); + } + assert(lv2_osc_writer_pop_item(writer, &frame_itm[0])); + } + assert(lv2_osc_writer_pop_bundle(writer, &frame_bndl[0])); + + _test_a(writer, raw_7, sizeof(raw_7)); +} + +static test_t tests [] = { + test_0_a, + test_1_a, + test_2_a, + test_3_a, + test_4_a, + test_5_a, + test_6_a, + test_7_a, + + NULL +}; + +int +main(int argc, char **argv) +{ + LV2_OSC_Writer writer; + + for(test_t *test=tests; *test; test++) + { + test_t cb = *test; + + memset(buf0, 0x0, BUF_SIZE); + memset(buf1, 0x0, BUF_SIZE); + + lv2_osc_writer_initialize(&writer, buf0, BUF_SIZE); + + cb(&writer); + } + + return 0; +} |