/* 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 */ #ifndef OSCMESSAGE_h #define OSCMESSAGE_h #include "OSCData.h" #include class OSCMessage { private: //friends friend class OSCBundle; /*============================================================================= PRIVATE VARIABLES =============================================================================*/ //the address char * address; //the data OSCData ** data; //the number of OSCData in the data array int dataCount; //error codes for potential runtime problems OSCErrorCode error; /*============================================================================= DECODING INCOMING BYTES =============================================================================*/ //the decoding states for incoming bytes enum DecodeState { STANDBY, ADDRESS, ADDRESS_PADDING, TYPES, TYPES_PADDING, DATA, DATA_PADDING, DONE, } decodeState; //stores incoming bytes until they can be decoded uint8_t * incomingBuffer; int incomingBufferSize; // how many bytes are stored int incomingBufferFree; // how many bytes are allocated but unused //adds a byte to the buffer void addToIncomingBuffer(uint8_t); //clears the incoming buffer void clearIncomingBuffer(); //decoding function void decode(uint8_t); void decodeAddress(); void decodeType(uint8_t); void decodeData(uint8_t); /*============================================================================= HELPER FUNCTIONS =============================================================================*/ void setupMessage(); //compares the OSCData's type char to a test char bool testType(int position, char type); //returns the number of bytes to pad to make it 4-bit aligned // int padSize(int bytes); public: //returns the OSCData at that position OSCData * getOSCData(int); /*============================================================================= CONSTRUCTORS / DESTRUCTOR =============================================================================*/ //new constructor needs an address OSCMessage (const char * _address); //no address //placeholder since it's invalid OSC OSCMessage(); //can optionally accept all of the data after the address //OSCMessage(const char * _address, char * types, ... ); //created from another OSCMessage OSCMessage (OSCMessage *); //DESTRUCTOR ~OSCMessage(); //empties all of the data OSCMessage& empty(); /*============================================================================= SETTING DATA =============================================================================*/ //returns the OSCMessage so that multiple 'add's can be strung together template OSCMessage& add(T datum){ //make a piece of data OSCData * d = new OSCData(datum); //check if it has any errors if (d->error == ALLOCFAILED){ error = ALLOCFAILED; } else { //resize the data array OSCData ** dataMem = (OSCData **) realloc(data, sizeof(OSCData *) * (dataCount + 1)); if (dataMem == NULL){ error = ALLOCFAILED; } else { data = dataMem; //add data to the end of the array data[dataCount] = d; //increment the data size dataCount++; } } return *this; } //blob specific add OSCMessage& add(uint8_t * blob, int length){ //make a piece of data OSCData * d = new OSCData(blob, length); //check if it has any errors if (d->error == ALLOCFAILED){ error = ALLOCFAILED; } else { //resize the data array OSCData ** dataMem = (OSCData **) realloc(data, sizeof(OSCData *) * (dataCount + 1)); if (dataMem == NULL){ error = ALLOCFAILED; } else { data = dataMem; //add data to the end of the array data[dataCount] = d; //increment the data size dataCount++; } } return *this; } //sets the data at a position template OSCMessage& set(int position, T datum){ if (position < dataCount){ //replace the OSCData with a new one OSCData * oldDatum = getOSCData(position); //destroy the old one delete oldDatum; //make a new one OSCData * newDatum = new OSCData(datum); //test if there was an error if (newDatum->error == ALLOCFAILED){ error = ALLOCFAILED; } else { //otherwise, put it in the data array data[position] = newDatum; } } else if (position == (dataCount)){ //add the data to the end add(datum); } else { //else out of bounds error error = INDEX_OUT_OF_BOUNDS; } return *this; } //blob specific setter OSCMessage& set(int position, uint8_t * blob, int length){ if (position < dataCount){ //replace the OSCData with a new one OSCData * oldDatum = getOSCData(position); //destroy the old one delete oldDatum; //make a new one OSCData * newDatum = new OSCData(blob, length); //test if there was an error if (newDatum->error == ALLOCFAILED){ error = ALLOCFAILED; } else { //otherwise, put it in the data array data[position] = newDatum; } } else if (position == (dataCount)){ //add the data to the end add(blob, length); } else { //else out of bounds error error = INDEX_OUT_OF_BOUNDS; } return *this; } OSCMessage& setAddress(const char *); /*============================================================================= GETTING DATA getters take a position as an argument =============================================================================*/ int32_t getInt(int); osctime_t getTime(int); float getFloat(int); double getDouble(int); bool getBoolean(int); //return the copied string's length int getString(int, char *); //check that it won't overflow the passed buffer's size with a third argument int getString(int, char *, int); //offset and size can be defined in order to only query a part of the string int getString(int, char *, int, int, int); //returns the number of unsigned int8's copied into the buffer int getBlob(int, uint8_t *); //check that it won't overflow the passed buffer's size with a third argument int getBlob(int, uint8_t *, int); //offset and size can be defined in order to only query a part of the blob's content int getBlob(int, uint8_t *, int, int, int); //get pointer to blob const uint8_t* getBlob(int); // returns the length of blob uint32_t getBlobLength(int position); //returns the number of bytes of the data at that position int getDataLength(int); //returns the type at the position char getType(int); //put the address in the buffer int getAddress(char * buffer, int offset = 0); int getAddress(char * buffer, int offset, int len); const char* getAddress(); // Find out address length so we can create a buffer int getAddressLength(int offset = 0); /*============================================================================= TESTING DATA testers take a position as an argument =============================================================================*/ bool isInt(int); bool isFloat(int); bool isBlob(int); bool isChar(int); bool isString(int); bool isDouble(int); bool isBoolean(int); bool isTime(int); /*============================================================================= PATTERN MATCHING =============================================================================*/ //match the pattern against the address //returns true only for a complete match bool fullMatch( const char * pattern, int = 0); //returns the number of characters matched in the address int match( const char * pattern, int = 0); //calls the function with the message as the arg if it was a full match bool dispatch(const char * pattern, void (*callback)(OSCMessage &), int = 0); //like dispatch, but allows for partial matches //the address match offset is sent as an argument to the callback //also room for an option address offset to allow for multiple nested routes bool route(const char * pattern, void (*callback)(OSCMessage &, int), int = 0); /*============================================================================= SIZE =============================================================================*/ //the number of data that the message contains int size(); //computes the number of bytes the OSCMessage occupies if everything is 32-bit aligned int bytes(); /*============================================================================= TRANSMISSION =============================================================================*/ //send the message OSCMessage& send(Print &p); //fill the message from a byte stream OSCMessage& fill(uint8_t); OSCMessage& fill(uint8_t *, int); /*============================================================================= ERROR =============================================================================*/ bool hasError(); OSCErrorCode getError(); }; #endif