gravitacija-perspektive/lib/OSC/OSCBundle.cpp

358 lines
11 KiB
C++

/*
Written by Yotam Mann, The Center for New Music and Audio Technologies,
University of California, Berkeley. Copyright (c) 2012, The Regents of
the University of California (Regents).
Permission to use, copy, modify, distribute, and distribute modified versions
of this software and its documentation without fee and without a signed
licensing agreement, is hereby granted, provided that the above copyright
notice, this paragraph and the following two paragraphs appear in all copies,
modifications, and distributions.
IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
For bug reports and feature requests please email me at yotam@cnmat.berkeley.edu
*/
#include "OSCBundle.h"
#include <stdlib.h>
/*=============================================================================
CONSTRUCTORS / DESTRUCTOR
=============================================================================*/
OSCBundle::OSCBundle(osctime_t _timetag){
setTimetag(_timetag);
numMessages = 0;
error = OSC_OK;
messages = NULL;
incomingBuffer = NULL;
incomingBufferSize = 0;
decodeState = STANDBY;
}
OSCBundle::~OSCBundle(){
for (int i = 0; i < numMessages; i++){
OSCMessage * msg = getOSCMessage(i);
delete msg;
}
free(messages);
free(incomingBuffer);
}
//clears all of the OSCMessages inside
OSCBundle& OSCBundle::empty(){
error = OSC_OK;
for (int i = 0; i < numMessages; i++){
OSCMessage * msg = getOSCMessage(i);
delete msg;
}
free(messages);
messages = NULL;
clearIncomingBuffer();
numMessages = 0;
return *this;
}
/*=============================================================================
SETTERS
=============================================================================*/
OSCMessage & OSCBundle::add(const char * _address){
OSCMessage * msg = new OSCMessage(_address);
if (!msg->hasError()){
//realloc the array to fit the message
OSCMessage ** messageMem = (OSCMessage **) realloc(messages, sizeof(OSCMessage *) * (numMessages + 1));
if (messageMem != NULL){
messages = messageMem;
messages[numMessages] = msg;
numMessages++;
} else {
error = ALLOCFAILED;
}
}
return *msg;
}
OSCMessage & OSCBundle::add(){
OSCMessage * msg = new OSCMessage();
//realloc the array to fit the message
OSCMessage ** messageMem = (OSCMessage **) realloc(messages, sizeof(OSCMessage *) * (numMessages + 1));
if (messageMem != NULL){
messages = messageMem;
messages[numMessages] = msg;
numMessages++;
} else {
error = ALLOCFAILED;
}
return *msg;
}
OSCMessage & OSCBundle::add(OSCMessage & _msg){
OSCMessage * msg = new OSCMessage(&_msg);
if (!msg->hasError()){
//realloc the array to fit the message
OSCMessage ** messageMem = (OSCMessage **) realloc(messages, sizeof(OSCMessage *) * (numMessages + 1));
if (messageMem != NULL){
messages = messageMem;
messages[numMessages] = msg;
numMessages++;
} else {
error = ALLOCFAILED;
}
}
return *msg;
}
/*=============================================================================
GETTERS
=============================================================================*/
//returns the first fullMatch.
OSCMessage * OSCBundle::getOSCMessage( char * addr){
for (int i = 0; i < numMessages; i++){
OSCMessage * msg = getOSCMessage(i);
if (msg->fullMatch(addr)){
return msg;
}
}
return NULL;
}
//the position is the same as the order they were declared in
OSCMessage * OSCBundle::getOSCMessage(int pos){
if (pos < numMessages){
return messages[pos];
}
return NULL;
}
/*=============================================================================
PATTERN MATCHING
=============================================================================*/
bool OSCBundle::dispatch(const char * pattern, void (*callback)(OSCMessage&), int initial_offset){
bool called = false;
for (int i = 0; i < numMessages; i++){
OSCMessage msg = getOSCMessage(i);
called = msg.dispatch(pattern, callback, initial_offset) || called ;
}
return called;
}
bool OSCBundle::route(const char * pattern, void (*callback)(OSCMessage&, int), int initial_offset){
bool called = false;
for (int i = 0; i < numMessages; i++){
OSCMessage msg = getOSCMessage(i);
called = msg.route(pattern, callback, initial_offset) || called;
}
return called;
}
/*=============================================================================
SIZE
=============================================================================*/
int OSCBundle::size(){
return numMessages;
}
/*=============================================================================
ERROR HANDLING
=============================================================================*/
bool OSCBundle::hasError(){
bool retError = error != OSC_OK;
//test each of the data
for (int i = 0; i < numMessages; i++){
OSCMessage * msg = getOSCMessage(i);
retError |= msg->hasError();
}
return retError;
}
OSCErrorCode OSCBundle::getError(){
return error;
}
/*=============================================================================
SENDING
=============================================================================*/
OSCBundle& OSCBundle::send(Print &p){
//don't send a bundle with errors
if (hasError()){
return *this;
}
//write the bundle header
static uint8_t header[] = {'#', 'b', 'u', 'n', 'd', 'l', 'e', 0};
p.write(header, 8);
//write the timetag
{
osctime_t time = timetag;
uint32_t d = BigEndian(time.seconds);
uint8_t * ptr = (uint8_t *) &d;
p.write(ptr, 4);
d = BigEndian(time.fractionofseconds);
ptr = (uint8_t *) &d;
p.write(ptr, 4);
}
//send the messages
for (int i = 0; i < numMessages; i++){
OSCMessage * msg = getOSCMessage(i);
int msgSize = msg->bytes();
//turn the message size into a pointer
uint32_t s32 = BigEndian((uint32_t) msgSize);
uint8_t * sptr = (uint8_t *) &s32;
//write the message size
p.write(sptr, 4);
msg->send(p);
}
return *this;
}
/*=============================================================================
FILLING
=============================================================================*/
OSCBundle& OSCBundle::fill(uint8_t incomingByte){
decode(incomingByte);
return *this;
}
OSCBundle& OSCBundle::fill(const uint8_t * incomingBytes, int length){
while (length--){
decode(*incomingBytes++);
}
return *this;
}
/*=============================================================================
DECODING
=============================================================================*/
void OSCBundle::decodeTimetag(){
//parse the incoming buffer as a uint64
setTimetag(incomingBuffer);
//make sure the endianness is right
//xxx time tag timetag = BigEndian(timetag);
decodeState = MESSAGE_SIZE;
clearIncomingBuffer();
}
void OSCBundle::decodeHeader(){
const char * header = "#bundle";
if (strcmp(header, (char *) incomingBuffer)!=0){
//otherwise go back to the top and wait for a new bundle header
decodeState = STANDBY;
error = INVALID_OSC;
} else {
decodeState = TIMETAG;
}
clearIncomingBuffer();
}
void OSCBundle::decodeMessage(uint8_t incomingByte){
//get the current message
if (numMessages > 0){
OSCMessage * lastMessage = messages[numMessages - 1];
//put the bytes in there
lastMessage->fill(incomingByte);
//if it's all done
if (incomingBufferSize == incomingMessageSize){
//move onto the next message
decodeState = MESSAGE_SIZE;
clearIncomingBuffer();
} else if (incomingBufferSize > incomingMessageSize){
error = INVALID_OSC;
}
}
}
//does not validate the incoming OSC for correctness
void OSCBundle::decode(uint8_t incomingByte){
addToIncomingBuffer(incomingByte);
switch (decodeState){
case STANDBY:
if (incomingByte == '#'){
decodeState = HEADER;
} else if (incomingByte == '/'){
add();//add a simple message to the bundle
decodeMessage(incomingByte);
decodeState = MESSAGE;
}
break;
case HEADER:
if (incomingBufferSize == 8){
decodeHeader();
decodeState = TIMETAG;
}
break;
case TIMETAG:
if (incomingBufferSize == 8){
decodeTimetag();
decodeState = MESSAGE_SIZE;
}
break;
case MESSAGE_SIZE:
if (incomingBufferSize == 4){
//make sure the message size is valid
int32_t msgSize;
memcpy(&msgSize, incomingBuffer, 4);
msgSize = BigEndian(msgSize);
if (msgSize % 4 != 0 || msgSize == 0){
error = INVALID_OSC;
} else {
//add a message to the buffer
decodeState = MESSAGE;
incomingMessageSize = msgSize;
clearIncomingBuffer();
//add a new empty message
add();
}
}
break;
case MESSAGE:
decodeMessage(incomingByte);
break;
}
}
/*=============================================================================
INCOMING BUFFER MANAGEMENT
=============================================================================*/
void OSCBundle::addToIncomingBuffer(uint8_t incomingByte){
//realloc some space for the new byte and stick it on the end
incomingBuffer = (uint8_t *) realloc ( incomingBuffer, incomingBufferSize + 1);
if (incomingBuffer != NULL){
incomingBuffer[incomingBufferSize++] = incomingByte;
} else {
error = ALLOCFAILED;
}
}
void OSCBundle::clearIncomingBuffer(){
incomingBufferSize = 0;
free(incomingBuffer);
incomingBuffer = NULL;
}