From d8847bccfcc9ac116d81cd18ce485f5595e7f6bb Mon Sep 17 00:00:00 2001 From: jdl Date: Wed, 10 Mar 2004 00:01:56 +0000 Subject: changed to single external style ... svn path=/trunk/externals/OSCx/; revision=1399 --- Makefile.in | 15 + README.txt | 13 +- configure.ac | 160 ++++++++ libOSC/Makefile | 26 -- libOSC/Makefile.in | 28 ++ send+dump/Makefile | 31 -- src/INVENTORY.txt | 21 + src/Makefile.in | 94 +++++ src/OSC-common.h | 62 +++ src/OSC-pattern-match.c | 207 ++++++++++ src/OSC-pattern-match.h | 35 ++ src/OSC.001 | 147 +++++++ src/OSC.c | 66 ++++ src/OSC.dsp | 148 +++++++ src/OSC.dsw | 29 ++ src/OSCroute.c | 437 +++++++++++++++++++++ src/TODO.txt | 27 ++ src/VERSION | 1 + src/dumpOSC.c | 996 ++++++++++++++++++++++++++++++++++++++++++++++++ src/htmsocket.c | 319 ++++++++++++++++ src/htmsocket.h | 49 +++ src/sendOSC.c | 881 ++++++++++++++++++++++++++++++++++++++++++ 22 files changed, 3733 insertions(+), 59 deletions(-) create mode 100644 Makefile.in create mode 100644 configure.ac delete mode 100644 libOSC/Makefile create mode 100644 libOSC/Makefile.in delete mode 100644 send+dump/Makefile create mode 100644 src/INVENTORY.txt create mode 100644 src/Makefile.in create mode 100644 src/OSC-common.h create mode 100644 src/OSC-pattern-match.c create mode 100644 src/OSC-pattern-match.h create mode 100644 src/OSC.001 create mode 100644 src/OSC.c create mode 100644 src/OSC.dsp create mode 100644 src/OSC.dsw create mode 100644 src/OSCroute.c create mode 100644 src/TODO.txt create mode 100644 src/VERSION create mode 100644 src/dumpOSC.c create mode 100644 src/htmsocket.c create mode 100644 src/htmsocket.h create mode 100644 src/sendOSC.c diff --git a/Makefile.in b/Makefile.in new file mode 100644 index 0000000..4a627a9 --- /dev/null +++ b/Makefile.in @@ -0,0 +1,15 @@ +dirs = libOSC send+dump src + +all: + cd libOSC ; make + cd send+dump ; make + cd src ; make + +install: + cd src ; make install + +clean: + cd libOSC && make clean + cd send+dump && make clean + cd src && make clean + diff --git a/README.txt b/README.txt index ccd1a9c..30ce444 100644 --- a/README.txt +++ b/README.txt @@ -4,14 +4,20 @@ for more information on OSC see: http://cnmat.cnmat.berkeley.edu/OSC they also have an osc_dev mailinglist. primary source for pd: http://lena.ucsd.edu/~msp/ -ok, merged the windows and linux trees. +to build run +./configure +make +make install + + +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) +src/ contains the code for OSC pd objects (send,dump,route) README.txt this file doc/ pd help files extra/ OSC.dll, the windows binary @@ -22,6 +28,9 @@ send+dump/ CNMAT's OSC commandline utils log: + 20040409: changed build setup to suit externals build system + single object objects, no lib + 20030531: added OSCroute /* (route everything) hard-fix 20030527: added sending to broadcast address capability to htmsocket diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..3a7e58c --- /dev/null +++ b/configure.ac @@ -0,0 +1,160 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.58) +AC_INIT(OSCxpd, 0.2, jdl@xdv.org) +AC_CONFIG_SRCDIR([libOSC/OSC-client.h]) +AC_CONFIG_HEADER([config.h]) + +# Checks for programs. +AC_PROG_CC +AC_PROG_LN_S +# AC_PROG_CXX +AC_PROG_CPP +# AC_PROG_INSTALL + +dnl ---------------------------------- +dnl here you can see that autoconf adds "-g -02" as a default +dnl these are flags for debugging, and will add extra weight +dnl to you external. CFLAGS, CPPFLAGS, etc are default variables +dnl ---------------------------------- +echo "default flags" +# echo "$CPPFLAGS" +# echo "$CXXFLAGS" +echo cflags "$CFLAGS" +echo ldflags "$LDFLAGS" + +dnl ------------------------------------------ +dnl ---- do some magic to gues the host opsys +dnl ---- taken from libvorbis configure.in +dnl ------------------------------------------ +# AC_CANONICAL_HOST + +# Checks for libraries. +# FIXME: Replace `main' with a function in `-lc': +AC_CHECK_LIB([c], [main]) +# FIXME: Replace `main' with a function in `-lm': +AC_CHECK_LIB([m], [main]) + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([arpa/inet.h fcntl.h netdb.h netinet/in.h stdlib.h string.h strings.h sys/file.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h unistd.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_CONST +AC_HEADER_TIME +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_MALLOC +AC_FUNC_SELECT_ARGTYPES +AC_FUNC_VPRINTF +AC_CHECK_FUNCS([bzero gethostbyname inet_ntoa select socket strchr]) +AC_TYPE_SIGNAL + +# from augusts template + +dnl ------------------------------------------ +dnl for each of the *-*-host's in the following, do the checking and then set your CFLAGS, CPPFLAGS and LDFLAGS +dnl CFLAGS get set for C code, CPPFLAGS for c++ code. LDFLAGS is the linking flags for both c and c++ +dnl ------------------------------------------ + +INCLUDES="-I../libOSC" +LDFLAGS="$LDFLAGS -L/usr/local/lib -ldl" +if test -z "$GCC"; then + case $host in + *-*-irix*) + dnl If we're on IRIX, we wanna use cc even if gcc + dnl is there (unless the user has overriden us)... + if test -z "$CC"; then + CC=cc + fi + ;; + sparc-sun-solaris*) + CFLAGS="-xO4 -fast -w -fsimple -native -xcg92" + ;; + *) + CFLAGS="-O" + ;; + esac +else + + case $host in + *86-*-linux*) + CFLAGS="$CFLAGS -DUNIX -Wall -Wimplicit -Wunused -Wmissing-prototypes -O2" + LDFLAGS="$LDFLAGS -shared" + dnl we could test for bad glibc here, but don't + pd_suffix=pd_linux + ;; + powerpc-*-linux*) + CFLAGS="$CFLAGS -DUNIX -Wall -Wimplicit -Wunused -Wmissing-prototypes -O1" + LDFLAGS="$LDFLAGS -shared" + pd_suffix=pd_linux + ;; + *-*-linux*) + CFLAGS="$CFLAGS -DUNIX -Wall -Wimplicit -Wunused -Wmissing-prototypes -O1" + LDFLAGS="$LDFLAGS -shared" + INCLUDES="$INCLUDES" + pd_suffix=pd_linux + ;; + sparc-sun-*) + echo "YOU HAVE A SPARC STATION, not setting any flags, not supported yet" + ;; + *-*-darwin*) + CFLAGS="$CPFLAGS -DUNIX -DMACOSX -Wall -Wimplicit -Wunused -Wmissing-prototypes -O3 " + # LDFLAGS="$LDFLAGS -bundle -bundle_loader /usr/local/pd/bin/pd -undefined suppress -flat_namespace" + LDFLAGS="$LDFLAGS -bundle -bundle_loader /usr/local/pd/bin/pd -flat_namespace" + pd_suffix=pd_darwin + ;; + *) + dnl assume unix + CFLAGS="$CFLAGS -DUNIX -Wall -Wimplicit -Wunused -Wmissing-prototypes -O1" + LDFLAGS="$LDFLAGS -shared" + pd_suffix=pd_linux + ;; + esac +fi + +CFLAGS="$CFLAGS $INCLUDES" + +echo "Using cflags= $CFLAGS" +echo "Using ldflags= $LDFLAGS" +echo "Using includes= $INCLUDES" + + + +dnl ------------------------------------------ +dnl ---- add PureData includes dir +dnl ---- usually /usr/local/include +dnl ------------------------------------------ +AC_ARG_WITH(pd_dir, + [ --with-pd-dir=path pd header path (default=/usr/local/include) ], + [ + CPPFLAGS="$CPPFLAGS -I$withval" + echo + echo "pd dir is $withval" + echo + ]) + +dnl ------------------------------------------ +dnl ---- check for PureData Header +dnl ------------------------------------------ +AC_CHECK_HEADER(m_pd.h, [have_pd_hdr=yes ], [ + have_pd_hdr=no + echo + echo "no m_pd.h header found. try with option --with-pd-dir=/path/to/pd/src" + echo + exit + ]) + + +AC_SUBST(pd_suffix) +AC_SUBST(INCLUDES) + +AC_CONFIG_FILES([src/Makefile + libOSC/Makefile + send+dump/Makefile + Makefile]) + +AC_OUTPUT diff --git a/libOSC/Makefile b/libOSC/Makefile deleted file mode 100644 index f1585fa..0000000 --- a/libOSC/Makefile +++ /dev/null @@ -1,26 +0,0 @@ -CFLAGS= -O2 -Wall -fPIC -ARFLAGS=srv -# uncomment the following for linux/win -# DEFS= -Dunix -# and this for osx -DEFS= -Dunix -DMACOSX -LIB=libOSC.a - -LIBOBJS= ${LIB}(OSC-client.o) ${LIB}(OSC-timetag.o) - -all: ${LIBOBJS} - -.c.a: - ${CC} -c ${CFLAGS} ${DEFS} $< - ${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/Makefile.in b/libOSC/Makefile.in new file mode 100644 index 0000000..26dca72 --- /dev/null +++ b/libOSC/Makefile.in @@ -0,0 +1,28 @@ +CFLAGS= -O2 -Wall -fPIC +ARFLAGS=srv +# uncomment the following for linux/win +# DEFS= -Dunix +# and this for osx +# DEFS= -Dunix -DMACOSX +LIB=libOSC.a + +CFLAGS= @CFLAGS@ + +LIBOBJS= ${LIB}(OSC-client.o) ${LIB}(OSC-timetag.o) + +all: ${LIBOBJS} + +.c.a: + ${CC} -c ${CFLAGS} ${DEFS} $< + ${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/send+dump/Makefile b/send+dump/Makefile deleted file mode 100644 index a2d1180..0000000 --- a/send+dump/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -LIBOSCDIR = ../libOSC -LIBOSC = ${LIBOSCDIR}/libOSC.a -# for eg. linux -# DEFS= -Dunix -# for MAC OSX, should be DARWIN -DEFS=-Dunix -DMACOSX - -CFLAGS= -O2 -I$(LIBOSCDIR) $(DEFS) -Wall - -DUMPOBJS=dumpOSC.o - - -both: sendOSC dumpOSC - -sendOSC: sendOSC.o htmsocket.o ${LIBOSC} - ${CC} ${CFLAGS} ${DEFS} -o sendOSC sendOSC.o htmsocket.o ${LIBOSC} - -dumpOSC: ${DUMPOBJS} - ${CC} ${CFLAGS} ${DEFS} -o $@ ${DUMPOBJS} - -dumpUDP: dumpUDP.o - ${CC} ${CFLAGS} ${DEFS} -o dumpUDP dumpUDP.o - -${LIBOSC}: - echo "You need to go to " ${LIBOSCDIR} " and do a make." - (cd ../libOSC ; make) - -clean: - rm -f sendOSC dumpOSC *.o - - diff --git a/src/INVENTORY.txt b/src/INVENTORY.txt new file mode 100644 index 0000000..98f3698 --- /dev/null +++ b/src/INVENTORY.txt @@ -0,0 +1,21 @@ +OSC protocol inventory for OSC4PD + +sendOSC, dumpOSC, OSCroute +========================== + INCLUDES + * typed and untyped packing of OSC messages + * #bundle packing + * transmission of OSC messages via UDP socket + + * receive OSC messages on UDP socket + * unpacking of typed and untyped OSC messages + * #bundle unpacking + + * static address resolution + * pattern matching + + OMITS + * working with timetags + * all advanced protocol features of documentation, typesigs, etc + * connection oriented communication + diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..7f9ba21 --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,94 @@ +# current: all +# pd_linux +############################### +NAME=OSC +EXT=o +LIBS = -lm -lc +LIBOSC = ../libOSC/libOSC.a +DEFS= -Dunix + +prefix=$(DESTDIR)/usr + +# ----------------------- LINUX i386 ----------------------- + +# pd_linux: $(NAME).pd_linux + +SFX=@pd_suffix@ + +.SUFFIXES: .$(SFX) + +CFLAGS += $(DEFS) -DPD -DUNIX -O2 -funroll-loops -fomit-frame-pointer \ + -Wall -W -Wshadow \ + -Wno-unused -Wno-parentheses -Wno-switch + +# where is your m_pd.h ??? +INCLUDE = -I../../build/include + +# LINUXEXTERNALS = htmsocket.o OSC-pattern-match.o sendOSC.o dumpOSC.o OSCroute.o +# SOURCES = $(wildcard *.c) +SOURCES = OSC-pattern-match.c OSC.c dumpOSC.c \ +htmsocket.c OSCroute.c sendOSC.c +TARGETS = $(SOURCES:.c=.o) +EXTS=sendOSC.@pd_suffix@ dumpOSC.@pd_suffix@ OSCroute.@pd_suffix@ OSC.@pd_suffix@ + +all: $(EXTS) +sendOSC.pd_linux: htmsocket.o sendOSC.o + cc -Wl,-export_dynamic -shared -o $*.@pd_suffix@ *.o -lc -lm ../libOSC/libOSC.a + +dumpOSC.pd_linux: dumpOSC.o + cc -Wl,-export_dynamic -shared -o $*.@pd_suffix@ $*.o -lc -lm +OSCroute.pd_linux: OSCroute.o OSC-pattern-match.o + cc -Wl,-export_dynamic -shared -o $*.@pd_suffix@ $? -lc -lm + +OSC.pd_linux: OSC.o + cc -Wl,-export_dynamic -shared -o $*.@pd_suffix@ $? -lc -lm + # $(LD) $(LDFLAGS) -o OSC.$(EXT) *.$(EXT) *.o $(LIBS) $(LIBOSC) +# $(LD) $(LDFLAGS) -o OSC.$(EXT) *.$(EXT) $(LIBS) $(LIBOSC) +$(TARGETS): %.o : %.c + cc $(CFLAGS) $(INCLUDE) -c -o $*.o $*.c + + # cc -c $(CFLAGS) OSC.c + +# .c.pd_linux: +# cc -O2 -Wall -DPD -fPIC $(LINUXCFLAGS) $(LINUXINCLUDE) -c *.c +# ld -export_dynamic -shared -o $*.pd_linux $*.o $(LINUXEXTERNALS) $(LIBS) $(LIBOSC) +# strip --strip-unneeded $*.pd_linux + +# ---------------------------------------------------------- + +install-doc: + @test -d $(prefix)/lib/pd/doc/5.reference || mkdir -p $(prefix)/lib/pd/doc/5.reference + cp -r ../doc/* $(prefix)/lib/pd/doc/5.reference/ + +install: install-doc + @test -d $(prefix)/lib/pd/extra || mkdir -p $(prefix)/lib/pd/extra + install -m644 *.pd_linux $(prefix)/lib/pd/extra + +clean: + rm -rf *.$(EXT) *.@pd_suffix@ + +# ----------------------- Mac OS X (Darwin) ----------------------- + +pd_darwin: $(NAME).pd_darwin + +SFX=.pd_darwin + +.SUFFIXES: $(SFX) + +DARWINCFLAGS = -DPD -DUNIX -DMACOSX -O2 \ + -Wall -W -Wshadow -Wstrict-prototypes \ + -Wno-unused -Wno-parentheses -Wno-switch + +# where is your m_pd.h ??? +DARWININCLUDE = -I../../../pd/src + +DARWINEXTERNALS = htmsocket.o OSC-pattern-match.o sendOSC.o dumpOSC.o OSCroute.o + +.c.pd_darwin: + cc $(DARWINCFLAGS) $(DARWININCLUDE) -c *.c + cc -bundle -bundle_loader /usr/local/pd/bin/pd -flat_namespace -o $*.pd_darwin $*.o $(DARWINEXTERNALS) $(LIBS) $(LIBOSC) + + rm -f $*.o ../$*.pd_darwin + ln -s $*/$*.pd_darwin .. + + diff --git a/src/OSC-common.h b/src/OSC-common.h new file mode 100644 index 0000000..8eb6576 --- /dev/null +++ b/src/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/src/OSC-pattern-match.c b/src/OSC-pattern-match.c new file mode 100644 index 0000000..39dce87 --- /dev/null +++ b/src/OSC-pattern-match.c @@ -0,0 +1,207 @@ +/* +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 + */ +#ifdef UNIX + #include +#endif + +// #ifdef MACOSX +// #include +// #endif + +#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) { + + // printf("OSC-pattern-match.c: pattern: %s, test: %s\n", pattern, test); + + theWholePattern = pattern; + + // post("pattern: %s, test: %s", pattern, test); + + if(test[0] == '*') { + return TRUE; + } + + 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/src/OSC-pattern-match.h b/src/OSC-pattern-match.h new file mode 100644 index 0000000..a5d7306 --- /dev/null +++ b/src/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/src/OSC.001 b/src/OSC.001 new file mode 100644 index 0000000..b9dd0d4 --- /dev/null +++ b/src/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/src/OSC.c b/src/OSC.c new file mode 100644 index 0000000..0a8e361 --- /dev/null +++ b/src/OSC.c @@ -0,0 +1,66 @@ +/* + + pd + ------------- + -- tweaks for Win32 www.zeggz.com/raf 13-April-2002 + +*/ + +#if HAVE_CONFIG_H +#include +#endif + +#include +#include "OSC-common.h" + +#define VERSION "0.2" + +#ifndef OSC_API +#define OSC_API +#endif + +typedef struct _OSC +{ + t_object x_obj; +} t_OSC; + + +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); +*/ + +static t_class* OSC_class; + + +static void* OSC_new(t_symbol* s) { + t_OSC *x = (t_OSC *)pd_new(OSC_class); + return (x); +} + + +OSC_API void OSC_version (t_OSC *x) { + + // 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(); +} + +OSC_API void OSC_setup(void) { + 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/src/OSC.dsp b/src/OSC.dsp new file mode 100644 index 0000000..8f0116d --- /dev/null +++ b/src/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/src/OSC.dsw b/src/OSC.dsw new file mode 100644 index 0000000..293b7f7 --- /dev/null +++ b/src/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/src/OSCroute.c b/src/OSCroute.c new file mode 100644 index 0000000..c6df417 --- /dev/null +++ b/src/OSCroute.c @@ -0,0 +1,437 @@ +/* +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 + + + */ + +#if HAVE_CONFIG_H +#include +#endif + + +/* the required include files */ +#include "m_pd.h" +#include "OSC-common.h" +#include "OSC-pattern-match.h" + +#ifdef WIN32 + #include + #include +#endif +#ifdef MACOSX + #include +#endif +#ifdef UNIX + #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? + t_int x_sendmode; // use pd internal sends instead of outlets + char *x_prefixes[MAX_NUM]; + void *x_outlets[MAX_NUM+1]; +} 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("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: jdl 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) +{ + + t_OSCroute *x = (t_OSCroute *)pd_new(OSCroute_class); // get memory for a new object & initialize + + 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->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++) { + for (i = 0; i <= x->x_num; 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 jdl, win32: raf.\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"); + // output on unmatched outlet jdl 20020908 + if (argv[0].a_type == A_FLOAT) { + outlet_float(x->x_outlets[x->x_num], argv[0].a_w.w_float); + } else { + post("* OSC-route: unrecognized atom type!"); + } + } + // 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); + outlet_anything(x->x_outlets[x->x_num], s, argc, argv); + 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); + } + } + + // output unmatched data on rightmost outlet a la normal 'route' object, jdl 20020908 + if (!matchedAnything) { + outlet_anything(x->x_outlets[x->x_num], s, argc, argv); + } + + +} + +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/src/TODO.txt b/src/TODO.txt new file mode 100644 index 0000000..63c2d7b --- /dev/null +++ b/src/TODO.txt @@ -0,0 +1,27 @@ + +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 ? + +-dynamic space allocation for message buffers. + +-configure proper (autoconf) + + +changelog: + +20020903: refixed MAXPDARG vs. MAX_ARGS bug causind sendOSC to crash + with msgs longer than 5 argmuents. ? + +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 + + diff --git a/src/VERSION b/src/VERSION new file mode 100644 index 0000000..3b04cfb --- /dev/null +++ b/src/VERSION @@ -0,0 +1 @@ +0.2 diff --git a/src/dumpOSC.c b/src/dumpOSC.c new file mode 100644 index 0000000..4a6fa2e --- /dev/null +++ b/src/dumpOSC.c @@ -0,0 +1,996 @@ +/* +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 + +*/ + +#if HAVE_CONFIG_H +#include +#endif + +#include "m_pd.h" +//#include "m_imp.h" +#include "s_stuff.h" +//#include "x_osc.h" + +/* declarations */ + +// typedef void (*t_fdpollfn)(void *ptr, int fd); +void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr); + + +#if defined(__sgi) || defined(__linux) || defined(WIN32) || defined(MACOSX) + +#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; + +typedef unsigned long long osc_time_t; + +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); +Boolean dumpOSC_SendReply(char *buf, int n, void *clientDesc, int clientDescLenght, int fd); +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, (struct sockaddr *)&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 + + if(!dumpOSC_SendReply(mbuf, n, &x->x_server, clilen, sockfd)) + { + 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 */ + if ((sockfd = socket(AF_INET, (udp ? SOCK_DGRAM : SOCK_STREAM), 0)) == -1) + { + 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 */ + { + + 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("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 WriteTime(char* dst, osc_time_t osctime) +{ + *(int32_t*)dst = htonl((int32_t)(osctime >> 32)); + *(int32_t*)(dst+4) = htonl((int32_t)osctime); +} + +void WriteMode(char* dst) +{ + *(int32_t*)dst = htonl(0); +} + +osc_time_t ReadTime(const char* src) +{ + osc_time_t osctime = ntohl(*(int32_t*)src); + return (osctime << 32) + ntohl(*(int32_t*)(src+4)); +} + +double TimeToSeconds(osc_time_t osctime) +{ + return (double)osctime * 2.3283064365386962890625e-10 /* 1/2^32 */; +} + +int timeRound(double x) +{ + return x >= 0.0 ? x+0.5 : x-0.5; +} +/* +void WriteLogicalTime(char* dst) +{ + static double startTime = -1.0; + double sTime; + + // Initialisierung der Startzeit. + // Knnte effizienter (ohne 'if') auch irgendwo vorher passieren. + // Knnte wahrscheinlich auch 0.0 sein. + if (startTime < 0.0) { + startTime = clock_getlogicaltime(); + } + + sTime = clock_gettimesince(startTime) * 0.001; + *(int32_t*)dst = hton'K l((int32_t)sTime); + *(int32_t*)(dst+4) = htonl((int32_t)(4294967296.0 * sTime)); +} +*/ + +void WriteLogicalTime(char* dst) +{ + double sTime = clock_gettimesince(19230720) / 1000.0; + double tau = sTime - timeRound(sTime); + + //fprintf(stderr, "sSec = %f tau = %f\n", sTime, tau); + + *(int32_t*)dst = htonl((int32_t)(sTime)); + *(int32_t*)(dst+4) = htonl((int32_t)(4294967296 * tau)); +} + +Boolean dumpOSC_SendReply(char *buf, int n, void *clientDesc, int clientDescLenght, int fd) +{ + if((n == 24) && (strcmp(buf, "#time") == 0)) + { + osc_time_t t0, t1, t2; + double dt0, dt1, dt2; + + WriteMode(buf+6); + + t0 = ReadTime(buf+8); + + WriteLogicalTime(buf+16); + t1 = ReadTime(buf+16); // reverse + dt0 = TimeToSeconds(t0); // client time + dt1 = TimeToSeconds(t1); // server time + + // fprintf(stderr, "%f\t%f\t%f\n", dt0, dt1, dt0 - dt1); + + sendto(fd, buf, n, 0, (struct sockaddr *)clientDesc, clientDescLenght); + return TRUE; + } + else + { + return FALSE; + } +} + +//********************** + +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 if ((n == 24) && (strcmp(buf, "#time") == 0)) + { + complain("Time message: %s\n :).\n", htm_error_string); + return; + + } + 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 + /* Martin Peach fix for negative floats: + * was: SETFLOAT(mya+myargc,ntohl(*((int *) p))); + * now is: + */ + SETFLOAT(mya+myargc,(signed)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/src/htmsocket.c b/src/htmsocket.c new file mode 100644 index 0000000..1463d86 --- /dev/null +++ b/src/htmsocket.c @@ -0,0 +1,319 @@ +/* +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. + */ + +#if HAVE_CONFIG_H +#include +#endif + +#ifdef MACOSX + #include +#endif + +#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; + int oval = 1; + + #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); + + // enable broadcast + if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &oval, sizeof(int)) == -1) { + perror("setsockopt"); + } + + 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); + + // enable broadcast + if(setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &oval, sizeof(int)) == -1) { + perror("setsockopt"); + } + + 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 + if(close(o->sockfd) == -1) + { + perror("CloseHTMSocket::closesocket failed"); + return; + } + #endif + + free(o); +} diff --git a/src/htmsocket.h b/src/htmsocket.h new file mode 100644 index 0000000..b035b57 --- /dev/null +++ b/src/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/src/sendOSC.c b/src/sendOSC.c new file mode 100644 index 0000000..5dc8f59 --- /dev/null +++ b/src/sendOSC.c @@ -0,0 +1,881 @@ +/* +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 + +*/ + +#if HAVE_CONFIG_H +#include +#endif + +//#define VERSION "http://cnmat.berkeley.edu/OpenSoundControl/sendOSC-0.1.html" +#define MAX_ARGS 2000 +#define SC_BUFFER_SIZE 64000 + +/* +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 + #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[MAX_ARGS]; + 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("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 int 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 -- cgit v1.2.1