From 189c0787586602185fea7dcbc3ef37665b75ba45 Mon Sep 17 00:00:00 2001 From: jdl Date: Tue, 6 Aug 2002 12:13:57 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r75, which included commits to RCS files with non-trunk default branches. svn path=/trunk/externals/OSCx/; revision=76 --- OSC/OSC-client.h | 182 +++++++++ OSC/OSC-common.h | 62 +++ OSC/OSC-pattern-match.c | 192 +++++++++ OSC/OSC-pattern-match.h | 35 ++ OSC/OSC-timetag.h | 93 +++++ OSC/OSC.001 | 147 +++++++ OSC/OSC.c | 79 ++++ OSC/OSC.dsp | 148 +++++++ OSC/OSC.dsw | 29 ++ OSC/TODO.txt | 20 + OSC/VERSION | 1 + OSC/dumpOSC.c | 889 ++++++++++++++++++++++++++++++++++++++++++ OSC/htmsocket.c | 297 ++++++++++++++ OSC/htmsocket.h | 49 +++ OSC/makefile | 69 ++++ OSC/makefile.in | 60 +++ OSC/routeOSC.c | 417 ++++++++++++++++++++ OSC/sendOSC.c | 875 +++++++++++++++++++++++++++++++++++++++++ README.txt | 81 ++++ doc/OSC-help.pd | 10 + doc/OSCroute-help.pd | 26 ++ doc/dumpOSC-help.pd | 7 + doc/sendOSC-help.pd | 57 +++ extra/OSC.dll | Bin 0 -> 65536 bytes libOSC/LIBOSC.001 | 94 +++++ libOSC/LIBOSC.002 | 100 +++++ libOSC/LIBOSC.DSP | 100 +++++ libOSC/LIBOSC.DSW | 29 ++ libOSC/LIBOSC.PLG | 29 ++ libOSC/Makefile | 21 + libOSC/OSC-client.c | 473 ++++++++++++++++++++++ libOSC/OSC-client.c.pre-htonl | 303 ++++++++++++++ libOSC/OSC-client.h | 181 +++++++++ libOSC/OSC-timetag.c | 175 +++++++++ libOSC/OSC-timetag.h | 93 +++++ libOSC/test_OSC.c | 149 +++++++ libOSC/test_OSC_timeTag.c | 36 ++ send+dump/Makefile | 25 ++ send+dump/dumpOSC.c | 716 ++++++++++++++++++++++++++++++++++ send+dump/dumpUDP.c | 191 +++++++++ send+dump/htmsocket.c | 230 +++++++++++ send+dump/htmsocket.h | 49 +++ send+dump/sendOSC.c | 600 ++++++++++++++++++++++++++++ 43 files changed, 7419 insertions(+) create mode 100644 OSC/OSC-client.h create mode 100644 OSC/OSC-common.h create mode 100644 OSC/OSC-pattern-match.c create mode 100644 OSC/OSC-pattern-match.h create mode 100644 OSC/OSC-timetag.h create mode 100644 OSC/OSC.001 create mode 100644 OSC/OSC.c create mode 100644 OSC/OSC.dsp create mode 100644 OSC/OSC.dsw create mode 100644 OSC/TODO.txt create mode 100644 OSC/VERSION create mode 100644 OSC/dumpOSC.c create mode 100644 OSC/htmsocket.c create mode 100644 OSC/htmsocket.h create mode 100644 OSC/makefile create mode 100644 OSC/makefile.in create mode 100644 OSC/routeOSC.c create mode 100644 OSC/sendOSC.c create mode 100644 README.txt create mode 100644 doc/OSC-help.pd create mode 100644 doc/OSCroute-help.pd create mode 100644 doc/dumpOSC-help.pd create mode 100644 doc/sendOSC-help.pd create mode 100644 extra/OSC.dll create mode 100644 libOSC/LIBOSC.001 create mode 100644 libOSC/LIBOSC.002 create mode 100644 libOSC/LIBOSC.DSP create mode 100644 libOSC/LIBOSC.DSW create mode 100644 libOSC/LIBOSC.PLG create mode 100644 libOSC/Makefile create mode 100644 libOSC/OSC-client.c create mode 100644 libOSC/OSC-client.c.pre-htonl create mode 100644 libOSC/OSC-client.h create mode 100644 libOSC/OSC-timetag.c create mode 100644 libOSC/OSC-timetag.h create mode 100644 libOSC/test_OSC.c create mode 100644 libOSC/test_OSC_timeTag.c create mode 100644 send+dump/Makefile create mode 100644 send+dump/dumpOSC.c create mode 100644 send+dump/dumpUDP.c create mode 100644 send+dump/htmsocket.c create mode 100644 send+dump/htmsocket.h create mode 100644 send+dump/sendOSC.c diff --git a/OSC/OSC-client.h b/OSC/OSC-client.h new file mode 100644 index 0000000..165808b --- /dev/null +++ b/OSC/OSC-client.h @@ -0,0 +1,182 @@ +/* +Copyright (c) 1996,1997. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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. +*/ + +/* + + 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. + +*/ + + +#include "OSC-timetag.h" + +/* The int4byte type has to be a 4-byte integer. You may have to + change this to long or something else on your system. */ +#ifdef __MWERKS__ + /* In Metrowerks you can set ints to be 2 or 4 bytes on 68K, but long is + always 4 bytes */ + typedef long int4byte; +#else + typedef int int4byte; +#endif + +/* 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; + + + +/* 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.) */ +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. */ +void OSC_resetBuffer(OSCbuf *buf); + + +/* Is the buffer empty? (I.e., would it be stupid to send the buffer + contents to the synth?) */ +int OSC_isBufferEmpty(OSCbuf *buf); + + +/* How much space is left in the buffer? */ +int OSC_freeSpaceInBuffer(OSCbuf *buf); + +/* Does the buffer contain a valid OSC packet? (Returns nonzero if yes.) */ +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.) */ +char *OSC_getPacket(OSCbuf *buf); +int OSC_packetSize(OSCbuf *buf); + + + +/* 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_writeFloatArgs() + OSC_writeIntArg() + OSC_writeStringArg() + + - Now your message is complete; you can send out the buffer or you can + add another message to it. +*/ + +int OSC_openBundle(OSCbuf *buf, OSCTimeTag tt); +int OSC_closeBundle(OSCbuf *buf); +int OSC_closeAllBundles(OSCbuf *buf); + +int OSC_writeAddress(OSCbuf *buf, char *name); +int OSC_writeAddressAndTypes(OSCbuf *buf, char *name, char *types); +int OSC_writeFloatArg(OSCbuf *buf, float arg); +int OSC_writeFloatArgs(OSCbuf *buf, int numFloats, float *args); +int OSC_writeIntArg(OSCbuf *buf, int4byte arg); +int OSC_writeStringArg(OSCbuf *buf, char *arg); + +//extern +char *OSC_errorMessage; + +/* 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. */ +int OSC_effectiveStringLength(char *string); diff --git a/OSC/OSC-common.h b/OSC/OSC-common.h new file mode 100644 index 0000000..8eb6576 --- /dev/null +++ b/OSC/OSC-common.h @@ -0,0 +1,62 @@ +/* +Copyright (c) 1998. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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 OpenSound Control WWW page is + http://www.cnmat.berkeley.edu/OpenSoundControl +*/ + + +/* OSC-common.h + Simple stuff to #include everywhere in the OSC package + + by Matt Wright, 3/13/98 +*/ + +/* Boolean type */ + +#ifndef TRUE +typedef int Boolean; +#define TRUE 1 +#define FALSE 0 +#endif + + +/* Fixed byte width types */ +typedef int int4; /* 4 byte int */ + +/* Printing type procedures. All take printf-style format string */ + +/* Catastrophic failure: print message and halt system */ +void fatal_error(char *s, ...); + +/* Error message for user */ +void OSCProblem(char *s, ...); + +/* Warning for user */ +void OSCWarning(char *s, ...); + + diff --git a/OSC/OSC-pattern-match.c b/OSC/OSC-pattern-match.c new file mode 100644 index 0000000..01515f8 --- /dev/null +++ b/OSC/OSC-pattern-match.c @@ -0,0 +1,192 @@ +/* +Copyright (c) 1998. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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 OpenSound Control WWW page is + http://www.cnmat.berkeley.edu/OpenSoundControl +*/ + + +/* + OSC-pattern-match.c + Matt Wright, 3/16/98 + Adapted from oscpattern.c, by Matt Wright and Amar Chaudhury + */ + +#include "OSC-common.h" +#include "OSC-pattern-match.h" + +static const char *theWholePattern; /* Just for warning messages */ + +static Boolean MatchBrackets (const char *pattern, const char *test); +static Boolean MatchList (const char *pattern, const char *test); + +Boolean PatternMatch (const char * pattern, const char * test) { + + theWholePattern = pattern; + + if (pattern == 0 || pattern[0] == 0) { + return test[0] == 0; + } + + if (test[0] == 0) { + if (pattern[0] == '*') + return PatternMatch (pattern+1,test); + else + return FALSE; + } + + switch (pattern[0]) { + case 0 : return test[0] == 0; + case '?' : return PatternMatch (pattern + 1, test + 1); + case '*' : + if (PatternMatch (pattern+1, test)) { + return TRUE; + } else { + return PatternMatch (pattern, test+1); + } + case ']' : + case '}' : + printf("Spurious %c in pattern \".../%s/...\"",pattern[0], theWholePattern); + return FALSE; + case '[' : + return MatchBrackets (pattern,test); + case '{' : + return MatchList (pattern,test); + case '\\' : + if (pattern[1] == 0) { + return test[0] == 0; + } else if (pattern[1] == test[0]) { + return PatternMatch (pattern+2,test+1); + } else { + return FALSE; + } + default : + if (pattern[0] == test[0]) { + return PatternMatch (pattern+1,test+1); + } else { + return FALSE; + } + } +} + + +/* we know that pattern[0] == '[' and test[0] != 0 */ + +static Boolean MatchBrackets (const char *pattern, const char *test) { + Boolean result; + Boolean negated = FALSE; + const char *p = pattern; + + if (pattern[1] == 0) { + printf("Unterminated [ in pattern \".../%s/...\"", theWholePattern); + return FALSE; + } + + if (pattern[1] == '!') { + negated = TRUE; + p++; + } + + while (*p != ']') { + if (*p == 0) { + printf("Unterminated [ in pattern \".../%s/...\"", theWholePattern); + return FALSE; + } + if (p[1] == '-' && p[2] != 0) { + if (test[0] >= p[0] && test[0] <= p[2]) { + result = !negated; + goto advance; + } + } + if (p[0] == test[0]) { + result = !negated; + goto advance; + } + p++; + } + + result = negated; + +advance: + + if (!result) + return FALSE; + + while (*p != ']') { + if (*p == 0) { + printf("Unterminated [ in pattern \".../%s/...\"", theWholePattern); + return FALSE; + } + p++; + } + + return PatternMatch (p+1,test+1); +} + +static Boolean MatchList (const char *pattern, const char *test) { + + const char *restOfPattern, *tp = test; + + + for(restOfPattern = pattern; *restOfPattern != '}'; restOfPattern++) { + if (*restOfPattern == 0) { + printf("Unterminated { in pattern \".../%s/...\"", theWholePattern); + return FALSE; + } + } + + restOfPattern++; /* skip close curly brace */ + + + pattern++; /* skip open curly brace */ + + while (1) { + + if (*pattern == ',') { + if (PatternMatch (restOfPattern, tp)) { + return TRUE; + } else { + tp = test; + ++pattern; + } + } else if (*pattern == '}') { + return PatternMatch (restOfPattern, tp); + } else if (*pattern == *tp) { + ++pattern; + ++tp; + } else { + tp = test; + while (*pattern != ',' && *pattern != '}') { + pattern++; + } + if (*pattern == ',') { + pattern++; + } + } + } + +} diff --git a/OSC/OSC-pattern-match.h b/OSC/OSC-pattern-match.h new file mode 100644 index 0000000..a5d7306 --- /dev/null +++ b/OSC/OSC-pattern-match.h @@ -0,0 +1,35 @@ +/* +Copyright (c) 1998. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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 OpenSound Control WWW page is http://www.cnmat.berkeley.edu/OpenSoundControl + + OSC-pattern-match.h +*/ + +//Boolean PatternMatch (const char *pattern, const char *test); +Boolean PatternMatch (const char *pattern, const char *test); + diff --git a/OSC/OSC-timetag.h b/OSC/OSC-timetag.h new file mode 100644 index 0000000..3ce693a --- /dev/null +++ b/OSC/OSC-timetag.h @@ -0,0 +1,93 @@ +/* +Copyright (c) 1998. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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 OpenSound Control WWW page is + http://www.cnmat.berkeley.edu/OpenSoundControl +*/ + +/* + 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 a 64-bit + unsigned long if possible, or else a struct. + + NB: On many architectures with 64-bit ints, it's illegal (like maybe a bus error) + to dereference a pointer to a 64-bit int that's not 64-bit aligned. +*/ + +#ifndef OSC_TIMETAG +#define OSC_TIMETAG + +#ifdef __sgi + #define HAS8BYTEINT + /* You may have to change this typedef if there's some other + way to specify 64 bit ints on your system */ + typedef long long int64; + typedef unsigned long long uint64; + typedef unsigned long uint32; +#else + /* You may have to redefine this typedef if ints on your system + aren't 32 bits. */ + typedef unsigned int uint32; +#endif + + +#ifdef HAS8BYTEINT + typedef uint64 OSCTimeTag; +#else + typedef struct { + uint32 seconds; + uint32 fraction; + } OSCTimeTag; +#endif + + + +/* Return a time tag representing the current time (as of when this + procedure is called). */ +OSCTimeTag OSCTT_CurrentTime(void); + +/* Return the time tag 0x0000000000000001, indicating to the receiving device + that it should process the message immediately. */ +OSCTimeTag OSCTT_Immediately(void); + +/* Return the time tag 0xffffffffffffffff, a time so far in the future that + it's effectively infinity. */ +OSCTimeTag OSCTT_BiggestPossibleTimeTag(void); + +/* Given a time tag and a number of seconds to add to the time tag, return + the new time tag */ +OSCTimeTag OSCTT_PlusSeconds(OSCTimeTag original, float secondsOffset); + +/* Compare two time tags. Return negative if first is < second, 0 if + they're equal, and positive if first > second. */ +int OSCTT_Compare(OSCTimeTag left, OSCTimeTag right); + +#endif /* OSC_TIMETAG */ diff --git a/OSC/OSC.001 b/OSC/OSC.001 new file mode 100644 index 0000000..b9dd0d4 --- /dev/null +++ b/OSC/OSC.001 @@ -0,0 +1,147 @@ +# Microsoft Developer Studio Project File - Name="OSC" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=OSC - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "OSC.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "OSC.mak" CFG="OSC - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "OSC - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "OSC - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "OSC - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RAFOSC_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../../pd/src" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OSC_EXPORTS" /D "NT" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 pd.lib libc.lib kernel32.lib wsock32.lib LIBOSC.lib /nologo /dll /machine:I386 /nodefaultlib /out:"../../../pd/extra/OSC.dll" /libpath:"../../../pd/lib" /libpath:"../../../pd/bin" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "OSC - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /GX /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RAFOSC_EXPORTS" /YX /FD /ZI /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OSC_EXPORTS" /D "NT" /YX /FD /ZI /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 pd.lib libc.lib kernel32.lib wsock32.lib LIBOSC.lib /nologo /dll /debug /machine:I386 /nodefaultlib /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "OSC - Win32 Release" +# Name "OSC - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\dumpOSC.c +# End Source File +# Begin Source File + +SOURCE=.\htmsocket.c +# End Source File +# Begin Source File + +SOURCE=".\OSC-pattern-match.c" +# End Source File +# Begin Source File + +SOURCE=.\OSC.c +# End Source File +# Begin Source File + +SOURCE=.\routeOSC.c +# End Source File +# Begin Source File + +SOURCE=.\sendOSC.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\htmsocket.h +# End Source File +# Begin Source File + +SOURCE=".\OSC-client.h" +# End Source File +# Begin Source File + +SOURCE=".\OSC-common.h" +# End Source File +# Begin Source File + +SOURCE=".\OSC-pattern-match.h" +# End Source File +# Begin Source File + +SOURCE=".\OSC-timetag.h" +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/OSC/OSC.c b/OSC/OSC.c new file mode 100644 index 0000000..10e719f --- /dev/null +++ b/OSC/OSC.c @@ -0,0 +1,79 @@ +/* + + pd + ------------- + -- tweaks for Win32 www.zeggz.com/raf 13-April-2002 + + */ + + +#ifndef VERSION + #ifdef WIN32 + #define VERSION "0.01-w32" + #else + #define VERSION "0.01" + #endif +#endif + +#ifndef __DATE__ +#define __DATE__ "without using a gnu compiler" +#endif + +#include + + +typedef struct _OSC +{ + t_object x_obj; +} t_OSC; + +static t_class* OSC_class; + +#ifdef WIN32 + #include "OSC-common.h" + OSC_API void OSC_setup(void); + OSC_API void OSC_version(t_OSC*); + OSC_API void sendOSC_setup(void); + OSC_API void dumpOSC_setup(void); + OSC_API void OSCroute_setup(void); +#else + void OSC_version(); + void sendOSC_setup(); + void dumpOSC_setup(); + void OSCroute_setup(); +#endif + +static void* OSC_new(t_symbol* s) { + t_OSC *x = (t_OSC *)pd_new(OSC_class); + return (x); +} + +#ifdef WIN32 + OSC_API void OSC_version (t_OSC *x) { +#else + void OSC_version (t_OSC *x) { +#endif + // EnterCallback(); + post("OSC4PD Version " VERSION + "\n ¯\\ original code by matt wright. pd-fication jdl@xdv.org\n" + " · Win32-port raf@interaccess.com\n \\_ Compiled " __TIME__ " " __DATE__); + // ExitCallback(); +} + +#ifdef WIN32 + OSC_API void OSC_setup(void) { +#else + void OSC_setup(void) { +#endif + OSC_class = class_new(gensym("OSC"), (t_newmethod)OSC_new, 0, + sizeof(t_OSC), 0,0); + class_addmethod(OSC_class, (t_method)OSC_version, gensym("version"), A_NULL, 0, 0); + + sendOSC_setup(); + dumpOSC_setup(); + OSCroute_setup(); + + post("O : Open Sound Control 4 PD, http://www.cnmat.berkeley.edu/OSC"); + post(" S : original code by matt wright, pd hakcs cxc, Win32-port raf@interaccess.com"); + post(" C: ver: "VERSION ", compiled: "__DATE__); +} diff --git a/OSC/OSC.dsp b/OSC/OSC.dsp new file mode 100644 index 0000000..8f0116d --- /dev/null +++ b/OSC/OSC.dsp @@ -0,0 +1,148 @@ +# Microsoft Developer Studio Project File - Name="OSC" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=OSC - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "OSC.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "OSC.mak" CFG="OSC - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "OSC - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "OSC - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "OSC - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RAFOSC_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I "../../../pd/src" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OSC_EXPORTS" /D "NT" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 pd.lib libc.lib kernel32.lib wsock32.lib LIBOSC.lib /nologo /dll /machine:I386 /nodefaultlib /out:"../../../pd/extra/OSC.dll" /libpath:"../../../pd/lib" /libpath:"../../../pd/bin" +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "OSC - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "RAFOSC_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /GX /ZI /Od /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "OSC_EXPORTS" /D "NT" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 pd.lib libc.lib kernel32.lib wsock32.lib LIBOSC.lib /nologo /dll /debug /machine:I386 /nodefaultlib /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "OSC - Win32 Release" +# Name "OSC - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=.\dumpOSC.c +# End Source File +# Begin Source File + +SOURCE=.\htmsocket.c +# End Source File +# Begin Source File + +SOURCE=".\OSC-pattern-match.c" +# End Source File +# Begin Source File + +SOURCE=.\OSC.c +# End Source File +# Begin Source File + +SOURCE=.\routeOSC.c +# End Source File +# Begin Source File + +SOURCE=.\sendOSC.c +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\htmsocket.h +# End Source File +# Begin Source File + +SOURCE=".\OSC-client.h" +# End Source File +# Begin Source File + +SOURCE=".\OSC-common.h" +# End Source File +# Begin Source File + +SOURCE=".\OSC-pattern-match.h" +# End Source File +# Begin Source File + +SOURCE=".\OSC-timetag.h" +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/OSC/OSC.dsw b/OSC/OSC.dsw new file mode 100644 index 0000000..293b7f7 --- /dev/null +++ b/OSC/OSC.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "OSC"=.\OSC.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/OSC/TODO.txt b/OSC/TODO.txt new file mode 100644 index 0000000..23f1855 --- /dev/null +++ b/OSC/TODO.txt @@ -0,0 +1,20 @@ + +for win32: port also the command line utilities (send, dump) + +-pd object hierarchy extract and automatic address construction + a la [/hostname]/pd/patchname/subpatch/test ? + + +changelog: + +20020305: -typetags in send and receive + sendOSC by default now send typetagged msgs + and dumOSC properly reads and outputs them. + +prior: + + -added OSCroute with source adapt from max object. + -fixed shared htmsock bug + -added sendtyped separately earlier and lost it again + + \ No newline at end of file diff --git a/OSC/VERSION b/OSC/VERSION new file mode 100644 index 0000000..689f5a7 --- /dev/null +++ b/OSC/VERSION @@ -0,0 +1 @@ +0.16-3 diff --git a/OSC/dumpOSC.c b/OSC/dumpOSC.c new file mode 100644 index 0000000..76dea42 --- /dev/null +++ b/OSC/dumpOSC.c @@ -0,0 +1,889 @@ +/* +Copyright (c) 1992,1993,1994,1995,1996,1997,2000. +The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright and Adrian Freed, The Center for New Music and Audio +Technologies, University of California, Berkeley. + + 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. +*/ + + /* + + dumpOSC.c + server that displays OpenSoundControl messages sent to it + for debugging client udp and UNIX protocol + + by Matt Wright, 6/3/97 + modified from dumpSC.c, by Matt Wright and Adrian Freed + + version 0.2: Added "-silent" option a.k.a. "-quiet" + + version 0.3: Incorporated patches from Nicola Bernardini to make + things Linux-friendly. Also added ntohl() in the right places + to support little-endian architectures. + + + + compile: + cc -o dumpOSC dumpOSC.c + + to-do: + + More robustness in saying exactly what's wrong with ill-formed + messages. (If they don't make sense, show exactly what was + received.) + + Time-based features: print time-received for each packet + + Clean up to separate OSC parsing code from socket/select stuff + + pd + ------------- + -- tweaks for Win32 www.zeggz.com/raf 13-April-2002 + +*/ + + +#include "m_imp.h" +//#include "m_pd.h" +//#include "x_osc.h" + +#if defined(__sgi) || defined(__linux) || defined(WIN32) + +#ifdef WIN32 + #include "OSC-common.h" + #include + #include + #include + #include + #include + #include + #include + #include +#else + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #ifdef NEED_SCHEDCTL_AND_LOCK + #include + #include + #endif +#endif + + +char *htm_error_string; +typedef int Boolean; +typedef void *OBJ; + +typedef struct ClientAddressStruct { + struct sockaddr_in cl_addr; + int clilen; + int sockfd; +} *ClientAddr; + +Boolean ShowBytes = FALSE; +Boolean Silent = FALSE; + +/* Declarations */ +#ifndef WIN32 +static int unixinitudp(int chan); +#endif + +static int initudp(int chan); +static void closeudp(int sockfd); +Boolean ClientReply(int packetsize, void *packet, int socketfd, + void *clientaddresspointer, int clientaddressbufferlength); +void sgi_CleanExit(void); +Boolean sgi_HaveToQuit(void); +int RegisterPollingDevice(int fd, void (*callbackfunction)(int , void *), void *dummy); +static void catch_sigint(); +static int Synthmessage(char *m, int n, void *clientdesc, int clientdesclength, int fd) ; +char *DataAfterAlignedString(char *string, char *boundary) ; +Boolean IsNiceString(char *string, char *boundary) ; +void complain(char *s, ...); + +#define MAXMESG 32768 +static char mbuf[MAXMESG]; + +/* ----------------------------- dumpOSC ------------------------- */ + +#define MAXOUTAT 50 + +static t_class *dumpOSC_class; + +typedef struct _dumpOSC +{ + t_object x_obj; + t_outlet *x_msgout; + t_outlet *x_connectout; + t_atom x_outat[MAXOUTAT]; + int x_outatc; + t_binbuf *x_b; + int x_connectsocket; + int x_nconnections; + int x_udp; + struct sockaddr_in x_server; + int x_clilen; +} t_dumpOSC; + +void dumpOSC_ParsePacket(t_dumpOSC *x, char *buf, int n, ClientAddr returnAddr); +static void dumpOSC_Smessage(t_dumpOSC *x, char *address, void *v, int n, ClientAddr returnAddr); +static void dumpOSC_PrintTypeTaggedArgs(t_dumpOSC *x, void *v, int n); +static void dumpOSC_PrintHeuristicallyTypeGuessedArgs(t_dumpOSC *x, void *v, int n, int skipComma); + +static void dumpOSC_read(t_dumpOSC *x, int sockfd) { + int clilen = x->x_clilen; + int n; + struct ClientAddressStruct ras; + ClientAddr ra = &ras; + + //catchupflag= FALSE; + +/* if (ShowBytes) { */ +/* int i; */ +/* printf("%d byte message:\n", n); */ +/* for (i = 0; i < n; ++i) { */ +/* printf(" %x (%c)\t", m[i], m[i]); */ +/* if (i%4 == 3) printf("\n"); */ +/* } */ +/* printf("\n"); */ +/* } */ + + // return catchupflag; + //struct sockaddr_in x->x_server; + //while( (n = recvfrom(sockfd, mbuf, MAXMESG, 0, &cl_addr, &clilen)) >0) + // while(( + #ifdef WIN32 + if ((n = recvfrom(sockfd, mbuf, MAXMESG, 0, (SOCKADDR*)&x->x_server, &clilen)) >0) + #else + if ((n = recvfrom(sockfd, mbuf, MAXMESG, 0, &x->x_server, &clilen)) >0) + #endif + { + //int r; + ras.cl_addr = *((struct sockaddr_in *) &x->x_server); + ras.clilen = x->x_clilen; + ras.sockfd = x->x_connectsocket; + + #ifdef DEBUG + printf("dumpOSC_read: received UDP packet of length %d\n", n); + #endif + dumpOSC_ParsePacket(x, mbuf, n, ra); + + //r = Synthmessage(mbuf, n, &x->x_server, clilen, sockfd); + //post ("%d", r); + //outlet_anything(x->x_msgout, at[msg].a_w.w_symbol, + // emsg-msg-1, at + msg + 1); + // outlet_list(x->x_msgout, 0, n, mbuf); + //if( sgi_HaveToQuit()) goto out; + //if(r>0) goto back; + //clilen = maxclilen; + } +} + +static void *dumpOSC_new(t_symbol *compatflag, + t_floatarg fportno) { + t_dumpOSC *x; + struct sockaddr_in server; + int clilen=sizeof(server); + int sockfd; + int portno=fportno; + int udp = 1; + + //x->x_b = binbuf_new(); + //x->x_outat = binbuf_getvec(x->x_b); + + //{{raf}} pointer not valid yet...moving this down + //x->x_outatc = 0; {{raf}} + + /* create a socket */ + sockfd = socket(AF_INET, (udp ? SOCK_DGRAM : SOCK_STREAM), 0); + + if (sockfd < 0) + { + sys_sockerror("socket"); + return (0); + } + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + /* assign server port number */ + server.sin_port = htons((u_short)portno); + /* name the socket */ + if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) + { + sys_sockerror("bind"); + sys_closesocket(sockfd); + return (0); + } + + x = (t_dumpOSC *)pd_new(dumpOSC_class); + x->x_outatc = 0; // {{raf}} now pointer is valid (less invalid) + + x->x_msgout = outlet_new(&x->x_obj, &s_anything); + + if (udp) /* datagram protocol */ + { + +/* t_socketreceiver *y = socketreceiver_new((void *)x, */ +/* 0, */ +/* (x->x_msgout ? dumpOSC_doit : 0)); */ + + // sys_addpollfn(sockfd, (t_fdpollfn)socketreceiver_read_x, y); + + sys_addpollfn(sockfd, (t_fdpollfn)dumpOSC_read, x); + x->x_connectout = 0; + } + // else /* streaming protocol */ + /* { */ + /* if (listen(sockfd, 5) < 0) */ + /* { */ + /* sys_sockerror("listen"); */ + /* sys_closesocket(sockfd); */ + /* sockfd = -1; */ + /* } */ + /* else */ + /* { */ + /* sys_addpollfn(sockfd, (t_fdpollfn)dumpOSC_connectpoll, x); */ + /* x->x_connectout = outlet_new(&x->x_obj, &s_float); */ + /* } */ + /* } */ + + x->x_connectsocket = sockfd; + x->x_server = server; + x->x_clilen = clilen; + x->x_nconnections = 0; + x->x_udp = udp; + + return (x); +} + +static void dumpOSC_free(t_dumpOSC *x) +{ + /* LATER make me clean up open connections */ + if (x->x_connectsocket >= 0) + { + sys_rmpollfn(x->x_connectsocket); + sys_closesocket(x->x_connectsocket); + } +} + +#ifdef WIN32 +OSC_API void dumpOSC_setup(void) +#else +void dumpOSC_setup(void) +#endif +{ + dumpOSC_class = class_new(gensym("dumpOSC"), + (t_newmethod)dumpOSC_new, (t_method)dumpOSC_free, + sizeof(t_dumpOSC), CLASS_NOINLET, A_DEFFLOAT, A_DEFFLOAT, + A_DEFSYM, 0); + class_sethelpsymbol(dumpOSC_class, gensym("OSC/dumpOSC-help.pd")); +} + + +#ifndef WIN32 + #define UNIXDG_PATH "/tmp/htm" + #define UNIXDG_TMP "/tmp/htm.XXXXXX" + static int unixinitudp(int chan) + { + struct sockaddr_un serv_addr; + int sockfd; + + if((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) + return sockfd; + + bzero((char *)&serv_addr, sizeof(serv_addr)); + serv_addr.sun_family = AF_UNIX; + strcpy(serv_addr.sun_path, UNIXDG_PATH); + sprintf(serv_addr.sun_path+strlen(serv_addr.sun_path), "%d", chan); + unlink(serv_addr.sun_path); + if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr.sun_family)+strlen(serv_addr.sun_path)) < 0) + { + perror("unable to bind\n"); + return -1; + } + + fcntl(sockfd, F_SETFL, FNDELAY); + return sockfd; + } +#endif // #ifndef WIN32 + + + +static int initudp(int chan) +{ + +#ifdef WIN32 + struct sockaddr_in serv_addr; + unsigned int sockfd; + ULONG nonBlocking = (ULONG) TRUE; + + if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) != INVALID_SOCKET ) { + ZeroMemory((char *)&serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + serv_addr.sin_port = htons(chan); + if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) >= 0) { + // set for non-blocking mode + if(ioctlsocket(sockfd, FIONBIO, &nonBlocking) == SOCKET_ERROR) { + perror("unable to set non-blocking\n"); + return -1; + } + } + else { perror("unable to bind\n"); return -1; } + } + return (sockfd == INVALID_SOCKET ? -1 : (int)sockfd); +#else + struct sockaddr_in serv_addr; + int sockfd; + + if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + return sockfd; + + bzero((char *)&serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + serv_addr.sin_port = htons(chan); + + if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) + { + perror("unable to bind\n"); + return -1; + } + + fcntl(sockfd, F_SETFL, FNDELAY); + return sockfd; +#endif +} + + + + + + + + +static void closeudp(int sockfd) { + #ifdef WIN32 + closesocket(sockfd); + #else + close(sockfd); + #endif +} + +static Boolean catchupflag=FALSE; +Boolean ClientReply(int packetsize, void *packet, int socketfd, + void *clientaddresspointer, int clientaddressbufferlength) +{ + if(!clientaddresspointer) return FALSE; + catchupflag= TRUE; + return packetsize==sendto(socketfd, packet, packetsize, 0, clientaddresspointer, clientaddressbufferlength); +} + +static Boolean exitflag= FALSE; +void sgi_CleanExit(void) { + exitflag = TRUE; +} + +Boolean sgi_HaveToQuit(void) { + return exitflag; +} + + +/* file descriptor poll table */ +static int npolldevs =0; +typedef struct polldev +{ + int fd; + void (*callbackfunction)(int , void *); + void *dummy; +} polldev; +#define TABMAX 8 +static polldev polldevs[TABMAX]; + + +/* Register a device (referred to by a file descriptor that the caller + should have already successfully obtained from a system call) to be + polled as real-time constraints allowed. + + When a select(2) call indicates activity on the file descriptor, the + callback function is called with the file descripter as first + argument and the given dummy argument (presumably a pointer to the + instance variables associated with the device). +*/ +int RegisterPollingDevice(int fd, void (*callbackfunction)(int , void *), void *dummy) +{ + if(npolldevscl_addr.sin_addr.s_addr; + printf("Client address %p:\n", CA); + printf(" clilen %d, sockfd %d\n", CA->clilen, CA->sockfd); + printf(" sin_family %d, sin_port %d\n", CA->cl_addr.sin_family, + CA->cl_addr.sin_port); + printf(" address: (%x) %s\n", addr, inet_ntoa(CA->cl_addr.sin_addr)); + + printf(" sin_zero = \"%c%c%c%c%c%c%c%c\"\n", + CA->cl_addr.sin_zero[0], + CA->cl_addr.sin_zero[1], + CA->cl_addr.sin_zero[2], + CA->cl_addr.sin_zero[3], + CA->cl_addr.sin_zero[4], + CA->cl_addr.sin_zero[5], + CA->cl_addr.sin_zero[6], + CA->cl_addr.sin_zero[7]); + + printf("\n"); +} + + +void dumpOSC_ParsePacket(t_dumpOSC *x, char *buf, int n, ClientAddr returnAddr) { + // t_dumpOSC *x; + int size, messageLen, i; + char *messageName; + char *args; + + //#ifdef PRINTADDRS + #ifdef DEBUG + //PrintClientAddr(returnAddr); + #endif + + + if ((n%4) != 0) { + complain("SynthControl packet size (%d) not a multiple of 4 bytes: dropping", n); + return; + } + + if ((n >= 8) && (strncmp(buf, "#bundle", 8) == 0)) { + /* This is a bundle message. */ + #ifdef DEBUG + printf("dumpOSC_ParsePacket: bundle msg: bundles not yet supported\n"); + #endif + + if (n < 16) { + complain("Bundle message too small (%d bytes) for time tag", n); + return; + } + + /* Print the time tag */ + #ifdef DEBUG + printf("[ %lx%08lx\n", ntohl(*((unsigned long *)(buf+8))), ntohl(*((unsigned long *)(buf+12)))); + #endif + + /* Note: if we wanted to actually use the time tag as a little-endian + 64-bit int, we'd have to word-swap the two 32-bit halves of it */ + + i = 16; /* Skip "#group\0" and time tag */ + + while(i n) { + complain("Bad size count %d in bundle (only %d bytes left in entire bundle)", + size, n-i-4); + return; + } + + /* Recursively handle element of bundle */ + dumpOSC_ParsePacket(x, buf+i+4, size, returnAddr); + i += 4 + size; + } + + if (i != n) { + complain("This can't happen"); + } + #ifdef DEBUG + printf("]\n"); + #endif + + } else { + /* This is not a bundle message */ + + messageName = buf; + args = DataAfterAlignedString(messageName, buf+n); + if (args == 0) { + complain("Bad message name string: %s\nDropping entire message.\n", + htm_error_string); + return; + } + messageLen = args-messageName; + dumpOSC_Smessage(x, messageName, (void *)args, n-messageLen, returnAddr); + } +} + +#define SMALLEST_POSITIVE_FLOAT 0.000001f + +static void dumpOSC_Smessage(t_dumpOSC *x, char *address, void *v, int n, ClientAddr returnAddr) { + char *chars = v; + t_atom at; + //t_atom myargv[50]; + + int myargc = x->x_outatc; + t_atom* mya = x->x_outat; + int myi; + +#ifdef DEBUG + printf("%s ", address); +#endif + + // ztoln+cvt from envgen.c, ggee-0.18 .. + // outlet_anything's 'symbol' gets set to address + // so we dont need to append address to the atomlist + /* + SETSYMBOL(mya,gensym(address));myargc++; + x->x_outatc = myargc; + */ + + if (n != 0) { + if (chars[0] == ',') { + if (chars[1] != ',') { + /* This message begins with a type-tag string */ + dumpOSC_PrintTypeTaggedArgs(x, v, n); + } else { + /* Double comma means an escaped real comma, not a type string */ + dumpOSC_PrintHeuristicallyTypeGuessedArgs(x, v, n, 1); + } + } else { + dumpOSC_PrintHeuristicallyTypeGuessedArgs(x, v, n, 0); + } + } + + outlet_anything(x->x_msgout,gensym(address),x->x_outatc,(t_atom*)&x->x_outat); + x->x_outatc = 0; +#ifdef DEBUG + printf("\n"); +#endif + fflush(stdout); /* Added for Sami 5/21/98 */ +} + +static void dumpOSC_PrintTypeTaggedArgs(t_dumpOSC *x, void *v, int n) { + char *typeTags, *thisType; + char *p; + + int myargc = x->x_outatc; + t_atom* mya = x->x_outat; + int myi; + + typeTags = v; + + if (!IsNiceString(typeTags, typeTags+n)) { + /* No null-termination, so maybe it wasn't a type tag + string after all */ + dumpOSC_PrintHeuristicallyTypeGuessedArgs(x, v, n, 0); + return; + } + + p = DataAfterAlignedString(typeTags, typeTags+n); + + + for (thisType = typeTags + 1; *thisType != 0; ++thisType) { + switch (*thisType) { + case 'i': case 'r': case 'm': case 'c': +#ifdef DEBUG + //post("integer: %d", ntohl(*((int *) p))); +#endif + SETFLOAT(mya+myargc,ntohl(*((int *) p))); + myargc++; + + p += 4; + break; + + case 'f': { + int i = ntohl(*((int *) p)); + float *floatp = ((float *) (&i)); +#ifdef DEBUG + post("float: %f", *floatp); +#endif + SETFLOAT(mya+myargc,*floatp); + myargc++; + + p += 4; + } + break; + + case 'h': case 't': +#ifdef DEBUG + printf("[A 64-bit int] "); +#endif + post("[A 64-bit int] not implemented"); + + p += 8; + break; + + case 'd': +#ifdef DEBUG + printf("[A 64-bit float] "); +#endif + post("[A 64-bit float] not implemented"); + + p += 8; + break; + + case 's': case 'S': + if (!IsNiceString(p, typeTags+n)) { + post("Type tag said this arg is a string but it's not!\n"); + return; + } else { +#ifdef DEBUG + post("string: \"%s\"", p); +#endif + SETSYMBOL(mya+myargc,gensym(p)); + myargc++; + //outlet_list(x->x_msgout, 0,sizeof(p), p); + //outlet_anything(x->x_msgout, 0, sizeof(p), p); + p = DataAfterAlignedString(p, typeTags+n); + // append to output vector .. + } + break; + + case 'T': +#ifdef DEBUG + printf("[True] "); +#endif + SETFLOAT(mya+myargc,1.); + myargc++; + break; + case 'F': +#ifdef DEBUG + printf("[False] "); +#endif + SETFLOAT(mya+myargc,0.); + myargc++; + break; + case 'N': +#ifdef DEBUG + printf("[Nil]"); +#endif + post("sendOSC: [Nil] not implemented"); + break; + case 'I': +#ifdef DEBUG + printf("[Infinitum]"); +#endif + post("sendOSC: [Infinitum] not implemented"); + break; + + default: + post("sendOSC: [Unrecognized type tag %c]", *thisType); + // return; + } + } + x->x_outatc = myargc; +} + +static void dumpOSC_PrintHeuristicallyTypeGuessedArgs(t_dumpOSC *x, void *v, int n, int skipComma) { + int i, thisi; + float thisf; + int *ints; + char *chars; + char *string, *nextString; + + int myargc= x->x_outatc; + t_atom* mya = x->x_outat; + int myi; + + + /* Go through the arguments 32 bits at a time */ + ints = v; + chars = v; + + for (i = 0; i= -1000 && thisi <= 1000000) { +#ifdef DEBUG + printf("%d ", thisi); +#endif + // append to output vector .. + SETFLOAT(mya+myargc,(t_float) (thisi)); + myargc++; + // outlet_float(x->x_msgout, thisi); + i++; + } else if (thisf >= -1000.f && thisf <= 1000000.f && + (thisf <=0.0f || thisf >= SMALLEST_POSITIVE_FLOAT)) { +#ifdef DEBUG + printf("%f ", thisf); +#endif + // append to output vector .. + SETFLOAT(mya+myargc,thisf); + myargc++; + //outlet_float(x->x_msgout, thisf); + i++; + } else if (IsNiceString(string, chars+n)) { + nextString = DataAfterAlignedString(string, chars+n); +#ifdef DEBUG + printf("\"%s\" ", (i == 0 && skipComma) ? string +1 : string); +#endif + // append to output vector .. + SETSYMBOL(mya+myargc,gensym(string)); + myargc++; + //outlet_symbol(x->x_msgout, gensym((i == 0 && skipComma) ? string +1 : string)); + i += (nextString-string) / 4; + } else { + // unhandled .. ;) +#ifdef DEBUG + printf("0x%x xx", ints[i]); +#endif + i++; + } + x->x_outatc = myargc; + } +} + + +#define STRING_ALIGN_PAD 4 + +char *DataAfterAlignedString(char *string, char *boundary) +{ + /* The argument is a block of data beginning with a string. The + string has (presumably) been padded with extra null characters + so that the overall length is a multiple of STRING_ALIGN_PAD + bytes. Return a pointer to the next byte after the null + byte(s). The boundary argument points to the character after + the last valid character in the buffer---if the string hasn't + ended by there, something's wrong. + + If the data looks wrong, return 0, and set htm_error_string */ + + int i; + + if ((boundary - string) %4 != 0) { + fprintf(stderr, "Internal error: DataAfterAlignedString: bad boundary\n"); + return 0; + } + + for (i = 0; string[i] != '\0'; i++) { + if (string + i >= boundary) { + htm_error_string = "DataAfterAlignedString: Unreasonably long string"; + return 0; + } + } + + /* Now string[i] is the first null character */ + i++; + + for (; (i % STRING_ALIGN_PAD) != 0; i++) { + if (string + i >= boundary) { + htm_error_string = "DataAfterAlignedString: Unreasonably long string"; + return 0; + } + if (string[i] != '\0') { + htm_error_string = "DataAfterAlignedString: Incorrectly padded string."; + return 0; + } + } + + return string+i; +} + +Boolean IsNiceString(char *string, char *boundary) +{ + /* Arguments same as DataAfterAlignedString(). Is the given "string" + really a string? I.e., is it a sequence of isprint() characters + terminated with 1-4 null characters to align on a 4-byte boundary? */ + + int i; + + if ((boundary - string) %4 != 0) { + fprintf(stderr, "Internal error: IsNiceString: bad boundary\n"); + return 0; + } + + for (i = 0; string[i] != '\0'; i++) { + if (!isprint(string[i])) return FALSE; + if (string + i >= boundary) return FALSE; + } + + /* If we made it this far, it's a null-terminated sequence of printing characters + in the given boundary. Now we just make sure it's null padded... */ + + /* Now string[i] is the first null character */ + i++; + for (; (i % STRING_ALIGN_PAD) != 0; i++) { + if (string[i] != '\0') return FALSE; + } + + return TRUE; +} + + + + + + + + + +#include +void complain(char *s, ...) { + va_list ap; + va_start(ap, s); + fprintf(stderr, "*** ERROR: "); + vfprintf(stderr, s, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +#endif /* __sgi or LINUX or WIN32 */ diff --git a/OSC/htmsocket.c b/OSC/htmsocket.c new file mode 100644 index 0000000..420853f --- /dev/null +++ b/OSC/htmsocket.c @@ -0,0 +1,297 @@ +/* +Copyright (c) 1992,1996,1998. +The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Adrian Freed, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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. +*/ + + /* htmsocket.c + + Adrian Freed + send parameters to htm servers by udp or UNIX protocol + + Modified 6/6/96 by Matt Wright to understand symbolic host names + in addition to X.X.X.X addresses. + */ + + +#ifdef WIN32 + #include + #include + #include + #include + #include + #include + #include + #include "OSC-common.h" +#else + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include +#endif + + + + + + + + + + +#define UNIXDG_PATH "/tmp/htm" +#define UNIXDG_TMP "/tmp/htm.XXXXXX" +#include "htmsocket.h" +typedef struct +{ + float srate; + + struct sockaddr_in serv_addr; /* udp socket */ + #ifndef WIN32 + struct sockaddr_un userv_addr; /* UNIX socket */ + #endif + int sockfd; /* socket file descriptor */ + int index, len,uservlen; + void *addr; + int id; +} desc; + +/* open a socket for HTM communication to given host on given portnumber */ +/* if host is 0 then UNIX protocol is used (i.e. local communication */ +void *OpenHTMSocket(char *host, int portnumber) +{ + struct sockaddr_in cl_addr; + #ifndef WIN32 + int sockfd; + struct sockaddr_un ucl_addr; + #else + unsigned int sockfd; + #endif + + desc *o; + o = malloc(sizeof(*o)); + if(!o) + return 0; + + #ifndef WIN32 + + if(!host) + { + char *mktemp(char *); + int clilen; + o->len = sizeof(ucl_addr); + /* + * Fill in the structure "userv_addr" with the address of the + * server that we want to send to. + */ + + bzero((char *) &o->userv_addr, sizeof(o->userv_addr)); + o->userv_addr.sun_family = AF_UNIX; + strcpy(o->userv_addr.sun_path, UNIXDG_PATH); + sprintf(o->userv_addr.sun_path+strlen(o->userv_addr.sun_path), "%d", portnumber); + o->uservlen = sizeof(o->userv_addr.sun_family) + strlen(o->userv_addr.sun_path); + o->addr = &(o->userv_addr); + /* + * Open a socket (a UNIX domain datagram socket). + */ + + if ( (sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) >= 0) + { + /* + * Bind a local address for us. + * In the UNIX domain we have to choose our own name (that + * should be unique). We'll use mktemp() to create a unique + * pathname, based on our process id. + */ + + bzero((char *) &ucl_addr, sizeof(ucl_addr)); /* zero out */ + ucl_addr.sun_family = AF_UNIX; + strcpy(ucl_addr.sun_path, UNIXDG_TMP); + + mktemp(ucl_addr.sun_path); + clilen = sizeof(ucl_addr.sun_family) + strlen(ucl_addr.sun_path); + + if (bind(sockfd, (struct sockaddr *) &ucl_addr, clilen) < 0) + { + perror("client: can't bind local address"); + close(sockfd); + sockfd = -1; + } + } + else + perror("unable to make socket\n"); + + }else + + #endif + + { + /* + * Fill in the structure "serv_addr" with the address of the + * server that we want to send to. + */ + o->len = sizeof(cl_addr); + + #ifdef WIN32 + ZeroMemory((char *)&o->serv_addr, sizeof(o->serv_addr)); + #else + bzero((char *)&o->serv_addr, sizeof(o->serv_addr)); + #endif + + o->serv_addr.sin_family = AF_INET; + + /* MW 6/6/96: Call gethostbyname() instead of inet_addr(), + so that host can be either an Internet host name (e.g., + "les") or an Internet address in standard dot notation + (e.g., "128.32.122.13") */ + { + struct hostent *hostsEntry; + unsigned long address; + + hostsEntry = gethostbyname(host); + if (hostsEntry == NULL) { + fprintf(stderr, "Couldn't decipher host name \"%s\"\n", host); + #ifndef WIN32 + herror(NULL); + #endif + return 0; + } + + address = *((unsigned long *) hostsEntry->h_addr_list[0]); + o->serv_addr.sin_addr.s_addr = address; + } + + /* was: o->serv_addr.sin_addr.s_addr = inet_addr(host); */ + + /* End MW changes */ + + /* + * Open a socket (a UDP domain datagram socket). + */ + + + #ifdef WIN32 + o->serv_addr.sin_port = htons((USHORT)portnumber); + o->addr = &(o->serv_addr); + if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) != INVALID_SOCKET) { + ZeroMemory((char *)&cl_addr, sizeof(cl_addr)); + cl_addr.sin_family = AF_INET; + cl_addr.sin_addr.s_addr = htonl(INADDR_ANY); + cl_addr.sin_port = htons(0); + + if(bind(sockfd, (struct sockaddr *) &cl_addr, sizeof(cl_addr)) < 0) { + perror("could not bind\n"); + closesocket(sockfd); + sockfd = -1; + } + } + else { perror("unable to make socket\n");} + #else + o->serv_addr.sin_port = htons(portnumber); + o->addr = &(o->serv_addr); + if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) { + bzero((char *)&cl_addr, sizeof(cl_addr)); + cl_addr.sin_family = AF_INET; + cl_addr.sin_addr.s_addr = htonl(INADDR_ANY); + cl_addr.sin_port = htons(0); + + if(bind(sockfd, (struct sockaddr *) &cl_addr, sizeof(cl_addr)) < 0) { + perror("could not bind\n"); + close(sockfd); + sockfd = -1; + } + } + else { perror("unable to make socket\n");} + #endif + } + #ifdef WIN32 + if(sockfd == INVALID_SOCKET) { + #else + if(sockfd < 0) { + #endif + free(o); + o = 0; + } + else + o->sockfd = sockfd; + return o; +} + + +#include + +static bool sendudp(const struct sockaddr *sp, int sockfd,int length, int count, void *b) +{ + int rcount; + if((rcount=sendto(sockfd, b, count, 0, sp, length)) != count) + { + printf("sockfd %d count %d rcount %dlength %d errno %d\n", sockfd,count,rcount,length, errno); + return FALSE; + } + return TRUE; +} +bool SendHTMSocket(void *htmsendhandle, int length_in_bytes, void *buffer) +{ + desc *o = (desc *)htmsendhandle; + return sendudp(o->addr, o->sockfd, o->len, length_in_bytes, buffer); +} +void CloseHTMSocket(void *htmsendhandle) +{ + desc *o = (desc *)htmsendhandle; + #ifdef WIN32 + if(SOCKET_ERROR == closesocket(o->sockfd)) { + perror("CloseHTMSocket::closesocket failed\n"); + return; + } + #else + close(o->sockfd); + #endif + + free(o); +} diff --git a/OSC/htmsocket.h b/OSC/htmsocket.h new file mode 100644 index 0000000..b035b57 --- /dev/null +++ b/OSC/htmsocket.h @@ -0,0 +1,49 @@ +/* +Copyright (c) 1992,1996. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Adrian Freed, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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. +*/ + + /* htmparam.h + + Adrian Freed + send parameters to htm servers by udp or UNIX protocol + */ +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif +typedef int bool; + +/* open a socket for HTM communication to given host on given portnumber */ +/* if host is 0 then UNIX protocol is used (i.e. local communication) */ +void *OpenHTMSocket(char *host, int portnumber); + +/* send a buffer of data over htm socket, returns TRUE on success. + Note that udp sends rarely fail. UNIX sends fail if a kernal buffer overflows */ +bool SendHTMSocket(void *htmsendhandle, int length_in_bytes, void *buffer); + +/* close the socket(2) and release memory associated with it */ +void CloseHTMSocket(void *htmsendhandle); diff --git a/OSC/makefile b/OSC/makefile new file mode 100644 index 0000000..8534bc9 --- /dev/null +++ b/OSC/makefile @@ -0,0 +1,69 @@ +############################### +# change these +IFLAGS = -I. -I../../pd-0.34-4/src +INSTALL_PREFIX=/usr + +# should be oke below .. +EXT = o +#DEFS = -DHAVE_LIBC=1 -DHAVE_LIBM=1 -DHAVE_LIBPTHREAD=1 -DSTDC_HEADERS=1 -DHAVE_FCNTL_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_UNISTD_H=1 -DTIME_WITH_SYS_TIME=1 -DHAVE_UNISTD_H=1 -DHAVE_GETPAGESIZE=1 -DHAVE_MMAP=1 -DHAVE_SELECT=1 -DHAVE_SOCKET=1 -DHAVE_STRERROR=1 -DPD_VERSION_MINOR=32 +DEFS = +CC = gcc +CXX = c++ +LD = ld +AFLAGS = +LFLAGS = -export_dynamic -shared +WFLAGS = + +# DEBUG = -DDEBUG -g +DEBUG = + +VERSION = \"$(shell cat VERSION)\" + +.SUFFIXES: .$(EXT) + +PDCFLAGS = -O2 $(DEFS) $(IFLAGS) $(WFLAGS) $(LFLAGS) $(AFLAGS) -DVERSION=$(VERSION) +CFLAGS = -O2 $(DEFS) $(IFLAGS) $(WFLAGS) -DVERSION=$(VERSION) $(DEBUG) +CXXFLAGS = $(CFLAGS) +LIBOSC = ../libOSC/libOSC.a +LIBS = -lpthread -lm -lc +SOURCES = htmsocket.c OSC-pattern-match.c sendOSC.c dumpOSC.c routeOSC.c +TARGETS = $(SOURCES:.c=.$(EXT)) + +all: $(TARGETS) + +OSC: $(TARGETS) + cc -c $(CFLAGS) -DPD OSC.c + $(LD) -export_dynamic -shared -o OSC.pd_linux *.o $(LIBS) $(LIBOSC) + strip --strip-unneeded OSC.pd_linux + +clean:: + -rm *.$(EXT) *.pd_linux *~ + +distclean: clean + -rm config.cache config.log config.status makefile + + +distcleancvs: + -rm -r CVS reference/CVS + + +.c.o: + $(CC) -c -o $@ $(CFLAGS) -DPD $*.c + +# cp $@ $*_stat.o + +#.o.pd_linux: +# $(CC) -o $@ $(PDCFLAGS) -DPD $*.o + + + +install:: + install -d $(INSTALL_PREFIX)/lib/pd/externs +# install -m 644 *.pd_linux $(INSTALL_PREFIX)/lib/pd/externs + install -m 644 OSC.pd_linux $(INSTALL_PREFIX)/lib/pd/externs + install -d $(INSTALL_PREFIX)/lib/pd/doc/5.reference/OSC + install -m 644 ../doc/*.pd $(INSTALL_PREFIX)/lib/pd/doc/5.reference/OSC + + +#dist: distclean +# (cd ..;tar czvf ggext.tgz ggext) diff --git a/OSC/makefile.in b/OSC/makefile.in new file mode 100644 index 0000000..1f951b6 --- /dev/null +++ b/OSC/makefile.in @@ -0,0 +1,60 @@ +EXT = o +DEFS = -DHAVE_LIBC=1 -DHAVE_LIBM=1 -DHAVE_LIBPTHREAD=1 -DSTDC_HEADERS=1 -DHAVE_FCNTL_H=1 -DHAVE_SYS_TIME_H=1 -DHAVE_UNISTD_H=1 -DTIME_WITH_SYS_TIME=1 -DHAVE_UNISTD_H=1 -DHAVE_GETPAGESIZE=1 -DHAVE_MMAP=1 -DHAVE_SELECT=1 -DHAVE_SOCKET=1 -DHAVE_STRERROR=1 -DPD_VERSION_MINOR=32 +CC = gcc +CXX = c++ +LD = ld +AFLAGS = +LFLAGS = -export_dynamic -shared +WFLAGS = +IFLAGS = -I. -I../../pd/src +INSTALL_PREFIX=@prefix@ + +VERSION = \"$(shell cat VERSION)\" + +.SUFFIXES: .$(EXT) + +PDCFLAGS = -g -O2 $(DEFS) $(IFLAGS) $(WFLAGS) $(LFLAGS) $(AFLAGS) -DVERSION=$(VERSION) +CFLAGS = -g -O2 $(DEFS) $(IFLAGS) $(WFLAGS) -DVERSION=$(VERSION) +CXXFLAGS = $(CFLAGS) +LIBOSC = ../libOSC/libOSC.a +LIBS = -lpthread -lm -lc +SOURCES = htmsocket.c OSC-pattern-match.c sendOSC.c dumpOSC.c routeOSC.c +TARGETS = $(SOURCES:.c=.$(EXT)) + +all: $(TARGETS) + +OSC: $(TARGETS) + cc -c $(CFLAGS) -DPD osc.c + $(LD) -export_dynamic -shared -o OSC.pd_linux *.o $(LIBS) $(LIBOSC) + strip --strip-unneeded OSC.pd_linux + +clean:: + -rm *.$(EXT) *.pd_linux *~ + +distclean: clean + -rm config.cache config.log config.status makefile + + +distcleancvs: + -rm -r CVS reference/CVS + + +.c.o: + $(CC) -c -o $@ $(CFLAGS) -DPD $*.c + +# cp $@ $*_stat.o + +#.o.pd_linux: +# $(CC) -o $@ $(PDCFLAGS) -DPD $*.o + + + +install:: + install -d $(INSTALL_PREFIX)/lib/pd/externs + install -m 644 *.pd_linux $(INSTALL_PREFIX)/lib/pd/externs + -install -m 644 OSC.pd_linux $(INSTALL_PREFIX)/lib/pd/externs + install -m 644 *.pd $(INSTALL_PREFIX)/lib/pd/doc/5.reference + + +dist: distclean + (cd ..;tar czvf ggext.tgz ggext) diff --git a/OSC/routeOSC.c b/OSC/routeOSC.c new file mode 100644 index 0000000..4ff38cb --- /dev/null +++ b/OSC/routeOSC.c @@ -0,0 +1,417 @@ +/* +Copyright (c) 1999, 2000, 20010 The Regents of the University of +California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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 OpenSound Control WWW page is + http://www.cnmat.berkeley.edu/OpenSoundControl + + OSC-route.c + Max object for OSC-style dispatching + + To-do: + + Match a pattern against a pattern? + Declare outlet types / distinguish leaf nodes from other children + More sophisticated (2-pass?) allmessages scheme + set message? + + + pd + ------------- + -- tweaks for Win32 www.zeggz.com/raf 13-April-2002 + + + */ + + + +/* the required include files */ +#include "m_pd.h" +#include "OSC-common.h" +#include "OSC-pattern-match.h" + +#ifdef WIN32 + #include + #include +#endif + + +/* structure definition of your object */ +#define MAX_NUM 20 +#define OSC_ROUTE_VERSION "1.05" +/* Version 1.04: Allows #1 thru #9 as typed-in arguments + Version 1.05: Allows "list" messages as well as "message" messages. +*/ + +static t_class *OSCroute_class; + +typedef struct _OSCroute +{ + t_object x_obj; // required header + t_int x_num; // Number of address prefixes we store + t_int x_complainmode; // Do we print a message if no match? + char *x_prefixes[MAX_NUM]; + void *x_outlets[MAX_NUM]; +} t_OSCroute; + +t_symbol *ps_list, *ps_complain, *ps_emptySymbol; + +/* prototypes */ + +void OSCroute_doanything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); +void OSCroute_anything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); +void OSCroute_list(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); +/* //void *OSCroute_new(t_symbol *s, int argc, atom *argv); */ +void *OSCroute_new(t_symbol *s, int argc, t_atom *argv); +void OSCroute_version (t_OSCroute *x); +/* void OSCroute_assist (OSCroute *x, void *box, long msg, long arg, */ +/* char *dstString); */ +void OSCroute_allmessages(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); + +static char *NextSlashOrNull(char *p); +static void StrCopyUntilSlash(char *target, const char *source); + + +// free +static void OSCroute_free(t_OSCroute *x) +{ + // freebytes(x->x_vec, x->x_nelement * sizeof(*x->x_vec)); +} + +/* initialization routine */ + +// setup +#ifdef WIN32 + OSC_API void OSCroute_setup(void) { +#else + void OSCroute_setup(void) { +#endif + OSCroute_class = class_new(gensym("OSCroute"), (t_newmethod)OSCroute_new, + (t_method)OSCroute_free,sizeof(t_OSCroute), 0, A_GIMME, 0); + class_addlist(OSCroute_class, OSCroute_list); + class_addanything(OSCroute_class, OSCroute_anything); + class_addmethod(OSCroute_class, (t_method)OSCroute_version, gensym("version"), A_NULL, 0, 0); + class_sethelpsymbol(OSCroute_class, gensym("OSC/OSCroute-help.pd")); + + /* + class_addmethod(OSCroute_class, (t_method)OSCroute_connect, + gensym("connect"), A_SYMBOL, A_FLOAT, 0); + class_addmethod(OSCroute_class, (t_method)OSCroute_disconnect, + gensym("disconnect"), 0); + class_addmethod(OSCroute_class, (t_method)OSCroute_send, gensym("send"), + A_GIMME, 0); + */ +/* ps_list = gensym("list"); */ +/* ps_complain = gensym("complain"); */ + ps_emptySymbol = gensym(""); + + post("OSCroute object version " OSC_ROUTE_VERSION " by Matt Wright. pd-fication cxc. Win32 raf."); + post("OSCroute Copyright © 1999 Regents of the University of California. All Rights Reserved."); +} + + + +/* instance creation routine */ + +void *OSCroute_new(t_symbol *s, int argc, t_atom *argv) +{ + //OSCroute *x; + t_OSCroute *x = (t_OSCroute *)pd_new(OSCroute_class); // get memory for a new object & initialize + + //int i, n; + int i; //{{raf}} n not used + + // EnterCallback(); + + if (argc > MAX_NUM) { + post("* OSC-route: too many arguments: %ld (max %ld)", argc, MAX_NUM); + // ExitCallback(); + return 0; + } + + //x = newobject(OSCroute_class); // get memory for a new object & initialize + + x->x_complainmode = 0; + x->x_num = 0; + for (i = 0; i < argc; ++i) { + if (argv[i].a_type == A_SYMBOL) { + if (argv[i].a_w.w_symbol->s_name[0] == '/') { + /* Now that's a nice prefix */ + x->x_prefixes[i] = argv[i].a_w.w_symbol->s_name; + ++(x->x_num); + } else if (argv[i].a_w.w_symbol->s_name[0] == '#' && + argv[i].a_w.w_symbol->s_name[1] >= '1' && + argv[i].a_w.w_symbol->s_name[1] <= '9') { + /* The Max programmer is trying to make a patch that will be + a subpatch with arguments. We have to make an outlet for this + argument. */ + x->x_prefixes[i] = "dummy"; + ++(x->x_num); + } else { + /* Maybe this is an option we support */ + +/* if (argv[i].a_w.w_sym == ps_complain) { */ +/* x->x_complainmode = 1; */ +/* } else { */ +/* post("* OSC-route: Unrecognized argument %s", argv[i].a_w.w_sym->s_name); */ +/* } */ + + } + + // no LONG + +/* } else if (argv[i].a_type == A_FLOAD) { */ +/* // Convert to a numeral. Max ints are -2147483648 to 2147483647 */ +/* char *string = getbytes(12); */ +/* // I can't be bothered to plug this 12 byte memory leak */ +/* if (string == 0) { */ +/* post("* OSC-route: out of memory!"); */ +/* // ExitCallback(); */ +/* return 0; */ +/* } */ +/* sprintf(string, "%d", argv[i].a_w.w_long); */ +/* x->x_prefixes[i] = string; */ +/* ++(x->x_num); */ + + } else if (argv[i].a_type == A_FLOAT) { + post("* OSC-route: float arguments are not OK."); + // ExitCallback(); + return 0; + } else { + post("* OSC-route: unrecognized argument type!"); + // ExitCallback(); + return 0; + } + } + + + /* Have to create the outlets in reverse order */ + /* well, not in pd ? */ + // for (i = x->x_num-1; i >= 0; --i) { + for (i = 0; i <= x->x_num-1; i++) { + // x->x_outlets[i] = listout(x); + x->x_outlets[i] = outlet_new(&x->x_obj, &s_list); + } + + // ExitCallback(); + return (x); +} + + +void OSCroute_version (t_OSCroute *x) { + // EnterCallback(); + post("OSCroute Version " OSC_ROUTE_VERSION + ", by Matt Wright. pd-fication cxc@web.fm\nOSCroute Compiled " __TIME__ " " __DATE__); + // ExitCallback(); +} + +/* I don't know why these aren't defined in some Max #include file. */ +#define ASSIST_INLET 1 +#define ASSIST_OUTLET 2 + +void OSCroute_assist (t_OSCroute *x, void *box, long msg, long arg, + char *dstString) { + // EnterCallback(); + + if (msg==ASSIST_INLET) { + sprintf(dstString, "Incoming OSC messages"); + } else if (msg==ASSIST_OUTLET) { + if (arg < 0 || arg >= x->x_num) { + post("* OSCroute_assist: No outlet corresponds to arg %ld!", arg); + } else { + sprintf(dstString, "subaddress + args for prefix %s", x->x_prefixes[arg]); + } + } else { + post("* OSCroute_assist: unrecognized message %ld", msg); + } + + // ExitCallback(); +} + +void OSCroute_list(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { + // EnterCallback(); + if (argc > 0 && argv[0].a_type == A_SYMBOL) { + /* Ignore the fact that this is a "list" */ + OSCroute_doanything(x, argv[0].a_w.w_symbol, argc-1, argv+1); + } else { + post("* OSC-route: invalid list beginning with a number"); + } + // ExitCallback(); +} + + +void OSCroute_anything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { + // EnterCallback(); + OSCroute_doanything(x, s, argc, argv); + // ExitCallback(); +} + + + + +void OSCroute_doanything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { + char *pattern, *nextSlash; + int i; + int matchedAnything; + // post("*** OSCroute_anything(s %s, argc %ld)", s->s_name, (long) argc); + + pattern = s->s_name; + if (pattern[0] != '/') { + post("* OSC-route: invalid message pattern %s does not begin with /", s->s_name); + return; + } + + matchedAnything = 0; + + nextSlash = NextSlashOrNull(pattern+1); + if (*nextSlash == '\0') { + /* last level of the address, so we'll output the argument list */ + + +#ifdef NULL_IS_DIFFERENT_FROM_BANG + if (argc==0) { + post("* OSC-route: why are you matching one level pattern %s with no args?", + pattern); + return; + } +#endif + + for (i = 0; i < x->x_num; ++i) { + if (PatternMatch(pattern+1, x->x_prefixes[i]+1)) { + ++matchedAnything; + + // I hate stupid Max lists with a special first element + if (argc == 0) { + outlet_bang(x->x_outlets[i]); + } else if (argv[0].a_type == A_SYMBOL) { + // Promote the symbol that was argv[0] to the special symbol + + outlet_anything(x->x_outlets[i], argv[0].a_w.w_symbol, argc-1, argv+1); + } else if (argc > 1) { + // Multiple arguments starting with a number, so naturally we have + // to use a special function to output this "list", since it's what + // Max originally meant by "list". + outlet_list(x->x_outlets[i], 0L, argc, argv); + } else { + // There was only one argument, and it was a number, so we output it + // not as a list +/* if (argv[0].a_type == A_LONG) { */ + +/* outlet_int(x->x_outlets[i], argv[0].a_w.w_long); */ + // } else + if (argv[0].a_type == A_FLOAT) { + + outlet_float(x->x_outlets[i], argv[0].a_w.w_float); + } else { + post("* OSC-route: unrecognized atom type!"); + } + } + } + } + } else { + /* There's more address after this part, so our output list will begin with + the next slash. */ + t_symbol *restOfPattern = 0; /* avoid the gensym unless we have to output */ + char patternBegin[1000]; + + + /* Get the first level of the incoming pattern to match against all our prefixes */ + StrCopyUntilSlash(patternBegin, pattern+1); + + for (i = 0; i < x->x_num; ++i) { + if (PatternMatch(patternBegin, x->x_prefixes[i]+1)) { + ++matchedAnything; + if (restOfPattern == 0) { + restOfPattern = gensym(nextSlash); + } + outlet_anything(x->x_outlets[i], restOfPattern, argc, argv); + } + } + } + + if (x->x_complainmode) { + if (!matchedAnything) { + post("* OSC-route: pattern %s did not match any prefixes", pattern); + } + } +} + +static char *NextSlashOrNull(char *p) { + while (*p != '/' && *p != '\0') { + p++; + } + return p; +} + +static void StrCopyUntilSlash(char *target, const char *source) { + while (*source != '/' && *source != '\0') { + *target = *source; + ++target; + ++source; + } + *target = 0; +} + +static int MyStrCopy(char *target, const char *source) { + int i = 0; + while (*source != '\0') { + *target = *source; + ++target; + ++source; + ++i; + } + *target = 0; + return i; +} + + + +void OSCroute_allmessages(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { + int i; + t_symbol *prefixSymbol = 0; + char prefixBuf[1000]; + char *endOfPrefix; + t_atom a[1]; + + if (argc >= 1 && argv[0].a_type == A_SYMBOL) { + prefixSymbol = argv[0].a_w.w_symbol; + endOfPrefix = prefixBuf + MyStrCopy(prefixBuf, + prefixSymbol->s_name); + } else { + prefixSymbol = ps_emptySymbol; + prefixBuf[0] = '\0'; + endOfPrefix = prefixBuf; + } + + + for (i = 0; i < x->x_num; ++i) { + post("OSC: %s%s", prefixSymbol->s_name, x->x_prefixes[i]); + MyStrCopy(endOfPrefix, x->x_prefixes[i]); + SETSYMBOL(a, gensym(prefixBuf)); + outlet_anything(x->x_outlets[i], s, 1, a); + } +} diff --git a/OSC/sendOSC.c b/OSC/sendOSC.c new file mode 100644 index 0000000..ce4e6ee --- /dev/null +++ b/OSC/sendOSC.c @@ -0,0 +1,875 @@ +/* +Copyright (c) 1996,1997. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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. +*/ + +/* sendOSC.c + + Matt Wright, 6/3/97 + based on sendOSC.c, which was based on a version by Adrian Freed + + Text-based OpenSoundControl client. User can enter messages via command + line arguments or standard input. + + Version 0.1: "play" feature + Version 0.2: Message type tags. + + + + pd + ------------- + -- added bundle stuff to send. jdl 20020416 + -- tweaks for Win32 www.zeggz.com/raf 13-April-2002 + -- ost_at_test.at + i22_at_test.at, 2000-2002 + modified to compile as pd externel + +*/ + +//#define VERSION "http://cnmat.berkeley.edu/OpenSoundControl/sendOSC-0.1.html" +#define MAX_ARGS 2000 +#define SC_BUFFER_SIZE 32000 + +/* +compiling: + cc -o sendOSC sendOSC.c htmsocket.c OpenSoundControl.c OSC_timeTag.c +*/ + +#ifdef WIN32 + #include "m_pd.h" + #include "OSC-client.h" + #include "htmsocket.h" + #include "OSC-common.h" + #include + #include + #include + #include + #include + #include + #include + #include +#else + #include "m_pd.h" + //#include "x_osc.h" + #include "OSC-client.h" + #include "htmsocket.h" + + #include + #include + #include + #include + #include + #include +#endif + +/////////////////////// +// from sendOSC + +typedef struct { + //enum {INT, FLOAT, STRING} type; + enum {INT_osc, FLOAT_osc, STRING_osc} type; + union { + int i; + float f; + char *s; + } datum; +} typedArg; + +void CommandLineMode(int argc, char *argv[], void *htmsocket); +//void InteractiveMode(void *htmsocket); +OSCTimeTag ParseTimeTag(char *s); +void ParseInteractiveLine(OSCbuf *buf, char *mesg); +typedArg ParseToken(char *token); +int WriteMessage(OSCbuf *buf, char *messageName, int numArgs, typedArg *args); +void SendBuffer(void *htmsocket, OSCbuf *buf); +void SendData(void *htmsocket, int size, char *data); +void fatal_error(char *s); +void send_complain(char *s, ...); + +//static void *htmsocket; +static int exitStatus = 0; +static int useTypeTags = 0; + +static char bufferForOSCbuf[SC_BUFFER_SIZE]; + + +///////// +// end from sendOSC + +static t_class *sendOSC_class; + +typedef struct _sendOSC +{ + t_object x_obj; + int x_protocol; // UDP/TCP (udp only atm) + t_int x_typetags; // typetag flag + void *x_htmsocket; // sending socket + int x_bundle; // bundle open flag + OSCbuf x_oscbuf[1]; // OSCbuffer + t_outlet *x_bdpthout;// bundle-depth floatoutlet +} t_sendOSC; + +static void *sendOSC_new(t_floatarg udpflag) +{ + t_sendOSC *x = (t_sendOSC *)pd_new(sendOSC_class); + outlet_new(&x->x_obj, &s_float); + x->x_htmsocket = 0; // {{raf}} + // set udp + x->x_protocol = SOCK_STREAM; + // set typetags to 1 by default + x->x_typetags = 1; + // bunlde is closed + x->x_bundle = 0; + OSC_initBuffer(x->x_oscbuf, SC_BUFFER_SIZE, bufferForOSCbuf); + x->x_bdpthout = outlet_new(&x->x_obj, 0); // outlet_float(); + //x->x_oscbuf = + return (x); +} + + +void sendOSC_openbundle(t_sendOSC *x) +{ + if (x->x_oscbuf->bundleDepth + 1 >= MAX_BUNDLE_NESTING || + OSC_openBundle(x->x_oscbuf, OSCTT_Immediately())) + { + send_complain("Problem opening bundle: %s\n", OSC_errorMessage); + return; + } + x->x_bundle = 1; + outlet_float(x->x_bdpthout, (float)x->x_oscbuf->bundleDepth); +} + +static void sendOSC_closebundle(t_sendOSC *x) +{ + if (OSC_closeBundle(x->x_oscbuf)) { + send_complain("Problem closing bundle: %s\n", OSC_errorMessage); + 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)) { + // post("x_oscbuf: something inside me?"); + if (x->x_htmsocket) { + SendBuffer(x->x_htmsocket, x->x_oscbuf); + } else { + post("sendOSC: not connected"); + } + OSC_initBuffer(x->x_oscbuf, SC_BUFFER_SIZE, bufferForOSCbuf); + x->x_bundle = 0; + return; + } + // post("x_oscbuf: something went wrong"); +} + +static void sendOSC_settypetags(t_sendOSC *x, t_float *f) + { + x->x_typetags = (int)f; + post("sendOSC.c: setting typetags %d",x->x_typetags); + } + + +static void sendOSC_connect(t_sendOSC *x, t_symbol *hostname, + t_floatarg fportno) +{ + int portno = fportno; + /* create a socket */ + + // make sure handle is available + if(x->x_htmsocket == 0) { + // + x->x_htmsocket = OpenHTMSocket(hostname->s_name, portno); + if (!x->x_htmsocket) + post("Couldn't open socket: "); + else { + post("connected to port %s:%d (hSock=%d)", hostname->s_name, portno, x->x_htmsocket); + outlet_float(x->x_obj.ob_outlet, 1); + } + } + else + perror("call to sendOSC_connect() against UNavailable socket handle"); +} + +void sendOSC_disconnect(t_sendOSC *x) +{ + if (x->x_htmsocket) + { + post("disconnecting htmsock (hSock=%d)...", x->x_htmsocket); + CloseHTMSocket(x->x_htmsocket); + x->x_htmsocket = 0; // {{raf}} semi-quasi-semaphorize this + outlet_float(x->x_obj.ob_outlet, 0); + } + else { + perror("call to sendOSC_disconnect() against unused socket handle"); + } +} + +void sendOSC_senduntyped(t_sendOSC *x, t_symbol *s, int argc, t_atom *argv) +{ + char* targv[MAXPDARG]; + char tmparg[MAXPDSTRING]; + char* tmp = tmparg; + //char testarg[MAXPDSTRING]; + int c; + + post("sendOSC: use typetags 0/1 message and plain send method so send untypetagged..."); + return; + + //atom_string(argv,testarg, MAXPDSTRING); + for (c=0;c= .. + if (x->x_htmsocket) + { + CommandLineMode(argc, targv, x->x_htmsocket); + // post("test %d", c); + } + else { + post("sendOSC: not connected"); + // exit(3); + } +} + +////////////////////////////////////////////////////////////////////// +// this is the real and only sending routine now, for both typed and undtyped mode. + +static void sendOSC_sendtyped(t_sendOSC *x, t_symbol *s, int argc, t_atom *argv) +{ + char* targv[MAXPDARG]; + char tmparg[MAXPDSTRING]; + char* tmp = tmparg; + int c; + + char *messageName; + char *token; + typedArg args[MAX_ARGS]; + int i,j; + int numArgs = 0; + + messageName = ""; +#ifdef DEBUG + post ("sendOSC: messageName: %s", messageName); +#endif + + + + for (c=0;c= .. + if (x->x_htmsocket > 0) + { +#ifdef DEBUG + post ("sendOSC: type tags? %d", useTypeTags); +#endif + + messageName = strtok(targv[0], ","); + j = 1; + for (i = j; i < argc; i++) { + token = strtok(targv[i],","); + args[i-j] = ParseToken(token); +#ifdef DEBUG + printf("cell-cont: %s\n", targv[i]); + printf(" type-id: %d\n", args[i-j]); +#endif + numArgs = i; + } + + + if(WriteMessage(x->x_oscbuf, messageName, numArgs, args)) { + post("sendOSC: usage error, write-msg failed: %s", OSC_errorMessage); + return; + } + + if(!x->x_bundle) { +/* // post("sendOSC: accumulating bundle, not sending things ..."); */ +/* } else { */ + // post("sendOSC: yeah and OUT!"); + SendBuffer(x->x_htmsocket, x->x_oscbuf); + OSC_initBuffer(x->x_oscbuf, SC_BUFFER_SIZE, bufferForOSCbuf); + } + + //CommandLineMode(argc, targv, x->x_htmsocket); + //useTypeTags = 0; + } + else { + post("sendOSC: not connected"); + // exit(3); + } +} + +void sendOSC_send(t_sendOSC *x, t_symbol *s, int argc, t_atom *argv) +{ + if(!argc) { + post("not sending empty message."); + return; + } + if(x->x_typetags) { + useTypeTags = 1; + sendOSC_sendtyped(x,s,argc,argv); + useTypeTags = 0; + } else { + sendOSC_sendtyped(x,s,argc,argv); + } +} + +static void sendOSC_free(t_sendOSC *x) +{ + sendOSC_disconnect(x); +} + +#ifdef WIN32 + OSC_API void sendOSC_setup(void) { +#else + void sendOSC_setup(void) { +#endif + sendOSC_class = class_new(gensym("sendOSC"), (t_newmethod)sendOSC_new, + (t_method)sendOSC_free, + sizeof(t_sendOSC), 0, A_DEFFLOAT, 0); + class_addmethod(sendOSC_class, (t_method)sendOSC_connect, + gensym("connect"), A_SYMBOL, A_FLOAT, 0); + class_addmethod(sendOSC_class, (t_method)sendOSC_disconnect, + gensym("disconnect"), 0); + class_addmethod(sendOSC_class, (t_method)sendOSC_settypetags, + gensym("typetags"), + A_FLOAT, 0); + class_addmethod(sendOSC_class, (t_method)sendOSC_send, + gensym("send"), + A_GIMME, 0); + class_addmethod(sendOSC_class, (t_method)sendOSC_send, + gensym("senduntyped"), + A_GIMME, 0); + class_addmethod(sendOSC_class, (t_method)sendOSC_send, + gensym("sendtyped"), + A_GIMME, 0); + class_addmethod(sendOSC_class, (t_method)sendOSC_openbundle, + gensym("["), + 0, 0); + class_addmethod(sendOSC_class, (t_method)sendOSC_closebundle, + gensym("]"), + 0, 0); + class_sethelpsymbol(sendOSC_class, gensym("OSC/sendOSC-help.pd")); +} + + + + + +/* Exit status codes: + 0: successful + 2: Message(s) dropped because of buffer overflow + 3: Socket error + 4: Usage error + 5: Internal error +*/ + +void CommandLineMode(int argc, char *argv[], void *htmsocket) { + char *messageName; + char *token; + typedArg args[MAX_ARGS]; + int i,j, numArgs; + OSCbuf buf[1]; + + OSC_initBuffer(buf, SC_BUFFER_SIZE, bufferForOSCbuf); + + if (argc > 1) { + post("argc (%d) > 1", argc); +/* if (OSC_openBundle(buf, OSCTT_Immediately())) { */ +/* send_complain("Problem opening bundle: %s\n", OSC_errorMessage); */ +/* return; */ +/* } */ + } + + // ParseInteractiveLine(buf, argv); + messageName = strtok(argv[0], ","); + + j = 1; + for (i = j; i < argc; i++) { + token = strtok(argv[i],","); + args[i-j] = ParseToken(token); +#ifdef DEBUG + printf("cell-cont: %s\n", argv[i]); + printf(" type-id: %d\n", args[i-j]); +#endif + numArgs = i; + } + + if(WriteMessage(buf, messageName, numArgs, args)) { + post("sendOSC: usage error. write-msg failed: %s", OSC_errorMessage); + return; + } + +/* for (i = 0; i < argc; i++) { */ +/* messageName = strtok(argv[i], ","); */ +/* //send_complain ("commandlinemode: count: %d %s\n",i, messageName); */ +/* if (messageName == NULL) { */ +/* break; */ +/* } */ + +/* j = 0; */ +/* while ((token = strtok(NULL, ",")) != NULL) { */ +/* args[j] = ParseToken(token); */ +/* j++; */ +/* if (j >= MAX_ARGS) { */ +/* send_complain("Sorry; your message has more than MAX_ARGS (%d) arguments; ignoring the rest.\n", */ +/* MAX_ARGS); */ +/* break; */ +/* } */ +/* } */ +/* numArgs = j; */ + +/* WriteMessage(buf, messageName, numArgs, args); */ + +/* } */ + +/* if (argc > 1) { */ +/* if (OSC_closeBundle(buf)) { */ +/* send_complain("Problem closing bundle: %s\n", OSC_errorMessage); */ +/* return; */ +/* } */ +/* } */ + + SendBuffer(htmsocket, buf); +} + +#define MAXMESG 2048 + +void InteractiveMode(void *htmsocket) { + char mesg[MAXMESG]; + OSCbuf buf[1]; + int bundleDepth = 0; /* At first, we haven't seen "[". */ + + OSC_initBuffer(buf, SC_BUFFER_SIZE, bufferForOSCbuf); + + while (fgets(mesg, MAXMESG, stdin) != NULL) { + if (mesg[0] == '\n') { + if (bundleDepth > 0) { + /* Ignore blank lines inside a group. */ + } else { + /* blank line => repeat previous send */ + SendBuffer(htmsocket, buf); + } + continue; + } + + if (bundleDepth == 0) { + OSC_resetBuffer(buf); + } + + if (mesg[0] == '[') { + OSCTimeTag tt = ParseTimeTag(mesg+1); + if (OSC_openBundle(buf, tt)) { + send_complain("Problem opening bundle: %s\n", OSC_errorMessage); + OSC_resetBuffer(buf); + bundleDepth = 0; + continue; + } + bundleDepth++; + } else if (mesg[0] == ']' && mesg[1] == '\n' && mesg[2] == '\0') { + if (bundleDepth == 0) { + send_complain("Unexpected ']': not currently in a bundle.\n"); + } else { + if (OSC_closeBundle(buf)) { + send_complain("Problem closing bundle: %s\n", OSC_errorMessage); + OSC_resetBuffer(buf); + bundleDepth = 0; + continue; + } + + bundleDepth--; + if (bundleDepth == 0) { + SendBuffer(htmsocket, buf); + } + } + } else { + ParseInteractiveLine(buf, mesg); + if (bundleDepth != 0) { + /* Don't send anything until we close all bundles */ + } else { + SendBuffer(htmsocket, buf); + } + } + } +} + +OSCTimeTag ParseTimeTag(char *s) { + char *p, *newline; + typedArg arg; + + p = s; + while (isspace(*p)) p++; + if (*p == '\0') return OSCTT_Immediately(); + + if (*p == '+') { + /* Time tag is for some time in the future. It should be a + number of seconds as an int or float */ + + newline = strchr(s, '\n'); + if (newline != NULL) *newline = '\0'; + + p++; /* Skip '+' */ + while (isspace(*p)) p++; + + arg = ParseToken(p); + if (arg.type == STRING_osc) { + send_complain("warning: inscrutable time tag request: %s\n", s); + return OSCTT_Immediately(); + } else if (arg.type == INT_osc) { + return OSCTT_PlusSeconds(OSCTT_CurrentTime(), + (float) arg.datum.i); + } else if (arg.type == FLOAT_osc) { + return OSCTT_PlusSeconds(OSCTT_CurrentTime(), arg.datum.f); + } else { + fatal_error("This can't happen!"); + } + } + + if (isdigit(*p) || (*p >= 'a' && *p <='f') || (*p >= 'A' && *p <='F')) { + /* They specified the 8-byte tag in hex */ + OSCTimeTag tt; + if (sscanf(p, "%llx", &tt) != 1) { + send_complain("warning: couldn't parse time tag %s\n", s); + return OSCTT_Immediately(); + } +#ifndef HAS8BYTEINT + if (ntohl(1) != 1) { + /* tt is a struct of seconds and fractional part, + and this machine is little-endian, so sscanf + wrote each half of the time tag in the wrong half + of the struct. */ + uint32 temp; + temp = tt.seconds; + tt.seconds = tt.fraction ; + tt.fraction = temp; + } +#endif + return tt; + } + + send_complain("warning: invalid time tag: %s\n", s); + return OSCTT_Immediately(); +} + + +void ParseInteractiveLine(OSCbuf *buf, char *mesg) { + char *messageName, *token, *p; + typedArg args[MAX_ARGS]; + int thisArg; + + p = mesg; + while (isspace(*p)) p++; + if (*p == '\0') return; + + messageName = p; + + if (strcmp(messageName, "play\n") == 0) { + /* Special kludge feature to save typing */ + typedArg arg; + + if (OSC_openBundle(buf, OSCTT_Immediately())) { + send_complain("Problem opening bundle: %s\n", OSC_errorMessage); + return; + } + + arg.type = INT_osc; + arg.datum.i = 0; + WriteMessage(buf, "/voices/0/tp/timbre_index", 1, &arg); + + arg.type = FLOAT_osc; + arg.datum.i = 0.0f; + WriteMessage(buf, "/voices/0/tm/goto", 1, &arg); + + if (OSC_closeBundle(buf)) { + send_complain("Problem closing bundle: %s\n", OSC_errorMessage); + } + + return; + } + + while (!isspace(*p) && *p != '\0') p++; + if (isspace(*p)) { + *p = '\0'; + p++; + } + + thisArg = 0; + while (*p != '\0') { + /* flush leading whitespace */ + while (isspace(*p)) p++; + if (*p == '\0') break; + + if (*p == '"') { + /* A string argument: scan for close quotes */ + p++; + args[thisArg].type = STRING_osc; + args[thisArg].datum.s = p; + + while (*p != '"') { + if (*p == '\0') { + send_complain("Unterminated quote mark: ignoring line\n"); + return; + } + p++; + } + *p = '\0'; + p++; + } else { + token = p; + while (!isspace(*p) && (*p != '\0')) p++; + if (isspace(*p)) { + *p = '\0'; + p++; + } + args[thisArg] = ParseToken(token); + } + thisArg++; + if (thisArg >= MAX_ARGS) { + send_complain("Sorry, your message has more than MAX_ARGS (%d) arguments; ignoring the rest.\n", + MAX_ARGS); + break; + } + } + + if (WriteMessage(buf, messageName, thisArg, args) != 0) { + send_complain("Problem sending message: %s\n", OSC_errorMessage); + } +} + +typedArg ParseToken(char *token) { + char *p = token; + typedArg returnVal; + + /* It might be an int, a float, or a string */ + + if (*p == '-') p++; + + if (isdigit(*p) || *p == '.') { + while (isdigit(*p)) p++; + if (*p == '\0') { + returnVal.type = INT_osc; + returnVal.datum.i = atoi(token); + return returnVal; + } + if (*p == '.') { + p++; + while (isdigit(*p)) p++; + if (*p == '\0') { + returnVal.type = FLOAT_osc; + returnVal.datum.f = atof(token); + return returnVal; + } + } + } + + returnVal.type = STRING_osc; + returnVal.datum.s = token; + return returnVal; +} + +int WriteMessage(OSCbuf *buf, char *messageName, int numArgs, typedArg *args) { + int j, returnVal; + const wmERROR = -1; + + returnVal = 0; + +#ifdef DEBUG + printf("WriteMessage: %s ", messageName); + + for (j = 0; j < numArgs; j++) { + switch (args[j].type) { + case INT_osc: + printf("%d ", args[j].datum.i); + break; + + case FLOAT_osc: + printf("%f ", args[j].datum.f); + break; + + case STRING_osc: + printf("%s ", args[j].datum.s); + break; + + default: + fatal_error("Unrecognized arg type, (not exiting)"); + return(wmERROR); + // exit(5); + } + } + printf("\n"); +#endif + + if (!useTypeTags) { + returnVal = OSC_writeAddress(buf, messageName); + if (returnVal) { + send_complain("Problem writing address: %s\n", OSC_errorMessage); + } + } else { + /* First figure out the type tags */ + char typeTags[MAX_ARGS+2]; + int i; + + typeTags[0] = ','; + + for (i = 0; i < numArgs; ++i) { + switch (args[i].type) { + case INT_osc: + typeTags[i+1] = 'i'; + break; + + case FLOAT_osc: + typeTags[i+1] = 'f'; + break; + + case STRING_osc: + typeTags[i+1] = 's'; + break; + + default: + fatal_error("Unrecognized arg type (not exiting)"); + return(wmERROR); + // exit(5); + } + } + typeTags[i+1] = '\0'; + + returnVal = OSC_writeAddressAndTypes(buf, messageName, typeTags); + if (returnVal) { + send_complain("Problem writing address: %s\n", OSC_errorMessage); + } + } + + 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: + fatal_error("Unrecognized arg type (not exiting)"); + returnVal = wmERROR; + // exit(5); + } + } + return returnVal; +} + +void SendBuffer(void *htmsocket, OSCbuf *buf) { +#ifdef DEBUG + printf("Sending buffer...\n"); +#endif + if (OSC_isBufferEmpty(buf)) { + post("SendBuffer() called but buffer empty"); + return; + } + if (!OSC_isBufferDone(buf)) { + fatal_error("SendBuffer() called but buffer not ready!, not exiting"); + // exit(5); + return; //{{raf}} + } + SendData(htmsocket, OSC_packetSize(buf), OSC_getPacket(buf)); +} + +void SendData(void *htmsocket, int size, char *data) { + if (!SendHTMSocket(htmsocket, size, data)) { + post("SendData::SendHTMSocket()failure -- not connected"); + CloseHTMSocket(htmsocket); + // sendOSC_disconnect(); + //exit(3); + // return; + } +} + +void fatal_error(char *s) { + fprintf(stderr, "FATAL ERROR: %s\n", s); + post("fatal error, not exiting ..."); + //exit(4); +} + +#include +void send_complain(char *s, ...) { + va_list ap; + va_start(ap, s); + vfprintf(stderr, s, ap); + va_end(ap); +} + + +#ifdef COMPUTE_MESSAGE_SIZE + /* Unused code to find the size of a message */ + + /* Compute size */ + size = SynthControl_effectiveStringLength(messageName); + + for (j = 0; j < numArgs; j++) { + switch (args[j].type) { + case INT_osc: case FLOAT_osc: + size += 4; + break; + + case STRING_osc: + size += SynthControl_effectiveStringLength(args[j].datum.s); + break; + + default: + fatal_error("Unrecognized token type ( not exiting)"); + // exit(4); + } + } + + if (!SynthControl_willMessageFit(buf, size)) { + send_complain("Message \"%s\" won't fit in buffer: dropping.", messageName); + return; + } +#endif diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..4a0dc8c --- /dev/null +++ b/README.txt @@ -0,0 +1,81 @@ +OSC, OpenSoundControl for pd +============================ +OSC: http://cnmat.cnmat.berkeley.edu/OSC +pd: http://lena.ucsd.edu/~msp/ + +ok, merged the windows and linux trees. +for linux do the usual makes etc, for window either use extra/OSC.dll, +.dsw and .dsp files are also included. + + +files: + +OSC/ contains the code for OSC pd objects (send,dump,route) +README.txt this file +doc/ pd help files +extra/ OSC.dll, the windows binary +libOSC/ CNMAT's OSC library +send+dump/ CNMAT's OSC commandline utils + http://cnmat.cnmat.berkeley.edu/OpenSoundControl/ + + +log: + + 20020417: 0.16-2: more changes by raf + jdl (send with no argument fix, send / fix, + ...) + + 20020416: added bundle stuff to sendOSC + + 200204: 0.15b1: windowified version and implied linux enhancements + by raf@interaccess.com + for now get it at http://207.208.254.239/pd/win32_osc_02.zip + most importantly: enhanced connect->disconnect-connect behaviour + (the win modifications to libOSC are still missing in _this_ + package but coming ..) + + + 200203: 0-0.1b1: all the rest + ost_at_test.at + i22_at_test.at, 2000-2002 + modified to compile as pd externel + + + + +INSTALL: + (linux) + +tar zxvf OSCx.tgz +cd OSCx +cat README +cd libOSC && make +cd ../OSC && "adjust makefile" && make OSC && make install +cd ../.. +pd -lib OSC OSCx/doc/OSC-help.pd + + PITFALLS: +make sure you compile libOSC before OSC objects +maybe adjust include path so pd include files will be found + + + (windo$) + +unzip and put .dll file in a pd-searched folder. + + +TYPETAGS: +supported and on by default. can be swtiched off with the "typetags 0" +message and on with 1. + + +TODO +==== +-timetags: output timetag when receiving a bundle for scheduling +-TCP mode +-address space integration with pd patch/subpatch/receive hierarchy ? + +see also TODO.txt in OSC/ + +-- +jdl at xdv.org, http://barely.a.live.fm/pd/OSC +windows version: +raf at interaccess.com, http://207.208.254.239/pd/win32_osc_02.zip diff --git a/doc/OSC-help.pd b/doc/OSC-help.pd new file mode 100644 index 0000000..b3b7a9a --- /dev/null +++ b/doc/OSC-help.pd @@ -0,0 +1,10 @@ +#N canvas 608 335 423 300 10; +#X obj 21 149 sendOSC-help; +#X obj 21 174 dumpOSC-help; +#X obj 21 200 OSCroute-help; +#X obj 17 43 OSC; +#X msg 17 19 version; +#X text 15 79 1 click sendOSC-help 2 click connect button 3 randomly +click the 3 available messages 4 watch console; +#X text 102 19 version 0.16; +#X connect 4 0 3 0; diff --git a/doc/OSCroute-help.pd b/doc/OSCroute-help.pd new file mode 100644 index 0000000..3d9dd2a --- /dev/null +++ b/doc/OSCroute-help.pd @@ -0,0 +1,26 @@ +#N canvas 112 247 446 321 10; +#X msg 20 72 version; +#X obj 86 72 r rcvOSC; +#X obj 86 109 OSCroute /test /ix /yps; +#X obj 161 143 print ix; +#X obj 242 142 print yps; +#X obj 85 183 OSCroute /bla /bli /blo; +#X obj 85 212 unpack f f f; +#X floatatom 85 239 4 0 0; +#X floatatom 124 239 4 0 0; +#X floatatom 164 239 4 0 0; +#X text 20 11 OSCroute works pretty much like route but outputs remainder +of routing tag after match.; +#X obj 68 143 print test; +#X connect 0 0 2 0; +#X connect 1 0 2 0; +#X connect 2 0 5 0; +#X connect 2 0 11 0; +#X connect 2 1 3 0; +#X connect 2 1 5 0; +#X connect 2 2 4 0; +#X connect 2 2 5 0; +#X connect 5 0 6 0; +#X connect 6 0 7 0; +#X connect 6 1 8 0; +#X connect 6 2 9 0; diff --git a/doc/dumpOSC-help.pd b/doc/dumpOSC-help.pd new file mode 100644 index 0000000..ab769ef --- /dev/null +++ b/doc/dumpOSC-help.pd @@ -0,0 +1,7 @@ +#N canvas 485 432 461 199 12; +#X obj 17 105 print rcvOSC; +#X obj 17 53 dumpOSC 9999; +#X obj 45 78 s rcvOSC; +#X text 15 7 dumpOSC listens on specified port / UDP \, outputs everything received on its only outlet. seems to be pretty stable.; +#X connect 1 0 0 0; +#X connect 1 0 2 0; diff --git a/doc/sendOSC-help.pd b/doc/sendOSC-help.pd new file mode 100644 index 0000000..dec1d76 --- /dev/null +++ b/doc/sendOSC-help.pd @@ -0,0 +1,57 @@ +#N canvas 496 472 848 467 10; +#X obj 59 396 sendOSC; +#X floatatom 59 423 5 0 0; +#X msg 44 119 disconnect; +#X msg 79 163 send /test/bla 1 2 3; +#X msg 26 93 connect localhost 9999; +#X msg 91 187 send /ix/bli 4 fuenf 6; +#X msg 103 211 send /yps/blo 7.8 9.1 zehnkommaelf; +#X text 8 142 ========================================; +#X msg 274 92 connect localhost 10001; +#X msg 149 301 typetags 1; +#X msg 144 333 typetags 0; +#X text 475 112 typetags; +#X text 475 129 sendOSC now by default uses; +#X text 477 145 typetags.; +#X text 477 166 dumOSC on the other hand; +#X text 477 186 should be able to interpret; +#X text 478 208 this.; +#X text 474 93 new (or again):; +#X msg 423 294 [; +#X msg 423 321 ]; +#X msg 256 249 send /bla 1 2 3 \, send /bli 3 4 5 \, send /blo 4 5 +6; +#X text 462 312 when last bundle is closed the buffer is sent.; +#X text 463 333 so: first "[" \, then add messages with send /bla; +#X text 466 351 open nested nudle etc \, close with "]"; +#X floatatom 113 425 5 0 0; +#X text 162 428 bundleDepth; +#X text 24 11 sendOSC: send OSC formatted packet to network. at the +moment over UDP only is supported.; +#X text 28 38 supported int \, flot + string.; +#X text 462 291 open / close bundles here.; +#X text 465 372 watch the bundleDepth index change while you do this. +; +#X text 231 299 switch usage of typetags; +#X text 232 316 on/off.; +#X text 231 332 default: on; +#X text 465 392 MAX_BUNDLE_NESTING: 32; +#X msg 315 154 send; +#X msg 315 179 send /; +#X msg 368 156 send /bang; +#X connect 0 0 1 0; +#X connect 0 1 24 0; +#X connect 2 0 0 0; +#X connect 3 0 0 0; +#X connect 4 0 0 0; +#X connect 5 0 0 0; +#X connect 6 0 0 0; +#X connect 8 0 0 0; +#X connect 9 0 0 0; +#X connect 10 0 0 0; +#X connect 18 0 0 0; +#X connect 19 0 0 0; +#X connect 20 0 0 0; +#X connect 34 0 0 0; +#X connect 35 0 0 0; +#X connect 36 0 0 0; diff --git a/extra/OSC.dll b/extra/OSC.dll new file mode 100644 index 0000000..2cb0455 Binary files /dev/null and b/extra/OSC.dll differ diff --git a/libOSC/LIBOSC.001 b/libOSC/LIBOSC.001 new file mode 100644 index 0000000..d1b40cc --- /dev/null +++ b/libOSC/LIBOSC.001 @@ -0,0 +1,94 @@ +# Microsoft Developer Studio Project File - Name="LIBOSC" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=LIBOSC - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "LIBOSC.MAK". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "LIBOSC.MAK" CFG="LIBOSC - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "LIBOSC - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "LIBOSC - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe + +!IF "$(CFG)" == "LIBOSC - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "LIBOSC - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "LIBOSC - Win32 Release" +# Name "LIBOSC - Win32 Debug" +# Begin Source File + +SOURCE=".\OSC-client.c" +# End Source File +# Begin Source File + +SOURCE=".\OSC-client.h" +# End Source File +# Begin Source File + +SOURCE=".\OSC-timetag.c" +# End Source File +# Begin Source File + +SOURCE=".\OSC-timetag.h" +# End Source File +# End Target +# End Project diff --git a/libOSC/LIBOSC.002 b/libOSC/LIBOSC.002 new file mode 100644 index 0000000..d2c0fe8 --- /dev/null +++ b/libOSC/LIBOSC.002 @@ -0,0 +1,100 @@ +# Microsoft Developer Studio Project File - Name="LIBOSC" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 5.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=LIBOSC - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "LIBOSC.MAK". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "LIBOSC.MAK" CFG="LIBOSC - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "LIBOSC - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "LIBOSC - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe + +!IF "$(CFG)" == "LIBOSC - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +RSC=rc.exe +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "../../pd/src" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\..\..\pd\lib\LIBOSC.lib" + +!ELSEIF "$(CFG)" == "LIBOSC - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +RSC=rc.exe +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /Z7 /Od /I "../../pd/src" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "LIBOSC - Win32 Release" +# Name "LIBOSC - Win32 Debug" +# Begin Source File + +SOURCE=".\OSC-client.c" +# End Source File +# Begin Source File + +SOURCE=".\OSC-client.h" +# End Source File +# Begin Source File + +SOURCE=".\OSC-timetag.c" +# End Source File +# Begin Source File + +SOURCE=".\OSC-timetag.h" +# End Source File +# End Target +# End Project diff --git a/libOSC/LIBOSC.DSP b/libOSC/LIBOSC.DSP new file mode 100644 index 0000000..99e661a --- /dev/null +++ b/libOSC/LIBOSC.DSP @@ -0,0 +1,100 @@ +# Microsoft Developer Studio Project File - Name="LIBOSC" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=LIBOSC - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "LIBOSC.MAK". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "LIBOSC.MAK" CFG="LIBOSC - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "LIBOSC - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "LIBOSC - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "LIBOSC - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /O2 /I "../../pd/src" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo /out:"..\..\..\pd\lib\LIBOSC.lib" + +!ELSEIF "$(CFG)" == "LIBOSC - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /Z7 /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD CPP /nologo /W3 /GX /Z7 /Od /I "../../pd/src" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /YX /FD /c +# ADD BASE RSC /l 0x409 +# ADD RSC /l 0x409 +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "LIBOSC - Win32 Release" +# Name "LIBOSC - Win32 Debug" +# Begin Source File + +SOURCE=".\OSC-client.c" +# End Source File +# Begin Source File + +SOURCE=".\OSC-client.h" +# End Source File +# Begin Source File + +SOURCE=".\OSC-timetag.c" +# End Source File +# Begin Source File + +SOURCE=".\OSC-timetag.h" +# End Source File +# End Target +# End Project diff --git a/libOSC/LIBOSC.DSW b/libOSC/LIBOSC.DSW new file mode 100644 index 0000000..ef47907 --- /dev/null +++ b/libOSC/LIBOSC.DSW @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "LIBOSC"=.\LIBOSC.DSP - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/libOSC/LIBOSC.PLG b/libOSC/LIBOSC.PLG new file mode 100644 index 0000000..d011835 --- /dev/null +++ b/libOSC/LIBOSC.PLG @@ -0,0 +1,29 @@ + + +
+

