00001 /* 00002 Copyright © 1998. The Regents of the University of California (Regents). 00003 All Rights Reserved. 00004 00005 Written by Matt Wright, The Center for New Music and Audio Technologies, 00006 University of California, Berkeley. 00007 00008 Permission to use, copy, modify, distribute, and distribute modified versions 00009 of this software and its documentation without fee and without a signed 00010 licensing agreement, is hereby granted, provided that the above copyright 00011 notice, this paragraph and the following two paragraphs appear in all copies, 00012 modifications, and distributions. 00013 00014 IN NO EVENT SHALL REGENTS BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, 00015 SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING 00016 OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF REGENTS HAS 00017 BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00018 00019 REGENTS SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 00020 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 00021 PURPOSE. THE SOFTWARE AND ACCOMPANYING DOCUMENTATION, IF ANY, PROVIDED 00022 HEREUNDER IS PROVIDED "AS IS". REGENTS HAS NO OBLIGATION TO PROVIDE 00023 MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 00024 00025 The OpenSound Control WWW page is 00026 http://www.cnmat.berkeley.edu/OpenSoundControl 00027 */ 00028 00029 00030 /* 00031 OSC-address-space.h 00032 Matt Wright, 11/20/97 00033 Version 2.0 5/28/98 00034 00035 C interface for registering the nodes in the OSC address space for an 00036 application. 00037 00038 include OSC-timetag.h before this file 00039 00040 ****************************** Introduction ****************************** 00041 00042 00043 The model is based on our original C++ design and consists of two kinds of 00044 objects: 00045 00046 methods are the leaf nodes of the address space hierarchy. A complete OSC 00047 address corresponds to a particular method, which has a corresponding 00048 callback procedure that will be invoked to implement commands sent by an 00049 OSC client. 00050 00051 containers are branch nodes of the address space hierarchy, and contain 00052 methods and other containers. Each container has a single namespace; 00053 it cannot contain a method and a subcontainer with the same name. 00054 00055 For example, let's examine the OSC message "/resonators/foo/decay 2.5". The 00056 address of this message is "/resonators/foo/decay" and the message has a 00057 single argument, 2.5. We'd say that the object corresponding to the prefix 00058 "/resonators" is a container, and that it contains another container whose 00059 address is "/resonators/foo". The "/resonators/foo" container has a method 00060 "decay". 00061 00062 The memory model used by this module is pre-allocation of fixed-size objects 00063 for containers, methods, and other internal objects. This preallocated 00064 memory is dynamically managed internally by a custom high-performance memory 00065 allocator. When the preallocated memory runs out, this module calls an 00066 optional realtime memory allocator that you provide. If your memory allocator 00067 gives this module more memory, it will add it to the pool of objects and 00068 never free the memory. If your system does not have a realtime memory 00069 allocator, provide a procedure that always returns 0. 00070 */ 00071 00072 /*************************** Type Definitions ******************************/ 00073 00074 /* Users of this module don't get to see what's inside these objects */ 00075 typedef struct OSCContainerStruct *OSCcontainer; 00076 typedef struct OSCMethodStruct *OSCMethod; 00077 00078 /* This makes it easy to change the way we represent symbolic names: */ 00079 typedef const char *Name; 00080 00081 00082 /************************ Initialization and Memory ************************/ 00083 00084 /* You will fill an OSCAddressSpaceMemoryTuner struct with the parameters that 00085 determine how memory will be allocated. 00086 00087 initNumContainers is the number of containers that will be allocated at 00088 initialization time. This should be the maximum number of containers you 00089 ever expect to have in your address space. 00090 00091 initNumMethods is the number of methods that will be allocated at 00092 initialization time. If you register the same method callback procedure 00093 multiple times at different places in the address space, each of these 00094 locations counts as a separate method as far as memory allocation is 00095 concerned. 00096 00097 The MemoryAllocator fields are procedures you will provide that allocate 00098 memory. Like malloc(), they take the number of bytes as arguments and return 00099 either a pointer to the new memory or 0 for failure. This memory will never 00100 be freed. 00101 00102 The InitTimeMemoryAllocator will be called only at initialization time, 00103 i.e., before OSCInitAddressSpace() returns. If it ever returns 0, that's 00104 a fatal error. 00105 00106 The RealTimeMemoryAllocator will be called if, while the application is 00107 running, the address space grows larger than can fit in what was allocated 00108 at initialization time. If the RealTimeMemoryAllocator() returns 0, the 00109 operation attempting to grow the address space will fail. If your system 00110 does not have real-time memory allocation, RealTimeMemoryAllocator should 00111 be a procedure that always returns 0. 00112 */ 00113 00114 struct OSCAddressSpaceMemoryTuner { 00115 int initNumContainers; 00116 int initNumMethods; 00117 void *(*InitTimeMemoryAllocator)(int numBytes); 00118 void *(*RealTimeMemoryAllocator)(int numBytes); 00119 }; 00120 00121 /* Given an OSCAddressSpaceMemoryTuner, return the number of bytes of 00122 memory that would be allocated if OSCInitAddressSpace() were called 00123 on it. */ 00124 int OSCAddressSpaceMemoryThatWouldBeAllocated(struct OSCAddressSpaceMemoryTuner *t); 00125 00126 00127 /* Call this before you call anything else. It returns the container that 00128 corresponds to the address "/" and is the root of the address space tree. 00129 */ 00130 OSCcontainer OSCInitAddressSpace(struct OSCAddressSpaceMemoryTuner *t); 00131 00132 00133 /**************************** Containers ****************************/ 00134 00135 /* Here's how this system deals with the standard OSC queries addressed to 00136 containers. This module handles the details of listening for these queries 00137 and returning a correctly-formatted answer; all it needs from you is the 00138 actual data that constitute the answers to these queries. 00139 00140 You pass this data in an OSCContainerQueryResponseInfo structure. Future versions 00141 of this module may have new kinds of queries that they can deal with, so 00142 the list of fields in this structure may grow. That's why your code should 00143 call OSCInitContainerQueryResponseInfo() on your struct before putting new values 00144 into it; this procedure will initialize all of the fields to 0, meaning 00145 "don't have that information", which will cause the associated queries to 00146 fail. 00147 00148 The "null" message, i.e., a message with a trailing slash, requesting the 00149 list of addresses under a given container, is handled automatically. 00150 */ 00151 00152 struct OSCContainerQueryResponseInfoStruct { 00153 char *comment; 00154 /* Other fields may go here */ 00155 }; 00156 00157 void OSCInitContainerQueryResponseInfo(struct OSCContainerQueryResponseInfoStruct *i); 00158 00159 00160 /* Allocate a new container and initialize it. Returns 0 if it cannot 00161 allocate a new container, e.g., if you've exceeded the initNumContainers 00162 limit of the OSCAddressSpaceMemoryTuner() and the RealTimeMemoryAllocator() 00163 didn't return any new memory. 00164 00165 This procedure doesn't make a copy of the name string or any of the 00166 contents of the OSCContainerQueryResponseInfoStruct. It does copy the fields 00167 of the OSCContainerQueryResponseInfoStruct. 00168 */ 00169 OSCcontainer OSCNewContainer(Name name, OSCcontainer parent, 00170 struct OSCContainerQueryResponseInfoStruct *queryInfo); 00171 00172 00173 /* Remove a container from the address space. This also removes all the 00174 container's methods and recursively removes all sub-containers. Memory 00175 freed by removing a container is kept in this module's internal pool. 00176 */ 00177 void OSCRemoveContainer(OSCcontainer container); 00178 00179 00180 /* Given a pointer to a container, and another name for that container, add or 00181 remove that name as an alias for the container. Return FALSE for failure. */ 00182 Boolean OSCAddContainerAlias(OSCcontainer container, Name otherName); 00183 Boolean OSCRemoveContainerAlias(OSCcontainer container, Name otherName); 00184 00185 00186 /* Write the OSC address of the given container into the given string. 00187 Return FALSE if the address won't fit in the string. */ 00188 Boolean OSCGetAddressString(char *target, int maxLength, OSCcontainer c); 00189 00190 00191 /* Given an address (not a pattern!), return the single OSCcontainer it names, 00192 or 0 if there is no container at that address */ 00193 OSCcontainer OSCLookUpContainer(Name address); 00194 00195 00196 /**************************** Methods ****************************/ 00197 00198 /* A methodCallback is a procedure that you write that will be called at the 00199 time that an OSC message is to take effect. It will be called with 5 00200 arguments: 00201 - A context pointer that was registered with the methodNode 00202 this is a method of. (Something like the C++ "this" pointer.) 00203 - The number of bytes of argument data 00204 - A pointer to the argument portion of the OSC message 00205 - The time tag at which this message is supposed to take effect. 00206 - A "return address" object you can use to send a message back to the 00207 client that sent this message. This return channel is guaranteed 00208 to be usable only during the invocation of your method, so your method 00209 must use the return address immediately or ignore it, not store it away 00210 for later use. 00211 */ 00212 typedef const struct NetworkReturnAddressStruct *const NetworkReturnAddressPtr; 00213 typedef void (*methodCallback)(void *context, int arglen, const void *args, 00214 OSCTimeTag when, NetworkReturnAddressPtr returnAddr); 00215 00216 00217 /* A ParamValQuerier is a procedure that the OSC system will call when the 00218 user wants to know the current value of a parameter. It will be passed the 00219 same context pointer as the associated method. It should write its return 00220 value in the given buffer in the same format as the associated 00221 methodCallback would expect its "args" argument, and should return the 00222 length of data just like the method would expect in its "arglen" argument. 00223 It doesn't have to worry about the address portion of the OSC message. 00224 */ 00225 typedef char OSCData; /* For pointers to OSC-formatted data */ 00226 typedef int (*ParamValQuerier)(OSCData *result, void *context); 00227 00228 00229 /* This system deals with other standard per-method queries, such as 00230 documentation, valid parameter types and ranges, units, default values, 00231 etc., pretty much just like per-container queries. 00232 */ 00233 00234 struct OSCMethodQueryResponseInfoStruct { 00235 char *description; 00236 ParamValQuerier pvq; 00237 /* For each argument of the method: 00238 min, max, default, units */ 00239 }; 00240 00241 void OSCInitMethodQueryResponseInfo(struct OSCMethodQueryResponseInfoStruct *i); 00242 00243 00244 /* Allocate a new method, initialize it, and add it to a container. Returns 0 00245 for failure, e.g., if you've exceeded the initNumMethods limit of the 00246 OSCAddressSpaceMemoryTuner() and the RealTimeMemoryAllocator() didn't return any 00247 new memory. 00248 00249 This procedure doesn't make a copy of the name string or any of the 00250 contents of the OSCMethodQueryResponseInfoStruct. 00251 */ 00252 00253 OSCMethod OSCNewMethod(Name name, OSCcontainer container, methodCallback meth, 00254 void *context, struct OSCMethodQueryResponseInfoStruct *queryInfo); 00255 00256 00257 00258 /******************************* Debug ********************************/ 00259 00260 //#define DEBUG 00261 00262 #ifdef DEBUG 00263 void OSCPrintWholeAddressSpace(void); 00264 #endif 00265 00266 00267 /**************************** Sample Code *****************************/ 00268 00269 /* Here's a gross approximation of how your application will invoke the 00270 procedures in this module. It registers an address space with 00271 containers with addresses "/foo", "/foo/foochild", and "/bar", 00272 and gives each of them "play" and "shuddup" messages. 00273 00274 00275 #include "OSC-common.h" 00276 #include "OSC-timetag.h" 00277 #include "OSC-address-space.h" 00278 00279 00280 typedef struct { 00281 int playing; 00282 int param; 00283 float otherParam; 00284 } Player; 00285 00286 void PlayCallback(void *context, int arglen, const void *vargs, 00287 OSCTimeTag when, NetworkReturnAddressPtr ra) { 00288 Player *p = context; 00289 const int *args = vargs; 00290 00291 00292 p->playing = 1; 00293 if (arglen >= 4) { 00294 p->param = args[0]; 00295 } 00296 } 00297 00298 void ShuddupCallback(void *context, int arglen, const void *vargs, 00299 OSCTimeTag when, NetworkReturnAddressPtr ra) { 00300 Player *p = context; 00301 const float *args = vargs; 00302 00303 00304 p->playing = 0; 00305 if (arglen >= 4) { 00306 p->otherParam = args[0]; 00307 } 00308 } 00309 00310 void *InitTimeMalloc(int numBytes) { 00311 return malloc(numBytes); 00312 } 00313 00314 void *NoRealTimeMalloc(int numBytes) { 00315 return 0; 00316 } 00317 00318 main() { 00319 struct OSCAddressSpaceMemoryTuner oasmt; 00320 OSCcontainer topLevelContainer, foo, foochild, bar; 00321 struct OSCContainerQueryResponseInfoStruct ocqris; 00322 struct OSCMethodQueryResponseInfoStruct omqris; 00323 00324 Player *players; 00325 00326 players = (Player *) malloc(3 * sizeof(*players)); 00327 if (!players) exit(1); 00328 00329 oasmt.initNumContainers = 10; 00330 oasmt.initNumMethods = 10; 00331 oasmt.InitTimeMemoryAllocator = InitTimeMalloc; 00332 oasmt.RealTimeMemoryAllocator = NoRealTimeMalloc; 00333 00334 topLevelContainer = OSCInitAddressSpace(&oasmt); 00335 00336 OSCInitContainerQueryResponseInfo(&ocqris); 00337 ocqris.comment = "Foo for you"; 00338 foo = OSCNewContainer("foo", topLevelContainer, &ocqris); 00339 00340 OSCInitContainerQueryResponseInfo(&ocqris); 00341 ocqris.comment = "Beware the son of foo!"; 00342 foochild = OSCNewContainer("foochild", foo, &ocqris); 00343 00344 OSCInitContainerQueryResponseInfo(&ocqris); 00345 ocqris.comment = "Belly up to the bar"; 00346 bar = OSCNewContainer("bar", topLevelContainer, &ocqris); 00347 00348 if (foo == 0 || foochild == 0 || bar == 0) { 00349 fprintf(stderr, "Problem!\n"); 00350 exit(1); 00351 } 00352 00353 OSCInitMethodQueryResponseInfo(&omqris); 00354 OSCNewMethod("play", foo, PlayCallback, &(players[0]), &omqris); 00355 OSCNewMethod("shuddup", foo, ShuddupCallback, &(players[0]), &omqris); 00356 00357 OSCNewMethod("play", foochild, PlayCallback, &(players[1]), &omqris); 00358 OSCNewMethod("shuddup", foochild, ShuddupCallback, &(players[1]), &omqris); 00359 00360 OSCNewMethod("play", bar, PlayCallback, &(players[2]), &omqris); 00361 OSCNewMethod("shuddup", bar, ShuddupCallback, &(players[2]), &omqris); 00362 } 00363 00364 */ 00365
1.5.8