modded version CNMAT/OSC https://github.com/CNMAT/OSC
Fork of osc-cnmat by
Diff: OSCBundle.cpp
- Revision:
- 3:f58c63b78853
- Child:
- 4:107c23eb31b6
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OSCBundle.cpp Sat May 17 12:42:29 2014 +0000 @@ -0,0 +1,340 @@ +/* + 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(uint64_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 +void 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; +} + +/*============================================================================= + SETTERS + =============================================================================*/ + +OSCMessage & OSCBundle::add(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; + } + } +} + +//the position is the same as the order they were declared in +OSCMessage * OSCBundle::getOSCMessage(int pos){ + if (pos < numMessages){ + return messages[pos]; + } +} + +/*============================================================================= + 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); + } + 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); + } + 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 + =============================================================================*/ + +/*void OSCBundle::send(Print &p){ + //don't send a bundle with errors + if (hasError()){ + return; + } + //write the bundle header + static uint8_t header[] = {'#', 'b', 'u', 'n', 'd', 'l', 'e', 0}; + p.write(header, 8); + //write the timetag + uint64_t t64 = BigEndian(timetag); + uint8_t * tptr = (uint8_t *) &t64; + p.write(tptr, 8); + //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 messsage size + p.write(sptr, 4); + msg->send(p); + } +}*/ + +/*============================================================================= + FILLING + =============================================================================*/ + +void OSCBundle::fill(uint8_t incomingByte){ + decode(incomingByte); +} + +void OSCBundle::fill(uint8_t * incomingBytes, int length){ + while (length--){ + decode(*incomingBytes++); + } +} + +/*============================================================================= + DECODING + =============================================================================*/ + +void OSCBundle::decodeTimetag(){ + //parse the incoming buffer as a uint64 + setTimetag(incomingBuffer); + //make sure the endianness is right + 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 == '/'){ + 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; +} +