/* 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 . */ #include #include #include #include #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': case 'S': // Symbol (URI-mapped integer) return 4; case 'h': case 'd': return 8; 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; }