526b5580da
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21070 a1c6a512-1295-4272-9138-f99709370657
1000 lines
26 KiB
C
1000 lines
26 KiB
C
/*
|
|
Written by Matt Wright and Adrian Freed, The Center for New Music and
|
|
Audio Technologies, University of California, Berkeley. Copyright (c)
|
|
1992,93,94,95,96,97,98,99,2000,01,02,03,04 The Regents of the University of
|
|
California (Regents).
|
|
|
|
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 OSC webpage is http://cnmat.cnmat.berkeley.edu/OpenSoundControl
|
|
*/
|
|
|
|
|
|
/*
|
|
|
|
dumpOSC.c
|
|
server that displays OpenSoundControl messages sent to it
|
|
for debugging client udp and UNIX protocol
|
|
|
|
by Matt Wright, 6/3/97
|
|
modified from dumpSC.c, by Matt Wright and Adrian Freed
|
|
|
|
version 0.2: Added "-silent" option a.k.a. "-quiet"
|
|
|
|
version 0.3: Incorporated patches from Nicola Bernardini to make
|
|
things Linux-friendly. Also added ntohl() in the right places
|
|
to support little-endian architectures.
|
|
|
|
|
|
|
|
compile:
|
|
cc -o dumpOSC dumpOSC.c
|
|
|
|
to-do:
|
|
|
|
More robustness in saying exactly what's wrong with ill-formed
|
|
messages. (If they don't make sense, show exactly what was
|
|
received.)
|
|
|
|
Time-based features: print time-received for each packet
|
|
|
|
Clean up to separate OSC parsing code from socket/select stuff
|
|
|
|
pd: branched from http://www.cnmat.berkeley.edu/OpenSoundControl/src/dumpOSC/dumpOSC.c
|
|
-------------
|
|
-- added pd functions
|
|
-- socket is made differently than original via pd mechanisms
|
|
-- tweaks for Win32 www.zeggz.com/raf 13-April-2002
|
|
-- the OSX changes from cnmat didnt make it here yet but this compiles
|
|
on OSX anyway.
|
|
|
|
*/
|
|
|
|
#if HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "m_pd.h"
|
|
//#include "m_imp.h"
|
|
#include "s_stuff.h"
|
|
|
|
/* declarations */
|
|
|
|
// typedef void (*t_fdpollfn)(void *ptr, int fd);
|
|
void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr);
|
|
|
|
|
|
#if defined(__sgi) || defined(__linux) || defined(WIN32) || defined(__APPLE__)
|
|
|
|
#ifdef WIN32
|
|
#include "OSC-common.h"
|
|
#include <winsock2.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <ctype.h>
|
|
#include <signal.h>
|
|
#else
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <netinet/in.h>
|
|
#include <rpc/rpc.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/times.h>
|
|
#include <sys/param.h>
|
|
#include <sys/time.h>
|
|
#include <sys/ioctl.h>
|
|
#include <ctype.h>
|
|
#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <pwd.h>
|
|
#include <signal.h>
|
|
#include <grp.h>
|
|
#include <sys/file.h>
|
|
//#include <sys/prctl.h>
|
|
|
|
#ifdef NEED_SCHEDCTL_AND_LOCK
|
|
#include <sys/schedctl.h>
|
|
#include <sys/lock.h>
|
|
#endif
|
|
#endif
|
|
|
|
|
|
char *htm_error_string;
|
|
typedef int Boolean;
|
|
typedef void *OBJ;
|
|
|
|
typedef struct ClientAddressStruct {
|
|
struct sockaddr_in cl_addr;
|
|
int clilen;
|
|
int sockfd;
|
|
} *ClientAddr;
|
|
|
|
typedef unsigned long long osc_time_t;
|
|
|
|
Boolean ShowBytes = FALSE;
|
|
Boolean Silent = FALSE;
|
|
|
|
/* Declarations */
|
|
#ifndef WIN32
|
|
static int unixinitudp(int chan);
|
|
#endif
|
|
|
|
static int initudp(int chan);
|
|
static void closeudp(int sockfd);
|
|
Boolean ClientReply(int packetsize, void *packet, int socketfd,
|
|
void *clientaddresspointer, int clientaddressbufferlength);
|
|
void sgi_CleanExit(void);
|
|
Boolean sgi_HaveToQuit(void);
|
|
int RegisterPollingDevice(int fd, void (*callbackfunction)(int , void *), void *dummy);
|
|
static void catch_sigint();
|
|
static int Synthmessage(char *m, int n, void *clientdesc, int clientdesclength, int fd) ;
|
|
char *DataAfterAlignedString(char *string, char *boundary) ;
|
|
Boolean IsNiceString(char *string, char *boundary) ;
|
|
void complain(char *s, ...);
|
|
|
|
#define MAXMESG 32768
|
|
static char mbuf[MAXMESG];
|
|
|
|
/* ----------------------------- dumpOSC ------------------------- */
|
|
|
|
#define MAXOUTAT 50
|
|
|
|
static t_class *dumpOSC_class;
|
|
|
|
typedef struct _dumpOSC
|
|
{
|
|
t_object x_obj;
|
|
t_outlet *x_msgout;
|
|
t_outlet *x_connectout;
|
|
t_atom x_outat[MAXOUTAT];
|
|
int x_outatc;
|
|
t_binbuf *x_b;
|
|
int x_connectsocket;
|
|
int x_nconnections;
|
|
int x_udp;
|
|
struct sockaddr_in x_server;
|
|
int x_clilen;
|
|
} t_dumpOSC;
|
|
|
|
void dumpOSC_ParsePacket(t_dumpOSC *x, char *buf, int n, ClientAddr returnAddr);
|
|
Boolean dumpOSC_SendReply(char *buf, int n, void *clientDesc, int clientDescLenght, int fd);
|
|
static void dumpOSC_Smessage(t_dumpOSC *x, char *address, void *v, int n, ClientAddr returnAddr);
|
|
static void dumpOSC_PrintTypeTaggedArgs(t_dumpOSC *x, void *v, int n);
|
|
static void dumpOSC_PrintHeuristicallyTypeGuessedArgs(t_dumpOSC *x, void *v, int n, int skipComma);
|
|
|
|
static void dumpOSC_read(t_dumpOSC *x, int sockfd) {
|
|
int clilen = x->x_clilen;
|
|
int n;
|
|
struct ClientAddressStruct ras;
|
|
ClientAddr ra = &ras;
|
|
|
|
//catchupflag= FALSE;
|
|
|
|
/* if (ShowBytes) { */
|
|
/* int i; */
|
|
/* printf("%d byte message:\n", n); */
|
|
/* for (i = 0; i < n; ++i) { */
|
|
/* printf(" %x (%c)\t", m[i], m[i]); */
|
|
/* if (i%4 == 3) printf("\n"); */
|
|
/* } */
|
|
/* printf("\n"); */
|
|
/* } */
|
|
|
|
// return catchupflag;
|
|
//struct sockaddr_in x->x_server;
|
|
//while( (n = recvfrom(sockfd, mbuf, MAXMESG, 0, &cl_addr, &clilen)) >0)
|
|
// while((
|
|
|
|
#ifdef WIN32
|
|
if ((n = recvfrom(sockfd, mbuf, MAXMESG, 0, (SOCKADDR*)&x->x_server, &clilen)) >0)
|
|
#else
|
|
if ((n = recvfrom(sockfd, mbuf, MAXMESG, 0, (struct sockaddr *)&x->x_server, &clilen)) >0)
|
|
#endif
|
|
{
|
|
//int r;
|
|
ras.cl_addr = *((struct sockaddr_in *) &x->x_server);
|
|
ras.clilen = x->x_clilen;
|
|
ras.sockfd = x->x_connectsocket;
|
|
|
|
#ifdef DEBUG
|
|
printf("dumpOSC_read: received UDP packet of length %d\n", n);
|
|
#endif
|
|
|
|
if(!dumpOSC_SendReply(mbuf, n, &x->x_server, clilen, sockfd))
|
|
{
|
|
dumpOSC_ParsePacket(x, mbuf, n, ra);
|
|
}
|
|
//r = Synthmessage(mbuf, n, &x->x_server, clilen, sockfd);
|
|
//post ("%d", r);
|
|
//outlet_anything(x->x_msgout, at[msg].a_w.w_symbol,
|
|
// emsg-msg-1, at + msg + 1);
|
|
// outlet_list(x->x_msgout, 0, n, mbuf);
|
|
//if( sgi_HaveToQuit()) goto out;
|
|
//if(r>0) goto back;
|
|
//clilen = maxclilen;
|
|
}
|
|
}
|
|
|
|
static void *dumpOSC_new(t_symbol *compatflag,
|
|
t_floatarg fportno) {
|
|
t_dumpOSC *x;
|
|
struct sockaddr_in server;
|
|
int clilen=sizeof(server);
|
|
int sockfd;
|
|
int portno=fportno;
|
|
int udp = 1;
|
|
|
|
//x->x_b = binbuf_new();
|
|
//x->x_outat = binbuf_getvec(x->x_b);
|
|
|
|
//{{raf}} pointer not valid yet...moving this down
|
|
//x->x_outatc = 0; {{raf}}
|
|
|
|
/* create a socket */
|
|
if ((sockfd = socket(AF_INET, (udp ? SOCK_DGRAM : SOCK_STREAM), 0)) == -1)
|
|
{
|
|
sys_sockerror("socket");
|
|
return (0);
|
|
}
|
|
|
|
server.sin_family = AF_INET;
|
|
server.sin_addr.s_addr = INADDR_ANY;
|
|
/* assign server port number */
|
|
server.sin_port = htons((u_short)portno);
|
|
/* name the socket */
|
|
if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
|
|
{
|
|
sys_sockerror("bind");
|
|
sys_closesocket(sockfd);
|
|
return (0);
|
|
}
|
|
|
|
x = (t_dumpOSC *)pd_new(dumpOSC_class);
|
|
x->x_outatc = 0; // {{raf}} now pointer is valid (less invalid)
|
|
|
|
x->x_msgout = outlet_new(&x->x_obj, &s_anything);
|
|
|
|
// if (udp) /* datagram protocol */
|
|
{
|
|
|
|
sys_addpollfn(sockfd, (t_fdpollfn)dumpOSC_read, x);
|
|
x->x_connectout = 0;
|
|
}
|
|
// else /* streaming protocol */
|
|
/* { */
|
|
/* if (listen(sockfd, 5) < 0) */
|
|
/* { */
|
|
/* sys_sockerror("listen"); */
|
|
/* sys_closesocket(sockfd); */
|
|
/* sockfd = -1; */
|
|
/* } */
|
|
/* else */
|
|
/* { */
|
|
/* sys_addpollfn(sockfd, (t_fdpollfn)dumpOSC_connectpoll, x); */
|
|
/* x->x_connectout = outlet_new(&x->x_obj, &s_float); */
|
|
/* } */
|
|
/* } */
|
|
|
|
x->x_connectsocket = sockfd;
|
|
x->x_server = server;
|
|
x->x_clilen = clilen;
|
|
x->x_nconnections = 0;
|
|
x->x_udp = udp;
|
|
|
|
return (x);
|
|
}
|
|
|
|
static void dumpOSC_free(t_dumpOSC *x)
|
|
{
|
|
/* LATER make me clean up open connections */
|
|
if (x->x_connectsocket >= 0)
|
|
{
|
|
sys_rmpollfn(x->x_connectsocket);
|
|
sys_closesocket(x->x_connectsocket);
|
|
}
|
|
}
|
|
|
|
#ifdef WIN32
|
|
OSC_API void dumpOSC_setup(void)
|
|
#else
|
|
void dumpOSC_setup(void)
|
|
#endif
|
|
{
|
|
dumpOSC_class = class_new(gensym("dumpOSC"),
|
|
(t_newmethod)dumpOSC_new, (t_method)dumpOSC_free,
|
|
sizeof(t_dumpOSC), CLASS_NOINLET, A_DEFFLOAT, A_DEFFLOAT,
|
|
A_DEFSYM, 0);
|
|
class_sethelpsymbol(dumpOSC_class, gensym("dumpOSC-help.pd"));
|
|
}
|
|
|
|
|
|
#ifndef WIN32
|
|
#define UNIXDG_PATH "/tmp/htm"
|
|
#define UNIXDG_TMP "/tmp/htm.XXXXXX"
|
|
static int unixinitudp(int chan)
|
|
{
|
|
struct sockaddr_un serv_addr;
|
|
int sockfd;
|
|
|
|
if((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0)
|
|
return sockfd;
|
|
|
|
bzero((char *)&serv_addr, sizeof(serv_addr));
|
|
serv_addr.sun_family = AF_UNIX;
|
|
strcpy(serv_addr.sun_path, UNIXDG_PATH);
|
|
sprintf(serv_addr.sun_path+strlen(serv_addr.sun_path), "%d", chan);
|
|
unlink(serv_addr.sun_path);
|
|
if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr.sun_family)+strlen(serv_addr.sun_path)) < 0)
|
|
{
|
|
perror("unable to bind\n");
|
|
return -1;
|
|
}
|
|
|
|
fcntl(sockfd, F_SETFL, FNDELAY);
|
|
return sockfd;
|
|
}
|
|
#endif // #ifndef WIN32
|
|
|
|
|
|
|
|
static int initudp(int chan)
|
|
{
|
|
|
|
#ifdef WIN32
|
|
struct sockaddr_in serv_addr;
|
|
unsigned int sockfd;
|
|
ULONG nonBlocking = (ULONG) TRUE;
|
|
|
|
if( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) != INVALID_SOCKET ) {
|
|
ZeroMemory((char *)&serv_addr, sizeof(serv_addr));
|
|
serv_addr.sin_family = AF_INET;
|
|
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
serv_addr.sin_port = htons(chan);
|
|
if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) >= 0) {
|
|
// set for non-blocking mode
|
|
if(ioctlsocket(sockfd, FIONBIO, &nonBlocking) == SOCKET_ERROR) {
|
|
perror("unable to set non-blocking\n");
|
|
return -1;
|
|
}
|
|
}
|
|
else { perror("unable to bind\n"); return -1; }
|
|
}
|
|
return (sockfd == INVALID_SOCKET ? -1 : (int)sockfd);
|
|
#else
|
|
struct sockaddr_in serv_addr;
|
|
int sockfd;
|
|
|
|
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
|
|
return sockfd;
|
|
|
|
bzero((char *)&serv_addr, sizeof(serv_addr));
|
|
serv_addr.sin_family = AF_INET;
|
|
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
serv_addr.sin_port = htons(chan);
|
|
|
|
if(bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
|
|
{
|
|
perror("unable to bind\n");
|
|
return -1;
|
|
}
|
|
|
|
fcntl(sockfd, F_SETFL, FNDELAY);
|
|
return sockfd;
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void closeudp(int sockfd) {
|
|
#ifdef WIN32
|
|
closesocket(sockfd);
|
|
#else
|
|
close(sockfd);
|
|
#endif
|
|
}
|
|
|
|
static Boolean catchupflag=FALSE;
|
|
Boolean ClientReply(int packetsize, void *packet, int socketfd,
|
|
void *clientaddresspointer, int clientaddressbufferlength)
|
|
{
|
|
if(!clientaddresspointer) return FALSE;
|
|
catchupflag= TRUE;
|
|
return packetsize==sendto(socketfd, packet, packetsize, 0, clientaddresspointer, clientaddressbufferlength);
|
|
}
|
|
|
|
static Boolean exitflag= FALSE;
|
|
void sgi_CleanExit(void) {
|
|
exitflag = TRUE;
|
|
}
|
|
|
|
Boolean sgi_HaveToQuit(void) {
|
|
return exitflag;
|
|
}
|
|
|
|
|
|
/* file descriptor poll table */
|
|
static int npolldevs =0;
|
|
typedef struct polldev
|
|
{
|
|
int fd;
|
|
void (*callbackfunction)(int , void *);
|
|
void *dummy;
|
|
} polldev;
|
|
#define TABMAX 8
|
|
static polldev polldevs[TABMAX];
|
|
|
|
|
|
/* Register a device (referred to by a file descriptor that the caller
|
|
should have already successfully obtained from a system call) to be
|
|
polled as real-time constraints allowed.
|
|
|
|
When a select(2) call indicates activity on the file descriptor, the
|
|
callback function is called with the file descripter as first
|
|
argument and the given dummy argument (presumably a pointer to the
|
|
instance variables associated with the device).
|
|
*/
|
|
int RegisterPollingDevice(int fd, void (*callbackfunction)(int , void *), void *dummy)
|
|
{
|
|
if(npolldevs<TABMAX)
|
|
{
|
|
polldevs[npolldevs].fd = fd;
|
|
polldevs[npolldevs].callbackfunction = callbackfunction;
|
|
polldevs[npolldevs].dummy = dummy;
|
|
}
|
|
else return -1;
|
|
return npolldevs++;
|
|
}
|
|
|
|
static int caught_sigint;
|
|
|
|
static void catch_sigint() {
|
|
caught_sigint = 1;
|
|
}
|
|
static int sockfd, usockfd;
|
|
|
|
|
|
void PrintClientAddr(ClientAddr CA) {
|
|
unsigned long addr = CA->cl_addr.sin_addr.s_addr;
|
|
printf("Client address %p:\n", CA);
|
|
printf(" clilen %d, sockfd %d\n", CA->clilen, CA->sockfd);
|
|
printf(" sin_family %d, sin_port %d\n", CA->cl_addr.sin_family,
|
|
CA->cl_addr.sin_port);
|
|
printf(" address: (%x) %s\n", addr, inet_ntoa(CA->cl_addr.sin_addr));
|
|
|
|
printf(" sin_zero = \"%c%c%c%c%c%c%c%c\"\n",
|
|
CA->cl_addr.sin_zero[0],
|
|
CA->cl_addr.sin_zero[1],
|
|
CA->cl_addr.sin_zero[2],
|
|
CA->cl_addr.sin_zero[3],
|
|
CA->cl_addr.sin_zero[4],
|
|
CA->cl_addr.sin_zero[5],
|
|
CA->cl_addr.sin_zero[6],
|
|
CA->cl_addr.sin_zero[7]);
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
//*******************
|
|
|
|
void WriteTime(char* dst, osc_time_t osctime)
|
|
{
|
|
*(int32_t*)dst = htonl((int32_t)(osctime >> 32));
|
|
*(int32_t*)(dst+4) = htonl((int32_t)osctime);
|
|
}
|
|
|
|
void WriteMode(char* dst)
|
|
{
|
|
*(int32_t*)dst = htonl(0);
|
|
}
|
|
|
|
osc_time_t ReadTime(const char* src)
|
|
{
|
|
osc_time_t osctime = ntohl(*(int32_t*)src);
|
|
return (osctime << 32) + ntohl(*(int32_t*)(src+4));
|
|
}
|
|
|
|
double TimeToSeconds(osc_time_t osctime)
|
|
{
|
|
return (double)osctime * 2.3283064365386962890625e-10 /* 1/2^32 */;
|
|
}
|
|
|
|
int timeRound(double x)
|
|
{
|
|
return x >= 0.0 ? x+0.5 : x-0.5;
|
|
}
|
|
/*
|
|
void WriteLogicalTime(char* dst)
|
|
{
|
|
static double startTime = -1.0;
|
|
double sTime;
|
|
|
|
// Initialisierung der Startzeit.
|
|
// Knnte effizienter (ohne 'if') auch irgendwo vorher passieren.
|
|
// Knnte wahrscheinlich auch 0.0 sein.
|
|
if (startTime < 0.0) {
|
|
startTime = clock_getlogicaltime();
|
|
}
|
|
|
|
sTime = clock_gettimesince(startTime) * 0.001;
|
|
*(int32_t*)dst = hton'K l((int32_t)sTime);
|
|
*(int32_t*)(dst+4) = htonl((int32_t)(4294967296.0 * sTime));
|
|
}
|
|
*/
|
|
|
|
void WriteLogicalTime(char* dst)
|
|
{
|
|
double sTime = clock_gettimesince(19230720) / 1000.0;
|
|
double tau = sTime - timeRound(sTime);
|
|
|
|
//fprintf(stderr, "sSec = %f tau = %f\n", sTime, tau);
|
|
|
|
*(int32_t*)dst = htonl((int32_t)(sTime));
|
|
*(int32_t*)(dst+4) = htonl((int32_t)(4294967296 * tau));
|
|
}
|
|
|
|
Boolean dumpOSC_SendReply(char *buf, int n, void *clientDesc, int clientDescLenght, int fd)
|
|
{
|
|
if((n == 24) && (strcmp(buf, "#time") == 0))
|
|
{
|
|
osc_time_t t0, t1, t2;
|
|
double dt0, dt1, dt2;
|
|
|
|
WriteMode(buf+6);
|
|
|
|
t0 = ReadTime(buf+8);
|
|
|
|
WriteLogicalTime(buf+16);
|
|
t1 = ReadTime(buf+16); // reverse
|
|
dt0 = TimeToSeconds(t0); // client time
|
|
dt1 = TimeToSeconds(t1); // server time
|
|
|
|
// fprintf(stderr, "%f\t%f\t%f\n", dt0, dt1, dt0 - dt1);
|
|
|
|
sendto(fd, buf, n, 0, (struct sockaddr *)clientDesc, clientDescLenght);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//**********************
|
|
|
|
void dumpOSC_ParsePacket(t_dumpOSC *x, char *buf, int n, ClientAddr returnAddr) {
|
|
// t_dumpOSC *x;
|
|
int size, messageLen, i;
|
|
char *messageName;
|
|
char *args;
|
|
|
|
//#ifdef PRINTADDRS
|
|
#ifdef DEBUG
|
|
//PrintClientAddr(returnAddr);
|
|
#endif
|
|
|
|
|
|
if ((n%4) != 0) {
|
|
complain("SynthControl packet size (%d) not a multiple of 4 bytes: dropping", n);
|
|
return;
|
|
}
|
|
|
|
if ((n >= 8) && (strncmp(buf, "#bundle", 8) == 0)) {
|
|
/* This is a bundle message. */
|
|
#ifdef DEBUG
|
|
printf("dumpOSC_ParsePacket: bundle msg: bundles not yet supported\n");
|
|
#endif
|
|
|
|
if (n < 16) {
|
|
complain("Bundle message too small (%d bytes) for time tag", n);
|
|
return;
|
|
}
|
|
|
|
/* Print the time tag */
|
|
#ifdef DEBUG
|
|
printf("[ %lx%08lx\n", ntohl(*((unsigned long *)(buf+8))), ntohl(*((unsigned long *)(buf+12))));
|
|
#endif
|
|
|
|
/* Note: if we wanted to actually use the time tag as a little-endian
|
|
64-bit int, we'd have to word-swap the two 32-bit halves of it */
|
|
|
|
i = 16; /* Skip "#group\0" and time tag */
|
|
|
|
while(i<n) {
|
|
size = ntohl(*((int *) (buf + i)));
|
|
if ((size % 4) != 0) {
|
|
complain("Bad size count %d in bundle (not a multiple of 4)", size);
|
|
return;
|
|
}
|
|
if ((size + i + 4) > n) {
|
|
complain("Bad size count %d in bundle (only %d bytes left in entire bundle)",
|
|
size, n-i-4);
|
|
return;
|
|
}
|
|
|
|
/* Recursively handle element of bundle */
|
|
dumpOSC_ParsePacket(x, buf+i+4, size, returnAddr);
|
|
i += 4 + size;
|
|
}
|
|
|
|
if (i != n) {
|
|
complain("This can't happen");
|
|
}
|
|
#ifdef DEBUG
|
|
printf("]\n");
|
|
#endif
|
|
|
|
}
|
|
else if ((n == 24) && (strcmp(buf, "#time") == 0))
|
|
{
|
|
complain("Time message: %s\n :).\n", htm_error_string);
|
|
return;
|
|
|
|
}
|
|
else
|
|
{
|
|
/* This is not a bundle message */
|
|
|
|
messageName = buf;
|
|
args = DataAfterAlignedString(messageName, buf+n);
|
|
if (args == 0) {
|
|
complain("Bad message name string: %s\nDropping entire message.\n",
|
|
htm_error_string);
|
|
return;
|
|
}
|
|
messageLen = args-messageName;
|
|
dumpOSC_Smessage(x, messageName, (void *)args, n-messageLen, returnAddr);
|
|
}
|
|
}
|
|
|
|
#define SMALLEST_POSITIVE_FLOAT 0.000001f
|
|
|
|
static void dumpOSC_Smessage(t_dumpOSC *x, char *address, void *v, int n, ClientAddr returnAddr) {
|
|
char *chars = v;
|
|
t_atom at;
|
|
//t_atom myargv[50];
|
|
|
|
int myargc = x->x_outatc;
|
|
t_atom* mya = x->x_outat;
|
|
int myi;
|
|
|
|
#ifdef DEBUG
|
|
printf("%s ", address);
|
|
#endif
|
|
|
|
// ztoln+cvt from envgen.c, ggee-0.18 ..
|
|
// outlet_anything's 'symbol' gets set to address
|
|
// so we dont need to append address to the atomlist
|
|
/*
|
|
SETSYMBOL(mya,gensym(address));myargc++;
|
|
x->x_outatc = myargc;
|
|
*/
|
|
|
|
if (n != 0) {
|
|
if (chars[0] == ',') {
|
|
if (chars[1] != ',') {
|
|
/* This message begins with a type-tag string */
|
|
dumpOSC_PrintTypeTaggedArgs(x, v, n);
|
|
} else {
|
|
/* Double comma means an escaped real comma, not a type string */
|
|
dumpOSC_PrintHeuristicallyTypeGuessedArgs(x, v, n, 1);
|
|
}
|
|
} else {
|
|
dumpOSC_PrintHeuristicallyTypeGuessedArgs(x, v, n, 0);
|
|
}
|
|
}
|
|
|
|
outlet_anything(x->x_msgout,gensym(address),x->x_outatc,(t_atom*)&x->x_outat);
|
|
x->x_outatc = 0;
|
|
#ifdef DEBUG
|
|
printf("\n");
|
|
#endif
|
|
fflush(stdout); /* Added for Sami 5/21/98 */
|
|
}
|
|
|
|
static void dumpOSC_PrintTypeTaggedArgs(t_dumpOSC *x, void *v, int n) {
|
|
char *typeTags, *thisType;
|
|
char *p;
|
|
|
|
int myargc = x->x_outatc;
|
|
t_atom* mya = x->x_outat;
|
|
int myi;
|
|
|
|
typeTags = v;
|
|
|
|
if (!IsNiceString(typeTags, typeTags+n)) {
|
|
/* No null-termination, so maybe it wasn't a type tag
|
|
string after all */
|
|
dumpOSC_PrintHeuristicallyTypeGuessedArgs(x, v, n, 0);
|
|
return;
|
|
}
|
|
|
|
p = DataAfterAlignedString(typeTags, typeTags+n);
|
|
|
|
|
|
for (thisType = typeTags + 1; *thisType != 0; ++thisType) {
|
|
switch (*thisType) {
|
|
case 'i': case 'r': case 'm': case 'c':
|
|
#ifdef DEBUG
|
|
//post("integer: %d", ntohl(*((int *) p)));
|
|
#endif
|
|
/* Martin Peach fix for negative floats:
|
|
* was: SETFLOAT(mya+myargc,ntohl(*((int *) p)));
|
|
* now is:
|
|
*/
|
|
SETFLOAT(mya+myargc,(signed)ntohl(*((int *) p)));
|
|
myargc++;
|
|
|
|
p += 4;
|
|
break;
|
|
|
|
case 'f': {
|
|
int i = ntohl(*((int *) p));
|
|
float *floatp = ((float *) (&i));
|
|
#ifdef DEBUG
|
|
post("float: %f", *floatp);
|
|
#endif
|
|
SETFLOAT(mya+myargc,*floatp);
|
|
myargc++;
|
|
|
|
p += 4;
|
|
}
|
|
break;
|
|
|
|
case 'h': case 't':
|
|
#ifdef DEBUG
|
|
printf("[A 64-bit int] ");
|
|
#endif
|
|
post("[A 64-bit int] not implemented");
|
|
|
|
p += 8;
|
|
break;
|
|
|
|
case 'd':
|
|
#ifdef DEBUG
|
|
printf("[A 64-bit float] ");
|
|
#endif
|
|
post("[A 64-bit float] not implemented");
|
|
|
|
p += 8;
|
|
break;
|
|
|
|
case 's': case 'S':
|
|
if (!IsNiceString(p, typeTags+n)) {
|
|
post("Type tag said this arg is a string but it's not!\n");
|
|
return;
|
|
} else {
|
|
#ifdef DEBUG
|
|
post("string: \"%s\"", p);
|
|
#endif
|
|
SETSYMBOL(mya+myargc,gensym(p));
|
|
myargc++;
|
|
//outlet_list(x->x_msgout, 0,sizeof(p), p);
|
|
//outlet_anything(x->x_msgout, 0, sizeof(p), p);
|
|
p = DataAfterAlignedString(p, typeTags+n);
|
|
// append to output vector ..
|
|
}
|
|
break;
|
|
|
|
case 'T':
|
|
#ifdef DEBUG
|
|
printf("[True] ");
|
|
#endif
|
|
SETFLOAT(mya+myargc,1.);
|
|
myargc++;
|
|
break;
|
|
case 'F':
|
|
#ifdef DEBUG
|
|
printf("[False] ");
|
|
#endif
|
|
SETFLOAT(mya+myargc,0.);
|
|
myargc++;
|
|
break;
|
|
case 'N':
|
|
#ifdef DEBUG
|
|
printf("[Nil]");
|
|
#endif
|
|
post("sendOSC: [Nil] not implemented");
|
|
break;
|
|
case 'I':
|
|
#ifdef DEBUG
|
|
printf("[Infinitum]");
|
|
#endif
|
|
post("sendOSC: [Infinitum] not implemented");
|
|
break;
|
|
|
|
default:
|
|
post("sendOSC: [Unrecognized type tag %c]", *thisType);
|
|
// return;
|
|
}
|
|
}
|
|
x->x_outatc = myargc;
|
|
}
|
|
|
|
static void dumpOSC_PrintHeuristicallyTypeGuessedArgs(t_dumpOSC *x, void *v, int n, int skipComma) {
|
|
int i, thisi;
|
|
float thisf;
|
|
int *ints;
|
|
char *chars;
|
|
char *string, *nextString;
|
|
|
|
int myargc= x->x_outatc;
|
|
t_atom* mya = x->x_outat;
|
|
int myi;
|
|
|
|
|
|
/* Go through the arguments 32 bits at a time */
|
|
ints = v;
|
|
chars = v;
|
|
|
|
for (i = 0; i<n/4; ) {
|
|
string = &chars[i*4];
|
|
thisi = ntohl(ints[i]);
|
|
/* Reinterpret the (potentially byte-reversed) thisi as a float */
|
|
thisf = *(((float *) (&thisi)));
|
|
|
|
if (thisi >= -1000 && thisi <= 1000000) {
|
|
#ifdef DEBUG
|
|
printf("%d ", thisi);
|
|
#endif
|
|
// append to output vector ..
|
|
SETFLOAT(mya+myargc,(t_float) (thisi));
|
|
myargc++;
|
|
// outlet_float(x->x_msgout, thisi);
|
|
i++;
|
|
} else if (thisf >= -1000.f && thisf <= 1000000.f &&
|
|
(thisf <=0.0f || thisf >= SMALLEST_POSITIVE_FLOAT)) {
|
|
#ifdef DEBUG
|
|
printf("%f ", thisf);
|
|
#endif
|
|
// append to output vector ..
|
|
SETFLOAT(mya+myargc,thisf);
|
|
myargc++;
|
|
//outlet_float(x->x_msgout, thisf);
|
|
i++;
|
|
} else if (IsNiceString(string, chars+n)) {
|
|
nextString = DataAfterAlignedString(string, chars+n);
|
|
#ifdef DEBUG
|
|
printf("\"%s\" ", (i == 0 && skipComma) ? string +1 : string);
|
|
#endif
|
|
// append to output vector ..
|
|
SETSYMBOL(mya+myargc,gensym(string));
|
|
myargc++;
|
|
//outlet_symbol(x->x_msgout, gensym((i == 0 && skipComma) ? string +1 : string));
|
|
i += (nextString-string) / 4;
|
|
} else {
|
|
// unhandled .. ;)
|
|
#ifdef DEBUG
|
|
printf("0x%x xx", ints[i]);
|
|
#endif
|
|
i++;
|
|
}
|
|
x->x_outatc = myargc;
|
|
}
|
|
}
|
|
|
|
|
|
#define STRING_ALIGN_PAD 4
|
|
|
|
char *DataAfterAlignedString(char *string, char *boundary)
|
|
{
|
|
/* The argument is a block of data beginning with a string. The
|
|
string has (presumably) been padded with extra null characters
|
|
so that the overall length is a multiple of STRING_ALIGN_PAD
|
|
bytes. Return a pointer to the next byte after the null
|
|
byte(s). The boundary argument points to the character after
|
|
the last valid character in the buffer---if the string hasn't
|
|
ended by there, something's wrong.
|
|
|
|
If the data looks wrong, return 0, and set htm_error_string */
|
|
|
|
int i;
|
|
|
|
if ((boundary - string) %4 != 0) {
|
|
fprintf(stderr, "Internal error: DataAfterAlignedString: bad boundary\n");
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; string[i] != '\0'; i++) {
|
|
if (string + i >= boundary) {
|
|
htm_error_string = "DataAfterAlignedString: Unreasonably long string";
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Now string[i] is the first null character */
|
|
i++;
|
|
|
|
for (; (i % STRING_ALIGN_PAD) != 0; i++) {
|
|
if (string + i >= boundary) {
|
|
htm_error_string = "DataAfterAlignedString: Unreasonably long string";
|
|
return 0;
|
|
}
|
|
if (string[i] != '\0') {
|
|
htm_error_string = "DataAfterAlignedString: Incorrectly padded string.";
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return string+i;
|
|
}
|
|
|
|
Boolean IsNiceString(char *string, char *boundary)
|
|
{
|
|
/* Arguments same as DataAfterAlignedString(). Is the given "string"
|
|
really a string? I.e., is it a sequence of isprint() characters
|
|
terminated with 1-4 null characters to align on a 4-byte boundary? */
|
|
|
|
int i;
|
|
|
|
if ((boundary - string) %4 != 0) {
|
|
fprintf(stderr, "Internal error: IsNiceString: bad boundary\n");
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; string[i] != '\0'; i++) {
|
|
if (!isprint(string[i])) return FALSE;
|
|
if (string + i >= boundary) return FALSE;
|
|
}
|
|
|
|
/* If we made it this far, it's a null-terminated sequence of printing characters
|
|
in the given boundary. Now we just make sure it's null padded... */
|
|
|
|
/* Now string[i] is the first null character */
|
|
i++;
|
|
for (; (i % STRING_ALIGN_PAD) != 0; i++) {
|
|
if (string[i] != '\0') return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
void complain(char *s, ...) {
|
|
va_list ap;
|
|
va_start(ap, s);
|
|
fprintf(stderr, "*** ERROR: ");
|
|
vfprintf(stderr, s, ap);
|
|
fprintf(stderr, "\n");
|
|
va_end(ap);
|
|
}
|
|
|
|
#endif /* __sgi or LINUX or WIN32 */
|
|
|