mirror of
https://github.com/game-stop/veejay.git
synced 2025-12-14 03:39:58 +01:00
Initial checkin of veejay 1.4
git-svn-id: svn://code.dyne.org/veejay/trunk@1172 eb8d1916-c9e9-0310-b8de-cf0c9472ead5
This commit is contained in:
893
veejay-current/veejay-server/libOSC/OSC-receive.c
Normal file
893
veejay-current/veejay-server/libOSC/OSC-receive.c
Normal file
@@ -0,0 +1,893 @@
|
||||
/*
|
||||
Copyright <20> 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
|
||||
*/
|
||||
|
||||
|
||||
#define PARANOID 0
|
||||
/*
|
||||
OSC-receive.c
|
||||
Matt Wright, 3/13/98, 6/3/98
|
||||
|
||||
Adapted from OSC-addressability.c (and seriously cleaned up!)
|
||||
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/time.h>
|
||||
#include <libOSC/OSC-common.h>
|
||||
#include <libOSC/OSC-timetag.h>
|
||||
#include <libOSC/OSC-address-space.h>
|
||||
#include <libOSC/NetworkReturnAddress.h>
|
||||
#include <libOSC/OSC-receive.h>
|
||||
#include <libOSC/OSC-priority-queue.h>
|
||||
#include <libOSC/OSC-string-help.h>
|
||||
#include <libOSC/OSC-drop.h>
|
||||
#include <libOSC/OSC-dispatch.h>
|
||||
#include <libOSC/NetworkUDP.h>
|
||||
#include <libOSC/OSC-callbacklist.h>
|
||||
#if defined(DEBUG_INTERNAL) || defined(DEBUG) || defined(DEBUG_PACKET_MEM) || defined(DEBUG_QD_MEM) || defined(DEBUG_8BYTE_ALIGN) || defined(SUSPECT_QD_PROB)
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
static int use_mcast_ = 0;
|
||||
static char mcast_groupname[200];
|
||||
|
||||
struct {
|
||||
OSCQueue TheQueue; /* The Priority Queue */
|
||||
OSCTimeTag lastTimeTag; /* Best approximation to current time */
|
||||
Boolean timePassed; /* TRUE if OSCInvokeMessagesThatAreReady() has been
|
||||
called since the last time OSCBeProductiveWhileWaiting() was. */
|
||||
int recvBufSize; /* Size of all receive buffers */
|
||||
void *(*InitTimeMalloc)(int numBytes);
|
||||
void *(*RealTimeMemoryAllocator)(int numBytes);
|
||||
} globals;
|
||||
|
||||
|
||||
/* Data structures */
|
||||
|
||||
struct OSCPacketBuffer_struct {
|
||||
char *buf; /* Contents of network packet go here */
|
||||
int n; /* Overall size of packet */
|
||||
int refcount; /* # queued things using memory from this buffer */
|
||||
struct OSCPacketBuffer_struct *nextFree; /* For linked list of free packets */
|
||||
|
||||
Boolean returnAddrOK; /* Because returnAddr points to memory we need to
|
||||
store future return addresses, we set this
|
||||
field to FALSE in situations where a packet
|
||||
buffer "has no return address" instead of
|
||||
setting returnAddr to 0 */
|
||||
|
||||
NetworkReturnAddressPtr returnAddr;
|
||||
//void *returnAddr; /* Addr of client this packet is from */
|
||||
/* This was of type NetworkReturnAddressPtr, but the constness
|
||||
was making it impossible for me to initialize it. There's
|
||||
probably a better way that I don't understand. */
|
||||
|
||||
};
|
||||
|
||||
/* These are the data objects that are inserted and removed from the
|
||||
scheduler. The idea is that we can insert a single message or
|
||||
an entire bundle on the scheduler, and we can leave it in various
|
||||
states of being parsed and pattern matched. */
|
||||
|
||||
#define NOT_DISPATCHED_YET ((callbackList) -1)
|
||||
|
||||
typedef struct queuedDataStruct {
|
||||
OSCTimeTag timetag; /* When this bundle or message is supposed to happen */
|
||||
OSCPacketBuffer myPacket; /* Ptr. to buffer this is contained in */
|
||||
|
||||
enum {MESSAGE, BUNDLE} type;
|
||||
|
||||
union {
|
||||
struct {
|
||||
char *bytes;
|
||||
int length;
|
||||
} bundle;
|
||||
|
||||
struct {
|
||||
char *messageName; /* Ptr. into receive buffer */
|
||||
int length; /* Includes name and arugments */
|
||||
void *args; /* 0 if not yet parsed */
|
||||
int argLength;
|
||||
callbackList callbacks; /* May be NOT_DISPATCHED_YET */
|
||||
} message;
|
||||
} data;
|
||||
|
||||
struct queuedDataStruct *nextFree; /* For linked list of free structures */
|
||||
} queuedData;
|
||||
|
||||
|
||||
|
||||
/* Static procedure declatations */
|
||||
static Boolean InitPackets(int receiveBufferSize, int clientAddrSize, int numReceiveBuffers);
|
||||
static Boolean InitQueuedData(int numQueuedObjects);
|
||||
static queuedData *AllocQD(void);
|
||||
static void FreeQD(queuedData *qd);
|
||||
static void CallWholeCallbackList(callbackList l, int argLength, void *args, OSCTimeTag when, NetworkReturnAddressPtr returnAddr);
|
||||
static void InsertBundleOrMessage(char *buf, int n, OSCPacketBuffer packet, OSCTimeTag enclosingTimeTag);
|
||||
static void ParseBundle(queuedData *qd);
|
||||
static Boolean ParseMessage(queuedData *qd);
|
||||
/* static void CheckPacketRefcount(OSCPacketBuffer packet); */
|
||||
static void PacketAddRef(OSCPacketBuffer packet);
|
||||
static void PacketRemoveRef(OSCPacketBuffer packet);
|
||||
|
||||
|
||||
/**************************************************
|
||||
Initialization and memory pre-allocation
|
||||
**************************************************/
|
||||
|
||||
|
||||
Boolean OSCInitReceive(struct OSCReceiveMemoryTuner *t) {
|
||||
globals.recvBufSize = t->receiveBufferSize;
|
||||
globals.InitTimeMalloc = t->InitTimeMemoryAllocator;
|
||||
globals.RealTimeMemoryAllocator = t->RealTimeMemoryAllocator;
|
||||
|
||||
globals.TheQueue = OSCNewQueue(t->numQueuedObjects, t->InitTimeMemoryAllocator);
|
||||
if (globals.TheQueue == 0) return FALSE;
|
||||
|
||||
globals.lastTimeTag = OSCTT_Immediately();
|
||||
globals.timePassed = TRUE;
|
||||
|
||||
if (InitPackets(t->receiveBufferSize, SizeOfNetworkReturnAddress(),
|
||||
t->numReceiveBuffers) == FALSE) return FALSE;
|
||||
if (InitQueuedData(t->numQueuedObjects) == FALSE) return FALSE;
|
||||
if (InitCallbackListNodes(t->numCallbackListNodes, t->InitTimeMemoryAllocator)
|
||||
== FALSE) return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************
|
||||
Managing packet data structures
|
||||
**************************************************/
|
||||
|
||||
static struct OSCPacketBuffer_struct *freePackets;
|
||||
|
||||
#ifdef DEBUG_PACKET_MEM
|
||||
static void PrintPacketFreeList(void) {
|
||||
struct OSCPacketBuffer_struct *p;
|
||||
printf("- freePackets:");
|
||||
if (freePackets == 0) {
|
||||
printf(" [none]");
|
||||
}
|
||||
for (p = freePackets; p != 0; p = p->nextFree) {
|
||||
printf(" %p", p);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#define MIN_REASONABLE_RCV_BUFSIZE 128
|
||||
|
||||
void OSCFreeReceiver(void)
|
||||
{
|
||||
if( freePackets) free(freePackets);
|
||||
if( globals.TheQueue ) free( globals.TheQueue );
|
||||
}
|
||||
|
||||
|
||||
static Boolean InitPackets(int receiveBufferSize, int clientAddrSize, int numReceiveBuffers) {
|
||||
int i;
|
||||
struct OSCPacketBuffer_struct *allPackets;
|
||||
|
||||
if (receiveBufferSize < MIN_REASONABLE_RCV_BUFSIZE) {
|
||||
fatal_error("OSCInitReceive: receiveBufferSize of %d is unreasonably small.",
|
||||
receiveBufferSize);
|
||||
}
|
||||
|
||||
allPackets = (*(globals.InitTimeMalloc))(numReceiveBuffers * sizeof(*allPackets));
|
||||
if (allPackets == 0) return FALSE;
|
||||
|
||||
for (i = 0; i < numReceiveBuffers; ++i) {
|
||||
allPackets[i].returnAddr = (*(globals.InitTimeMalloc))(clientAddrSize);
|
||||
if (allPackets[i].returnAddr == 0) return FALSE;
|
||||
|
||||
allPackets[i].buf = (*(globals.InitTimeMalloc))(receiveBufferSize);
|
||||
if (allPackets[i].buf == 0) return FALSE;
|
||||
|
||||
allPackets[i].nextFree = &(allPackets[i+1]);
|
||||
}
|
||||
allPackets[numReceiveBuffers-1].nextFree = ((struct OSCPacketBuffer_struct *) 0);
|
||||
freePackets = allPackets;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
char *OSCPacketBufferGetBuffer(OSCPacketBuffer p) {
|
||||
return p->buf;
|
||||
}
|
||||
|
||||
int *OSCPacketBufferGetSize(OSCPacketBuffer p) {
|
||||
return &(p->n);
|
||||
}
|
||||
|
||||
int OSCGetReceiveBufferSize(void) {
|
||||
return globals.recvBufSize;
|
||||
}
|
||||
|
||||
NetworkReturnAddressPtr OSCPacketBufferGetClientAddr(OSCPacketBuffer p) {
|
||||
return p->returnAddr;
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
void PrintPacket(OSCPacketBuffer p) {
|
||||
printf("Packet %p. buf %p, n %d, refcount %d, nextFree %p\n",
|
||||
p, p->buf, p->n, p->refcount, p->nextFree);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
OSCPacketBuffer OSCAllocPacketBuffer(void) {
|
||||
OSCPacketBuffer result;
|
||||
if (freePackets == 0) {
|
||||
/* Could try to call the real-time memory allocator here */
|
||||
OSCWarning("OSCAllocPacketBuffer: no free packets!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = freePackets;
|
||||
freePackets = result->nextFree;
|
||||
result->refcount = 0;
|
||||
|
||||
#ifdef DEBUG_PACKET_MEM
|
||||
printf("OSCAllocPacketBuffer: allocating %p ", result);
|
||||
PrintPacketFreeList();
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void OSCFreePacket(OSCPacketBuffer p) {
|
||||
#ifdef PARANOID
|
||||
if (p->refcount != 0) {
|
||||
OSCWarning("OSCFreePacket: %p's refcount is %d!\n", p, p->refcount);
|
||||
}
|
||||
#endif
|
||||
|
||||
p->nextFree = freePackets;
|
||||
freePackets = p;
|
||||
|
||||
#ifdef DEBUG_PACKET_MEM
|
||||
printf("OSCFreePacket: freed %p ", p);
|
||||
PrintPacketFreeList();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**************************************************
|
||||
Dealing with OpenSoundControl packets and
|
||||
making the messages take effect.
|
||||
**************************************************/
|
||||
|
||||
static queuedData *freeQDList;
|
||||
|
||||
#ifdef DEBUG_QD_MEM
|
||||
static void PrintQDFreeList(void) {
|
||||
static queuedData *p;
|
||||
printf("- freeQDList:");
|
||||
if (freeQDList == 0) {
|
||||
printf(" [none]");
|
||||
}
|
||||
for (p = freeQDList; p != 0; p = p->nextFree) {
|
||||
printf(" %p", p);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
static Boolean InitQueuedData(int numQueuedObjects) {
|
||||
int i;
|
||||
queuedData *allQD;
|
||||
|
||||
allQD = (*(globals.InitTimeMalloc))(numQueuedObjects * (sizeof(*allQD)));
|
||||
if (allQD == 0) return FALSE;
|
||||
|
||||
for (i = 0; i < numQueuedObjects; ++i) {
|
||||
allQD[i].nextFree = &(allQD[i+1]);
|
||||
}
|
||||
allQD[numQueuedObjects-1].nextFree = 0;
|
||||
freeQDList = &(allQD[0]);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static queuedData *AllocQD(void) {
|
||||
queuedData *result;
|
||||
|
||||
if (freeQDList == 0) {
|
||||
/* Could try to call realtime malloc() */
|
||||
OSCWarning("AllocQD: no QD objects free now; returning 0.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = freeQDList;
|
||||
freeQDList = freeQDList->nextFree;
|
||||
return result;
|
||||
}
|
||||
|
||||
static void FreeQD(queuedData *qd) {
|
||||
qd->nextFree = freeQDList;
|
||||
freeQDList = qd;
|
||||
}
|
||||
|
||||
|
||||
void OSCAcceptPacket(OSCPacketBuffer packet) {
|
||||
if ((packet->n % 4) != 0) {
|
||||
OSCProblem("OSC packet size (%d bytes) not a multiple of 4.", packet->n);
|
||||
DropPacket(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("OSCAcceptPacket(OSCPacketBuffer %p, buf %p, size %d)\n",
|
||||
packet, packet->buf, packet->n);
|
||||
#endif
|
||||
|
||||
/* If the packet came from the user, it's return address is OK. */
|
||||
packet->returnAddrOK = TRUE;
|
||||
|
||||
InsertBundleOrMessage(packet->buf, packet->n, packet, OSCTT_Immediately());
|
||||
|
||||
#ifdef PARANOID
|
||||
if (packet->refcount == 0) {
|
||||
if (freePackets != packet) {
|
||||
fatal_error("OSCAcceptPacket: packet refcount 0, but it's not the head of the free list!");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
OSCInvokeAllMessagesThatAreReady(globals.lastTimeTag);
|
||||
}
|
||||
|
||||
Boolean OSCBeProductiveWhileWaiting(void) {
|
||||
/* Here's where we could be clever if an allocation fails.
|
||||
(I.e., if we're out of QD objects, we should avoid
|
||||
parsing bundles.) The code isn't that smart yet. */
|
||||
|
||||
queuedData *qd;
|
||||
|
||||
if (globals.timePassed) {
|
||||
OSCQueueScanStart(globals.TheQueue);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
qd = (queuedData *) OSCQueueScanNext(globals.TheQueue);
|
||||
if (qd == 0) return FALSE;
|
||||
|
||||
if (qd->type == BUNDLE) {
|
||||
ParseBundle(qd);
|
||||
OSCQueueRemoveCurrentScanItem(globals.TheQueue);
|
||||
return TRUE;
|
||||
} else {
|
||||
if (qd->data.message.callbacks == NOT_DISPATCHED_YET) {
|
||||
if (ParseMessage(qd) == FALSE) {
|
||||
/* Problem with this message - flush it. */
|
||||
DropMessage(qd->data.message.messageName,
|
||||
qd->data.message.length,
|
||||
qd->myPacket);
|
||||
OSCQueueRemoveCurrentScanItem(globals.TheQueue);
|
||||
PacketRemoveRef(qd->myPacket);
|
||||
FreeQD(qd);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
/* The item we found was an already-dispatched message,
|
||||
so continue the while loop. */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Boolean OSCInvokeMessagesThatAreReady(OSCTimeTag now) {
|
||||
queuedData *x;
|
||||
OSCTimeTag thisTimeTag;
|
||||
|
||||
globals.lastTimeTag = now;
|
||||
globals.timePassed = TRUE;
|
||||
|
||||
thisTimeTag = OSCQueueEarliestTimeTag(globals.TheQueue);
|
||||
|
||||
if (OSCTT_Compare(thisTimeTag, now) > 0) {
|
||||
/* No messages ready yet. */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("OSCInvokeMessagesThatAreReady(%llx) - yes, some are ready; earliest %llx\n", now, thisTimeTag);
|
||||
#endif
|
||||
|
||||
while (OSCTT_Compare(thisTimeTag, OSCQueueEarliestTimeTag(globals.TheQueue)) == 0) {
|
||||
x = (queuedData *) OSCQueueRemoveEarliest(globals.TheQueue);
|
||||
if (!x) return FALSE;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("...Just removed earliest entry from queue: %p, TT %llx, %s\n",
|
||||
x, x->timetag, x->type == MESSAGE ? "message" : "bundle");
|
||||
if (x->type == MESSAGE) {
|
||||
printf("...message %s, len %d, args %p, arglen %d, callbacks %p\n",
|
||||
x->data.message.messageName, x->data.message.length, x->data.message.args,
|
||||
x->data.message.argLength, x->data.message.callbacks);
|
||||
} else {
|
||||
if (x->data.bundle.length == 0) {
|
||||
printf("...bundle is empty.\n");
|
||||
} else {
|
||||
printf("...bundle len %d, first count %d, first msg %s\n",
|
||||
x->data.bundle.length, *((int *) x->data.bundle.bytes), x->data.bundle.bytes+4);
|
||||
}
|
||||
}
|
||||
PrintPacket(x->myPacket);
|
||||
#endif
|
||||
|
||||
if (x->type == BUNDLE) {
|
||||
ParseBundle(x);
|
||||
} else {
|
||||
if (x->data.message.callbacks == NOT_DISPATCHED_YET) {
|
||||
if (ParseMessage(x) == FALSE) {
|
||||
/* Problem with this message - flush it. */
|
||||
PacketRemoveRef(x->myPacket);
|
||||
FreeQD(x);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
CallWholeCallbackList(x->data.message.callbacks,
|
||||
x->data.message.argLength,
|
||||
x->data.message.args,
|
||||
thisTimeTag,
|
||||
x->myPacket->returnAddrOK ? x->myPacket->returnAddr : 0);
|
||||
|
||||
PacketRemoveRef(x->myPacket);
|
||||
FreeQD(x);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef PARANOID
|
||||
if (OSCTT_Compare(thisTimeTag, OSCQueueEarliestTimeTag(globals.TheQueue)) > 0) {
|
||||
fatal_error("OSCInvokeMessagesThatAreReady: corrupt queue!\n"
|
||||
" just did %llx; earliest in queue is now %llx",
|
||||
thisTimeTag, OSCQueueEarliestTimeTag(globals.TheQueue));
|
||||
}
|
||||
#endif
|
||||
|
||||
return OSCTT_Compare(OSCQueueEarliestTimeTag(globals.TheQueue), now) <= 0;
|
||||
}
|
||||
|
||||
void OSCInvokeAllMessagesThatAreReady(OSCTimeTag now) {
|
||||
while (OSCInvokeMessagesThatAreReady(now)) {
|
||||
/* Do nothing */
|
||||
}
|
||||
}
|
||||
|
||||
static void CallWholeCallbackList(callbackList l, int argLength, void *args, OSCTimeTag when,
|
||||
NetworkReturnAddressPtr returnAddr) {
|
||||
/* In a multithreaded application, this might run in a different thread
|
||||
than the thread that deals with the priority queue. */
|
||||
|
||||
callbackList next;
|
||||
|
||||
while (l != 0) {
|
||||
(*(l->callback))(l->context, argLength, args, when, returnAddr);
|
||||
next = l->next;
|
||||
FreeCallbackListNode(l);
|
||||
l = next;
|
||||
}
|
||||
}
|
||||
|
||||
static void InsertBundleOrMessage(char *buf, int n, OSCPacketBuffer packet, OSCTimeTag enclosingTimeTag) {
|
||||
Boolean IsBundle;
|
||||
queuedData *qd;
|
||||
|
||||
/* We add the reference first thing so in case any of the upcoming
|
||||
potential failure situations come we can call PacketRemoveRef, thereby
|
||||
freeing the packet if necessary. */
|
||||
PacketAddRef(packet);
|
||||
|
||||
if ((n % 4) != 0) {
|
||||
OSCProblem("OSC message or bundle size (%d bytes) not a multiple of 4.", n);
|
||||
DropMessage(buf, n, packet);
|
||||
PacketRemoveRef(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((n >= 8) && (strncmp(buf, "#bundle", 8) == 0)) {
|
||||
IsBundle = TRUE;
|
||||
|
||||
if (n < 16) {
|
||||
OSCProblem("Bundle message too small (%d bytes) for time tag.", n);
|
||||
DropBundle(buf, n, packet);
|
||||
PacketRemoveRef(packet);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
IsBundle = FALSE;
|
||||
}
|
||||
|
||||
|
||||
qd = AllocQD();
|
||||
|
||||
if (qd == 0) {
|
||||
OSCProblem("Not enough memory for queued data!");
|
||||
DropBundle(buf, n, packet);
|
||||
PacketRemoveRef(packet);
|
||||
return;
|
||||
}
|
||||
|
||||
qd->myPacket = packet;
|
||||
qd->type = IsBundle ? BUNDLE : MESSAGE;
|
||||
|
||||
if (IsBundle) {
|
||||
/* Be careful of 8-byte alignment when copying the time tag. Here's a good
|
||||
way to get a bus error when buf happens not to be 8-byte aligned:
|
||||
qd->timetag = *((OSCTimeTag *)(buf+8));
|
||||
*/
|
||||
memcpy(&(qd->timetag), buf+8, sizeof(OSCTimeTag));
|
||||
|
||||
if (OSCTT_Compare(qd->timetag, enclosingTimeTag) < 0) {
|
||||
OSCProblem("Time tag of sub-bundle is before time tag of enclosing bundle.");
|
||||
DropBundle(buf, n, packet);
|
||||
PacketRemoveRef(packet);
|
||||
FreeQD(qd);
|
||||
return;
|
||||
}
|
||||
qd->data.bundle.bytes = buf + 16;
|
||||
qd->data.bundle.length = n - 16;
|
||||
} else {
|
||||
qd->timetag = enclosingTimeTag;
|
||||
qd->data.message.messageName = buf;
|
||||
qd->data.message.length = n;
|
||||
qd->data.message.callbacks = NOT_DISPATCHED_YET;
|
||||
}
|
||||
|
||||
OSCQueueInsert(globals.TheQueue, (OSCSchedulableObject) qd);
|
||||
}
|
||||
|
||||
|
||||
static void ParseBundle(queuedData *qd) {
|
||||
/* A queued bundle has been removed from the scheduler queue, and now it's
|
||||
time to parse all the stuff inside it and schedule the enclosed
|
||||
messages and bundles. Once all the contents of the bundle have been
|
||||
parsed and scheduled, we trash the bundle, decrementing the packet
|
||||
count and freeing the QD. */
|
||||
|
||||
int size;
|
||||
int i = 0;
|
||||
|
||||
if (qd->type != BUNDLE) {
|
||||
fatal_error("This can't happen: bundle isn't a bundle!");
|
||||
}
|
||||
|
||||
while (i < qd->data.bundle.length) {
|
||||
size = *((int *) (qd->data.bundle.bytes + i));
|
||||
if ((size % 4) != 0) {
|
||||
OSCProblem("Bad size count %d in bundle (not a multiple of 4).", size);
|
||||
DropBundle(qd->data.bundle.bytes, qd->data.bundle.length, qd->myPacket);
|
||||
goto bag;
|
||||
}
|
||||
if ((size + i + 4) > qd->data.bundle.length) {
|
||||
OSCProblem("Bad size count %d in bundle (only %d bytes left in entire bundle).",
|
||||
size, qd->data.bundle.length-i-4);
|
||||
DropBundle(qd->data.bundle.bytes, qd->data.bundle.length, qd->myPacket);
|
||||
goto bag;
|
||||
}
|
||||
|
||||
/* Recursively handle element of bundle */
|
||||
InsertBundleOrMessage(qd->data.bundle.bytes+i+4, size, qd->myPacket, qd->timetag);
|
||||
i += 4 + size;
|
||||
}
|
||||
|
||||
if (i != qd->data.bundle.length) {
|
||||
fatal_error("This can't happen: internal logic error parsing bundle");
|
||||
}
|
||||
|
||||
bag:
|
||||
/* If we got here successfully, we've added to the packet's reference count for
|
||||
each message or subbundle by calling InsertBundleOrMessage(), so we remove the one
|
||||
reference for bundle that we just parsed. If we got here by "goto bag", there's a
|
||||
problem with the bundle so we also want to lose the reference count. */
|
||||
|
||||
PacketRemoveRef(qd->myPacket);
|
||||
FreeQD(qd);
|
||||
}
|
||||
|
||||
|
||||
static Boolean ParseMessage(queuedData *qd) {
|
||||
/* Fill in all the information we'll need to execute the message as
|
||||
quickly as possible when the time comes. This means figuring out where
|
||||
the address ends and the arguments begin, and also pattern matching the
|
||||
address to find the callbacks associated with it.
|
||||
|
||||
The message may be something we have to invoke now, or it may be some
|
||||
message scheduled for the future that's just waiting on the queue; this
|
||||
procedure doesn't care. */
|
||||
|
||||
|
||||
char *args; /* char * so we can do pointer subtraction */
|
||||
int messageLen;
|
||||
char *DAAS_errormsg;
|
||||
|
||||
|
||||
if (qd->type != MESSAGE) {
|
||||
fatal_error("This can't happen: message isn't a message!");
|
||||
}
|
||||
|
||||
args = OSCDataAfterAlignedString(qd->data.message.messageName,
|
||||
qd->data.message.messageName+qd->data.message.length,
|
||||
&DAAS_errormsg);
|
||||
|
||||
if (args == 0) {
|
||||
OSCProblem("Bad message name string: %s\n", DAAS_errormsg);
|
||||
DropMessage(qd->data.message.messageName, qd->data.message.length, qd->myPacket);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
qd->data.message.args = args;
|
||||
messageLen = args - qd->data.message.messageName;
|
||||
qd->data.message.argLength = qd->data.message.length - messageLen;
|
||||
|
||||
qd->data.message.callbacks = OSCDispatchMessage(qd->data.message.messageName);
|
||||
|
||||
if (qd->data.message.callbacks == 0) {
|
||||
OSCWarning("Message pattern \"%s\" did not correspond to any address in the synth.",
|
||||
qd->data.message.messageName);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void PacketAddRef(OSCPacketBuffer packet) {
|
||||
++(packet->refcount);
|
||||
}
|
||||
|
||||
static void PacketRemoveRef(OSCPacketBuffer packet) {
|
||||
--(packet->refcount);
|
||||
if (packet->refcount == 0) {
|
||||
OSCFreePacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**************************************************
|
||||
Implementation of procedures declared in
|
||||
OSC-internal-messages.h
|
||||
**************************************************/
|
||||
|
||||
#include <libOSC/OSC-internal-messages.h>
|
||||
|
||||
Boolean OSCSendInternalMessage(char *address, int arglen, void *args) {
|
||||
return OSCSendInternalMessageWithRSVP(address, arglen, args, 0);
|
||||
}
|
||||
|
||||
Boolean OSCSendInternalMessageWithRSVP(char *address, int arglen, void *args,
|
||||
NetworkReturnAddressPtr returnAddr) {
|
||||
callbackList l = OSCDispatchMessage(address);
|
||||
|
||||
if (l == 0) return FALSE;
|
||||
|
||||
CallWholeCallbackList(l, arglen, args, OSCTT_Immediately(), returnAddr);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Boolean OSCScheduleInternalMessages(OSCTimeTag when, int numMessages,
|
||||
char **addresses, int *arglens, void **args) {
|
||||
int i, bufSizeNeeded;
|
||||
OSCPacketBuffer p;
|
||||
queuedData *qd;
|
||||
char *bufPtr;
|
||||
|
||||
|
||||
|
||||
/* Figure out how big of a buffer we'll need to hold this huge bundle.
|
||||
We don't store the "#bundle" string or the time tag, just the 4-byte
|
||||
size counts, the addresses, possible extra null padding for the
|
||||
addresses, and the arguments. */
|
||||
|
||||
bufSizeNeeded = 0;
|
||||
for (i = 0; i < numMessages; ++i) {
|
||||
bufSizeNeeded += 4 + OSCPaddedStrlen(addresses[i]) + arglens[i];
|
||||
}
|
||||
|
||||
if (bufSizeNeeded > OSCGetReceiveBufferSize()) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
/* Now try to allocate the data objects to hold these messages */
|
||||
qd = AllocQD();
|
||||
if (qd == 0) return FALSE;
|
||||
|
||||
p = OSCAllocPacketBuffer();
|
||||
if (p == 0) {
|
||||
FreeQD(qd);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Now fill in the buffer with a fake #bundle message. This is almost like
|
||||
putting a real #bundle message in the buffer and then calling OSCAcceptPacket,
|
||||
except that we save a little time and memory by not writing "#bundle" or the time tag,
|
||||
and by pre-parsing the messages a little. Thus, this code duplicates a lot
|
||||
of what's in InsertBundleOrMessage() */
|
||||
|
||||
bufPtr = p->buf;
|
||||
|
||||
for (i = 0; i < numMessages; ++i) {
|
||||
/* First the size count of this bundle element */
|
||||
*((int4 *) bufPtr) = OSCPaddedStrlen(addresses[i]) + arglens[i];
|
||||
bufPtr += sizeof(int4);
|
||||
|
||||
/* Then the address */
|
||||
bufPtr = OSCPaddedStrcpy(bufPtr, addresses[i]);
|
||||
|
||||
/* Then the arguments */
|
||||
memcpy(bufPtr, args[i], arglens[i]);
|
||||
bufPtr += arglens[i];
|
||||
}
|
||||
|
||||
#ifdef PARANOID
|
||||
if (bufPtr != p->buf+bufSizeNeeded) {
|
||||
fatal_error("OSCScheduleInternalMessages: internal error");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Fill in the rest of the packet fields */
|
||||
p->n = bufSizeNeeded;
|
||||
p->returnAddrOK = FALSE;
|
||||
PacketAddRef(p);
|
||||
|
||||
/* Now fill in the queuedData object */
|
||||
qd->timetag = when;
|
||||
qd->myPacket = p;
|
||||
qd->type = BUNDLE;
|
||||
qd->data.bundle.length = bufSizeNeeded;
|
||||
qd->data.bundle.bytes = p->buf;
|
||||
|
||||
/* Now we can put it into the scheduling queue. */
|
||||
OSCQueueInsert(globals.TheQueue, (OSCSchedulableObject) qd);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Boolean NetworkPacketWaiting(OSCPacketBuffer packet) {
|
||||
int n;
|
||||
NetworkReturnAddressPtr na = OSCPacketBufferGetClientAddr(packet);
|
||||
|
||||
// if( use_mcast_ )
|
||||
// {
|
||||
fd_set fds;
|
||||
struct timeval no_wait;
|
||||
int status;
|
||||
memset( &no_wait, 0, sizeof(no_wait));
|
||||
FD_ZERO(&fds);
|
||||
FD_SET( na->sockfd , &fds );
|
||||
status = select( na->sockfd + 1, &fds, 0, 0, &no_wait );
|
||||
if(status <= 0)
|
||||
return FALSE;
|
||||
if(FD_ISSET( na->sockfd, &fds ))
|
||||
return TRUE;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if( ioctl( na->sockfd, FIONREAD, &n, 0)==-1) return FALSE;
|
||||
// if( n==0 ) return FALSE;
|
||||
// }
|
||||
// return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Boolean NetworkReceivePacket( OSCPacketBuffer packet ) {
|
||||
int n;
|
||||
NetworkReturnAddressPtr na = OSCPacketBufferGetClientAddr(packet);
|
||||
|
||||
if( use_mcast_ )
|
||||
{
|
||||
n = recv( na->sockfd, packet->buf, 100, 0 );
|
||||
if( n<= 0)
|
||||
return FALSE;
|
||||
packet->n = n;
|
||||
}
|
||||
else
|
||||
{
|
||||
n = recvfrom( na->sockfd, packet->buf, 100, 0,
|
||||
(struct sockaddr*) &(na->cl_addr), &(na->clilen));
|
||||
if(n<=0) {
|
||||
return FALSE;
|
||||
}
|
||||
packet->n = n;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void GoMultiCast( const char *group_name )
|
||||
{
|
||||
use_mcast_ = 1;
|
||||
strncpy( mcast_groupname, group_name, strlen(group_name ));
|
||||
}
|
||||
|
||||
int IsMultiCast( char *dst )
|
||||
{
|
||||
if(use_mcast_)
|
||||
sprintf(dst, "%s", mcast_groupname );
|
||||
return use_mcast_;
|
||||
}
|
||||
|
||||
Boolean NetworkStartUDPServer(OSCPacketBuffer packet, int port_id) {
|
||||
struct sockaddr_in my_addr;
|
||||
my_addr.sin_family = AF_INET;
|
||||
my_addr.sin_port = htons(port_id);
|
||||
my_addr.sin_addr.s_addr = INADDR_ANY;
|
||||
|
||||
memset( &(my_addr.sin_zero), 0, 8);
|
||||
|
||||
if( use_mcast_ )
|
||||
{
|
||||
struct ip_mreq mcast_req;
|
||||
int on = 1;
|
||||
int err= 0;
|
||||
memset( &mcast_req, 0, sizeof(mcast_req));
|
||||
packet->returnAddr->sockfd = socket( AF_INET, SOCK_DGRAM, 0);
|
||||
#ifdef SO_REUSEADDR
|
||||
setsockopt( packet->returnAddr->sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
|
||||
#endif
|
||||
#ifdef SO_REUSEPORT
|
||||
setsockopt( packet->returnAddr->sockfd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
|
||||
#endif
|
||||
err = bind( packet->returnAddr->sockfd, (struct sockaddr*) &my_addr, sizeof( my_addr ));
|
||||
if( err < 0 )
|
||||
return FALSE;
|
||||
|
||||
mcast_req.imr_multiaddr.s_addr = inet_addr( mcast_groupname );
|
||||
mcast_req.imr_interface.s_addr = htonl( INADDR_ANY );
|
||||
setsockopt( packet->returnAddr->sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
|
||||
&mcast_req, sizeof(mcast_req) );
|
||||
}
|
||||
else
|
||||
{
|
||||
packet->returnAddr->sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if( bind( packet->returnAddr->sockfd,
|
||||
(struct sockaddr*) &my_addr,
|
||||
sizeof(struct sockaddr)) == -1) return FALSE;
|
||||
|
||||
packet->returnAddr->clilen = sizeof(struct sockaddr);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
Reference in New Issue
Block a user