diff options
Diffstat (limited to 'ext/osc.lv2/lv2_osc.c')
-rw-r--r-- | ext/osc.lv2/lv2_osc.c | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/ext/osc.lv2/lv2_osc.c b/ext/osc.lv2/lv2_osc.c new file mode 100644 index 0000000..0c1d1e0 --- /dev/null +++ b/ext/osc.lv2/lv2_osc.c @@ -0,0 +1,314 @@ +/* LV2 OSC Messages Extension + * Copyright (C) 2007-2009 David Robillard + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <errno.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include "lv2_osc.h" +#include "lv2_osc_print.h" + +/*#ifndef BIG_ENDIAN + #ifndef LITTLE_ENDIAN + #warning This code requires BIG_ENDIAN or LITTLE_ENDIAN to be defined + #warning Assuming little endian. THIS MAY BREAK HORRIBLY! + #endif +#endif*/ + +#define lv2_osc_swap32(x) \ +({ \ + uint32_t __x = (x); \ + ((uint32_t)( \ + (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )); \ +}) + +#define lv2_osc_swap64(x) \ +({ \ + uint64_t __x = (x); \ + ((uint64_t)( \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \ +}) + + +/** Pad a size to a multiple of 32 bits */ +inline static uint32_t +lv2_osc_pad_size(uint32_t size) +{ + return size + 3 - ((size-1) % 4); +} + + +inline static uint32_t +lv2_osc_string_size(const char *s) +{ + return lv2_osc_pad_size((uint32_t)strlen(s) + 1); +} + + +static inline uint32_t +lv2_osc_blob_size(const void* blob) +{ + return sizeof(uint32_t) + lv2_osc_pad_size(*((uint32_t*)blob)); +} + + +uint32_t +lv2_osc_arg_size(char type, const LV2_OSC_Argument* arg) +{ + switch (type) { + case 'c': + case 'i': + case 'f': + return 4; + + case 'h': + case 'd': + return 8; + + case 's': + return lv2_osc_string_size(&arg->s); + + /*case 'S': + return lv2_osc_string_size(&arg->S);*/ + + case 'b': + return lv2_osc_blob_size(&arg->b); + + default: + fprintf(stderr, "Warning: unknown OSC type '%c'.", type); + return 0; + } +} + + +void +lv2_osc_argument_swap_byte_order(char type, LV2_OSC_Argument* arg) +{ + switch (type) { + case 'i': + case 'f': + case 'b': + case 'c': + *(int32_t*)arg = lv2_osc_swap32(*(int32_t*)arg); + break; + + case 'h': + case 'd': + *(int64_t*)arg = lv2_osc_swap64(*(int64_t*)arg); + break; + } +} + + +/** Convert a message from network byte order to host byte order. */ +void +lv2_osc_message_swap_byte_order(LV2_OSC_Event* msg) +{ + const char* const types = lv2_osc_get_types(msg); + + for (uint32_t i=0; i < msg->argument_count; ++i) + lv2_osc_argument_swap_byte_order(types[i], lv2_osc_get_argument(msg, i)); +} + + +/** Not realtime safe, returned value must be free()'d by caller. */ +LV2_OSC_Event* +lv2_osc_message_new(const char* path, const char* types, ...) +{ + /* FIXME: path only */ + + LV2_OSC_Event* result = malloc(sizeof(LV2_OSC_Event) + + 4 + lv2_osc_string_size(path)); + + const uint32_t path_size = lv2_osc_string_size(path); + result->data_size = path_size + 4; // 4 for types + result->argument_count = 0; + result->types_offset = lv2_osc_string_size(path) + 1; + (&result->data)[result->types_offset - 1] = ','; + (&result->data)[result->types_offset] = '\0'; + + memcpy(&result->data, path, strlen(path) + 1); + + return result; +} + + +/** Create a new LV2_OSC_Event from a raw OSC message. + * + * If \a out_buf is NULL, new memory will be allocated. Otherwise the returned + * value will be equal to buf, unless there is insufficient space in which + * case NULL is returned. + */ +LV2_OSC_Event* +lv2_osc_message_from_raw(uint32_t out_buf_size, + void* out_buf, + uint32_t raw_msg_size, + void* raw_msg) +{ + const uint32_t message_header_size = (sizeof(uint32_t) * 4); + + const uint32_t path_size = lv2_osc_string_size((char*)raw_msg); + const uint32_t types_len = strlen((char*)(raw_msg + path_size + 1)); + uint32_t index_size = types_len * sizeof(uint32_t); + + if (out_buf == NULL) { + out_buf_size = message_header_size + index_size + raw_msg_size; + out_buf = malloc((size_t)out_buf_size); + } else if (out_buf && out_buf_size < message_header_size + raw_msg_size) { + return NULL; + } + + LV2_OSC_Event* write_loc = (LV2_OSC_Event*)(out_buf); + write_loc->argument_count = types_len; + write_loc->data_size = index_size + raw_msg_size; + + // Copy raw message + memcpy(&write_loc->data + index_size, raw_msg, raw_msg_size); + + write_loc->types_offset = index_size + path_size + 1; + const char* const types = lv2_osc_get_types(write_loc); + + // Calculate/Write index + uint32_t args_base_offset = write_loc->types_offset + lv2_osc_string_size(types) - 1; + uint32_t arg_offset = 0; + + for (uint32_t i=0; i < write_loc->argument_count; ++i) { + ((uint32_t*)&write_loc->data)[i] = args_base_offset + arg_offset; + const LV2_OSC_Argument* const arg = (LV2_OSC_Argument*)(&write_loc->data + args_base_offset + arg_offset); + // Special case because size is still big-endian +#ifndef BIG_ENDIAN + if (types[i] == 'b') // special case because size is still big-endian + arg_offset += lv2_osc_swap32(*((int32_t*)arg)); + else +#endif + arg_offset += lv2_osc_arg_size(types[i], arg); + } + + /*printf("Index:\n"); + for (uint32_t i=0; i < write_loc->argument_count; ++i) { + printf("%u ", ((uint32_t*)&write_loc->data)[i]); + } + printf("\n"); + + printf("Data:\n"); + for (uint32_t i=0; i < (write_loc->argument_count * 4) + size; ++i) { + printf("%3u", i % 10); + } + printf("\n"); + for (uint32_t i=0; i < (write_loc->argument_count * 4) + size; ++i) { + char c = *(((char*)&write_loc->data) + i); + if (c >= 32 && c <= 126) + printf("%3c", c); + else + printf("%3d", (int)c); + } + printf("\n");*/ + + // Swap to host byte order if necessary +#ifndef BIG_ENDIAN + lv2_osc_message_swap_byte_order(write_loc); +#endif + + printf("Created message:\n"); + lv2_osc_message_print(write_loc); + + return write_loc; +} + + +#if 0 +/** Allocate a new LV2OSCBuffer. + * + * This function is NOT realtime safe. + */ +LV2_OSCBuffer* +lv2_osc_buffer_new(uint32_t capacity) +{ + LV2OSCBuffer* buf = (LV2OSCBuffer*)malloc((sizeof(uint32_t) * 3) + capacity); + buf->capacity = capacity; + buf->size = 0; + buf->message_count = 0; + memset(&buf->data, 0, capacity); + return buf; +} + + +void +lv2_osc_buffer_clear(LV2OSCBuffer* buf) +{ + buf->size = 0; + buf->message_count = 0; +} + +int +lv2_osc_buffer_append_message(LV2OSCBuffer* buf, LV2_OSC_Event* msg) +{ + const uint32_t msg_size = lv2_message_get_size(msg); + + if (buf->capacity - buf->size - ((buf->message_count + 1) * sizeof(uint32_t)) < msg_size) + return ENOBUFS; + + char* write_loc = &buf->data + buf->size; + + memcpy(write_loc, msg, msg_size); + + // Index is written backwards, starting at end of data + uint32_t* index_end = (uint32_t*)(&buf->data + buf->capacity - sizeof(uint32_t)); + *(index_end - buf->message_count) = buf->size; + + ++buf->message_count; + + buf->size += msg_size; + + return 0; +} + +int +lv2_osc_buffer_append(LV2OSCBuffer* buf, double time, const char* path, const char* types, ...) +{ + // FIXME: crazy unsafe + LV2_OSC_Event* write_msg = (LV2_OSC_Event*)(&buf->data + buf->size); + + write_msg->time = time; + write_msg->data_size = 0; + write_msg->argument_count = 0; + write_msg->types_offset = strlen(path) + 1; + + memcpy(&write_msg->data, path, write_msg->types_offset); + + /*fprintf(stderr, "Append message:\n"); + lv2_osc_message_print(write_msg); + fprintf(stderr, "\n");*/ + + uint32_t msg_size = lv2_message_get_size(write_msg); + buf->size += msg_size; + buf->message_count++; + + return 0; +} +#endif + |