mirror of
https://github.com/game-stop/veejay.git
synced 2025-12-13 19:30:00 +01:00
365 lines
14 KiB
C
365 lines
14 KiB
C
/*
|
|
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);
|
|
}
|
|
|
|
*/
|
|
|