/* 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;
}