Build Log

+

+--------------------Configuration: LIBOSC - Win32 Release-------------------- +

+

Command Lines

+Creating temporary file "C:\DOCUME~1\gustav\LOCALS~1\Temp\RSPB.tmp" with contents +[ +/nologo /ML /W3 /GX /O2 /I "../../pd/src" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /Fp"Release/LIBOSC.pch" /YX /Fo"Release/" /Fd"Release/" /FD /c +"D:\pd\OSC\LIBOSC\OSC-client.c" +"D:\pd\OSC\LIBOSC\OSC-timetag.c" +] +Creating command line "cl.exe @C:\DOCUME~1\gustav\LOCALS~1\Temp\RSPB.tmp" +Creating command line "link.exe -lib /nologo /out:"..\..\..\pd\lib\LIBOSC.lib" ".\Release\OSC-client.obj" ".\Release\OSC-timetag.obj" " +

Output Window

+Compiling... +OSC-client.c +OSC-timetag.c +Creating library... + + + +

Results

+LIBOSC.lib - 0 error(s), 0 warning(s) +
+ + diff --git a/libOSC/Makefile b/libOSC/Makefile new file mode 100644 index 0000000..6261b37 --- /dev/null +++ b/libOSC/Makefile @@ -0,0 +1,21 @@ +CFLAGS= -O2 +LIB=libOSC.a + +LIBOBJS= ${LIB}(OSC-client.o) ${LIB}(OSC-timetag.o) + +all: ${LIBOBJS} + +.c.a: + ${CC} -c ${CFLAGS} $< + ${AR} ${ARFLAGS} $@ $*.o + rm -f $*.o + +test_OSC: test_OSC.o ${LIB} + cc -o test_OSC test_OSC.o ${LIB} + +test_OSC_timeTag: test_OSC_timeTag.o OSC-timetag.o + cc -o test_OSC_timeTag test_OSC_timeTag.o OSC-timetag.o + + +clean: + rm -f ${LIB} *.o diff --git a/libOSC/OSC-client.c b/libOSC/OSC-client.c new file mode 100644 index 0000000..27b07d0 --- /dev/null +++ b/libOSC/OSC-client.c @@ -0,0 +1,473 @@ +/* +Copyright (c) 1996. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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. +*/ + + +/* + 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 + +*/ + +/* 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 */ + +#ifdef WIN32 + #include + #include + #include + #include + #include + #include + #include +#endif + + +#include "OSC-client.h" + +char *OSC_errorMessage; + +#ifndef WIN32 + static int strlen(char *s); +#endif +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); + +void OSC_initBuffer(OSCbuf *buf, int size, char *byteArray) { + buf->buffer = byteArray; + buf->size = size; + OSC_resetBuffer(buf); +} + +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; +} + +int OSC_isBufferEmpty(OSCbuf *buf) { + return buf->bufptr == buf->buffer; +} + +int OSC_freeSpaceInBuffer(OSCbuf *buf) { + return buf->size - (buf->bufptr - buf->buffer); +} + +int OSC_isBufferDone(OSCbuf *buf) { + return (buf->state == DONE || buf->state == ONE_MSG_ARGS); +} + +char *OSC_getPacket(OSCbuf *buf) { +#ifdef ERROR_CHECK_GETPACKET + if (buf->state == DONE || buf->state == ONE_MSG_ARGS) { + return buf->buffer; + } else { + OSC_errorMessage = "Packet has unterminated bundles"; + return 0; + } +#else + return buf->buffer; +#endif +} + +int OSC_packetSize(OSCbuf *buf) { +#ifdef ERROR_CHECK_PACKETSIZE + if (buf->state == DONE || buf->state == ONE_MSG_ARGS) { + return (buf->bufptr - buf->buffer); + } else { + OSC_errorMessage = "Packet has unterminated bundles"; + return 0; + } +#else + return (buf->bufptr - buf->buffer); +#endif +} + +#define CheckOverflow(buf, bytesNeeded) { \ + if ((bytesNeeded) > OSC_freeSpaceInBuffer(buf)) { \ + OSC_errorMessage = "buffer overflow"; \ + return 1; \ + } \ +} + +static void PatchMessageSize(OSCbuf *buf) { + int4byte size; + size = buf->bufptr - ((char *) buf->thisMsgSize) - 4; + *(buf->thisMsgSize) = htonl(size); +} + +int OSC_openBundle(OSCbuf *buf, OSCTimeTag tt) { + if (buf->state == ONE_MSG_ARGS) { + OSC_errorMessage = "Can't open a bundle in a one-message packet"; + return 3; + } + + if (buf->state == DONE) { + OSC_errorMessage = "This packet is finished; can't open a new bundle"; + return 4; + } + + if (++(buf->bundleDepth) >= MAX_BUNDLE_NESTING) { + OSC_errorMessage = "Bundles nested too deeply; change MAX_BUNDLE_NESTING in OpenSoundControl.h"; + 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 */ + CheckOverflow(buf, 16); + } else { + /* This bundle is inside another bundle, so we need to leave + a blank size count for the size of this current bundle. */ + CheckOverflow(buf, 20); + *((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]); + +#ifdef HAS8BYTEINT + { /* tt is a 64-bit int so we have to swap the two 32-bit words. + (Otherwise 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.) */ + int4byte temp = intp[0]; + intp[0] = intp[1]; + intp[1] = temp; + } +#endif + } + + buf->bufptr += sizeof(OSCTimeTag); + + buf->state = NEED_COUNT; + + buf->gettingFirstUntypedArg = 0; + buf->typeStringPtr = 0; + return 0; +} + + +int OSC_closeBundle(OSCbuf *buf) { + if (buf->bundleDepth == 0) { + /* This handles EMPTY, ONE_MSG, ARGS, and DONE */ + OSC_errorMessage = "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; +} + + +int OSC_closeAllBundles(OSCbuf *buf) { + if (buf->bundleDepth == 0) { + /* This handles EMPTY, ONE_MSG, ARGS, and DONE */ + OSC_errorMessage = "Can't close all bundles; no bundle is open!"; + return 6; + } + + if (CheckTypeTag(buf, '\0')) return 9; + + while (buf->bundleDepth > 0) { + OSC_closeBundle(buf); + } + buf->typeStringPtr = 0; + return 0; +} + +int OSC_writeAddress(OSCbuf *buf, char *name) { + int4byte paddedLength; + + if (buf->state == ONE_MSG_ARGS) { + OSC_errorMessage = "This packet is not a bundle, so you can't write another address"; + return 7; + } + + if (buf->state == DONE) { + OSC_errorMessage = "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 */ + CheckOverflow(buf, paddedLength); + buf->state = ONE_MSG_ARGS; + } else { + /* GET_ARGS or NEED_COUNT */ + CheckOverflow(buf, 4+paddedLength); + 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; +} + +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); + + CheckOverflow(buf, paddedLength); + + 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') { + OSC_errorMessage = + "According to the type tag I expected more arguments."; + } else if (*(buf->typeStringPtr) == '\0') { + OSC_errorMessage = + "According to the type tag I didn't expect any more arguments."; + } else { + OSC_errorMessage = + "According to the type tag I expected an argument of a different type."; + printf("* Expected %c, string now %s\n", expectedType, buf->typeStringPtr); + } + return 9; + } + ++(buf->typeStringPtr); + } + return 0; +} + + +int OSC_writeFloatArg(OSCbuf *buf, float arg) { + int4byte *intp; + //int result; + + CheckOverflow(buf, 4); + + 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; +} + + + +int OSC_writeFloatArgs(OSCbuf *buf, int numFloats, float *args) { + int i; + int4byte *intp; + + CheckOverflow(buf, 4 * numFloats); + + /* Pretend args are long ints so we can use htonl() */ + intp = ((int4byte *) args); + + for (i = 0; i < numFloats; i++) { + if (CheckTypeTag(buf, 'f')) return 9; + *((int4byte *) buf->bufptr) = htonl(intp[i]); + buf->bufptr += 4; + } + + buf->gettingFirstUntypedArg = 0; + return 0; +} + +int OSC_writeIntArg(OSCbuf *buf, int4byte arg) { + CheckOverflow(buf, 4); + if (CheckTypeTag(buf, 'i')) return 9; + + *((int4byte *) buf->bufptr) = htonl(arg); + buf->bufptr += 4; + + buf->gettingFirstUntypedArg = 0; + return 0; +} + +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. */ + + CheckOverflow(buf, len+4); /* Too conservative */ + buf->bufptr += + OSC_padStringWithAnExtraStupidComma(buf->bufptr, arg); + + } else { + CheckOverflow(buf, len); + buf->bufptr += OSC_padString(buf->bufptr, arg); + } + + buf->gettingFirstUntypedArg = 0; + return 0; + +} + +/* String utilities */ + +#ifndef WIN32 +static int strlen(char *s) { + int i; + for (i=0; s[i] != '\0'; i++) /* Do nothing */ ; + return i; +} +#endif + +#define STRING_ALIGN_PAD 4 +int OSC_effectiveStringLength(char *string) { + int len = 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; +} diff --git a/libOSC/OSC-client.c.pre-htonl b/libOSC/OSC-client.c.pre-htonl new file mode 100644 index 0000000..6aebfe2 --- /dev/null +++ b/libOSC/OSC-client.c.pre-htonl @@ -0,0 +1,303 @@ +/* +Copyright (c) 1996. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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. +*/ + + +/* + Author: Matt Wright + Version 2.1 + */ + + +/* 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 */ + +#include "OSC-client.h" + +char *OSC_errorMessage; + + +static int strlen(char *s); +static int OSC_padString(char *dest, char *str); + +void OSC_initBuffer(OSCbuf *buf, int size, char *byteArray) { + buf->buffer = byteArray; + buf->size = size; + OSC_resetBuffer(buf); +} + +void OSC_resetBuffer(OSCbuf *buf) { + buf->bufptr = buf->buffer; + buf->state = EMPTY; + buf->bundleDepth = 0; + buf->prevCounts[0] = 0; +} + +int OSC_isBufferEmpty(OSCbuf *buf) { + return buf->bufptr == buf->buffer; +} + +int OSC_freeSpaceInBuffer(OSCbuf *buf) { + return buf->size - (buf->bufptr - buf->buffer); +} + +int OSC_isBufferDone(OSCbuf *buf) { + return (buf->state == DONE || buf->state == ONE_MSG_ARGS); +} + +char *OSC_getPacket(OSCbuf *buf) { +#ifdef ERROR_CHECK_GETPACKET + if (buf->state == DONE || buf->state == ONE_MSG_ARGS) { + return buf->buffer; + } else { + OSC_errorMessage = "Packet has unterminated bundles"; + return 0; + } +#else + return buf->buffer; +#endif +} + +int OSC_packetSize(OSCbuf *buf) { +#ifdef ERROR_CHECK_PACKETSIZE + if (buf->state == DONE || buf->state == ONE_MSG_ARGS) { + return (buf->bufptr - buf->buffer); + } else { + OSC_errorMessage = "Packet has unterminated bundles"; + return 0; + } +#else + return (buf->bufptr - buf->buffer); +#endif +} + +#define CheckOverflow(buf, bytesNeeded) { \ + if ((bytesNeeded) > OSC_freeSpaceInBuffer(buf)) { \ + OSC_errorMessage = "buffer overflow"; \ + return 1; \ + } \ +} + +static void PatchMessageSize(OSCbuf *buf) { + int4byte size; + size = buf->bufptr - ((char *) buf->thisMsgSize) - 4; + *(buf->thisMsgSize) = size; +} + +int OSC_openBundle(OSCbuf *buf, OSCTimeTag tt) { + if (buf->state == ONE_MSG_ARGS) { + OSC_errorMessage = "Can't open a bundle in a one-message packet"; + return 3; + } + + if (buf->state == DONE) { + OSC_errorMessage = "This packet is finished; can't open a new bundle"; + return 4; + } + + if (++(buf->bundleDepth) >= MAX_BUNDLE_NESTING) { + OSC_errorMessage = "Bundles nested too deeply; change MAX_BUNDLE_NESTING in OpenSoundControl.h"; + return 2; + } + + if (buf->state == GET_ARGS) { + PatchMessageSize(buf); + } + + if (buf->state == EMPTY) { + /* Need 16 bytes for "#bundle" and time tag */ + CheckOverflow(buf, 16); + } else { + /* This bundle is inside another bundle, so we need to leave + a blank size count for the size of this current bundle. */ + CheckOverflow(buf, 20); + *((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; + buf->bufptr += sizeof(OSCTimeTag); + + buf->state = NEED_COUNT; + return 0; +} + + +int OSC_closeBundle(OSCbuf *buf) { + if (buf->bundleDepth == 0) { + /* This handles EMPTY, ONE_MSG, ARGS, and DONE */ + OSC_errorMessage = "Can't close bundle; no bundle is open!"; + return 5; + } + + 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]) = size; + buf->state = NEED_COUNT; + } + + --buf->bundleDepth; + return 0; +} + + +int OSC_closeAllBundles(OSCbuf *buf) { + if (buf->bundleDepth == 0) { + /* This handles EMPTY, ONE_MSG, ARGS, and DONE */ + OSC_errorMessage = "Can't close all bundles; no bundle is open!"; + return 6; + } + + while (buf->bundleDepth > 0) { + OSC_closeBundle(buf); + } + return 0; +} + +int OSC_writeAddress(OSCbuf *buf, char *name) { + int4byte paddedLength; + + if (buf->state == ONE_MSG_ARGS) { + OSC_errorMessage = "This packet is not a bundle, so you can't write another address"; + return 7; + } + + if (buf->state == DONE) { + OSC_errorMessage = "This packet is finished; can't write another address"; + return 8; + } + + paddedLength = OSC_effectiveStringLength(name); + + if (buf->state == EMPTY) { + /* This will be a one-message packet, so no sizes to worry about */ + CheckOverflow(buf, paddedLength); + buf->state = ONE_MSG_ARGS; + } else { + /* GET_ARGS or NEED_COUNT */ + CheckOverflow(buf, 4+paddedLength); + 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); + return 0; +} + +int OSC_writeFloatArg(OSCbuf *buf, float arg) { + CheckOverflow(buf, 4); + *((float *) buf->bufptr) = arg; + buf->bufptr += 4; + return 0; +} + +int OSC_writeFloatArgs(OSCbuf *buf, int numFloats, float *args) { + int i; + CheckOverflow(buf, 4 * numFloats); + for (i = 0; i < numFloats; i++) { + *((float *) buf->bufptr) = args[i]; + buf->bufptr += 4; + } + return 0; +} + +int OSC_writeIntArg(OSCbuf *buf, int4byte arg) { + CheckOverflow(buf, 4); + *((int4byte *) buf->bufptr) = arg; + buf->bufptr += 4; + return 0; +} + +int OSC_writeStringArg(OSCbuf *buf, char *arg) { + CheckOverflow(buf, OSC_effectiveStringLength(arg)); + buf->bufptr += OSC_padString(buf->bufptr, arg); + return 0; +} + +/* String utilities */ + +static int strlen(char *s) { + int i; + for (i=0; s[i] != '\0'; i++) /* Do nothing */ ; + return i; +} + +#define STRING_ALIGN_PAD 4 +int OSC_effectiveStringLength(char *string) { + int len = 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]; + } + + dest[i] = '\0'; + i++; + + for (; (i % STRING_ALIGN_PAD) != 0; i++) { + dest[i] = '\0'; + } + + return i; +} + diff --git a/libOSC/OSC-client.h b/libOSC/OSC-client.h new file mode 100644 index 0000000..b1fd833 --- /dev/null +++ b/libOSC/OSC-client.h @@ -0,0 +1,181 @@ +/* +Copyright (c) 1996,1997. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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. +*/ + +/* + + 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. + +*/ + + +#include "OSC-timetag.h" + +/* The int4byte type has to be a 4-byte integer. You may have to + change this to long or something else on your system. */ +#ifdef __MWERKS__ + /* In Metrowerks you can set ints to be 2 or 4 bytes on 68K, but long is + always 4 bytes */ + typedef long int4byte; +#else + typedef int int4byte; +#endif + +/* 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; + + + +/* 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.) */ +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. */ +void OSC_resetBuffer(OSCbuf *buf); + + +/* Is the buffer empty? (I.e., would it be stupid to send the buffer + contents to the synth?) */ +int OSC_isBufferEmpty(OSCbuf *buf); + + +/* How much space is left in the buffer? */ +int OSC_freeSpaceInBuffer(OSCbuf *buf); + +/* Does the buffer contain a valid OSC packet? (Returns nonzero if yes.) */ +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.) */ +char *OSC_getPacket(OSCbuf *buf); +int OSC_packetSize(OSCbuf *buf); + + + +/* 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_writeFloatArgs() + OSC_writeIntArg() + OSC_writeStringArg() + + - Now your message is complete; you can send out the buffer or you can + add another message to it. +*/ + +int OSC_openBundle(OSCbuf *buf, OSCTimeTag tt); +int OSC_closeBundle(OSCbuf *buf); +int OSC_closeAllBundles(OSCbuf *buf); + +int OSC_writeAddress(OSCbuf *buf, char *name); +int OSC_writeAddressAndTypes(OSCbuf *buf, char *name, char *types); +int OSC_writeFloatArg(OSCbuf *buf, float arg); +int OSC_writeFloatArgs(OSCbuf *buf, int numFloats, float *args); +int OSC_writeIntArg(OSCbuf *buf, int4byte arg); +int OSC_writeStringArg(OSCbuf *buf, char *arg); + +extern char *OSC_errorMessage; + +/* 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. */ +int OSC_effectiveStringLength(char *string); diff --git a/libOSC/OSC-timetag.c b/libOSC/OSC-timetag.c new file mode 100644 index 0000000..639eae9 --- /dev/null +++ b/libOSC/OSC-timetag.c @@ -0,0 +1,175 @@ +/* +Copyright (c) 1998. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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 OpenSound Control WWW page is + http://www.cnmat.berkeley.edu/OpenSoundControl +*/ + +/* + + 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. + +*/ + +#include "OSC-timetag.h" + + +#ifdef HAS8BYTEINT +#define TWO_TO_THE_32_FLOAT 4294967296.0f + +OSCTimeTag OSCTT_Immediately(void) { + return (OSCTimeTag) 1; +} + +OSCTimeTag OSCTT_BiggestPossibleTimeTag(void) { + return (OSCTimeTag) 0xffffffffffffffff; +} + +OSCTimeTag OSCTT_PlusSeconds(OSCTimeTag original, float secondsOffset) { + int64 offset = (int64) (secondsOffset * TWO_TO_THE_32_FLOAT); + +/* printf("* OSCTT_PlusSeconds %llx plus %f seconds (i.e., %lld offset) is %llx\n", original, + secondsOffset, offset, original + offset); */ + + return original + offset; +} + +int OSCTT_Compare(OSCTimeTag left, OSCTimeTag right) { +#if 0 + printf("***** OSCTT_Compare(%llx, %llx): %d\n", left, right, + (left + +#define SECONDS_FROM_1900_to_1970 2208988800 /* 17 leap years */ +#define TWO_TO_THE_32_OVER_ONE_MILLION 4295 + + +OSCTimeTag OSCTT_CurrentTime(void) { + uint64 result; + uint32 usecOffset; + struct timeval tv; + struct timezone tz; + + BSDgettimeofday(&tv, &tz); + + /* First get the seconds right */ + result = (unsigned) SECONDS_FROM_1900_to_1970 + + (unsigned) tv.tv_sec - + (unsigned) 60 * tz.tz_minuteswest + + (unsigned) (tz.tz_dsttime ? 3600 : 0); + +#if 0 + /* No timezone, no DST version ... */ + result = (unsigned) SECONDS_FROM_1900_to_1970 + + (unsigned) tv.tv_sec; +#endif + + + /* make seconds the high-order 32 bits */ + result = result << 32; + + /* Now get the fractional part. */ + usecOffset = (unsigned) tv.tv_usec * (unsigned) TWO_TO_THE_32_OVER_ONE_MILLION; + /* printf("** %ld microsec is offset %x\n", tv.tv_usec, usecOffset); */ + + result += usecOffset; + +/* printf("* OSCTT_CurrentTime is %llx\n", result); */ + return result; +} + +#else /* __sgi */ + +/* Instead of asking your operating system what time it is, it might be + clever to find out the current time at the instant your application + starts audio processing, and then keep track of the number of samples + output to know how much time has passed. */ + +/* Loser version for systems that have no ability to tell the current time: */ +OSCTimeTag OSCTT_CurrentTime(void) { + return (OSCTimeTag) 1; +} + +#endif /* __sgi */ + + +#else /* Not HAS8BYTEINT */ + +OSCTimeTag OSCTT_CurrentTime(void) { + OSCTimeTag result; + result.seconds = 0; + result.fraction = 1; + return result; +} + +OSCTimeTag OSCTT_BiggestPossibleTimeTag(void) { + OSCTimeTag result; + result.seconds = 0xffffffff; + result.fraction = 0xffffffff; + return result; +} + +OSCTimeTag OSCTT_Immediately(void) { + OSCTimeTag result; + result.seconds = 0; + result.fraction = 1; + return result; +} + +OSCTimeTag OSCTT_PlusSeconds(OSCTimeTag original, float secondsOffset) { + OSCTimeTag result; + result.seconds = 0; + result.fraction = 1; + return result; +} + +int OSCTT_Compare(OSCTimeTag left, OSCTimeTag right) { + /* Untested! */ + int highResult = left.seconds - right.seconds; + + if (highResult != 0) return highResult; + + return left.fraction - right.fraction; +} + + +#endif /* HAS8BYTEINT */ + diff --git a/libOSC/OSC-timetag.h b/libOSC/OSC-timetag.h new file mode 100644 index 0000000..3ce693a --- /dev/null +++ b/libOSC/OSC-timetag.h @@ -0,0 +1,93 @@ +/* +Copyright (c) 1998. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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 OpenSound Control WWW page is + http://www.cnmat.berkeley.edu/OpenSoundControl +*/ + +/* + 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 a 64-bit + unsigned long if possible, or else a struct. + + NB: On many architectures with 64-bit ints, it's illegal (like maybe a bus error) + to dereference a pointer to a 64-bit int that's not 64-bit aligned. +*/ + +#ifndef OSC_TIMETAG +#define OSC_TIMETAG + +#ifdef __sgi + #define HAS8BYTEINT + /* You may have to change this typedef if there's some other + way to specify 64 bit ints on your system */ + typedef long long int64; + typedef unsigned long long uint64; + typedef unsigned long uint32; +#else + /* You may have to redefine this typedef if ints on your system + aren't 32 bits. */ + typedef unsigned int uint32; +#endif + + +#ifdef HAS8BYTEINT + typedef uint64 OSCTimeTag; +#else + typedef struct { + uint32 seconds; + uint32 fraction; + } OSCTimeTag; +#endif + + + +/* Return a time tag representing the current time (as of when this + procedure is called). */ +OSCTimeTag OSCTT_CurrentTime(void); + +/* Return the time tag 0x0000000000000001, indicating to the receiving device + that it should process the message immediately. */ +OSCTimeTag OSCTT_Immediately(void); + +/* Return the time tag 0xffffffffffffffff, a time so far in the future that + it's effectively infinity. */ +OSCTimeTag OSCTT_BiggestPossibleTimeTag(void); + +/* Given a time tag and a number of seconds to add to the time tag, return + the new time tag */ +OSCTimeTag OSCTT_PlusSeconds(OSCTimeTag original, float secondsOffset); + +/* Compare two time tags. Return negative if first is < second, 0 if + they're equal, and positive if first > second. */ +int OSCTT_Compare(OSCTimeTag left, OSCTimeTag right); + +#endif /* OSC_TIMETAG */ diff --git a/libOSC/test_OSC.c b/libOSC/test_OSC.c new file mode 100644 index 0000000..4593ec6 --- /dev/null +++ b/libOSC/test_OSC.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 1997 Regents of the University of California. + * All rights reserved. + * + * The name of the University may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE + * IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE. + */ + +/* + test_OSC.c + Trivial program to test OpenSoundControl.[ch] + + Matt Wright 6/2/97 +*/ + +#include +#include +#include "OpenSoundControl.h" + +#define SIZE 10000 + +void PrintBuf(OSCbuf *b) { + printf("Buffer is %sempty.\n", OSC_isBufferEmpty(b) ? "" : "not "); + printf("%d bytes free in buffer\n", OSC_freeSpaceInBuffer(b)); + printf("Buffer is %sready to send\n", OSC_isBufferDone(b) ?"":"not "); + + printf("Buffer: bufptr %p, state %d, thisMsgSize %p, bundleDepth %d\n" + "prevCounts[%d] %p\n", b->bufptr, b->state, b->thisMsgSize, + b->bundleDepth, b->bundleDepth, b->prevCounts[b->bundleDepth]); +} + +void PrintPacket(OSCbuf *b) { + char *p = OSC_getPacket(b); + int size = OSC_packetSize(b); + unsigned int *intp; + int i; + + printf("PrintPacket: packet at %p, size %d\n", p, size); + if (p == 0 || size == 0) return; + + printf("Hex version:"); + for (i = 0, intp = (unsigned int *)p; i < size; i += 4, intp++) { + if (i % 40 == 0) printf("\n"); + printf("%x ", *intp); + } + + printf("\n\nString version:"); + for (i = 0; i < size; i++) { + if (i % 40 == 0) printf("\n"); + if (isprint(p[i])) { + printf("%c", p[i]); + } else { + printf("\\%x", p[i] & 0x000000ff); + } + } + printf("\n"); +} + + +main() { + OSCbuf myBuf; + OSCbuf *b = &myBuf; + char bytes[SIZE]; + OSCTimeTag tt; + + printf("OSC_initBuffer\n"); + OSC_initBuffer(b, SIZE, bytes); + + PrintBuf(b); + + printf("Testing one-message packet\n"); + if (OSC_writeAddress(b, "/blah/bleh/singlemessage")) { + printf("** ERROR: %s\n", OSC_errorMessage); + } + + if (OSC_writeFloatArg(b, 1.23456f)) { + printf("** ERROR: %s\n", OSC_errorMessage); + } + + { + float floatarray[10]; + int i; + for (i = 0; i < 10; ++i) { + floatarray[i] = i * 10.0f; + } + if (OSC_writeFloatArgs(b, 10, floatarray)) { + printf("** ERROR: %s\n", OSC_errorMessage); + } + } + + if (OSC_writeIntArg(b, 123456)) { + printf("** ERROR: %s\n", OSC_errorMessage); + } + + if (OSC_writeStringArg(b, "This is a cool string, dude.")) { + printf("** ERROR: %s\n", OSC_errorMessage); + } + + PrintBuf(b); + PrintPacket(b); + + printf("Resetting\n"); + OSC_resetBuffer(b); + + printf("Testing time tags\n"); + tt = OSCTT_CurrentTime(); + printf("Time now is %llx\n", tt); + + printf("Testing bundles\n"); + if (OSC_openBundle(b, tt)) { + printf("** ERROR: %s\n", OSC_errorMessage); + } + + if (OSC_writeAddress(b, "/a/hello")) { + printf("** ERROR: %s\n", OSC_errorMessage); + } + + if (OSC_writeIntArg(b, 16)) { + printf("** ERROR: %s\n", OSC_errorMessage); + } + + if (OSC_writeIntArg(b, 32)) { + printf("** ERROR: %s\n", OSC_errorMessage); + } + + if (OSC_openBundle(b, OSCTT_PlusSeconds(tt, 1.0f))) { + printf("** ERROR: %s\n", OSC_errorMessage); + } + + if (OSC_writeAddress(b, "/b/hello")) { + printf("** ERROR: %s\n", OSC_errorMessage); + } + + if (OSC_writeAddress(b, "/c/hello")) { + printf("** ERROR: %s\n", OSC_errorMessage); + } + + OSC_closeAllBundles(b); + + PrintBuf(b); + PrintPacket(b); +} + + + diff --git a/libOSC/test_OSC_timeTag.c b/libOSC/test_OSC_timeTag.c new file mode 100644 index 0000000..8a1cabf --- /dev/null +++ b/libOSC/test_OSC_timeTag.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1997 Regents of the University of California. + * All rights reserved. + * + * The name of the University may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE + * IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE. + */ + +/* + test_OSC_timeTag.c + Matt Wright, 5/30/97 +*/ + +#include +#include "OSC_timeTag.h" + +main() { + OSCTimeTag now, later; + + now = OSCTT_CurrentTime(); + printf("Now it's %llu (0x%llx)\n", now, now); + + printf("Immediately would be %llu (0x%llx)\n", OSCTT_Immediately(), + OSCTT_Immediately()); + + later = OSCTT_PlusSeconds(now, 1.0f); + printf("One second from now would be %llu (0x%llx)\n", later, later); + + now = OSCTT_CurrentTime(); + printf("And *now* it's %llu (0x%llx)\n", now, now); +} + diff --git a/send+dump/Makefile b/send+dump/Makefile new file mode 100644 index 0000000..7b14a43 --- /dev/null +++ b/send+dump/Makefile @@ -0,0 +1,25 @@ +LIBOSCDIR = ../libOSC +LIBOSC = ${LIBOSCDIR}/libOSC.a +CFLAGS= -O2 -I$(LIBOSCDIR) + +DUMPOBJS=dumpOSC.o + + +both: sendOSC dumpOSC + +sendOSC: sendOSC.o htmsocket.o ${LIBOSC} + ${CC} -o sendOSC sendOSC.o htmsocket.o ${LIBOSC} + +dumpOSC: ${DUMPOBJS} + ${CC} -o $@ ${DUMPOBJS} + +dumpUDP: dumpUDP.o + ${CC} -o dumpUDP dumpUDP.o + +${LIBOSC}: + echo "You need to go to " ${LIBOSCDIR} " and do a make." + +clean: + rm -f sendOSC dumpOSC *.o + + diff --git a/send+dump/dumpOSC.c b/send+dump/dumpOSC.c new file mode 100644 index 0000000..143a994 --- /dev/null +++ b/send+dump/dumpOSC.c @@ -0,0 +1,716 @@ +/* +Copyright (c) 1992,1993,1994,1995,1996,1997,2000. +The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright and Adrian Freed, The Center for New Music and Audio +Technologies, University of California, Berkeley. + + 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. +*/ + + /* + + dumpOSC.c + server that displays OpenSoundControl messages sent to it + for debugging client udp and UNIX protocol + + by Matt Wright, 6/3/97 + modified from dumpSC.c, by Matt Wright and Adrian Freed + + version 0.2: Added "-silent" option a.k.a. "-quiet" + + version 0.3: Incorporated patches from Nicola Bernardini to make + things Linux-friendly. Also added ntohl() in the right places + to support little-endian architectures. + + + + compile: + cc -o dumpOSC dumpOSC.c + + to-do: + + More robustness in saying exactly what's wrong with ill-formed + messages. (If they don't make sense, show exactly what was + received.) + + Time-based features: print time-received for each packet + + Clean up to separate OSC parsing code from socket/select stuff + +*/ + + +#if defined(__sgi) || defined(__linux) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef NEED_SCHEDCTL_AND_LOCK +#include +#include +#endif + + +char *htm_error_string; +typedef int Boolean; +typedef void *OBJ; + +typedef struct ClientAddressStruct { + struct sockaddr_in cl_addr; + int clilen; + int sockfd; +} *ClientAddr; + +Boolean ShowBytes = FALSE; +Boolean Silent = FALSE; + +/* Declarations */ +static int unixinitudp(int chan); +static int initudp(int chan); +static void closeudp(int sockfd); +Boolean ClientReply(int packetsize, void *packet, int socketfd, + void *clientaddresspointer, int clientaddressbufferlength); +void sgi_CleanExit(void); +Boolean sgi_HaveToQuit(void); +int RegisterPollingDevice(int fd, void (*callbackfunction)(int , void *), void *dummy); +static void catch_sigint(); +static int Synthmessage(char *m, int n, void *clientdesc, int clientdesclength, int fd) ; +void ParseOSCPacket(char *buf, int n, ClientAddr returnAddr); +static void Smessage(char *address, void *v, int n, ClientAddr returnAddr); +static void PrintTypeTaggedArgs(void *v, int n); +static void PrintHeuristicallyTypeGuessedArgs(void *v, int n, int skipComma); +char *DataAfterAlignedString(char *string, char *boundary) ; +Boolean IsNiceString(char *string, char *boundary) ; +void complain(char *s, ...); + + +#define UNIXDG_PATH "/tmp/htm" +#define UNIXDG_TMP "/tmp/htm.XXXXXX" +static int unixinitudp(int chan) +{ + struct sockaddr_un serv_addr; + int sockfd; + + if((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) + return sockfd; + + bzero((char *)&serv_addr, sizeof(serv_addr)); + serv_addr.sun_family = AF_UNIX; + strcpy(serv_addr.sun_path, UNIXDG_PATH); + sprintf(serv_addr.sun_path+strlen(serv_addr.sun_path), "%d", chan); + unlink(serv_addr.sun_path); + if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr.sun_family)+strlen(serv_addr.sun_path)) < 0) + { + perror("unable to bind\n"); + return -1; + } + + fcntl(sockfd, F_SETFL, FNDELAY); + return sockfd; +} + +static int initudp(int chan) +{ + struct sockaddr_in serv_addr; + int sockfd; + + if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + return sockfd; + bzero((char *)&serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + serv_addr.sin_port = htons(chan); + + if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) + { + perror("unable to bind\n"); + return -1; + } + + fcntl(sockfd, F_SETFL, FNDELAY); + return sockfd; +} + +static void closeudp(int sockfd) { + close(sockfd); +} + +static Boolean catchupflag=FALSE; +Boolean ClientReply(int packetsize, void *packet, int socketfd, + void *clientaddresspointer, int clientaddressbufferlength) +{ + if(!clientaddresspointer) return FALSE; + catchupflag= TRUE; + return packetsize==sendto(socketfd, packet, packetsize, 0, clientaddresspointer, clientaddressbufferlength); +} + +static Boolean exitflag= FALSE; +void sgi_CleanExit(void) { + exitflag = TRUE; +} + +Boolean sgi_HaveToQuit(void) { + return exitflag; +} + + +/* file descriptor poll table */ +static int npolldevs =0; +typedef struct polldev +{ + int fd; + void (*callbackfunction)(int , void *); + void *dummy; +} polldev; +#define TABMAX 8 +static polldev polldevs[TABMAX]; + + +/* Register a device (referred to by a file descriptor that the caller + should have already successfully obtained from a system call) to be + polled as real-time constraints allowed. + + When a select(2) call indicates activity on the file descriptor, the + callback function is called with the file descripter as first + argument and the given dummy argument (presumably a pointer to the + instance variables associated with the device). +*/ +int RegisterPollingDevice(int fd, void (*callbackfunction)(int , void *), void *dummy) +{ + if(npolldevscl_addr.sin_addr.s_addr; + printf("Client address %p:\n", CA); + printf(" clilen %d, sockfd %d\n", CA->clilen, CA->sockfd); + printf(" sin_family %d, sin_port %d\n", CA->cl_addr.sin_family, + CA->cl_addr.sin_port); + printf(" address: (%x) %s\n", addr, inet_ntoa(CA->cl_addr.sin_addr)); + + printf(" sin_zero = \"%c%c%c%c%c%c%c%c\"\n", + CA->cl_addr.sin_zero[0], + CA->cl_addr.sin_zero[1], + CA->cl_addr.sin_zero[2], + CA->cl_addr.sin_zero[3], + CA->cl_addr.sin_zero[4], + CA->cl_addr.sin_zero[5], + CA->cl_addr.sin_zero[6], + CA->cl_addr.sin_zero[7]); + + printf("\n"); +} + + +void ParseOSCPacket(char *buf, int n, ClientAddr returnAddr) { + int size, messageLen, i; + char *messageName; + char *args; + +#ifdef PRINTADDRS + PrintClientAddr(returnAddr); +#endif + + + if ((n%4) != 0) { + complain("SynthControl packet size (%d) not a multiple of 4 bytes: dropping", + n); + return; + } + + if ((n >= 8) && (strncmp(buf, "#bundle", 8) == 0)) { + /* This is a bundle message. */ + + if (n < 16) { + complain("Bundle message too small (%d bytes) for time tag", n); + return; + } + + /* Print the time tag */ + printf("[ %lx%08lx\n", ntohl(*((unsigned long *)(buf+8))), + ntohl(*((unsigned long *)(buf+12)))); + /* Note: if we wanted to actually use the time tag as a little-endian + 64-bit int, we'd have to word-swap the two 32-bit halves of it */ + + i = 16; /* Skip "#group\0" and time tag */ + while(i n) { + complain("Bad size count %d in bundle (only %d bytes left in entire bundle)", + size, n-i-4); + return; + } + + /* Recursively handle element of bundle */ + ParseOSCPacket(buf+i+4, size, returnAddr); + i += 4 + size; + } + if (i != n) { + complain("This can't happen"); + } + printf("]\n"); + } else { + /* This is not a bundle message */ + + messageName = buf; + args = DataAfterAlignedString(messageName, buf+n); + if (args == 0) { + complain("Bad message name string: %s\nDropping entire message.\n", + htm_error_string); + return; + } + messageLen = args-messageName; + Smessage(messageName, (void *)args, n-messageLen, returnAddr); + } +} + +#define SMALLEST_POSITIVE_FLOAT 0.000001f + +static void Smessage(char *address, void *v, int n, ClientAddr returnAddr) { + char *chars = v; + + printf("%s ", address); + + if (n != 0) { + if (chars[0] == ',') { + if (chars[1] != ',') { + /* This message begins with a type-tag string */ + PrintTypeTaggedArgs(v, n); + } else { + /* Double comma means an escaped real comma, not a type string */ + PrintHeuristicallyTypeGuessedArgs(v, n, 1); + } + } else { + PrintHeuristicallyTypeGuessedArgs(v, n, 0); + } + } + + printf("\n"); + fflush(stdout); /* Added for Sami 5/21/98 */ +} + +static void PrintTypeTaggedArgs(void *v, int n) { + char *typeTags, *thisType; + char *p; + + typeTags = v; + + if (!IsNiceString(typeTags, typeTags+n)) { + /* No null-termination, so maybe it wasn't a type tag + string after all */ + PrintHeuristicallyTypeGuessedArgs(v, n, 0); + return; + } + + p = DataAfterAlignedString(typeTags, typeTags+n); + + + for (thisType = typeTags + 1; *thisType != 0; ++thisType) { + switch (*thisType) { + case 'i': case 'r': case 'm': case 'c': + printf("%d ", ntohl(*((int *) p))); + p += 4; + break; + + case 'f': { + int i = ntohl(*((int *) p)); + float *floatp = ((float *) (&i)); + printf("%f ", *floatp); + p += 4; + } + break; + + case 'h': case 't': + printf("[A 64-bit int] "); + p += 8; + break; + + case 'd': + printf("[A 64-bit float] "); + p += 8; + break; + + case 's': case 'S': + if (!IsNiceString(p, typeTags+n)) { + printf("Type tag said this arg is a string but it's not!\n"); + return; + } else { + printf("\"%s\" ", p); + p = DataAfterAlignedString(p, typeTags+n); + } + break; + + case 'T': printf("[True] "); break; + case 'F': printf("[False] "); break; + case 'N': printf("[Nil]"); break; + case 'I': printf("[Infinitum]"); break; + + default: + printf("[Unrecognized type tag %c]", *thisType); + return; + } + } +} + +static void PrintHeuristicallyTypeGuessedArgs(void *v, int n, int skipComma) { + int i, thisi; + float thisf; + int *ints; + char *chars; + char *string, *nextString; + + + /* Go through the arguments 32 bits at a time */ + ints = v; + chars = v; + + for (i = 0; i= -1000 && thisi <= 1000000) { + printf("%d ", thisi); + i++; + } else if (thisf >= -1000.f && thisf <= 1000000.f && + (thisf <=0.0f || thisf >= SMALLEST_POSITIVE_FLOAT)) { + printf("%f ", thisf); + i++; + } else if (IsNiceString(string, chars+n)) { + nextString = DataAfterAlignedString(string, chars+n); + printf("\"%s\" ", (i == 0 && skipComma) ? string +1 : string); + i += (nextString-string) / 4; + } else { + printf("0x%x ", ints[i]); + i++; + } + } +} + + +#define STRING_ALIGN_PAD 4 + +char *DataAfterAlignedString(char *string, char *boundary) +{ + /* The argument is a block of data beginning with a string. The + string has (presumably) been padded with extra null characters + so that the overall length is a multiple of STRING_ALIGN_PAD + bytes. Return a pointer to the next byte after the null + byte(s). The boundary argument points to the character after + the last valid character in the buffer---if the string hasn't + ended by there, something's wrong. + + If the data looks wrong, return 0, and set htm_error_string */ + + int i; + + if ((boundary - string) %4 != 0) { + fprintf(stderr, "Internal error: DataAfterAlignedString: bad boundary\n"); + return 0; + } + + for (i = 0; string[i] != '\0'; i++) { + if (string + i >= boundary) { + htm_error_string = "DataAfterAlignedString: Unreasonably long string"; + return 0; + } + } + + /* Now string[i] is the first null character */ + i++; + + for (; (i % STRING_ALIGN_PAD) != 0; i++) { + if (string + i >= boundary) { + htm_error_string = "DataAfterAlignedString: Unreasonably long string"; + return 0; + } + if (string[i] != '\0') { + htm_error_string = "DataAfterAlignedString: Incorrectly padded string."; + return 0; + } + } + + return string+i; +} + +Boolean IsNiceString(char *string, char *boundary) +{ + /* Arguments same as DataAfterAlignedString(). Is the given "string" + really a string? I.e., is it a sequence of isprint() characters + terminated with 1-4 null characters to align on a 4-byte boundary? */ + + int i; + + if ((boundary - string) %4 != 0) { + fprintf(stderr, "Internal error: IsNiceString: bad boundary\n"); + return 0; + } + + for (i = 0; string[i] != '\0'; i++) { + if (!isprint(string[i])) return FALSE; + if (string + i >= boundary) return FALSE; + } + + /* If we made it this far, it's a null-terminated sequence of printing characters + in the given boundary. Now we just make sure it's null padded... */ + + /* Now string[i] is the first null character */ + i++; + for (; (i % STRING_ALIGN_PAD) != 0; i++) { + if (string[i] != '\0') return FALSE; + } + + return TRUE; +} + + + + + + + +#define MAXMESG 32768 +static char mbuf[MAXMESG]; + +int main(int argc, char **argv) { + int udp_port; /* port to receive parameter updates from */ + + struct sockaddr_in cl_addr; + int clilen,maxclilen=sizeof(cl_addr); + struct sockaddr_un ucl_addr; + int uclilen,umaxclilen=sizeof(ucl_addr); + int i,n; + + + clilen = maxclilen; + uclilen = umaxclilen; + + udp_port = -1; + for (i=1; i < argc; ++i) { + if (strcmp(argv[i], "-showbytes") == 0) { + ShowBytes = TRUE; + } else if (strcmp(argv[i], "-silent") == 0 || + strcmp(argv[i], "-quiet") == 0) { + Silent = TRUE; + } else if (udp_port != -1) { + goto usageError; + } else { + udp_port = atoi(argv[i]); + if (udp_port == 0) { + goto usageError; + } + } + } + + if (udp_port == -1) { + usageError: + fprintf(stderr, "Usage\n\tdumpOSC portno [-showbytes] [-quiet]\n\t(responds to udp and UNIX packets on that port no)\n"); + exit(1); + } + + + n = recvfrom(0, mbuf, MAXMESG, 0, &cl_addr, &clilen); + if(n>0) + { + sockfd = 0; + udp_port = -1; + Synthmessage(mbuf, n, &cl_addr, clilen,sockfd) ; + } + else + { sockfd=initudp(udp_port); + usockfd=unixinitudp(udp_port); + } + + if (!Silent) { + printf("dumpOSC version 0.2 (6/18/97 Matt Wright). Unix/UDP Port %d \n", udp_port); + printf("Copyright (c) 1992,1996,1997 Regents of the University of California.\n"); + } + if(sockfd>=0 && usockfd>=0) + { + fd_set read_fds, write_fds; + int nfds; +#define max(a,b) (((a) > (b)) ? (a) : (b)) + nfds = max(sockfd, usockfd)+ 1; + { + int j; + for(j=0;j=nfds) + { + nfds = polldevs[j].fd+1; +/* +printf("polldev %d\n", polldevs[j].fd); +*/ + } + } +/* + printf("nfds %d\n", nfds); +*/ + caught_sigint = 0; + sigset(SIGINT, catch_sigint); /* set sig handler */ + + while(!caught_sigint) + { + + int r; + + back: + + FD_ZERO(&read_fds); /* clear read_fds */ + FD_ZERO(&write_fds); /* clear write_fds */ + FD_SET(sockfd, &read_fds); + FD_SET(usockfd, &read_fds); + { + int j; + + for(j=0;j0) + { + int r; + /* printf("received UDP packet of length %d\n", n); */ + r = Synthmessage(mbuf, n, &cl_addr, clilen, sockfd) ; + + if( sgi_HaveToQuit()) goto out; + if(r>0) goto back; + clilen = maxclilen; + } + } + if(FD_ISSET(usockfd, &read_fds)) + { + uclilen = umaxclilen; + while( (n = recvfrom(usockfd, mbuf, MAXMESG, 0, &ucl_addr, &uclilen)) >0) + { + int r; + /* printf("received UNIX packet of length %d\n", n); */ + + r=Synthmessage(mbuf, n, &ucl_addr, uclilen,usockfd) ; + + if( sgi_HaveToQuit()) goto out; + if(r>0) goto back; + uclilen = umaxclilen; + } + } + } /* End of while(!caught_sigint) */ + + +out: ; + } + else + perror("initudp"); + + return 0; +} + + +#include +void complain(char *s, ...) { + va_list ap; + va_start(ap, s); + fprintf(stderr, "*** ERROR: "); + vfprintf(stderr, s, ap); + fprintf(stderr, "\n"); + va_end(ap); +} + +#endif /* __sgi or LINUX */ diff --git a/send+dump/dumpUDP.c b/send+dump/dumpUDP.c new file mode 100644 index 0000000..3876812 --- /dev/null +++ b/send+dump/dumpUDP.c @@ -0,0 +1,191 @@ +/* +Copyright (c) 1998. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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. + +dumpUDP.c: smallest UDP receiving application +by Matt Wright, 9/9/98 + +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef struct ClientAddressStruct { + struct sockaddr_in cl_addr; + int clilen; + int sockfd; +} *ClientAddr; + +void PrintClientAddr(ClientAddr CA) { + unsigned long addr = CA->cl_addr.sin_addr.s_addr; + printf("Client address %p:\n", CA); + printf(" clilen %d, sockfd %d\n", CA->clilen, CA->sockfd); + printf(" sin_family %d, sin_port %d\n", CA->cl_addr.sin_family, + CA->cl_addr.sin_port); + printf(" address: (%x) %s\n", addr, inet_ntoa(CA->cl_addr.sin_addr)); + + printf(" sin_zero = \"%c%c%c%c%c%c%c%c\"\n", + CA->cl_addr.sin_zero[0], + CA->cl_addr.sin_zero[1], + CA->cl_addr.sin_zero[2], + CA->cl_addr.sin_zero[3], + CA->cl_addr.sin_zero[4], + CA->cl_addr.sin_zero[5], + CA->cl_addr.sin_zero[6], + CA->cl_addr.sin_zero[7]); + + printf("\n"); +} + + +static int initudp(int port) { + struct sockaddr_in serv_addr; + int n, sockfd; + + if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + return sockfd; + bzero((char *)&serv_addr, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); + serv_addr.sin_port = htons(port); + + if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) + { + perror("unable to bind\n"); + return -1; + } + + fcntl(sockfd, F_SETFL, FNDELAY); + return sockfd; +} + + +static void closeudp(int sockfd) { + close(sockfd); +} + + +static int time_to_quit; + +static void catch_sigint() { + time_to_quit = 1; +} + +void GotAPacket(char *buf, int n, ClientAddr returnAddr) { + printf("received UDP packet of length %d\n", n); + PrintClientAddr(returnAddr); +} + +#define MAXMESG 32768 +static char mbuf[MAXMESG]; + +void ReceivePacket(int sockfd) { + struct ClientAddressStruct returnAddress; + int maxclilen=sizeof(returnAddress.cl_addr); + int n; + + returnAddress.clilen = maxclilen; + while( (n = recvfrom(sockfd, mbuf, MAXMESG, 0, &(returnAddress.cl_addr), + &(returnAddress.clilen))) >0) { + GotAPacket(mbuf, n, &returnAddress); + + if (time_to_quit) return; + returnAddress.clilen = maxclilen; + } +} + +void main(int argc, char **argv) { + int udp_port; /* port to receive parameter updates from */ + int sockfd; + int i; + + fd_set read_fds, write_fds; + int nfds; + + udp_port = 7000; + + sockfd=initudp(udp_port); + + if(sockfd<0) { + perror("initudp"); + return; + } + + nfds = sockfd + 1; + + time_to_quit = 0; + sigset(SIGINT, catch_sigint); /* set sig handler */ + + while(!time_to_quit) + { + + int c,r; + + back: + + FD_ZERO(&read_fds); /* clear read_fds */ + FD_ZERO(&write_fds); /* clear write_fds */ + FD_SET(sockfd, &read_fds); + + + r = select(nfds, &read_fds, &write_fds, (fd_set *)0, + (struct timeval *)0); + if (r < 0) /* select reported an error */ + goto out; + + if(FD_ISSET(sockfd, &read_fds)) { + ReceivePacket(sockfd); + } + + } /* End of while(!time_to_quit) */ +out: ; +} diff --git a/send+dump/htmsocket.c b/send+dump/htmsocket.c new file mode 100644 index 0000000..e4f447f --- /dev/null +++ b/send+dump/htmsocket.c @@ -0,0 +1,230 @@ +/* +Copyright (c) 1992,1996,1998. +The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Adrian Freed, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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. +*/ + + /* htmsocket.c + + Adrian Freed + send parameters to htm servers by udp or UNIX protocol + + Modified 6/6/96 by Matt Wright to understand symbolic host names + in addition to X.X.X.X addresses. + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define UNIXDG_PATH "/tmp/htm" +#define UNIXDG_TMP "/tmp/htm.XXXXXX" +#include "htmsocket.h" +typedef struct +{ + float srate; + + struct sockaddr_in serv_addr; /* udp socket */ + struct sockaddr_un userv_addr; /* UNIX socket */ + int sockfd; /* socket file descriptor */ + int index, len,uservlen; + void *addr; + int id; +} desc; + +/* open a socket for HTM communication to given host on given portnumber */ +/* if host is 0 then UNIX protocol is used (i.e. local communication */ +void *OpenHTMSocket(char *host, int portnumber) +{ + int sockfd; + struct sockaddr_in cl_addr; + struct sockaddr_un ucl_addr; + desc *o; + o = malloc(sizeof(*o)); + if(!o) + return 0; + if(!host) + { + char *mktemp(char *); + int clilen; + o->len = sizeof(ucl_addr); + /* + * Fill in the structure "userv_addr" with the address of the + * server that we want to send to. + */ + + bzero((char *) &o->userv_addr, sizeof(o->userv_addr)); + o->userv_addr.sun_family = AF_UNIX; + strcpy(o->userv_addr.sun_path, UNIXDG_PATH); + sprintf(o->userv_addr.sun_path+strlen(o->userv_addr.sun_path), "%d", portnumber); + o->uservlen = sizeof(o->userv_addr.sun_family) + strlen(o->userv_addr.sun_path); + o->addr = &(o->userv_addr); + /* + * Open a socket (a UNIX domain datagram socket). + */ + + if ( (sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) >= 0) + { + /* + * Bind a local address for us. + * In the UNIX domain we have to choose our own name (that + * should be unique). We'll use mktemp() to create a unique + * pathname, based on our process id. + */ + + bzero((char *) &ucl_addr, sizeof(ucl_addr)); /* zero out */ + ucl_addr.sun_family = AF_UNIX; + strcpy(ucl_addr.sun_path, UNIXDG_TMP); + + mktemp(ucl_addr.sun_path); + clilen = sizeof(ucl_addr.sun_family) + strlen(ucl_addr.sun_path); + + if (bind(sockfd, (struct sockaddr *) &ucl_addr, clilen) < 0) + { + perror("client: can't bind local address"); + close(sockfd); + sockfd = -1; + } + } + else + perror("unable to make socket\n"); + + }else + { + /* + * Fill in the structure "serv_addr" with the address of the + * server that we want to send to. + */ + o->len = sizeof(cl_addr); + bzero((char *)&o->serv_addr, sizeof(o->serv_addr)); + o->serv_addr.sin_family = AF_INET; + + /* MW 6/6/96: Call gethostbyname() instead of inet_addr(), + so that host can be either an Internet host name (e.g., + "les") or an Internet address in standard dot notation + (e.g., "128.32.122.13") */ + { + struct hostent *hostsEntry; + unsigned long address; + + hostsEntry = gethostbyname(host); + if (hostsEntry == NULL) { + fprintf(stderr, "Couldn't decipher host name \"%s\"\n", + host); + herror(NULL); + return 0; + } + + address = *((unsigned long *) hostsEntry->h_addr_list[0]); + o->serv_addr.sin_addr.s_addr = address; + } + + /* was: o->serv_addr.sin_addr.s_addr = inet_addr(host); */ + + /* End MW changes */ + + o->serv_addr.sin_port = htons(portnumber); + o->addr = &(o->serv_addr); + /* + * Open a socket (a UDP domain datagram socket). + */ + if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) + { + bzero((char *)&cl_addr, sizeof(cl_addr)); + cl_addr.sin_family = AF_INET; + cl_addr.sin_addr.s_addr = htonl(INADDR_ANY); + cl_addr.sin_port = htons(0); + + if(bind(sockfd, (struct sockaddr *) &cl_addr, sizeof(cl_addr)) < 0) + { + perror("could not bind\n"); + close(sockfd); + sockfd = -1; + } + } + else + { + perror("unable to make socket\n"); + } + + } + if(sockfd<0) + { + free(o); o = 0; + } + else + o->sockfd = sockfd; + return o; +} +#include + +static bool sendudp(const struct sockaddr *sp, int sockfd,int length, int count, void *b) +{ + int rcount; + if((rcount=sendto(sockfd, b, count, 0, sp, length)) != count) + { +/* printf("sockfd %d count %d rcount %dlength %d errno %d\n", sockfd,count,rcount,length, + errno); */ + return FALSE; + } + return TRUE; +} +bool SendHTMSocket(void *htmsendhandle, int length_in_bytes, void *buffer) +{ + desc *o = (desc *)htmsendhandle; + return sendudp(o->addr, o->sockfd, o->len, length_in_bytes, buffer); +} +void CloseHTMSocket(void *htmsendhandle) +{ + desc *o = (desc *)htmsendhandle; + close(o->sockfd); + free(o); +} diff --git a/send+dump/htmsocket.h b/send+dump/htmsocket.h new file mode 100644 index 0000000..b035b57 --- /dev/null +++ b/send+dump/htmsocket.h @@ -0,0 +1,49 @@ +/* +Copyright (c) 1992,1996. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Adrian Freed, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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. +*/ + + /* htmparam.h + + Adrian Freed + send parameters to htm servers by udp or UNIX protocol + */ +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif +typedef int bool; + +/* open a socket for HTM communication to given host on given portnumber */ +/* if host is 0 then UNIX protocol is used (i.e. local communication) */ +void *OpenHTMSocket(char *host, int portnumber); + +/* send a buffer of data over htm socket, returns TRUE on success. + Note that udp sends rarely fail. UNIX sends fail if a kernal buffer overflows */ +bool SendHTMSocket(void *htmsendhandle, int length_in_bytes, void *buffer); + +/* close the socket(2) and release memory associated with it */ +void CloseHTMSocket(void *htmsendhandle); diff --git a/send+dump/sendOSC.c b/send+dump/sendOSC.c new file mode 100644 index 0000000..db188dd --- /dev/null +++ b/send+dump/sendOSC.c @@ -0,0 +1,600 @@ +/* +Copyright (c) 1996,1997. The Regents of the University of California (Regents). +All Rights Reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation for educational, research, and not-for-profit purposes, 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. Contact The Office of +Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, +CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. + +Written by Matt Wright, The Center for New Music and Audio Technologies, +University of California, Berkeley. + + 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. +*/ + +/* sendOSC.c + + Matt Wright, 6/3/97 + based on sendSC.c, which was based on a version by Adrian Freed + + Text-based OpenSoundControl client. User can enter messages via command + line arguments or standard input. + + Version 0.1: "play" feature + Version 0.2: Message type tags. + +*/ + +#define VERSION "http://cnmat.berkeley.edu/OpenSoundControl/sendOSC-0.1.html" + +/* +compiling: + cc -o sendOSC sendOSC.c htmsocket.c OpenSoundControl.c OSC_timeTag.c +*/ + + +#include "OSC-client.h" +#include "htmsocket.h" + +#include +#include +/* #include */ +#include + + +typedef struct { + enum {INT, FLOAT, STRING} type; + union { + int i; + float f; + char *s; + } datum; +} typedArg; + +void CommandLineMode(int argc, char *argv[], void *htmsocket); +void InteractiveMode(void *htmsocket); +OSCTimeTag ParseTimeTag(char *s); +void ParseInteractiveLine(OSCbuf *buf, char *mesg); +typedArg ParseToken(char *token); +int WriteMessage(OSCbuf *buf, char *messageName, int numArgs, typedArg *args); +void SendBuffer(void *htmsocket, OSCbuf *buf); +void SendData(void *htmsocket, int size, char *data); +void fatal_error(char *s); +void complain(char *s, ...); + +/* Exit status codes: + 0: successful + 2: Message(s) dropped because of buffer overflow + 3: Socket error + 4: Usage error + 5: Internal error +*/ +static int exitStatus = 0; + + +static int useTypeTags = 1; + +main(int argc, char *argv[]) { + int portnumber; + char *hostname = 0; + void *htmsocket; + + argc--; + argv++; + + if (argc == 0) { + goto usageerror; + } + + if (argc >= 1 && (strncmp(*argv, "-notypetags", 2) == 0)) { + useTypeTags = 0; + argv++; + argc--; + } + + if (argc >= 2 && (strncmp(*argv, "-r", 2) == 0)) { + hostname = getenv("REMOTE_ADDR"); + if (hostname == NULL) { + complain("sendSC -r: REMOTE_ADDR not in environment\n"); + exit(4); + } + argv++; + argc--; + } + + if (argc >= 3 && (strncmp(*argv, "-h", 2) == 0)) { + hostname = argv[1]; + argv += 2; + argc -= 2; + } + portnumber = atoi(*argv); + argv++; + argc--; + + htmsocket = OpenHTMSocket(hostname, portnumber); + if (!htmsocket) { + perror("Couldn't open socket: "); + exit(3); + } + + if (argc > 0) { + printf("host %s, port %d, %s\n", hostname, portnumber, + useTypeTags ? "use type tags" : "don't use type tags"); + CommandLineMode(argc, argv, htmsocket); + } else { + printf("sendOSC version " VERSION "\n"); + printf("by Matt Wright. Copyright (c) 1996, 1997 Regents of the University of California.\n"); + printf("host %s, port %d, %s\n", hostname, portnumber, + useTypeTags ? "use type tags" : "don't use type tags"); + InteractiveMode(htmsocket); + } + CloseHTMSocket(htmsocket); + exit(exitStatus); + + + usageerror: + complain("usage: %s [-notypetags] [-r] [-h target_host_name] port_number [message...]\n", + argv[-1]); + exit(4); + +} + + +#define MAX_ARGS 2000 +#define SC_BUFFER_SIZE 32000 +static char bufferForOSCbuf[SC_BUFFER_SIZE]; + +void CommandLineMode(int argc, char *argv[], void *htmsocket) { + char *messageName; + char *token; + typedArg args[MAX_ARGS]; + int i,j, numArgs; + OSCbuf buf[1]; + + OSC_initBuffer(buf, SC_BUFFER_SIZE, bufferForOSCbuf); + + if (argc > 1) { + if (OSC_openBundle(buf, OSCTT_Immediately())) { + complain("Problem opening bundle: %s\n", OSC_errorMessage); + return; + } + } + + for (i = 0; i < argc; i++) { + messageName = strtok(argv[i], ","); + if (messageName == NULL) { + break; + } + + j = 0; + while ((token = strtok(NULL, ",")) != NULL) { + args[j] = ParseToken(token); + j++; + if (j >= MAX_ARGS) { + complain("Sorry; your message has more than MAX_ARGS (%d) arguments; ignoring the rest.\n", + MAX_ARGS); + break; + } + } + numArgs = j; + + WriteMessage(buf, messageName, numArgs, args); + } + + if (argc > 1) { + if (OSC_closeBundle(buf)) { + complain("Problem closing bundle: %s\n", OSC_errorMessage); + return; + } + } + + SendBuffer(htmsocket, buf); +} + +#define MAXMESG 2048 + +void InteractiveMode(void *htmsocket) { + char mesg[MAXMESG]; + OSCbuf buf[1]; + int bundleDepth = 0; /* At first, we haven't seen "[". */ + + OSC_initBuffer(buf, SC_BUFFER_SIZE, bufferForOSCbuf); + + while (fgets(mesg, MAXMESG, stdin) != NULL) { + if (mesg[0] == '\n') { + if (bundleDepth > 0) { + /* Ignore blank lines inside a group. */ + } else { + /* blank line => repeat previous send */ + SendBuffer(htmsocket, buf); + } + continue; + } + + if (bundleDepth == 0) { + OSC_resetBuffer(buf); + } + + if (mesg[0] == '[') { + OSCTimeTag tt = ParseTimeTag(mesg+1); + if (OSC_openBundle(buf, tt)) { + complain("Problem opening bundle: %s\n", OSC_errorMessage); + OSC_resetBuffer(buf); + bundleDepth = 0; + continue; + } + bundleDepth++; + } else if (mesg[0] == ']' && mesg[1] == '\n' && mesg[2] == '\0') { + if (bundleDepth == 0) { + complain("Unexpected ']': not currently in a bundle.\n"); + } else { + if (OSC_closeBundle(buf)) { + complain("Problem closing bundle: %s\n", OSC_errorMessage); + OSC_resetBuffer(buf); + bundleDepth = 0; + continue; + } + + bundleDepth--; + if (bundleDepth == 0) { + SendBuffer(htmsocket, buf); + } + } + } else { + ParseInteractiveLine(buf, mesg); + if (bundleDepth != 0) { + /* Don't send anything until we close all bundles */ + } else { + SendBuffer(htmsocket, buf); + } + } + } +} + +OSCTimeTag ParseTimeTag(char *s) { + char *p, *newline; + typedArg arg; + + p = s; + while (isspace(*p)) p++; + if (*p == '\0') return OSCTT_Immediately(); + + if (*p == '+') { + /* Time tag is for some time in the future. It should be a + number of seconds as an int or float */ + + newline = strchr(s, '\n'); + if (newline != NULL) *newline = '\0'; + + p++; /* Skip '+' */ + while (isspace(*p)) p++; + + arg = ParseToken(p); + if (arg.type == STRING) { + complain("warning: inscrutable time tag request: %s\n", s); + return OSCTT_Immediately(); + } else if (arg.type == INT) { + return OSCTT_PlusSeconds(OSCTT_CurrentTime(), + (float) arg.datum.i); + } else if (arg.type == FLOAT) { + return OSCTT_PlusSeconds(OSCTT_CurrentTime(), arg.datum.f); + } else { + fatal_error("This can't happen!"); + } + } + + if (isdigit(*p) || (*p >= 'a' && *p <='f') || (*p >= 'A' && *p <='F')) { + /* They specified the 8-byte tag in hex */ + OSCTimeTag tt; + if (sscanf(p, "%llx", &tt) != 1) { + complain("warning: couldn't parse time tag %s\n", s); + return OSCTT_Immediately(); + } +#ifndef HAS8BYTEINT + if (ntohl(1) != 1) { + /* tt is a struct of seconds and fractional part, + and this machine is little-endian, so sscanf + wrote each half of the time tag in the wrong half + of the struct. */ + uint32 temp; + temp = tt.seconds; + tt.seconds = tt.fraction ; + tt.fraction = temp; + } +#endif + return tt; + } + + complain("warning: invalid time tag: %s\n", s); + return OSCTT_Immediately(); +} + + +void ParseInteractiveLine(OSCbuf *buf, char *mesg) { + char *messageName, *token, *p; + typedArg args[MAX_ARGS]; + int thisArg; + + p = mesg; + while (isspace(*p)) p++; + if (*p == '\0') return; + + messageName = p; + + if (strcmp(messageName, "play\n") == 0) { + /* Special kludge feature to save typing */ + typedArg arg; + + if (OSC_openBundle(buf, OSCTT_Immediately())) { + complain("Problem opening bundle: %s\n", OSC_errorMessage); + return; + } + + arg.type = INT; + arg.datum.i = 0; + WriteMessage(buf, "/voices/0/tp/timbre_index", 1, &arg); + + arg.type = FLOAT; + arg.datum.i = 0.0f; + WriteMessage(buf, "/voices/0/tm/goto", 1, &arg); + + if (OSC_closeBundle(buf)) { + complain("Problem closing bundle: %s\n", OSC_errorMessage); + } + + return; + } + + while (!isspace(*p) && *p != '\0') p++; + if (isspace(*p)) { + *p = '\0'; + p++; + } + + thisArg = 0; + while (*p != '\0') { + /* flush leading whitespace */ + while (isspace(*p)) p++; + if (*p == '\0') break; + + if (*p == '"') { + /* A string argument: scan for close quotes */ + p++; + args[thisArg].type = STRING; + args[thisArg].datum.s = p; + + while (*p != '"') { + if (*p == '\0') { + complain("Unterminated quote mark: ignoring line\n"); + return; + } + p++; + } + *p = '\0'; + p++; + } else { + token = p; + while (!isspace(*p) && (*p != '\0')) p++; + if (isspace(*p)) { + *p = '\0'; + p++; + } + args[thisArg] = ParseToken(token); + } + thisArg++; + if (thisArg >= MAX_ARGS) { + complain("Sorry, your message has more than MAX_ARGS (%d) arguments; ignoring the rest.\n", + MAX_ARGS); + break; + } + } + + if (WriteMessage(buf, messageName, thisArg, args) != 0) { + complain("Problem sending message: %s\n", OSC_errorMessage); + } +} + +typedArg ParseToken(char *token) { + char *p = token; + typedArg returnVal; + + /* It might be an int, a float, or a string */ + + if (*p == '-') p++; + + if (isdigit(*p) || *p == '.') { + while (isdigit(*p)) p++; + if (*p == '\0') { + returnVal.type = INT; + returnVal.datum.i = atoi(token); + return returnVal; + } + if (*p == '.') { + p++; + while (isdigit(*p)) p++; + if (*p == '\0') { + returnVal.type = FLOAT; + returnVal.datum.f = atof(token); + return returnVal; + } + } + } + + returnVal.type = STRING; + returnVal.datum.s = token; + return returnVal; +} + +int WriteMessage(OSCbuf *buf, char *messageName, int numArgs, typedArg *args) { + int j, returnVal; + + returnVal = 0; + +#ifdef DEBUG + printf("WriteMessage: %s ", messageName); + + for (j = 0; j < numArgs; j++) { + switch (args[j].type) { + case INT: + printf("%d ", args[j].datum.i); + break; + + case FLOAT: + printf("%f ", args[j].datum.f); + break; + + case STRING: + printf("%s ", args[j].datum.s); + break; + + default: + fatal_error("Unrecognized arg type"); + exit(5); + } + } + printf("\n"); +#endif + + if (!useTypeTags) { + returnVal = OSC_writeAddress(buf, messageName); + if (returnVal) { + complain("Problem writing address: %s\n", OSC_errorMessage); + } + } else { + /* First figure out the type tags */ + char typeTags[MAX_ARGS+2]; + int i; + + typeTags[0] = ','; + + for (i = 0; i < numArgs; ++i) { + switch (args[i].type) { + case INT: + typeTags[i+1] = 'i'; + break; + + case FLOAT: + typeTags[i+1] = 'f'; + break; + + case STRING: + typeTags[i+1] = 's'; + break; + + default: + fatal_error("Unrecognized arg type"); + exit(5); + } + } + typeTags[i+1] = '\0'; + + returnVal = OSC_writeAddressAndTypes(buf, messageName, typeTags); + if (returnVal) { + complain("Problem writing address: %s\n", OSC_errorMessage); + } + } + + for (j = 0; j < numArgs; j++) { + switch (args[j].type) { + case INT: + if ((returnVal = OSC_writeIntArg(buf, args[j].datum.i)) != 0) { + return returnVal; + } + break; + + case FLOAT: + if ((returnVal = OSC_writeFloatArg(buf, args[j].datum.f)) != 0) { + return returnVal; + } + break; + + case STRING: + if ((returnVal = OSC_writeStringArg(buf, args[j].datum.s)) != 0) { + return returnVal; + } + break; + + default: + fatal_error("Unrecognized arg type"); + exit(5); + } + } + + return returnVal; +} + +void SendBuffer(void *htmsocket, OSCbuf *buf) { +#ifdef DEBUG + printf("Sending buffer...\n"); +#endif + if (OSC_isBufferEmpty(buf)) return; + if (!OSC_isBufferDone(buf)) { + fatal_error("SendBuffer() called but buffer not ready!"); + exit(5); + } + SendData(htmsocket, OSC_packetSize(buf), OSC_getPacket(buf)); +} + +void SendData(void *htmsocket, int size, char *data) { + if (!SendHTMSocket(htmsocket, size, data)) { + perror("Couldn't send out socket: "); + CloseHTMSocket(htmsocket); + exit(3); + } +} + +void fatal_error(char *s) { + fprintf(stderr, "%s\n", s); + exit(4); +} + +#include +void complain(char *s, ...) { + va_list ap; + va_start(ap, s); + vfprintf(stderr, s, ap); + va_end(ap); +} + + +#ifdef COMPUTE_MESSAGE_SIZE + /* Unused code to find the size of a message */ + + /* Compute size */ + size = SynthControl_effectiveStringLength(messageName); + + for (j = 0; j < numArgs; j++) { + switch (args[j].type) { + case INT: case FLOAT: + size += 4; + break; + + case STRING: + size += SynthControl_effectiveStringLength(args[j].datum.s); + break; + + default: + fatal_error("Unrecognized token type"); + exit(4); + } + } + + if (!SynthControl_willMessageFit(buf, size)) { + complain("Message \"%s\" won't fit in buffer: dropping.", messageName); + return; + } +#endif -- cgit v1.2.1