You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
357 lines
11 KiB
357 lines
11 KiB
/* |
|
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; |
|
} |
|
|
|
|
|
|
|
|