Update of /cvsroot/pure-data/externals/mrpeach/osc In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv1850/mrpeach/osc
Added Files: packOSC-help.pd packOSC.c routeOSC-help.pd routeOSC.c unpackOSC.c Log Message: Added the net, osc and sqosc~ directories
--- NEW FILE: packOSC.c --- /* packOSC is like sendOSC but outputs a list of floats which are the bytes making up the OSC packet. */ /* This allows for the separation of the protocol and its transport. */ /* Started by Martin Peach 20060403 */ /* 20060425 version independent of libOSC */ /* packOSC.c makes extensive use of code from OSC-client.c and sendOSC.c */ /* as well as some from OSC-timetag.c. These files have the following header: */ /* Written by Matt Wright, The Center for New Music and Audio Technologies, University of California, Berkeley. Copyright (c) 1996,97,98,99,2000,01,02,03 The Regents of the University of California (Regents).
Permission to use, copy, modify, distribute, and distribute modified versions of this software and its documentation without fee and without a signed licensing agreement, is hereby granted, provided that the above copyright notice, this paragraph and the following two paragraphs appear in all copies, modifications, and distributions.
IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING [...961 lines suppressed...] }
/* The next bit is modified from OSC-timetag.c. */ /*
OSC_timeTag.c: library for manipulating OSC time tags Matt Wright, 5/29/97
Version 0.2 (9/11/98): cleaned up so no explicit type names in the .c file.
*/
static OSCTimeTag OSCTT_Immediately(void) { OSCTimeTag tt; tt.fraction = 1; tt.seconds = 0; return tt; } /* end packOSC.c*/
--- NEW FILE: packOSC-help.pd --- #N canvas 611 6 488 317 12; #X obj 171 184 udpsend; #X msg 177 158 disconnect; #X msg 171 96 connect 127.0.0.1 9997; #X obj 71 96 packOSC; #X obj 71 128 prepend send; #X obj 171 207 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1; #X msg 14 18 send /test/one/two/three zz 88 T; #X msg 71 65 send /test 1 2 3; #X msg 90 43 send /west 35; #X msg 201 41 send /*/left 22; #X msg 296 24 send /?est/ 1; #X msg 246 66 send /left one two; #X msg 339 43 send /right 88; #X text 4 226 packOSC is like sendOSC except that it outputs a list of floats instead of directly connecting to the network; #X text 158 276 2006/04/25 Martin Peach; #X connect 0 0 5 0; #X connect 1 0 0 0; #X connect 2 0 0 0; #X connect 3 0 4 0; #X connect 4 0 0 0; #X connect 6 0 3 0; #X connect 7 0 3 0; #X connect 8 0 3 0; #X connect 9 0 3 0; #X connect 10 0 3 0; #X connect 11 0 3 0; #X connect 12 0 3 0;
--- NEW FILE: routeOSC-help.pd --- #N canvas 0 0 574 322 12; #X obj 58 82 udpreceive 9997; #X obj 173 106 unpack 0 0 0 0; #X floatatom 173 129 3 0 0 0 - - -; #X floatatom 208 129 3 0 0 0 - - -; #X floatatom 244 129 3 0 0 0 - - -; #X floatatom 280 129 3 0 0 0 - - -; #X text 137 128 from; #X obj 58 114 unpackOSC; #X obj 56 158 print; #X obj 70 206 routeOSC /test /west; #X obj 70 241 print a; #X obj 147 241 print b; #X obj 225 241 print c; #X msg 203 171 set /left; #X msg 294 171 set /left /right; #X text 10 7 routeOSC; #X text 10 25 accepts lists of floats that are interpreted as OSC packets ; #X text 10 43 set message reassigns outputs; #X text 244 206 arguments are OSC addresses to route; #X text 296 284 2006/04/25 Martin Peach; #X connect 0 0 7 0; #X connect 0 1 1 0; #X connect 1 0 2 0; #X connect 1 1 3 0; #X connect 1 2 4 0; #X connect 1 3 5 0; #X connect 7 0 8 0; #X connect 7 0 9 0; #X connect 9 0 10 0; #X connect 9 1 11 0; #X connect 9 2 12 0; #X connect 13 0 9 0; #X connect 14 0 9 0;
--- NEW FILE: unpackOSC.c --- /* unpackOSC is like dumpOSC but outputs two lists: a list of symbols for the path */ /* and a list of floats and/or symbols for the data */ /* This allows for the separation of the protocol and its transport. */ /* Started by Martin Peach 20060420 */ /* This version tries to be standalone from LIBOSC MP 20060425 */ /* MP 20060505 fixed a bug (line 209) where bytes are wrongly interpreted as negative */ /* dumpOSC.c header follows: */ /* Written by Matt Wright and Adrian Freed, The Center for New Music and Audio Technologies, University of California, Berkeley. Copyright (c) 1992,93,94,95,96,97,98,99,2000,01,02,03,04 The Regents of the University of California (Regents).
Permission to use, copy, modify, distribute, and distribute modified versions of this software and its documentation without fee and without a signed licensing agreement, is hereby granted, provided that the above copyright notice, this paragraph and the following two paragraphs appear in all copies, modifications, and distributions.
IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
The OSC webpage is http://cnmat.cnmat.berkeley.edu/OpenSoundControl */
/*
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: branched from http://www.cnmat.berkeley.edu/OpenSoundControl/src/dumpOSC/dumpOSC.c ------------- -- added pd functions -- socket is made differently than original via pd mechanisms -- tweaks for Win32 www.zeggz.com/raf 13-April-2002 -- the OSX changes from cnmat didnt make it here yet but this compiles on OSX anyway. */
#if HAVE_CONFIG_H #include <config.h> #endif
#include "m_pd.h"
/* declarations */
#ifdef _WIN32 #ifdef _MSC_VER // #include "OSC-common.h" #endif /* _MSC_VER */ #include <string.h> #include <stdlib.h> #include <winsock2.h> #include <stdio.h> #else #include <stdio.h> #include <string.h> #include <stdlib.h> // #include <unistd.h> // #include <fcntl.h> #include <sys/types.h> // #include <sys/stat.h> #include <netinet/in.h> // #include <rpc/rpc.h> // #include <sys/socket.h> // #include <sys/un.h> // #include <sys/times.h> // #include <sys/param.h> // #include <sys/time.h> // #include <sys/ioctl.h> #include <ctype.h> // #include <arpa/inet.h> // #include <netdb.h> // #include <pwd.h> // #include <signal.h> // #include <grp.h> // #include <sys/file.h> //#include <sys/prctl.h>
// #ifdef NEED_SCHEDCTL_AND_LOCK // #include <sys/schedctl.h> // #include <sys/lock.h> // #endif #endif /* _WIN32 */
/* Declarations */ #ifdef WIN32 typedef unsigned __int64 osc_time_t; #else typedef unsigned long long osc_time_t; #endif
#define MAX_MESG 65536 // was 32768 MP: make same as MAX_UDP_PACKET
/* ----------------------------- was dumpOSC ------------------------- */
#define MAX_PATH_AT 50 // maximum nuber of elements in OSC path
static t_class *unpackOSC_class;
typedef struct _unpackOSC { t_object x_obj; t_outlet *x_data_out; t_atom x_data_at[MAX_MESG];// symbols making up the path + payload int x_data_atc;// number of symbols to be output char x_raw[MAX_MESG];// bytes making up the entire OSC message int x_raw_c;// number of bytes in OSC message } t_unpackOSC;
#ifdef MSW __declspec(dllexport) #endif void unpackOSC_setup(void); static void *unpackOSC_new(void); static void unpackOSC_free(t_unpackOSC *x); void unpackOSC_setup(void); static void unpackOSC_list(t_unpackOSC *x, t_symbol *s, int argc, t_atom *argv); static int unpackOSC_path(t_unpackOSC *x, char *path); static void unpackOSC_Smessage(t_unpackOSC *x, void *v, int n); static void unpackOSC_PrintTypeTaggedArgs(t_unpackOSC *x, void *v, int n); static void unpackOSC_PrintHeuristicallyTypeGuessedArgs(t_unpackOSC *x, void *v, int n, int skipComma); static char *unpackOSC_DataAfterAlignedString(char *string, char *boundary); static int unpackOSC_IsNiceString(char *string, char *boundary);
static void *unpackOSC_new(void) { t_unpackOSC *x;
x = (t_unpackOSC *)pd_new(unpackOSC_class); x->x_data_out = outlet_new(&x->x_obj, &s_list); x->x_raw_c = x->x_data_atc = 0; return (x); }
static void unpackOSC_free(t_unpackOSC *x) { }
#ifdef MSW __declspec(dllexport) #endif void unpackOSC_setup(void) { unpackOSC_class = class_new(gensym("unpackOSC"), (t_newmethod)unpackOSC_new, (t_method)unpackOSC_free, sizeof(t_unpackOSC), 0, 0); class_addlist(unpackOSC_class, (t_method)unpackOSC_list); }
/* unpackOSC_list expects an OSC packet in the form of a list of floats on [0..255] */ static void unpackOSC_list(t_unpackOSC *x, t_symbol *s, int argc, t_atom *argv) { int size, messageLen, i, j; char *messageName, *args, *buf;
if ((argc%4) != 0) { post("unpackOSC: packet size (%d) not a multiple of 4 bytes: dropping packet", argc); return; } /* copy the list to a byte buffer, checking for bytes only */ for (i = 0; i < argc; ++i) { if (argv[i].a_type == A_FLOAT) { j = (int)argv[i].a_w.w_float; // if ((j == argv[i].a_w.w_float) && (j >= 0) && (j <= 255)) // this can miss bytes between 128 and 255 because they are interpreted somewhere as negative // , so change to this: if ((j == argv[i].a_w.w_float) && (j >= -128) && (j <= 255)) { x->x_raw[i] = (char)j; } else { post("unpackOSC: data out of range (%d), dropping packet", argv[i].a_w.w_float); return; } } else { post("unpackOSC: data not float, dropping packet"); return; } } x->x_raw_c = argc; buf = x->x_raw;
if ((argc >= 8) && (strncmp(buf, "#bundle", 8) == 0)) { /* This is a bundle message. */ #ifdef DEBUG post("unpackOSC: bundle msg: bundles not yet supported\n"); #endif
if (argc < 16) { post("unpackOSC: Bundle message too small (%d bytes) for time tag", argc); return; }
/* Print the time tag */ #ifdef DEBUG printf("unpackOSC: [ %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 < argc) { size = ntohl(*((int *) (buf + i))); if ((size % 4) != 0) { post("unpackOSC: Bad size count %d in bundle (not a multiple of 4)", size); return; } if ((size + i + 4) > argc) { post("unpackOSC: Bad size count %d in bundle (only %d bytes left in entire bundle)", size, argc-i-4); return; }
/* Recursively handle element of bundle */ unpackOSC_list(x, s, size, &argv[i+4]); i += 4 + size; }
if (i != argc) { post("unpackOSC: This can't happen"); } #ifdef DEBUG printf("]\n"); #endif
} else if ((argc == 24) && (strcmp(buf, "#time") == 0)) { post("unpackOSC: Time message: %s\n :).\n", buf); return; } else { /* This is not a bundle message or a time message */
messageName = buf; args = unpackOSC_DataAfterAlignedString(messageName, buf+x->x_raw_c); if (args == 0) { post("unpackOSC: Bad message name string: (%s) Dropping entire message.", messageName); return; } #ifdef DEBUG post("unpackOSC: message name string: %s", messageName); #endif messageLen = args-messageName; /* put the OSC path into a single symbol */ x->x_data_atc = unpackOSC_path(x, messageName); if (x->x_data_atc == 1) unpackOSC_Smessage(x, (void *)args, x->x_raw_c-messageLen); } if (x->x_data_atc >= 1) outlet_list(x->x_data_out, &s_list, x->x_data_atc, x->x_data_at); x->x_data_atc = 0; }
static int unpackOSC_path(t_unpackOSC *x, char *path) { int i;
if (path[0] != '/') { post("unpackOSC: bad path (%s)", path); return 0; } for (i = 1; i < MAX_MESG; ++i) { if (path[i] == '\0') { /* the end of the path: turn path into a symbol */ SETSYMBOL(&x->x_data_at[0],gensym(path)); return 1; } } post("unpackOSC: path too long"); return 0; } #define SMALLEST_POSITIVE_FLOAT 0.000001f
static void unpackOSC_Smessage(t_unpackOSC *x, void *v, int n) { char *chars = v;
if (n != 0) { if (chars[0] == ',') { if (chars[1] != ',') { /* This message begins with a type-tag string */ unpackOSC_PrintTypeTaggedArgs(x, v, n); } else { /* Double comma means an escaped real comma, not a type string */ unpackOSC_PrintHeuristicallyTypeGuessedArgs(x, v, n, 1); } } else { unpackOSC_PrintHeuristicallyTypeGuessedArgs(x, v, n, 0); } } }
static void unpackOSC_PrintTypeTaggedArgs(t_unpackOSC *x, void *v, int n) { char *typeTags, *thisType, *p; int myargc = x->x_data_atc; t_atom *mya = x->x_data_at;
typeTags = v;
if (!unpackOSC_IsNiceString(typeTags, typeTags+n)) { /* No null-termination, so maybe it wasn't a type tag string after all */ unpackOSC_PrintHeuristicallyTypeGuessedArgs(x, v, n, 0); return; }
p = unpackOSC_DataAfterAlignedString(typeTags, typeTags+n);
for (thisType = typeTags + 1; *thisType != 0; ++thisType) { switch (*thisType) { case 'i': case 'r': case 'm': case 'c': #ifdef DEBUG post("integer: %d", ntohl(*((int *) p))); #endif SETFLOAT(mya+myargc,(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 (!unpackOSC_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++; p = unpackOSC_DataAfterAlignedString(p, typeTags+n);
} 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); } } x->x_data_atc = myargc; }
static void unpackOSC_PrintHeuristicallyTypeGuessedArgs(t_unpackOSC *x, void *v, int n, int skipComma) { int i, thisi; int *ints; float thisf; char *chars, *string, *nextString; int myargc= x->x_data_atc; t_atom* mya = x->x_data_at;
/* Go through the arguments 32 bits at a time */ ints = v; chars = v;
for (i = 0; i < n/4; ) { string = &chars[i*4]; thisi = ntohl(ints[i]); /* Reinterpret the (potentially byte-reversed) thisi as a float */ thisf = *(((float *) (&thisi)));
if (thisi >= -1000 && thisi <= 1000000) { #ifdef DEBUG printf("%d ", thisi); #endif SETFLOAT(mya+myargc,(t_float) (thisi)); myargc++; i++; } else if (thisf >= -1000.f && thisf <= 1000000.f && (thisf <=0.0f || thisf >= SMALLEST_POSITIVE_FLOAT)) { #ifdef DEBUG printf("%f ", thisf); #endif SETFLOAT(mya+myargc,thisf); myargc++; i++; } else if (unpackOSC_IsNiceString(string, chars+n)) { nextString = unpackOSC_DataAfterAlignedString(string, chars+n); #ifdef DEBUG printf(""%s" ", (i == 0 && skipComma) ? string +1 : string); #endif SETSYMBOL(mya+myargc,gensym(string)); myargc++; i += (nextString-string) / 4; } else { // unhandled .. ;) #ifdef DEBUG post("unpackOSC: indeterminate type: 0x%x xx", ints[i]); #endif i++; } x->x_data_atc = myargc; } }
#define STRING_ALIGN_PAD 4
static char *unpackOSC_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) { post("unpackOSC: DataAfterAlignedString: bad boundary"); return 0; }
for (i = 0; string[i] != '\0'; i++) { if (string + i >= boundary) { post("unpackOSC: 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) { post("unpackOSC: DataAfterAlignedString: Unreasonably long string"); return 0; } if (string[i] != '\0') { post("unpackOSC:DataAfterAlignedString: Incorrectly padded string"); return 0; } }
return string+i; }
static int unpackOSC_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? Returns 1 if true, else 0. */
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])) || (string + i >= boundary)) return 0;
/* 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 0;
return 1; }
/* end of unpackOSC.c */
--- NEW FILE: routeOSC.c --- /* routeOSC.c 20060424 by Martin Peach, based on OSCroute and OSC-pattern-match.c. */ /* 20060425 cleaned up some more */ /* OSCroute.c header follows: */ /* Written by Adrian Freed, The Center for New Music and Audio Technologies, University of California, Berkeley. Copyright (c) 1992,93,94,95,96,97,98,99,2000,01,02,03,04 The Regents of the University of California (Regents).
Permission to use, copy, modify, distribute, and distribute modified versions of this software and its documentation without fee and without a signed licensing agreement, is hereby granted, provided that the above copyright notice, this paragraph and the following two paragraphs appear in all copies, modifications, and distributions.
IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
The OSC webpage is http://cnmat.cnmat.berkeley.edu/OpenSoundControl */
/* OSC-route.c Max object for OSC-style dispatching
To-do:
Match a pattern against a pattern? [Done: Only Slash-Star is allowed, see MyPatternMatch.] 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
*/ /* OSC-pattern-match.c header follows: */ /* Copyright © 1998. The Regents of the University of California (Regents). All Rights Reserved.
Written by Matt Wright, The Center for New Music and Audio Technologies, University of California, Berkeley.
Permission to use, copy, modify, distribute, and distribute modified versions of this software and its documentation without fee and without a signed licensing agreement, is hereby granted, provided that the above copyright notice, this paragraph and the following two paragraphs appear in all copies, modifications, and distributions.
IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
The 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 */
#if HAVE_CONFIG_H #include <config.h> #endif
/* the required include files */ #include "m_pd.h"
#define MAX_NUM 128 // maximum number of paths (prefixes) we can route
typedef struct _routeOSC { t_object x_obj; // required header t_int x_num; // Number of prefixes we store char *x_prefixes[MAX_NUM]; void *x_outlets[MAX_NUM+1]; // one for each prefix plus one for everything else } t_routeOSC;
/* prototypes */
#ifdef MSW __declspec(dllexport) #endif void routeOSC_setup(void); static int MyPatternMatch (const char *pattern, const char *test); static void routeOSC_doanything(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv); static void routeOSC_list(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv); static void *routeOSC_new(t_symbol *s, int argc, t_atom *argv); static void routeOSC_set(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv); static char *NextSlashOrNull(char *p); static void StrCopyUntilSlash(char *target, const char *source);
/* from OSC-pattern-match.c */ static const char *theWholePattern; /* Just for warning messages */ static int MatchBrackets (const char *pattern, const char *test); static int MatchList (const char *pattern, const char *test); static int PatternMatch (const char * pattern, const char * test);
static t_class *routeOSC_class; t_symbol *ps_list, *ps_complain, *ps_emptySymbol;
static int MyPatternMatch (const char *pattern, const char *test) { // This allows the special case of "routeOSC /* " to be an outlet that // matches anything; i.e., it always outputs the input with the first level // of the address stripped off.
if (test[0] == '*' && test[1] == '\0') return 1; else return PatternMatch(pattern, test); }
static void routeOSC_free(t_routeOSC *x) { }
/* initialization routine */ // setup #ifdef MSW __declspec(dllexport) #endif void routeOSC_setup(void) { routeOSC_class = class_new(gensym("routeOSC"), (t_newmethod)routeOSC_new, (t_method)routeOSC_free,sizeof(t_routeOSC), 0, A_GIMME, 0); class_addlist(routeOSC_class, routeOSC_list); class_addanything(routeOSC_class, routeOSC_doanything); class_addmethod(routeOSC_class, (t_method)routeOSC_set, gensym("set"), A_GIMME, 0); class_sethelpsymbol(routeOSC_class, gensym("routeOSC-help.pd"));
ps_emptySymbol = gensym("");
post("routeOSC object version 1.0 by Martin Peach, based on OSCroute by Matt Wright. pd: jdl Win32 raf."); post("OSCroute Copyright © 1999 Regents of the Univ. of California. All Rights Reserved."); }
/* instance creation routine */ static void *routeOSC_new(t_symbol *s, int argc, t_atom *argv) {
t_routeOSC *x = (t_routeOSC *)pd_new(routeOSC_class); // get memory for a new object & initialize int i;
if (argc > MAX_NUM) { post("* routeOSC: too many arguments: %ld (max %ld)", argc, MAX_NUM); return 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); } #if 0 /* this doesn't make sense to me MP20060425 */ 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); } #endif /* 0 */ } else if (argv[i].a_type == A_FLOAT) { post("* routeOSC: float arguments are not OK."); return 0; } else { post("* routeOSC: unrecognized argument type!"); return 0; } } /* Have to create the outlets in reverse order */ /* well, not in pd ? */ for (i = 0; i <= x->x_num; i++) { x->x_outlets[i] = outlet_new(&x->x_obj, &s_list); } return (x); }
static void routeOSC_set(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv) { int i;
if (argc > x->x_num) { post ("routeOSC: too many paths"); return; } for (i = 0; i < argc; ++i) { if (argv[i].a_type != A_SYMBOL) { post ("routeOSC: path %d not a symbol", i); return; } if (argv[i].a_w.w_symbol->s_name[0] != '/') { post ("routeOSC: path %d doesn't start with /", i); return; } } for (i = 0; i < argc; ++i) { 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; } } }
static void routeOSC_list(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv) { if (argc > 0 && argv[0].a_type == A_SYMBOL) { /* Ignore the fact that this is a "list" */ routeOSC_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("* routeOSC: unrecognized atom type!"); } } }
static void routeOSC_doanything(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv) { char *pattern, *nextSlash; int i; int matchedAnything; // post("*** routeOSC_anything(s %s, argc %ld)", s->s_name, (long) argc);
pattern = s->s_name; if (pattern[0] != '/') { post("* routeOSC: 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 */
for (i = 0; i < x->x_num; ++i) { if (MyPatternMatch(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_FLOAT) { outlet_float(x->x_outlets[i], argv[0].a_w.w_float); } else { post("* routeOSC: 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 (MyPatternMatch(patternBegin, x->x_prefixes[i]+1)) { ++matchedAnything; if (restOfPattern == 0) restOfPattern = gensym(nextSlash); outlet_anything(x->x_outlets[i], restOfPattern, argc, argv); } } }
if (!matchedAnything) { // output unmatched data on rightmost outlet a la normal 'route' object, jdl 20020908 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; }
/* from OSC-pattern-match.c Matt Wright, 3/16/98 Adapted from oscpattern.c, by Matt Wright and Amar Chaudhury */
static int PatternMatch (const char * pattern, const char * test) { theWholePattern = pattern;
if (pattern == 0 || pattern[0] == 0) return test[0] == 0;
if (test[0] == 0) { if (pattern[0] == '*') return PatternMatch (pattern+1, test); return 0; }
switch (pattern[0]) { case 0: return test[0] == 0; case '?': return PatternMatch (pattern+1, test+1); case '*': if (PatternMatch (pattern+1, test)) return 1; return PatternMatch (pattern, test+1); case ']': case '}': post("routeOSC: Spurious %c in pattern ".../%s/..."",pattern[0], theWholePattern); return 0; case '[': return MatchBrackets (pattern,test); case '{': return MatchList (pattern,test); case '\': if (pattern[1] == 0) return test[0] == 0; if (pattern[1] == test[0]) return PatternMatch (pattern+2,test+1); return 0; default: if (pattern[0] == test[0]) return PatternMatch (pattern+1,test+1); return 0; } }
/* we know that pattern[0] == '[' and test[0] != 0 */
static int MatchBrackets (const char *pattern, const char *test) { int result; int negated = 0; const char *p = pattern;
if (pattern[1] == 0) { post("routeOSC: Unterminated [ in pattern ".../%s/..."", theWholePattern); return 0; } if (pattern[1] == '!') { negated = 1; p++; } while (*p != ']') { if (*p == 0) { post("Unterminated [ in pattern ".../%s/..."", theWholePattern); return 0; } 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 0; while (*p != ']') { if (*p == 0) { post("Unterminated [ in pattern ".../%s/..."", theWholePattern); return 0; } p++; } return PatternMatch (p+1,test+1); }
static int MatchList (const char *pattern, const char *test) { const char *restOfPattern, *tp = test;
for(restOfPattern = pattern; *restOfPattern != '}'; restOfPattern++) { if (*restOfPattern == 0) { post("Unterminated { in pattern ".../%s/..."", theWholePattern); return 0; } } restOfPattern++; /* skip close curly brace */ pattern++; /* skip open curly brace */ while (1) { if (*pattern == ',') { if (PatternMatch (restOfPattern, tp)) return 1; 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++; } } }
/* end of routeOSC.c */