From f29e5ba61aebdda221b201e35bb47a4a9c885735 Mon Sep 17 00:00:00 2001 From: Martin Peach Date: Wed, 16 Aug 2006 20:22:22 +0000 Subject: Added the net, osc and sqosc~ directories svn path=/trunk/externals/mrpeach/; revision=5629 --- osc/packOSC.c | 1000 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1000 insertions(+) create mode 100755 osc/packOSC.c (limited to 'osc/packOSC.c') diff --git a/osc/packOSC.c b/osc/packOSC.c new file mode 100755 index 0000000..eed5f0a --- /dev/null +++ b/osc/packOSC.c @@ -0,0 +1,1000 @@ +/* packOSC is like sendOSC but outputs a list of floats which are the bytes making up the OSC packet. */ +/* This allows for the separation of the protocol and its transport. */ +/* Started by Martin Peach 20060403 */ +/* 20060425 version independent of libOSC */ +/* packOSC.c makes extensive use of code from OSC-client.c and sendOSC.c */ +/* as well as some from OSC-timetag.c. These files have the following header: */ +/* +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. Copyright (c) 1996,97,98,99,2000,01,02,03 +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. + + +The OSC webpage is http://cnmat.cnmat.berkeley.edu/OpenSoundControl +*/ + +#define MAX_ARGS 2000 +#define SC_BUFFER_SIZE 64000 + +#include "m_pd.h" +#include +#include +#include + +#ifdef MSW +#include +#else +#include +#endif + +#ifdef unix +#include +#endif +/* This is from OSC-client.h :*/ +/* + + OSC-client.h: library for constructing OpenSoundControl messages. + Derived from SynthControl.h + Author: Matt Wright + Version 0.1: 6/13/97 + Version 0.2: 7/21/2000: Support for type-tagged messages + + + General notes: + + This library abstracts away the data format for the OpenSoundControl + protocol. Users of this library can construct OpenSoundControl packets + with a function call interface instead of knowing how to lay out the bits. + + All issues of memory allocation are deferred to the user of this library. + There are two data structures that the user must allocate. The first + is the actual buffer that the message will be written into. This buffer + can be any size, but if it's too small there's a possibility that it + will become overfull. The other data structure is called an OSCbuf, + and it holds all the state used by the library as it's constructing + a buffer. + + All procedures that have the possibility of an error condition return int, + with 0 indicating no error and nonzero indicating an error. The variable + OSC_errorMessage will be set to point to a string containing an error + message explaining what the problem is. + +*/ +/* + OSC_timeTag.h: library for manipulating OSC time tags + Matt Wright, 5/29/97 + + Time tags in OSC have the same format as in NTP: 64 bit fixed point, with the + top 32 bits giving number of seconds sinve midnight 1/1/1900 and the bottom + 32 bits giving fractional parts of a second. We represent this by an 8-byte + unsigned long if possible, or else a struct. + + NB: On many architectures with 8-byte ints, it's illegal (like maybe a bus error) + to dereference a pointer to an 8 byte int that's not 8-byte aligned. +*/ + +/* You may have to redefine this typedef if ints on your system + aren't 4 bytes. */ +typedef unsigned int uint4; +typedef struct +{ + uint4 seconds; + uint4 fraction; +} OSCTimeTag; + +/* Return the time tag 0x0000000000000001, indicating to the receiving device + that it should process the message immediately. */ +static OSCTimeTag OSCTT_Immediately(void); + +/* The int4byte type has to be a 4-byte integer. You may have to + change this to long or something else on your system. */ +typedef int int4byte; + +/* The maximum depth of bundles within bundles within bundles within... + This is the size of a static array. If you exceed this limit you'll + get an error message. */ +#define MAX_BUNDLE_NESTING 32 + +/* Don't ever manipulate the data in the OSCbuf struct directly. (It's + declared here in the header file only so your program will be able to + declare variables of type OSCbuf and have the right amount of memory + be allocated.) */ + +typedef struct OSCbuf_struct +{ + char *buffer; /* The buffer to hold the OSC packet */ + int size; /* Size of the buffer */ + char *bufptr; /* Current position as we fill the buffer */ + int state; /* State of partially-constructed message */ + int4byte *thisMsgSize; /* Pointer to count field before */ + /* currently-being-written message */ + int4byte *prevCounts[MAX_BUNDLE_NESTING]; /* Pointers to count */ + /* field before each currently open bundle */ + int bundleDepth; /* How many sub-sub-bundles are we in now? */ + char *typeStringPtr; /* This pointer advances through the type */ + /* tag string as you add arguments. */ + int gettingFirstUntypedArg; /* nonzero if this message doesn't have */ + /* a type tag and we're waiting for the 1st arg */ +} OSCbuf; + +/* Here are the possible values of the state field: */ + +#define EMPTY 0 /* Nothing written to packet yet */ +#define ONE_MSG_ARGS 1 /* Packet has a single message; gathering arguments */ +#define NEED_COUNT 2 /* Just opened a bundle; must write message name or */ + /* open another bundle */ +#define GET_ARGS 3 /* Getting arguments to a message. If we see a message */ + /* name or a bundle open/close then the current message */ + /* will end. */ +#define DONE 4 /* All open bundles have been closed, so can't write */ + /* anything else */ + + +static int OSC_strlen(char *s); +static int OSC_padString(char *dest, char *str); +static int OSC_padStringWithAnExtraStupidComma(char *dest, char *str); +static int OSC_WritePadding(char *dest, int i); +static int CheckTypeTag(OSCbuf *buf, char expectedType); + +/* Initialize the given OSCbuf. The user of this module must pass in the + block of memory that this OSCbuf will use for a buffer, and the number of + bytes in that block. (It's the user's job to allocate the memory because + you do it differently in different systems.) */ +static void OSC_initBuffer(OSCbuf *buf, int size, char *byteArray); + + +/* Reset the given OSCbuf. Do this after you send out the contents of + the buffer and want to start writing new data into it. */ +static void OSC_resetBuffer(OSCbuf *buf); + + +/* Is the buffer empty? (I.e., would it be stupid to send the buffer + contents to the synth?) */ +static int OSC_isBufferEmpty(OSCbuf *buf); + + +/* How much space is left in the buffer? */ +static int OSC_freeSpaceInBuffer(OSCbuf *buf); + +/* Does the buffer contain a valid OSC packet? (Returns nonzero if yes.) */ +static int OSC_isBufferDone(OSCbuf *buf); + +/* When you're ready to send out the buffer (i.e., when OSC_isBufferDone() + returns true), call these two procedures to get the OSC packet that's been + assembled and its size in bytes. (And then call OSC_resetBuffer() if you + want to re-use this OSCbuf for the next packet.) */ +static char *OSC_getPacket(OSCbuf *buf); +static int OSC_packetSize(OSCbuf *buf); +static int OSC_CheckOverflow(OSCbuf *buf, int bytesNeeded); + +/* Here's the basic model for building up OSC messages in an OSCbuf: + - Make sure the OSCbuf has been initialized with OSC_initBuffer(). + - To open a bundle, call OSC_openBundle(). You can then write + messages or open new bundles within the bundle you opened. + Call OSC_closeBundle() to close the bundle. Note that a packet + does not have to have a bundle; it can instead consist of just a + single message. + - For each message you want to send: + - Call OSC_writeAddress() with the name of your message. (In + addition to writing your message name into the buffer, this + procedure will also leave space for the size count of this message.) + - Alternately, call OSC_writeAddressAndTypes() with the name of + your message and with a type string listing the types of all the + arguments you will be putting in this message. + - Now write each of the arguments into the buffer, by calling one of: + OSC_writeFloatArg() + OSC_writeIntArg() + OSC_writeStringArg() + - Now your message is complete; you can send out the buffer or you can + add another message to it. +*/ + +static int OSC_openBundle(OSCbuf *buf, OSCTimeTag tt); +static int OSC_closeBundle(OSCbuf *buf); +static int OSC_writeAddress(OSCbuf *buf, char *name); +static int OSC_writeAddressAndTypes(OSCbuf *buf, char *name, char *types); +static int OSC_writeFloatArg(OSCbuf *buf, float arg); +static int OSC_writeIntArg(OSCbuf *buf, int4byte arg); +static int OSC_writeStringArg(OSCbuf *buf, char *arg); + +/* How many bytes will be needed in the OSC format to hold the given + string? The length of the string, plus the null char, plus any padding + needed for 4-byte alignment. */ +static int OSC_effectiveStringLength(char *string); + +typedef struct +{ + enum {INT_osc, FLOAT_osc, STRING_osc, NOTYPE_osc} type; + union + { + int i; + float f; + char *s; + } datum; +} typedArg; + +static int useTypeTags = 0; +static char bufferForOSCbuf[SC_BUFFER_SIZE]; +static t_atom bufferForOSClist[SC_BUFFER_SIZE]; + +static t_class *packOSC_class; + +typedef struct _packOSC +{ + t_object x_obj; + t_int x_typetags; // typetag flag + int x_bundle; // bundle open flag + OSCbuf x_oscbuf[1]; // OSCbuffer + t_atom *x_osclist; + t_outlet *x_bdpthout; // bundle-depth floatoutlet + t_outlet *x_listout; // OSC packet list ouput +} t_packOSC; + +static void *packOSC_new(t_floatarg udpflag); +static void packOSC_openbundle(t_packOSC *x); +static void packOSC_closebundle(t_packOSC *x); +static void packOSC_settypetags(t_packOSC *x, t_float *f); +static void packOSC_sendtyped(t_packOSC *x, t_symbol *s, int argc, t_atom *argv); +static void packOSC_send(t_packOSC *x, t_symbol *s, int argc, t_atom *argv); +static void packOSC_free(t_packOSC *x); +#ifdef MSW +__declspec(dllexport) +#endif +void packOSC_setup(void); +static typedArg packOSC_parseatom(t_atom *a); +static int packOSC_writemessage(OSCbuf *buf, char *messageName, int numArgs, typedArg *args); +static void packOSC_sendbuffer(t_packOSC *x); + +static void *packOSC_new(t_floatarg udpflag) +{ + t_packOSC *x = (t_packOSC *)pd_new(packOSC_class); + // set typetags to 1 by default + x->x_typetags = 1; + // bundle is closed + x->x_bundle = 0; + OSC_initBuffer(x->x_oscbuf, SC_BUFFER_SIZE, bufferForOSCbuf); + x->x_osclist = bufferForOSClist; + x->x_listout = outlet_new(&x->x_obj, &s_list); + x->x_bdpthout = outlet_new(&x->x_obj, &s_float); + return (x); +} + + +static void packOSC_openbundle(t_packOSC *x) +{ + if (x->x_oscbuf->bundleDepth + 1 >= MAX_BUNDLE_NESTING || + OSC_openBundle(x->x_oscbuf, OSCTT_Immediately())) + { + post("packOSC: Problem opening bundle."); + return; + } + x->x_bundle = 1; + outlet_float(x->x_bdpthout, (float)x->x_oscbuf->bundleDepth); +} + +static void packOSC_closebundle(t_packOSC *x) +{ + if (OSC_closeBundle(x->x_oscbuf)) + { + post("packOSC: Problem closing bundle."); + return; + } + outlet_float(x->x_bdpthout, (float)x->x_oscbuf->bundleDepth); + // in bundle mode we send when bundle is closed? + if(!OSC_isBufferEmpty(x->x_oscbuf) > 0 && OSC_isBufferDone(x->x_oscbuf)) + { + packOSC_sendbuffer(x); + OSC_initBuffer(x->x_oscbuf, SC_BUFFER_SIZE, bufferForOSCbuf); + x->x_bundle = 0; + return; + } +} + +static void packOSC_settypetags(t_packOSC *x, t_float *f) +{ + x->x_typetags = (f != 0)?1:0; + post("packOSC: setting typetags %d",x->x_typetags); +} + + +////////////////////////////////////////////////////////////////////// +// this is the real and only sending routine now, for both typed and +// undtyped mode. + +static void packOSC_sendtyped(t_packOSC *x, t_symbol *s, int argc, t_atom *argv) +{ + char messageName[MAXPDSTRING]; + typedArg args[MAX_ARGS]; + int i; + + messageName[0] = '\0'; // empty + + if(argc>MAX_ARGS) + { + post ("packOSC: too many arguments! (max: %d)", MAX_ARGS); + return; + } + +#ifdef DEBUG + post ("packOSC: type tags? %d", useTypeTags); +#endif + + atom_string(&argv[0], messageName, MAXPDSTRING); + for (i = 0; i < argc-1; i++) + { + args[i] = packOSC_parseatom(&argv[i+1]); +#ifdef DEBUG + switch (args[i].type) + { + case INT_osc: + post("packOSC: cell-cont: %d\n", args[i].datum.i); + break; + case FLOAT_osc: + post("packOSC: cell-cont: %f\n", args[i].datum.f); + break; + case STRING_osc: + post("packOSC: cell-cont: %s\n", args[i].datum.s); + break; + case NOTYPE_osc: + post("packOSC: unknown type\n"); + break; + } + post("packOSC: type-id: %d\n", args[i].type); +#endif + } + + if(packOSC_writemessage(x->x_oscbuf, messageName, i, args)) + { + post("packOSC: usage error, write-msg failed."); + return; + } + + if(!x->x_bundle) + { + packOSC_sendbuffer(x); + OSC_initBuffer(x->x_oscbuf, SC_BUFFER_SIZE, bufferForOSCbuf); + } +} + +static void packOSC_send(t_packOSC *x, t_symbol *s, int argc, t_atom *argv) +{ + if(!argc) + { + post("packOSC: not sending empty message."); + return; + } + if(x->x_typetags) + { + useTypeTags = 1; + packOSC_sendtyped(x, s, argc, argv); + useTypeTags = 0; + } + else + { + packOSC_sendtyped(x, s, argc, argv); + } +} + +static void packOSC_free(t_packOSC *x) +{ +} + +#ifdef MSW +__declspec(dllexport) +#endif +void packOSC_setup(void) +{ + packOSC_class = class_new(gensym("packOSC"), (t_newmethod)packOSC_new, + (t_method)packOSC_free, + sizeof(t_packOSC), 0, A_DEFFLOAT, 0); + class_addmethod(packOSC_class, (t_method)packOSC_settypetags, + gensym("typetags"), A_FLOAT, 0); + class_addmethod(packOSC_class, (t_method)packOSC_send, + gensym("send"), A_GIMME, 0); + class_addmethod(packOSC_class, (t_method)packOSC_send, + gensym("senduntyped"), A_GIMME, 0); + class_addmethod(packOSC_class, (t_method)packOSC_send, + gensym("sendtyped"), A_GIMME, 0); + class_addmethod(packOSC_class, (t_method)packOSC_openbundle, + gensym("["), 0, 0); + class_addmethod(packOSC_class, (t_method)packOSC_closebundle, + gensym("]"), 0, 0); +} + +static typedArg packOSC_parseatom(t_atom *a) +{ + typedArg returnVal; + t_float f; + t_int i; + t_symbol s; + char buf[MAXPDSTRING]; + + atom_string(a, buf, MAXPDSTRING); +#ifdef DEBUG + post("packOSC: atom type %d (%s)", a->a_type, buf); +#endif + /* It might be an int, a float, or a string */ + switch (a->a_type) + { + case A_FLOAT: + f = atom_getfloat(a); + i = atom_getint(a); + if (f == (t_float)i) + { // assume that if the int and float are the same, it's an int + returnVal.type = INT_osc; + returnVal.datum.i = i; + } + else + { + returnVal.type = FLOAT_osc; + returnVal.datum.f = f; + } + return returnVal; + case A_SYMBOL: + s = *atom_getsymbol(a); + returnVal.type = STRING_osc; + returnVal.datum.s = s.s_name; + return returnVal; + default: + atom_string(a, buf, MAXPDSTRING); + error("packOSC: atom type %d not implemented (%s)", a->a_type, buf); + returnVal.type = NOTYPE_osc; + returnVal.datum.s = NULL; + return returnVal; + } +} + +static int packOSC_writemessage(OSCbuf *buf, char *messageName, int numArgs, typedArg *args) +{ + int j, returnVal; + + returnVal = 0; +#ifdef DEBUG + post("packOSC: packOSC_writemessage: %s ", messageName); + + for (j = 0; j < numArgs; j++) + { + switch (args[j].type) + { + case INT_osc: + post("packOSC: %d ", args[j].datum.i); + break; + case FLOAT_osc: + post("packOSC: %f ", args[j].datum.f); + break; + case STRING_osc: + post("packOSC: %s ", args[j].datum.s); + break; + default: + post("packOSC: Unrecognized arg type %d", args[j].type); + break; + } + } +#endif + + if (!useTypeTags) + { + returnVal = OSC_writeAddress(buf, messageName); + if (returnVal) + { + post("packOSC: Problem writing address."); + } + } + else + { + /* First figure out the type tags */ + char typeTags[MAX_ARGS+2]; + + typeTags[0] = ','; + + for (j = 0; j < numArgs; ++j) + { + switch (args[j].type) + { + case INT_osc: + typeTags[j+1] = 'i'; + break; + case FLOAT_osc: + typeTags[j+1] = 'f'; + break; + case STRING_osc: + typeTags[j+1] = 's'; + break; + default: + post("packOSC: arg %d type is unrecognized(%d)", j, args[j].type); + break; + } + } + typeTags[j+1] = '\0'; + returnVal = OSC_writeAddressAndTypes(buf, messageName, typeTags); + if (returnVal) + { + post("packOSC: Problem writing address."); + } + } + + for (j = 0; j < numArgs; j++) + { + switch (args[j].type) + { + case INT_osc: + if ((returnVal = OSC_writeIntArg(buf, args[j].datum.i)) != 0) + { + return returnVal; + } + break; + case FLOAT_osc: + if ((returnVal = OSC_writeFloatArg(buf, args[j].datum.f)) != 0) + { + return returnVal; + } + break; + case STRING_osc: + if ((returnVal = OSC_writeStringArg(buf, args[j].datum.s)) != 0) + { + return returnVal; + } + break; + default: + break; // just skip bad types (which we won't get anyway unless this code is buggy) + } + } + return returnVal; +} + +static void packOSC_sendbuffer(t_packOSC *x) +{ + int i; + int length; + char *buf; +#ifdef DEBUG + post("packOSC_sendbuffer: Sending buffer...\n"); +#endif + if (OSC_isBufferEmpty(x->x_oscbuf)) + { + post("packOSC_sendbuffer() called but buffer empty"); + return; + } + if (!OSC_isBufferDone(x->x_oscbuf)) + { + post("packOSC_sendbuffer() called but buffer not ready!, not exiting"); + return; //{{raf}} + } + length = OSC_packetSize(x->x_oscbuf); + buf = OSC_getPacket(x->x_oscbuf); +#ifdef DEBUG + post ("packOSC_sendbuffer: length: %lu", length); +#endif + /* convert the bytes in the buffer to floats in a list */ + for (i = 0; i < length; ++i) SETFLOAT(&x->x_osclist[i], buf[i]); + /* send the list out the outlet */ + outlet_list(x->x_listout, &s_list, length, x->x_osclist); +} + +/* The next part is copied and morphed from OSC-client.c. */ +/* + Author: Matt Wright + Version 2.2: Calls htonl in the right places 20000620 + Version 2.3: Gets typed messages right. + */ + +/* + pd + ------------- + + raf@interaccess.com: + rev. for Win32 build (verified under Win-2ooo) 11-April-2002 + + -- changed licence part (20040820) jdl + -- Version 2.4 changes not in here (20040820) jdl + +*/ + + +static void OSC_initBuffer(OSCbuf *buf, int size, char *byteArray) +{ + buf->buffer = byteArray; + buf->size = size; + OSC_resetBuffer(buf); +} + +static void OSC_resetBuffer(OSCbuf *buf) +{ + buf->bufptr = buf->buffer; + buf->state = EMPTY; + buf->bundleDepth = 0; + buf->prevCounts[0] = 0; + buf->gettingFirstUntypedArg = 0; + buf->typeStringPtr = 0; +} + +static int OSC_isBufferEmpty(OSCbuf *buf) +{ + return buf->bufptr == buf->buffer; +} + +static int OSC_freeSpaceInBuffer(OSCbuf *buf) +{ + return buf->size - (buf->bufptr - buf->buffer); +} + +static int OSC_isBufferDone(OSCbuf *buf) +{ + return (buf->state == DONE || buf->state == ONE_MSG_ARGS); +} + +static char *OSC_getPacket(OSCbuf *buf) +{ + return buf->buffer; +} + +static int OSC_packetSize(OSCbuf *buf) +{ + return (buf->bufptr - buf->buffer); +} + +static int OSC_CheckOverflow(OSCbuf *buf, int bytesNeeded) +{ + if ((bytesNeeded) > OSC_freeSpaceInBuffer(buf)) + { + post("packOSC: buffer overflow"); + return 1; + } + return 0; +} + +static void PatchMessageSize(OSCbuf *buf) +{ + int4byte size = buf->bufptr - ((char *) buf->thisMsgSize) - 4; + *(buf->thisMsgSize) = htonl(size); +} + +static int OSC_openBundle(OSCbuf *buf, OSCTimeTag tt) +{ + if (buf->state == ONE_MSG_ARGS) + { + post("packOSC: Can't open a bundle in a one-message packet"); + return 3; + } + + if (buf->state == DONE) + { + post("packOSC: This packet is finished; can't open a new bundle"); + return 4; + } + + if (++(buf->bundleDepth) >= MAX_BUNDLE_NESTING) + { + post("packOSC: Bundles nested too deeply; change MAX_BUNDLE_NESTING"); + return 2; + } + + if (CheckTypeTag(buf, '\0')) return 9; + + if (buf->state == GET_ARGS) + { + PatchMessageSize(buf); + } + + if (buf->state == EMPTY) + { + /* Need 16 bytes for "#bundle" and time tag */ + if(OSC_CheckOverflow(buf, 16)) return 1; + } + else + { + /* This bundle is inside another bundle, so we need to leave + a blank size count for the size of this current bundle. */ + if(OSC_CheckOverflow(buf, 20))return 1; + *((int4byte *)buf->bufptr) = 0xaaaaaaaa; + buf->prevCounts[buf->bundleDepth] = (int4byte *)buf->bufptr; + + buf->bufptr += 4; + } + + buf->bufptr += OSC_padString(buf->bufptr, "#bundle"); + + + *((OSCTimeTag *) buf->bufptr) = tt; + + if (htonl(1) != 1) + { + /* Byte swap the 8-byte integer time tag */ + int4byte *intp = (int4byte *)buf->bufptr; + intp[0] = htonl(intp[0]); + intp[1] = htonl(intp[1]); + + /* tt is a struct of two 32-bit words, and even though + each word was wrong-endian, they were in the right order + in the struct.) */ + } + + buf->bufptr += sizeof(OSCTimeTag); + + buf->state = NEED_COUNT; + + buf->gettingFirstUntypedArg = 0; + buf->typeStringPtr = 0; + return 0; +} + + +static int OSC_closeBundle(OSCbuf *buf) +{ + if (buf->bundleDepth == 0) + { + /* This handles EMPTY, ONE_MSG, ARGS, and DONE */ + post("packOSC: Can't close bundle; no bundle is open!"); + return 5; + } + + if (CheckTypeTag(buf, '\0')) return 9; + + if (buf->state == GET_ARGS) + { + PatchMessageSize(buf); + } + + if (buf->bundleDepth == 1) + { + /* Closing the last bundle: No bundle size to patch */ + buf->state = DONE; + } + else + { + /* Closing a sub-bundle: patch bundle size */ + int size = buf->bufptr - ((char *) buf->prevCounts[buf->bundleDepth]) - 4; + *(buf->prevCounts[buf->bundleDepth]) = htonl(size); + buf->state = NEED_COUNT; + } + + --buf->bundleDepth; + buf->gettingFirstUntypedArg = 0; + buf->typeStringPtr = 0; + return 0; +} + +static int OSC_writeAddress(OSCbuf *buf, char *name) +{ + int4byte paddedLength; + + if (buf->state == ONE_MSG_ARGS) + { + post("packOSC: This packet is not a bundle, so you can't write another address"); + return 7; + } + + if (buf->state == DONE) + { + post("packOSC: This packet is finished; can't write another address"); + return 8; + } + + if (CheckTypeTag(buf, '\0')) return 9; + + paddedLength = OSC_effectiveStringLength(name); + + if (buf->state == EMPTY) + { + /* This will be a one-message packet, so no sizes to worry about */ + if(OSC_CheckOverflow(buf, paddedLength))return 1; + buf->state = ONE_MSG_ARGS; + } + else + { + /* GET_ARGS or NEED_COUNT */ + if(OSC_CheckOverflow(buf, 4+paddedLength))return 1; + if (buf->state == GET_ARGS) + { + /* Close the old message */ + PatchMessageSize(buf); + } + buf->thisMsgSize = (int4byte *)buf->bufptr; + *(buf->thisMsgSize) = 0xbbbbbbbb; + buf->bufptr += 4; + buf->state = GET_ARGS; + } + + /* Now write the name */ + buf->bufptr += OSC_padString(buf->bufptr, name); + buf->typeStringPtr = 0; + buf->gettingFirstUntypedArg = 1; + + return 0; +} + +static int OSC_writeAddressAndTypes(OSCbuf *buf, char *name, char *types) +{ + int result; + int4byte paddedLength; + + if (CheckTypeTag(buf, '\0')) return 9; + + result = OSC_writeAddress(buf, name); + + if (result) return result; + + paddedLength = OSC_effectiveStringLength(types); + + if(OSC_CheckOverflow(buf, paddedLength))return 1; + + buf->typeStringPtr = buf->bufptr + 1; /* skip comma */ + buf->bufptr += OSC_padString(buf->bufptr, types); + + buf->gettingFirstUntypedArg = 0; + return 0; +} + +static int CheckTypeTag(OSCbuf *buf, char expectedType) +{ + if (buf->typeStringPtr) + { + if (*(buf->typeStringPtr) != expectedType) + { + if (expectedType == '\0') + { + post("packOSC: According to the type tag I expected more arguments."); + } + else if (*(buf->typeStringPtr) == '\0') + { + post("packOSC: According to the type tag I didn't expect any more arguments."); + } + else + { + post("packOSC: According to the type tag I expected an argument of a different type."); + post("* Expected %c, string now %s\n", expectedType, buf->typeStringPtr); + } + return 9; + } + ++(buf->typeStringPtr); + } + return 0; +} + +static int OSC_writeFloatArg(OSCbuf *buf, float arg) +{ + int4byte *intp; + + if(OSC_CheckOverflow(buf, 4))return 1; + + if (CheckTypeTag(buf, 'f')) return 9; + + /* Pretend arg is a long int so we can use htonl() */ + intp = ((int4byte *) &arg); + *((int4byte *) buf->bufptr) = htonl(*intp); + + buf->bufptr += 4; + + buf->gettingFirstUntypedArg = 0; + return 0; +} + +static int OSC_writeIntArg(OSCbuf *buf, int4byte arg) +{ + if(OSC_CheckOverflow(buf, 4))return 1; + if (CheckTypeTag(buf, 'i')) return 9; + + *((int4byte *) buf->bufptr) = htonl(arg); + buf->bufptr += 4; + + buf->gettingFirstUntypedArg = 0; + return 0; +} + +static int OSC_writeStringArg(OSCbuf *buf, char *arg) +{ + int len; + + if (CheckTypeTag(buf, 's')) return 9; + + len = OSC_effectiveStringLength(arg); + + if (buf->gettingFirstUntypedArg && arg[0] == ',') + { + /* This un-type-tagged message starts with a string + that starts with a comma, so we have to escape it + (with a double comma) so it won't look like a type + tag string. */ + + if(OSC_CheckOverflow(buf, len+4))return 1; /* Too conservative */ + buf->bufptr += + OSC_padStringWithAnExtraStupidComma(buf->bufptr, arg); + + } + else + { + if(OSC_CheckOverflow(buf, len))return 1; + buf->bufptr += OSC_padString(buf->bufptr, arg); + } + + buf->gettingFirstUntypedArg = 0; + return 0; + +} + +/* String utilities */ + +static int OSC_strlen(char *s) +{ + int i; + for (i = 0; s[i] != '\0'; i++) /* Do nothing */ ; + return i; +} + +#define STRING_ALIGN_PAD 4 +static int OSC_effectiveStringLength(char *string) +{ + int len = OSC_strlen(string) + 1; /* We need space for the null char. */ + + /* Round up len to next multiple of STRING_ALIGN_PAD to account for alignment padding */ + if ((len % STRING_ALIGN_PAD) != 0) + { + len += STRING_ALIGN_PAD - (len % STRING_ALIGN_PAD); + } + return len; +} + +static int OSC_padString(char *dest, char *str) +{ + int i; + + for (i = 0; str[i] != '\0'; i++) dest[i] = str[i]; + + return OSC_WritePadding(dest, i); +} + +static int OSC_padStringWithAnExtraStupidComma(char *dest, char *str) +{ + int i; + + dest[0] = ','; + for (i = 0; str[i] != '\0'; i++) dest[i+1] = str[i]; + + return OSC_WritePadding(dest, i+1); +} + +static int OSC_WritePadding(char *dest, int i) +{ + dest[i] = '\0'; + i++; + + for (; (i % STRING_ALIGN_PAD) != 0; i++) dest[i] = '\0'; + + return i; +} + +/* The next bit is modified from OSC-timetag.c. */ +/* + + OSC_timeTag.c: library for manipulating OSC time tags + Matt Wright, 5/29/97 + + Version 0.2 (9/11/98): cleaned up so no explicit type names in the .c file. + +*/ + +static OSCTimeTag OSCTT_Immediately(void) +{ + OSCTimeTag tt; + tt.fraction = 1; + tt.seconds = 0; + return tt; +} +/* end packOSC.c*/ -- cgit v1.2.1