[PD-cvs] externals/mrpeach/osc packOSC-help.pd, NONE, 1.1 packOSC.c, NONE, 1.1 routeOSC-help.pd, NONE, 1.1 routeOSC.c, NONE, 1.1 unpackOSC.c, NONE, 1.1

Martin Peach mrpeach at users.sourceforge.net
Wed Aug 16 22:22:24 CEST 2006


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 */





More information about the Pd-cvs mailing list