2 Written by Adrian Freed, The Center for New Music and Audio Technologies,
3 University of California, Berkeley. Copyright (c) 1992,93,94,95,96,97,98,99,2000,01,02,03,04
4 The Regents of the University of California (Regents).
6 Permission to use, copy, modify, distribute, and distribute modified versions
7 of this software and its documentation without fee and without a signed
8 licensing agreement, is hereby granted, provided that the above copyright
9 notice, this paragraph and the following two paragraphs appear in all copies,
10 modifications, and distributions.
12 IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
13 SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING
14 OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS
15 BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17 REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
18 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED
20 HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE
21 MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 The OSC webpage is http://cnmat.cnmat.berkeley.edu/OpenSoundControl
28 Max object for OSC-style dispatching
32 Match a pattern against a pattern?
33 Declare outlet types / distinguish leaf nodes from other children
34 More sophisticated (2-pass?) allmessages scheme
40 -- tweaks for Win32 www.zeggz.com/raf 13-April-2002
47 #include "../../pdbox.h"
61 /* structure definition of your object */
64 #define OSC_ROUTE_VERSION "1.05"
65 #define OSCWarning(x...) post(x)
67 /* the required include files */
68 #include "../src/m_pd.h"
78 /* Fixed byte width types */
79 typedef int int4
; /* 4 byte int */
81 Boolean
PatternMatch (const char *pattern
, const char *test
);
85 /* Version 1.04: Allows #1 thru #9 as typed-in arguments
86 Version 1.05: Allows "list" messages as well as "message" messages.
89 static t_class
*OSCroute_class
;
91 typedef struct _OSCroute
93 t_object x_obj
; // required header
94 t_int x_num
; // Number of address prefixes we store
95 t_int x_complainmode
; // Do we print a message if no match?
96 t_int x_sendmode
; // use pd internal sends instead of outlets
97 char *x_prefixes
[MAX_NUM
];
98 void *x_outlets
[MAX_NUM
+1];
101 t_symbol
*ps_list
, *ps_complain
, *ps_emptySymbol
;
105 void OSCroute_doanything(t_OSCroute
*x
, t_symbol
*s
, int argc
, t_atom
*argv
);
106 void OSCroute_anything(t_OSCroute
*x
, t_symbol
*s
, int argc
, t_atom
*argv
);
107 void OSCroute_list(t_OSCroute
*x
, t_symbol
*s
, int argc
, t_atom
*argv
);
108 /* //void *OSCroute_new(t_symbol *s, int argc, atom *argv); */
109 void *OSCroute_new(t_symbol
*s
, int argc
, t_atom
*argv
);
110 void OSCroute_version (t_OSCroute
*x
);
111 /* void OSCroute_assist (OSCroute *x, void *box, long msg, long arg, */
112 /* char *dstString); */
113 void OSCroute_allmessages(t_OSCroute
*x
, t_symbol
*s
, int argc
, t_atom
*argv
);
115 static char *NextSlashOrNull(char *p
);
116 static void StrCopyUntilSlash(char *target
, const char *source
);
120 static void OSCroute_free(t_OSCroute
*x
)
125 // freebytes(x->x_vec, x->x_nelement * sizeof(*x->x_vec));
128 /* initialization routine */
132 OSC_API
void OSCroute_setup(void) {
134 void OSCroute_setup(void) {
136 OSCroute_class
= class_new(gensym("OSCroute"), (t_newmethod
)OSCroute_new
,
137 (t_method
)OSCroute_free
,sizeof(t_OSCroute
), 0, A_GIMME
, 0);
138 class_addlist(OSCroute_class
, OSCroute_list
);
139 class_addanything(OSCroute_class
, OSCroute_anything
);
140 class_addmethod(OSCroute_class
, (t_method
)OSCroute_version
, gensym("version"), A_NULL
, 0, 0);
141 class_sethelpsymbol(OSCroute_class
, gensym("OSCroute-help.pd"));
144 class_addmethod(OSCroute_class, (t_method)OSCroute_connect,
145 gensym("connect"), A_SYMBOL, A_FLOAT, 0);
146 class_addmethod(OSCroute_class, (t_method)OSCroute_disconnect,
147 gensym("disconnect"), 0);
148 class_addmethod(OSCroute_class, (t_method)OSCroute_send, gensym("send"),
151 /* ps_list = gensym("list"); */
152 /* ps_complain = gensym("complain"); */
153 ps_emptySymbol
= gensym("");
155 post("OSCroute object version " OSC_ROUTE_VERSION
" by Matt Wright. pd: jdl Win32 raf.");
156 post("OSCroute Copyright © 1999 Regents of the University of California. All Rights Reserved.");
161 /* instance creation routine */
163 void *OSCroute_new(t_symbol
*s
, int argc
, t_atom
*argv
)
168 t_OSCroute
*x
= (t_OSCroute
*)pd_new(OSCroute_class
); // get memory for a new object & initialize
170 int i
; //{{raf}} n not used
174 if (argc
> MAX_NUM
) {
175 post("* OSC-route: too many arguments: %ld (max %ld)", argc
, MAX_NUM
);
180 x
->x_complainmode
= 0;
182 for (i
= 0; i
< argc
; ++i
) {
183 if (argv
[i
].a_type
== A_SYMBOL
) {
184 if (argv
[i
].a_w
.w_symbol
->s_name
[0] == '/') {
185 /* Now that's a nice prefix */
186 x
->x_prefixes
[i
] = argv
[i
].a_w
.w_symbol
->s_name
;
188 } else if (argv
[i
].a_w
.w_symbol
->s_name
[0] == '#' &&
189 argv
[i
].a_w
.w_symbol
->s_name
[1] >= '1' &&
190 argv
[i
].a_w
.w_symbol
->s_name
[1] <= '9') {
191 /* The Max programmer is trying to make a patch that will be
192 a subpatch with arguments. We have to make an outlet for this
194 x
->x_prefixes
[i
] = "dummy";
197 /* Maybe this is an option we support */
199 /* if (argv[i].a_w.w_sym == ps_complain) { */
200 /* x->x_complainmode = 1; */
202 /* post("* OSC-route: Unrecognized argument %s", argv[i].a_w.w_sym->s_name); */
209 /* } else if (argv[i].a_type == A_FLOAD) { */
210 /* // Convert to a numeral. Max ints are -2147483648 to 2147483647 */
211 /* char *string = getbytes(12); */
212 /* // I can't be bothered to plug this 12 byte memory leak */
213 /* if (string == 0) { */
214 /* post("* OSC-route: out of memory!"); */
215 /* // ExitCallback(); */
218 /* sprintf(string, "%d", argv[i].a_w.w_long); */
219 /* x->x_prefixes[i] = string; */
222 } else if (argv
[i
].a_type
== A_FLOAT
) {
223 post("* OSC-route: float arguments are not OK.");
227 post("* OSC-route: unrecognized argument type!");
234 /* Have to create the outlets in reverse order */
235 /* well, not in pd ? */
236 // for (i = x->x_num-1; i >= 0; --i) {
237 // for (i = 0; i <= x->x_num-1; i++) {
238 for (i
= 0; i
<= x
->x_num
; i
++) {
239 // x->x_outlets[i] = listout(x);
240 x
->x_outlets
[i
] = outlet_new(&x
->x_obj
, &s_list
);
248 void OSCroute_version (t_OSCroute
*x
) {
253 post("OSCroute Version " OSC_ROUTE_VERSION
254 ", by Matt Wright. pd jdl, win32: raf.\nOSCroute Compiled " __TIME__
" " __DATE__
);
258 /* I don't know why these aren't defined in some Max #include file. */
259 #define ASSIST_INLET 1
260 #define ASSIST_OUTLET 2
262 void OSCroute_assist (t_OSCroute
*x
, void *box
, long msg
, long arg
,
269 if (msg
==ASSIST_INLET
) {
271 strcpy(dstString
, "Incoming OSC messages");
273 sprintf(dstString
, "Incoming OSC messages");
275 } else if (msg
==ASSIST_OUTLET
) {
276 if (arg
< 0 || arg
>= x
->x_num
) {
277 post("* OSCroute_assist: No outlet corresponds to arg %ld!", arg
);
280 strcpy(dstString
, "subaddress + args for prefix ");
281 strcat(dstString
, x
->x_prefixes
[arg
]);
283 sprintf(dstString
, "subaddress + args for prefix %s", x
->x_prefixes
[arg
]);
287 post("* OSCroute_assist: unrecognized message %ld", msg
);
293 void OSCroute_list(t_OSCroute
*x
, t_symbol
*s
, int argc
, t_atom
*argv
) {
298 if (argc
> 0 && argv
[0].a_type
== A_SYMBOL
) {
299 /* Ignore the fact that this is a "list" */
300 OSCroute_doanything(x
, argv
[0].a_w
.w_symbol
, argc
-1, argv
+1);
302 // post("* OSC-route: invalid list beginning with a number");
303 // output on unmatched outlet jdl 20020908
304 if (argv
[0].a_type
== A_FLOAT
) {
305 outlet_float(x
->x_outlets
[x
->x_num
], argv
[0].a_w
.w_float
);
307 post("* OSC-route: unrecognized atom type!");
314 void OSCroute_anything(t_OSCroute
*x
, t_symbol
*s
, int argc
, t_atom
*argv
) {
316 OSCroute_doanything(x
, s
, argc
, argv
);
323 void OSCroute_doanything(t_OSCroute
*x
, t_symbol
*s
, int argc
, t_atom
*argv
) {
324 char *pattern
, *nextSlash
;
327 // post("*** OSCroute_anything(s %s, argc %ld)", s->s_name, (long) argc);
330 if (pattern
[0] != '/') {
331 post("* OSC-route: invalid message pattern %s does not begin with /", s
->s_name
);
332 outlet_anything(x
->x_outlets
[x
->x_num
], s
, argc
, argv
);
338 nextSlash
= NextSlashOrNull(pattern
+1);
339 if (*nextSlash
== '\0') {
340 /* last level of the address, so we'll output the argument list */
343 #ifdef NULL_IS_DIFFERENT_FROM_BANG
345 post("* OSC-route: why are you matching one level pattern %s with no args?",
351 for (i
= 0; i
< x
->x_num
; ++i
) {
352 if (PatternMatch(pattern
+1, x
->x_prefixes
[i
]+1)) {
355 // I hate stupid Max lists with a special first element
357 outlet_bang(x
->x_outlets
[i
]);
358 } else if (argv
[0].a_type
== A_SYMBOL
) {
359 // Promote the symbol that was argv[0] to the special symbol
360 outlet_anything(x
->x_outlets
[i
], argv
[0].a_w
.w_symbol
, argc
-1, argv
+1);
361 } else if (argc
> 1) {
362 // Multiple arguments starting with a number, so naturally we have
363 // to use a special function to output this "list", since it's what
364 // Max originally meant by "list".
365 outlet_list(x
->x_outlets
[i
], 0L, argc
, argv
);
367 // There was only one argument, and it was a number, so we output it
369 /* if (argv[0].a_type == A_LONG) { */
371 /* outlet_int(x->x_outlets[i], argv[0].a_w.w_long); */
373 if (argv
[0].a_type
== A_FLOAT
) {
375 outlet_float(x
->x_outlets
[i
], argv
[0].a_w
.w_float
);
377 post("* OSC-route: unrecognized atom type!");
383 /* There's more address after this part, so our output list will begin with
385 t_symbol
*restOfPattern
= 0; /* avoid the gensym unless we have to output */
386 char patternBegin
[1000];
389 /* Get the first level of the incoming pattern to match against all our prefixes */
390 StrCopyUntilSlash(patternBegin
, pattern
+1);
392 for (i
= 0; i
< x
->x_num
; ++i
) {
393 if (PatternMatch(patternBegin
, x
->x_prefixes
[i
]+1)) {
395 if (restOfPattern
== 0) {
396 restOfPattern
= gensym(nextSlash
);
398 outlet_anything(x
->x_outlets
[i
], restOfPattern
, argc
, argv
);
403 if (x
->x_complainmode
) {
404 if (!matchedAnything
) {
405 post("* OSC-route: pattern %s did not match any prefixes", pattern
);
409 // output unmatched data on rightmost outlet a la normal 'route' object, jdl 20020908
410 if (!matchedAnything
) {
411 outlet_anything(x
->x_outlets
[x
->x_num
], s
, argc
, argv
);
417 static char *NextSlashOrNull(char *p
) {
418 while (*p
!= '/' && *p
!= '\0') {
424 static void StrCopyUntilSlash(char *target
, const char *source
) {
425 while (*source
!= '/' && *source
!= '\0') {
433 static int MyStrCopy(char *target
, const char *source
) {
435 while (*source
!= '\0') {
447 void OSCroute_allmessages(t_OSCroute
*x
, t_symbol
*s
, int argc
, t_atom
*argv
) {
449 t_symbol
*prefixSymbol
= 0;
450 char prefixBuf
[1000];
454 if (argc
>= 1 && argv
[0].a_type
== A_SYMBOL
) {
455 prefixSymbol
= argv
[0].a_w
.w_symbol
;
456 endOfPrefix
= prefixBuf
+ MyStrCopy(prefixBuf
,
457 prefixSymbol
->s_name
);
459 prefixSymbol
= ps_emptySymbol
;
461 endOfPrefix
= prefixBuf
;
465 for (i
= 0; i
< x
->x_num
; ++i
) {
466 post("OSC: %s%s", prefixSymbol
->s_name
, x
->x_prefixes
[i
]);
467 MyStrCopy(endOfPrefix
, x
->x_prefixes
[i
]);
468 SETSYMBOL(a
, gensym(prefixBuf
));
469 outlet_anything(x
->x_outlets
[i
], s
, 1, a
);
474 /* --------------------------------------------------- */
478 static const char *theWholePattern
; /* Just for warning messages */
480 static Boolean
MatchBrackets (const char *pattern
, const char *test
);
481 static Boolean
MatchList (const char *pattern
, const char *test
);
483 Boolean
PatternMatch (const char * pattern
, const char * test
) {
484 theWholePattern
= pattern
;
486 if (pattern
== 0 || pattern
[0] == 0) {
491 if (pattern
[0] == '*')
492 return PatternMatch (pattern
+1,test
);
497 switch (pattern
[0]) {
498 case 0 : return test
[0] == 0;
499 case '?' : return PatternMatch (pattern
+ 1, test
+ 1);
501 if (PatternMatch (pattern
+1, test
)) {
504 return PatternMatch (pattern
, test
+1);
508 OSCWarning("Spurious %c in pattern \".../%s/...\"",pattern
[0], theWholePattern
);
511 return MatchBrackets (pattern
,test
);
513 return MatchList (pattern
,test
);
515 if (pattern
[1] == 0) {
517 } else if (pattern
[1] == test
[0]) {
518 return PatternMatch (pattern
+2,test
+1);
523 if (pattern
[0] == test
[0]) {
524 return PatternMatch (pattern
+1,test
+1);
532 /* we know that pattern[0] == '[' and test[0] != 0 */
534 static Boolean
MatchBrackets (const char *pattern
, const char *test
) {
536 Boolean negated
= FALSE
;
537 const char *p
= pattern
;
539 if (pattern
[1] == 0) {
540 OSCWarning("Unterminated [ in pattern \".../%s/...\"", theWholePattern
);
544 if (pattern
[1] == '!') {
551 OSCWarning("Unterminated [ in pattern \".../%s/...\"", theWholePattern
);
554 if (p
[1] == '-' && p
[2] != 0) {
555 if (test
[0] >= p
[0] && test
[0] <= p
[2]) {
560 if (p
[0] == test
[0]) {
576 OSCWarning("Unterminated [ in pattern \".../%s/...\"", theWholePattern
);
582 return PatternMatch (p
+1,test
+1);
585 static Boolean
MatchList (const char *pattern
, const char *test
) {
587 const char *restOfPattern
, *tp
= test
;
590 for(restOfPattern
= pattern
; *restOfPattern
!= '}'; restOfPattern
++) {
591 if (*restOfPattern
== 0) {
592 OSCWarning("Unterminated { in pattern \".../%s/...\"", theWholePattern
);
597 restOfPattern
++; /* skip close curly brace */
600 pattern
++; /* skip open curly brace */
604 if (*pattern
== ',') {
605 if (PatternMatch (restOfPattern
, tp
)) {
611 } else if (*pattern
== '}') {
612 return PatternMatch (restOfPattern
, tp
);
613 } else if (*pattern
== *tp
) {
618 while (*pattern
!= ',' && *pattern
!= '}') {
621 if (*pattern
== ',') {