1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364
|
/*
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-address-space.h
Matt Wright, 11/20/97
Version 2.0 5/28/98
C interface for registering the nodes in the OSC address space for an
application.
include OSC-timetag.h before this file
****************************** Introduction ******************************
The model is based on our original C++ design and consists of two kinds of
objects:
methods are the leaf nodes of the address space hierarchy. A complete OSC
address corresponds to a particular method, which has a corresponding
callback procedure that will be invoked to implement commands sent by an
OSC client.
containers are branch nodes of the address space hierarchy, and contain
methods and other containers. Each container has a single namespace;
it cannot contain a method and a subcontainer with the same name.
For example, let's examine the OSC message "/resonators/foo/decay 2.5". The
address of this message is "/resonators/foo/decay" and the message has a
single argument, 2.5. We'd say that the object corresponding to the prefix
"/resonators" is a container, and that it contains another container whose
address is "/resonators/foo". The "/resonators/foo" container has a method
"decay".
The memory model used by this module is pre-allocation of fixed-size objects
for containers, methods, and other internal objects. This preallocated
memory is dynamically managed internally by a custom high-performance memory
allocator. When the preallocated memory runs out, this module calls an
optional realtime memory allocator that you provide. If your memory allocator
gives this module more memory, it will add it to the pool of objects and
never free the memory. If your system does not have a realtime memory
allocator, provide a procedure that always returns 0.
*/
/*************************** Type Definitions ******************************/
/* Users of this module don't get to see what's inside these objects */
typedef struct OSCContainerStruct *OSCcontainer;
typedef struct OSCMethodStruct *OSCMethod;
/* This makes it easy to change the way we represent symbolic names: */
typedef const char *Name;
/************************ Initialization and Memory ************************/
/* You will fill an OSCAddressSpaceMemoryTuner struct with the parameters that
determine how memory will be allocated.
initNumContainers is the number of containers that will be allocated at
initialization time. This should be the maximum number of containers you
ever expect to have in your address space.
initNumMethods is the number of methods that will be allocated at
initialization time. If you register the same method callback procedure
multiple times at different places in the address space, each of these
locations counts as a separate method as far as memory allocation is
concerned.
The MemoryAllocator fields are procedures you will provide that allocate
memory. Like malloc(), they take the number of bytes as arguments and return
either a pointer to the new memory or 0 for failure. This memory will never
be freed.
The InitTimeMemoryAllocator will be called only at initialization time,
i.e., before OSCInitAddressSpace() returns. If it ever returns 0, that's
a fatal error.
The RealTimeMemoryAllocator will be called if, while the application is
running, the address space grows larger than can fit in what was allocated
at initialization time. If the RealTimeMemoryAllocator() returns 0, the
operation attempting to grow the address space will fail. If your system
does not have real-time memory allocation, RealTimeMemoryAllocator should
be a procedure that always returns 0.
*/
struct OSCAddressSpaceMemoryTuner {
int initNumContainers;
int initNumMethods;
void *(*InitTimeMemoryAllocator)(int numBytes);
void *(*RealTimeMemoryAllocator)(int numBytes);
};
/* Given an OSCAddressSpaceMemoryTuner, return the number of bytes of
memory that would be allocated if OSCInitAddressSpace() were called
on it. */
int OSCAddressSpaceMemoryThatWouldBeAllocated(struct OSCAddressSpaceMemoryTuner *t);
/* Call this before you call anything else. It returns the container that
corresponds to the address "/" and is the root of the address space tree.
*/
OSCcontainer OSCInitAddressSpace(struct OSCAddressSpaceMemoryTuner *t);
/**************************** Containers ****************************/
/* Here's how this system deals with the standard OSC queries addressed to
containers. This module handles the details of listening for these queries
and returning a correctly-formatted answer; all it needs from you is the
actual data that constitute the answers to these queries.
You pass this data in an OSCContainerQueryResponseInfo structure. Future versions
of this module may have new kinds of queries that they can deal with, so
the list of fields in this structure may grow. That's why your code should
call OSCInitContainerQueryResponseInfo() on your struct before putting new values
into it; this procedure will initialize all of the fields to 0, meaning
"don't have that information", which will cause the associated queries to
fail.
The "null" message, i.e., a message with a trailing slash, requesting the
list of addresses under a given container, is handled automatically.
*/
struct OSCContainerQueryResponseInfoStruct {
char *comment;
/* Other fields may go here */
};
void OSCInitContainerQueryResponseInfo(struct OSCContainerQueryResponseInfoStruct *i);
/* Allocate a new container and initialize it. Returns 0 if it cannot
allocate a new container, e.g., if you've exceeded the initNumContainers
limit of the OSCAddressSpaceMemoryTuner() and the RealTimeMemoryAllocator()
didn't return any new memory.
This procedure doesn't make a copy of the name string or any of the
contents of the OSCContainerQueryResponseInfoStruct. It does copy the fields
of the OSCContainerQueryResponseInfoStruct.
*/
OSCcontainer OSCNewContainer(Name name, OSCcontainer parent,
struct OSCContainerQueryResponseInfoStruct *queryInfo);
/* Remove a container from the address space. This also removes all the
container's methods and recursively removes all sub-containers. Memory
freed by removing a container is kept in this module's internal pool.
*/
void OSCRemoveContainer(OSCcontainer container);
/* Given a pointer to a container, and another name for that container, add or
remove that name as an alias for the container. Return FALSE for failure. */
Boolean OSCAddContainerAlias(OSCcontainer container, Name otherName);
Boolean OSCRemoveContainerAlias(OSCcontainer container, Name otherName);
/* Write the OSC address of the given container into the given string.
Return FALSE if the address won't fit in the string. */
Boolean OSCGetAddressString(char *target, int maxLength, OSCcontainer c);
/* Given an address (not a pattern!), return the single OSCcontainer it names,
or 0 if there is no container at that address */
OSCcontainer OSCLookUpContainer(Name address);
/**************************** Methods ****************************/
/* A methodCallback is a procedure that you write that will be called at the
time that an OSC message is to take effect. It will be called with 5
arguments:
- A context pointer that was registered with the methodNode
this is a method of. (Something like the C++ "this" pointer.)
- The number of bytes of argument data
- A pointer to the argument portion of the OSC message
- The time tag at which this message is supposed to take effect.
- A "return address" object you can use to send a message back to the
client that sent this message. This return channel is guaranteed
to be usable only during the invocation of your method, so your method
must use the return address immediately or ignore it, not store it away
for later use.
*/
typedef struct NetworkReturnAddressStruct *NetworkReturnAddressPtr;
/* removed const */
typedef void (*methodCallback)(void *context, int arglen, const void *args,
OSCTimeTag when, NetworkReturnAddressPtr returnAddr);
/* A ParamValQuerier is a procedure that the OSC system will call when the
user wants to know the current value of a parameter. It will be passed the
same context pointer as the associated method. It should write its return
value in the given buffer in the same format as the associated
methodCallback would expect its "args" argument, and should return the
length of data just like the method would expect in its "arglen" argument.
It doesn't have to worry about the address portion of the OSC message.
*/
typedef char OSCData; /* For pointers to OSC-formatted data */
typedef int (*ParamValQuerier)(OSCData *result, void *context);
/* This system deals with other standard per-method queries, such as
documentation, valid parameter types and ranges, units, default values,
etc., pretty much just like per-container queries.
*/
struct OSCMethodQueryResponseInfoStruct {
char *description;
ParamValQuerier pvq;
/* For each argument of the method:
min, max, default, units */
};
void OSCInitMethodQueryResponseInfo(struct OSCMethodQueryResponseInfoStruct *i);
/* Allocate a new method, initialize it, and add it to a container. Returns 0
for failure, e.g., if you've exceeded the initNumMethods limit of the
OSCAddressSpaceMemoryTuner() and the RealTimeMemoryAllocator() didn't return any
new memory.
This procedure doesn't make a copy of the name string or any of the
contents of the OSCMethodQueryResponseInfoStruct.
*/
OSCMethod OSCNewMethod(Name name, OSCcontainer container, methodCallback meth,
void *context, struct OSCMethodQueryResponseInfoStruct *queryInfo);
/******************************* Debug ********************************/
void OSCPrintWholeAddressSpace(void);
/**************************** Sample Code *****************************/
/* Here's a gross approximation of how your application will invoke the
procedures in this module. It registers an address space with
containers with addresses "/foo", "/foo/foochild", and "/bar",
and gives each of them "play" and "shuddup" messages.
#include "OSC-common.h"
#include "OSC-timetag.h"
#include "OSC-address-space.h"
typedef struct {
int playing;
int param;
float otherParam;
} Player;
void PlayCallback(void *context, int arglen, const void *vargs,
OSCTimeTag when, NetworkReturnAddressPtr ra) {
Player *p = context;
const int *args = vargs;
p->playing = 1;
if (arglen >= 4) {
p->param = args[0];
}
}
void ShuddupCallback(void *context, int arglen, const void *vargs,
OSCTimeTag when, NetworkReturnAddressPtr ra) {
Player *p = context;
const float *args = vargs;
p->playing = 0;
if (arglen >= 4) {
p->otherParam = args[0];
}
}
void *InitTimeMalloc(int numBytes) {
return malloc(numBytes);
}
void *NoRealTimeMalloc(int numBytes) {
return 0;
}
main() {
struct OSCAddressSpaceMemoryTuner oasmt;
OSCcontainer topLevelContainer, foo, foochild, bar;
struct OSCContainerQueryResponseInfoStruct ocqris;
struct OSCMethodQueryResponseInfoStruct omqris;
Player *players;
players = (Player *) malloc(3 * sizeof(*players));
if (!players) exit(1);
oasmt.initNumContainers = 10;
oasmt.initNumMethods = 10;
oasmt.InitTimeMemoryAllocator = InitTimeMalloc;
oasmt.RealTimeMemoryAllocator = NoRealTimeMalloc;
topLevelContainer = OSCInitAddressSpace(&oasmt);
OSCInitContainerQueryResponseInfo(&ocqris);
ocqris.comment = "Foo for you";
foo = OSCNewContainer("foo", topLevelContainer, &ocqris);
OSCInitContainerQueryResponseInfo(&ocqris);
ocqris.comment = "Beware the son of foo!";
foochild = OSCNewContainer("foochild", foo, &ocqris);
OSCInitContainerQueryResponseInfo(&ocqris);
ocqris.comment = "Belly up to the bar";
bar = OSCNewContainer("bar", topLevelContainer, &ocqris);
if (foo == 0 || foochild == 0 || bar == 0) {
fprintf(stderr, "Problem!\n");
exit(1);
}
OSCInitMethodQueryResponseInfo(&omqris);
OSCNewMethod("play", foo, PlayCallback, &(players[0]), &omqris);
OSCNewMethod("shuddup", foo, ShuddupCallback, &(players[0]), &omqris);
OSCNewMethod("play", foochild, PlayCallback, &(players[1]), &omqris);
OSCNewMethod("shuddup", foochild, ShuddupCallback, &(players[1]), &omqris);
OSCNewMethod("play", bar, PlayCallback, &(players[2]), &omqris);
OSCNewMethod("shuddup", bar, ShuddupCallback, &(players[2]), &omqris);
}
*/
|