/* Copyright (c) 1999, 2000, 20010 The Regents of the University of California (Regents). All Rights Reserved. Permission to use, copy, modify, and distribute this software and its documentation for educational, research, and not-for-profit purposes, without fee and without a signed licensing agreement, is hereby granted, provided that the above copyright notice, this paragraph and the following two paragraphs appear in all copies, modifications, and distributions. Contact The Office of Technology Licensing, UC Berkeley, 2150 Shattuck Avenue, Suite 510, Berkeley, CA 94720-1620, (510) 643-7201, for commercial licensing opportunities. Written by Matt Wright, The Center for New Music and Audio Technologies, University of California, Berkeley. IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. The OpenSound Control WWW page is http://www.cnmat.berkeley.edu/OpenSoundControl OSC-route.c Max object for OSC-style dispatching To-do: Match a pattern against a pattern? Declare outlet types / distinguish leaf nodes from other children More sophisticated (2-pass?) allmessages scheme set message? pd ------------- -- tweaks for Win32 www.zeggz.com/raf 13-April-2002 */ /* the required include files */ #include "m_pd.h" #include "OSC-common.h" #include "OSC-pattern-match.h" #ifdef WIN32 #include #include #endif /* structure definition of your object */ #define MAX_NUM 20 #define OSC_ROUTE_VERSION "1.05" /* Version 1.04: Allows #1 thru #9 as typed-in arguments Version 1.05: Allows "list" messages as well as "message" messages. */ static t_class *OSCroute_class; typedef struct _OSCroute { t_object x_obj; // required header t_int x_num; // Number of address prefixes we store t_int x_complainmode; // Do we print a message if no match? char *x_prefixes[MAX_NUM]; void *x_outlets[MAX_NUM]; } t_OSCroute; t_symbol *ps_list, *ps_complain, *ps_emptySymbol; /* prototypes */ void OSCroute_doanything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); void OSCroute_anything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); void OSCroute_list(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); /* //void *OSCroute_new(t_symbol *s, int argc, atom *argv); */ void *OSCroute_new(t_symbol *s, int argc, t_atom *argv); void OSCroute_version (t_OSCroute *x); /* void OSCroute_assist (OSCroute *x, void *box, long msg, long arg, */ /* char *dstString); */ void OSCroute_allmessages(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv); static char *NextSlashOrNull(char *p); static void StrCopyUntilSlash(char *target, const char *source); // free static void OSCroute_free(t_OSCroute *x) { // freebytes(x->x_vec, x->x_nelement * sizeof(*x->x_vec)); } /* initialization routine */ // setup #ifdef WIN32 OSC_API void OSCroute_setup(void) { #else void OSCroute_setup(void) { #endif OSCroute_class = class_new(gensym("OSCroute"), (t_newmethod)OSCroute_new, (t_method)OSCroute_free,sizeof(t_OSCroute), 0, A_GIMME, 0); class_addlist(OSCroute_class, OSCroute_list); class_addanything(OSCroute_class, OSCroute_anything); class_addmethod(OSCroute_class, (t_method)OSCroute_version, gensym("version"), A_NULL, 0, 0); class_sethelpsymbol(OSCroute_class, gensym("OSC/OSCroute-help.pd")); /* class_addmethod(OSCroute_class, (t_method)OSCroute_connect, gensym("connect"), A_SYMBOL, A_FLOAT, 0); class_addmethod(OSCroute_class, (t_method)OSCroute_disconnect, gensym("disconnect"), 0); class_addmethod(OSCroute_class, (t_method)OSCroute_send, gensym("send"), A_GIMME, 0); */ /* ps_list = gensym("list"); */ /* ps_complain = gensym("complain"); */ ps_emptySymbol = gensym(""); post("OSCroute object version " OSC_ROUTE_VERSION " by Matt Wright. pd-fication cxc. Win32 raf."); post("OSCroute Copyright © 1999 Regents of the University of California. All Rights Reserved."); } /* instance creation routine */ void *OSCroute_new(t_symbol *s, int argc, t_atom *argv) { //OSCroute *x; t_OSCroute *x = (t_OSCroute *)pd_new(OSCroute_class); // get memory for a new object & initialize //int i, n; int i; //{{raf}} n not used // EnterCallback(); if (argc > MAX_NUM) { post("* OSC-route: too many arguments: %ld (max %ld)", argc, MAX_NUM); // ExitCallback(); return 0; } //x = newobject(OSCroute_class); // get memory for a new object & initialize x->x_complainmode = 0; x->x_num = 0; for (i = 0; i < argc; ++i) { if (argv[i].a_type == A_SYMBOL) { if (argv[i].a_w.w_symbol->s_name[0] == '/') { /* Now that's a nice prefix */ x->x_prefixes[i] = argv[i].a_w.w_symbol->s_name; ++(x->x_num); } else if (argv[i].a_w.w_symbol->s_name[0] == '#' && argv[i].a_w.w_symbol->s_name[1] >= '1' && argv[i].a_w.w_symbol->s_name[1] <= '9') { /* The Max programmer is trying to make a patch that will be a subpatch with arguments. We have to make an outlet for this argument. */ x->x_prefixes[i] = "dummy"; ++(x->x_num); } else { /* Maybe this is an option we support */ /* if (argv[i].a_w.w_sym == ps_complain) { */ /* x->x_complainmode = 1; */ /* } else { */ /* post("* OSC-route: Unrecognized argument %s", argv[i].a_w.w_sym->s_name); */ /* } */ } // no LONG /* } else if (argv[i].a_type == A_FLOAD) { */ /* // Convert to a numeral. Max ints are -2147483648 to 2147483647 */ /* char *string = getbytes(12); */ /* // I can't be bothered to plug this 12 byte memory leak */ /* if (string == 0) { */ /* post("* OSC-route: out of memory!"); */ /* // ExitCallback(); */ /* return 0; */ /* } */ /* sprintf(string, "%d", argv[i].a_w.w_long); */ /* x->x_prefixes[i] = string; */ /* ++(x->x_num); */ } else if (argv[i].a_type == A_FLOAT) { post("* OSC-route: float arguments are not OK."); // ExitCallback(); return 0; } else { post("* OSC-route: unrecognized argument type!"); // ExitCallback(); return 0; } } /* Have to create the outlets in reverse order */ /* well, not in pd ? */ // for (i = x->x_num-1; i >= 0; --i) { for (i = 0; i <= x->x_num-1; i++) { // x->x_outlets[i] = listout(x); x->x_outlets[i] = outlet_new(&x->x_obj, &s_list); } // ExitCallback(); return (x); } void OSCroute_version (t_OSCroute *x) { // EnterCallback(); post("OSCroute Version " OSC_ROUTE_VERSION ", by Matt Wright. pd-fication cxc@web.fm\nOSCroute Compiled " __TIME__ " " __DATE__); // ExitCallback(); } /* I don't know why these aren't defined in some Max #include file. */ #define ASSIST_INLET 1 #define ASSIST_OUTLET 2 void OSCroute_assist (t_OSCroute *x, void *box, long msg, long arg, char *dstString) { // EnterCallback(); if (msg==ASSIST_INLET) { sprintf(dstString, "Incoming OSC messages"); } else if (msg==ASSIST_OUTLET) { if (arg < 0 || arg >= x->x_num) { post("* OSCroute_assist: No outlet corresponds to arg %ld!", arg); } else { sprintf(dstString, "subaddress + args for prefix %s", x->x_prefixes[arg]); } } else { post("* OSCroute_assist: unrecognized message %ld", msg); } // ExitCallback(); } void OSCroute_list(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { // EnterCallback(); if (argc > 0 && argv[0].a_type == A_SYMBOL) { /* Ignore the fact that this is a "list" */ OSCroute_doanything(x, argv[0].a_w.w_symbol, argc-1, argv+1); } else { post("* OSC-route: invalid list beginning with a number"); } // ExitCallback(); } void OSCroute_anything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { // EnterCallback(); OSCroute_doanything(x, s, argc, argv); // ExitCallback(); } void OSCroute_doanything(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { char *pattern, *nextSlash; int i; int matchedAnything; // post("*** OSCroute_anything(s %s, argc %ld)", s->s_name, (long) argc); pattern = s->s_name; if (pattern[0] != '/') { post("* OSC-route: invalid message pattern %s does not begin with /", s->s_name); return; } matchedAnything = 0; nextSlash = NextSlashOrNull(pattern+1); if (*nextSlash == '\0') { /* last level of the address, so we'll output the argument list */ #ifdef NULL_IS_DIFFERENT_FROM_BANG if (argc==0) { post("* OSC-route: why are you matching one level pattern %s with no args?", pattern); return; } #endif for (i = 0; i < x->x_num; ++i) { if (PatternMatch(pattern+1, x->x_prefixes[i]+1)) { ++matchedAnything; // I hate stupid Max lists with a special first element if (argc == 0) { outlet_bang(x->x_outlets[i]); } else if (argv[0].a_type == A_SYMBOL) { // Promote the symbol that was argv[0] to the special symbol outlet_anything(x->x_outlets[i], argv[0].a_w.w_symbol, argc-1, argv+1); } else if (argc > 1) { // Multiple arguments starting with a number, so naturally we have // to use a special function to output this "list", since it's what // Max originally meant by "list". outlet_list(x->x_outlets[i], 0L, argc, argv); } else { // There was only one argument, and it was a number, so we output it // not as a list /* if (argv[0].a_type == A_LONG) { */ /* outlet_int(x->x_outlets[i], argv[0].a_w.w_long); */ // } else if (argv[0].a_type == A_FLOAT) { outlet_float(x->x_outlets[i], argv[0].a_w.w_float); } else { post("* OSC-route: unrecognized atom type!"); } } } } } else { /* There's more address after this part, so our output list will begin with the next slash. */ t_symbol *restOfPattern = 0; /* avoid the gensym unless we have to output */ char patternBegin[1000]; /* Get the first level of the incoming pattern to match against all our prefixes */ StrCopyUntilSlash(patternBegin, pattern+1); for (i = 0; i < x->x_num; ++i) { if (PatternMatch(patternBegin, x->x_prefixes[i]+1)) { ++matchedAnything; if (restOfPattern == 0) { restOfPattern = gensym(nextSlash); } outlet_anything(x->x_outlets[i], restOfPattern, argc, argv); } } } if (x->x_complainmode) { if (!matchedAnything) { post("* OSC-route: pattern %s did not match any prefixes", pattern); } } } static char *NextSlashOrNull(char *p) { while (*p != '/' && *p != '\0') { p++; } return p; } static void StrCopyUntilSlash(char *target, const char *source) { while (*source != '/' && *source != '\0') { *target = *source; ++target; ++source; } *target = 0; } static int MyStrCopy(char *target, const char *source) { int i = 0; while (*source != '\0') { *target = *source; ++target; ++source; ++i; } *target = 0; return i; } void OSCroute_allmessages(t_OSCroute *x, t_symbol *s, int argc, t_atom *argv) { int i; t_symbol *prefixSymbol = 0; char prefixBuf[1000]; char *endOfPrefix; t_atom a[1]; if (argc >= 1 && argv[0].a_type == A_SYMBOL) { prefixSymbol = argv[0].a_w.w_symbol; endOfPrefix = prefixBuf + MyStrCopy(prefixBuf, prefixSymbol->s_name); } else { prefixSymbol = ps_emptySymbol; prefixBuf[0] = '\0'; endOfPrefix = prefixBuf; } for (i = 0; i < x->x_num; ++i) { post("OSC: %s%s", prefixSymbol->s_name, x->x_prefixes[i]); MyStrCopy(endOfPrefix, x->x_prefixes[i]); SETSYMBOL(a, gensym(prefixBuf)); outlet_anything(x->x_outlets[i], s, 1, a); } }