aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Peach <mrpeach@users.sourceforge.net>2006-08-16 20:22:22 +0000
committerMartin Peach <mrpeach@users.sourceforge.net>2006-08-16 20:22:22 +0000
commitf29e5ba61aebdda221b201e35bb47a4a9c885735 (patch)
treedbe7cb71b355e8c07c9c7d7e9f1a052128b1221d
Added the net, osc and sqosc~ directoriessvn2git-root
svn path=/trunk/externals/mrpeach/; revision=5629
-rwxr-xr-xnet/tcpclient-help.pd72
-rwxr-xr-xnet/tcpreceive-help.pd20
-rwxr-xr-xnet/tcpsend-help.pd16
-rwxr-xr-xnet/tcpserver-help.pd72
-rwxr-xr-xnet/udpreceive-help.pd17
-rwxr-xr-xnet/udpsend-help.pd16
-rwxr-xr-xnet/x_net_tcp_client.c443
-rwxr-xr-xnet/x_net_tcp_server.c600
-rwxr-xr-xnet/x_net_tcpclient.c443
-rwxr-xr-xnet/x_net_tcpreceive.c313
-rwxr-xr-xnet/x_net_tcpsend.c221
-rwxr-xr-xnet/x_net_tcpserver.c619
-rwxr-xr-xnet/x_net_udpreceive.c161
-rwxr-xr-xnet/x_net_udpsend.c214
-rwxr-xr-xosc/packOSC-help.pd30
-rwxr-xr-xosc/packOSC.c1000
-rwxr-xr-xosc/routeOSC-help.pd35
-rwxr-xr-xosc/routeOSC.c510
-rwxr-xr-xosc/unpackOSC.c604
-rwxr-xr-xsqosc~/sqosc-help.pd116
-rwxr-xr-xsqosc~/sqosc.c461
21 files changed, 5983 insertions, 0 deletions
diff --git a/net/tcpclient-help.pd b/net/tcpclient-help.pd
new file mode 100755
index 0000000..952ea84
--- /dev/null
+++ b/net/tcpclient-help.pd
@@ -0,0 +1,72 @@
+#N canvas 89 21 1092 501 12;
+#X msg 61 93 disconnect;
+#X obj 78 222 unpack 0 0 0 0;
+#X floatatom 78 245 3 0 0 0 - - -;
+#X floatatom 113 245 3 0 0 0 - - -;
+#X floatatom 149 245 3 0 0 0 - - -;
+#X floatatom 185 245 3 0 0 0 - - -;
+#X text 35 244 from;
+#X msg 45 30 connect 132.205.142.12 80;
+#X obj 45 170 tcpclient;
+#X msg 55 53 send 71 69 84 32 104 116 116 112 58 47 47 47 105 110 100
+101 120 46 104 116 109 108 13 10;
+#X text 566 53 GET http:///index.html CRLF;
+#X obj 112 196 tgl 15 0 empty empty connected 18 7 0 8 -24198 -241291
+-1 0 1;
+#X msg 160 115 dump \$1;
+#X obj 160 96 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1
+;
+#X msg 231 139 receive;
+#X msg 295 139 recv;
+#X text 214 -21 connect with an IP address and port number;
+#X msg 23 5 connect www.concordia.ca 80;
+#X text 231 114 print received messages to main window in hexdump format
+;
+#X text 131 171 tcpclient opens a tcp socket to send and receive bytes
+on;
+#X text 276 263 See also:;
+#X obj 354 263 netclient;
+#X msg 10 -20 connect 127.0.0.1 9997;
+#X obj 354 286 tcpreceive;
+#X text 574 285 can receive messages from tcpclient;
+#X text 442 263 is what tcpclient is based on;
+#X text 21 325 Received messages are output as a list of bytes;
+#X text 20 343 Attempting to print long messages output can hang pd!
+;
+#X text 343 132 get any received data (not useful unless you need it
+faster than once per 20ms);
+#X text 445 93 semicolon-terminated string for netserver or netreceive
+;
+#X msg 249 93 send 49 127 128 51 59;
+#X obj -86 317 textfile;
+#X msg -86 213 clear;
+#X msg -67 285 write tcpclientreceived.txt;
+#X obj -73 254 prepend add;
+#X obj -86 186 loadbang;
+#X text 575 342 2006/05/12 Martin Peach;
+#X obj 482 286 tcpserver;
+#X text 448 285 and;
+#X text -71 -82 tcpclient can connect to a server and send and receive
+messages as lists of bytes. Any integer value between 0 and 255 can
+be transmitted or received.;
+#X connect 0 0 8 0;
+#X connect 1 0 2 0;
+#X connect 1 1 3 0;
+#X connect 1 2 4 0;
+#X connect 1 3 5 0;
+#X connect 7 0 8 0;
+#X connect 8 0 34 0;
+#X connect 8 1 1 0;
+#X connect 8 2 11 0;
+#X connect 9 0 8 0;
+#X connect 12 0 8 0;
+#X connect 13 0 12 0;
+#X connect 14 0 8 0;
+#X connect 15 0 8 0;
+#X connect 17 0 8 0;
+#X connect 22 0 8 0;
+#X connect 30 0 8 0;
+#X connect 32 0 31 0;
+#X connect 33 0 31 0;
+#X connect 34 0 31 0;
+#X connect 35 0 32 0;
diff --git a/net/tcpreceive-help.pd b/net/tcpreceive-help.pd
new file mode 100755
index 0000000..b2d781d
--- /dev/null
+++ b/net/tcpreceive-help.pd
@@ -0,0 +1,20 @@
+#N canvas 713 11 478 294 12;
+#X obj 212 117 unpack 0 0 0 0;
+#X floatatom 212 140 3 0 0 0 - - -;
+#X floatatom 247 140 3 0 0 0 - - -;
+#X floatatom 283 140 3 0 0 0 - - -;
+#X floatatom 319 140 3 0 0 0 - - -;
+#X text 169 139 from;
+#X obj 155 185 print message;
+#X obj 155 57 tcpreceive 9997;
+#X floatatom 270 96 5 0 0 0 - - -;
+#X text 316 94 connections;
+#X text 32 16 tcpreceive receives bytes over a tcp connection.;
+#X text 265 235 Martin Peach 2006/04/21;
+#X connect 0 0 1 0;
+#X connect 0 1 2 0;
+#X connect 0 2 3 0;
+#X connect 0 3 4 0;
+#X connect 7 0 6 0;
+#X connect 7 1 0 0;
+#X connect 7 2 8 0;
diff --git a/net/tcpsend-help.pd b/net/tcpsend-help.pd
new file mode 100755
index 0000000..e61a322
--- /dev/null
+++ b/net/tcpsend-help.pd
@@ -0,0 +1,16 @@
+#N canvas 0 0 555 316 12;
+#X msg 177 158 disconnect;
+#X msg 171 96 connect 127.0.0.1 9997;
+#X obj 171 207 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 171 184 tcpsend;
+#X text 375 97 <--first;
+#X msg 67 129 send 0 1 2 3;
+#X text 10 38 tcpsend sends bytes over a tcp connection.;
+#X text 10 61 Used in conjunction with packOSC will send OSC over tcp
+;
+#X text 236 259 Martin Peach 2006/04/21;
+#X connect 0 0 3 0;
+#X connect 1 0 3 0;
+#X connect 3 0 2 0;
+#X connect 5 0 3 0;
diff --git a/net/tcpserver-help.pd b/net/tcpserver-help.pd
new file mode 100755
index 0000000..33e4b13
--- /dev/null
+++ b/net/tcpserver-help.pd
@@ -0,0 +1,72 @@
+#N canvas 13 208 1115 644 12;
+#X msg 18 -6 print;
+#X obj 136 109 tcpserver 9997;
+#X floatatom 171 237 5 0 0 0 - - -;
+#X floatatom 286 191 5 0 0 0 - - -;
+#X obj 324 138 unpack 0 0 0 0;
+#X floatatom 324 161 3 0 0 0 - - -;
+#X floatatom 359 161 3 0 0 0 - - -;
+#X floatatom 395 161 3 0 0 0 - - -;
+#X floatatom 431 161 3 0 0 0 - - -;
+#X text 281 160 from;
+#X text 224 236 connections;
+#X obj 43 161 print received;
+#X msg 118 45 client 1 1 2 3;
+#X msg 86 20 broadcast 1 2 3;
+#X text 69 -6 list of connections;
+#X text 226 19 send to all clients;
+#X text 249 45 send to client 1;
+#X text 347 254 2006/05/11 Martin Peach;
+#X text 199 191 on socket;
+#X msg 140 71 send 504 1 2 3;
+#X text 273 71 send to client on socket 504;
+#X msg 564 -365 client 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
+18 19;
+#X msg 563 -340 client 1 20 21 22 23 24 25 26 27 28 29 30 31 32 33
+34 35 36 37 38 39;
+#X msg 563 -297 client 1 40 41 42 43 44 45 46 47 48 49 50 51 52 53
+54 55 56 57 58 59;
+#X msg 563 -255 client 1 60 61 62 63 64 65 66 67 68 69 70 71 72 73
+74 75 76 77 78 79;
+#X msg 561 -213 client 1 80 81 82 83 84 85 86 87 88 89 90 91 92 93
+94 95 96 97 98 99;
+#X msg 560 -170 client 1 100 101 102 103 104 105 106 107 108 109 110
+111 112 113 114 115 116 117 118 119;
+#X msg 559 -127 client 1 120 121 122 123 124 125 126 127 138 139 140
+141 142 143 144 145 146 147 148 149;
+#X msg 559 -84 client 1 150 151 152 153 154 155 156 157 158 159 160
+161 162 163 164 165 166 167 168 169;
+#X msg 559 -39 client 1 170 171 172 173 174 175 176 177 178 179 180
+181 182 183 184 185 186 187 188 189;
+#X msg 558 5 client 1 190 191 192 193 194 195 196 197 198 199 200 201
+202 203 204 205 206 207 208 209;
+#X msg 558 47 client 1 210 211 212 213 214 215 216 217 218 219 220
+221 222 223 224 225 226 227 228 229;
+#X msg 558 89 client 1 230 231 232 233 234 235 236 237 238 239 240
+241 242 243 244 245 246 247 248 249;
+#X msg 558 134 client 1 250 251 252 253 254 255;
+#X connect 0 0 1 0;
+#X connect 1 0 11 0;
+#X connect 1 1 2 0;
+#X connect 1 2 3 0;
+#X connect 1 3 4 0;
+#X connect 4 0 5 0;
+#X connect 4 1 6 0;
+#X connect 4 2 7 0;
+#X connect 4 3 8 0;
+#X connect 12 0 1 0;
+#X connect 13 0 1 0;
+#X connect 19 0 1 0;
+#X connect 21 0 1 0;
+#X connect 22 0 1 0;
+#X connect 23 0 1 0;
+#X connect 24 0 1 0;
+#X connect 25 0 1 0;
+#X connect 26 0 1 0;
+#X connect 27 0 1 0;
+#X connect 28 0 1 0;
+#X connect 29 0 1 0;
+#X connect 30 0 1 0;
+#X connect 31 0 1 0;
+#X connect 32 0 1 0;
+#X connect 33 0 1 0;
diff --git a/net/udpreceive-help.pd b/net/udpreceive-help.pd
new file mode 100755
index 0000000..b86e734
--- /dev/null
+++ b/net/udpreceive-help.pd
@@ -0,0 +1,17 @@
+#N canvas 658 564 478 294 12;
+#X obj 222 119 unpack 0 0 0 0;
+#X floatatom 222 142 3 0 0 0 - - -;
+#X floatatom 257 142 3 0 0 0 - - -;
+#X floatatom 293 142 3 0 0 0 - - -;
+#X floatatom 329 142 3 0 0 0 - - -;
+#X text 179 141 from;
+#X obj 107 194 print message;
+#X obj 107 91 udpreceive 9997;
+#X text 32 16 udpreceive receives bytes over a udp connection.;
+#X text 265 235 Martin Peach 2006/04/21;
+#X connect 0 0 1 0;
+#X connect 0 1 2 0;
+#X connect 0 2 3 0;
+#X connect 0 3 4 0;
+#X connect 7 0 6 0;
+#X connect 7 1 0 0;
diff --git a/net/udpsend-help.pd b/net/udpsend-help.pd
new file mode 100755
index 0000000..629eace
--- /dev/null
+++ b/net/udpsend-help.pd
@@ -0,0 +1,16 @@
+#N canvas 58 443 538 316 12;
+#X msg 177 158 disconnect;
+#X msg 171 96 connect 127.0.0.1 9997;
+#X obj 171 207 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X text 375 97 <--first;
+#X msg 67 129 send 0 1 2 3;
+#X text 10 38 udpsend sends bytes over a udp connection.;
+#X text 10 61 Used in conjunction with packOSC will send OSC over udp
+;
+#X obj 171 184 udpsend;
+#X text 236 259 Martin Peach 2006/04/21;
+#X connect 0 0 7 0;
+#X connect 1 0 7 0;
+#X connect 4 0 7 0;
+#X connect 7 0 2 0;
diff --git a/net/x_net_tcp_client.c b/net/x_net_tcp_client.c
new file mode 100755
index 0000000..366d421
--- /dev/null
+++ b/net/x_net_tcp_client.c
@@ -0,0 +1,443 @@
+/* x_net_tcp_client.c Martin Peach 20060508, working version 20060512 */
+/* linux version 20060515 */
+/* x_net_tcp_client.c is based on netclient: */
+/* -------------------------- netclient ------------------------------------- */
+/* */
+/* Extended 'netsend', connects to 'netserver'. */
+/* Uses child thread to connect to server. Thus needs pd0.35-test17 or later. */
+/* Written by Olaf Matthes (olaf.matthes@gmx.de) */
+/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */
+/* */
+/* This program is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU General Public License */
+/* as published by the Free Software Foundation; either version 2 */
+/* of the License, or (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+/* */
+/* Based on PureData by Miller Puckette and others. */
+/* */
+/* ---------------------------------------------------------------------------- */
+//define DEBUG
+
+#include "m_pd.h"
+#include "s_stuff.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include <pthread.h>
+#if defined(UNIX) || defined(unix)
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <unistd.h>
+#define SOCKET_ERROR -1
+#else
+#include <winsock2.h>
+#endif
+
+#define DEFPOLLTIME 20 /* check for input every 20 ms */
+
+static t_class *tcpclient_class;
+static char objName[] = "tcpclient";
+#define MAX_UDP_RECEIVE 65536L // longer than data in maximum UDP packet
+
+typedef struct _tcpclient
+{
+ t_object x_obj;
+ t_clock *x_clock;
+ t_clock *x_poll;
+ t_outlet *x_msgout;
+ t_outlet *x_addrout;
+ t_outlet *x_outconnect;
+ int x_dump; // 1 = hexdump received bytes
+ int x_fd; // the socket
+ char *x_hostname; // address we want to connect to as text
+ int x_connectstate; // 0 = not connected, 1 = connected
+ int x_port; // port we're connected to
+ long x_addr; // address we're connected to as 32bit int
+ t_atom x_addrbytes[4]; // address we're connected to as 4 bytes
+ t_atom x_msgoutbuf[MAX_UDP_RECEIVE]; // received data as float atoms
+ unsigned char x_msginbuf[MAX_UDP_RECEIVE]; // received data as bytes
+ /* multithread stuff */
+ pthread_t x_threadid; /* id of child thread */
+ pthread_attr_t x_threadattr; /* attributes of child thread */
+} t_tcpclient;
+
+static void tcpclient_dump(t_tcpclient *x, t_float dump);
+static void tcp_client_hexdump(unsigned char *buf, long len);
+static void tcpclient_tick(t_tcpclient *x);
+static void *tcpclient_child_connect(void *w);
+static void tcpclient_connect(t_tcpclient *x, t_symbol *hostname, t_floatarg fportno);
+static void tcpclient_disconnect(t_tcpclient *x);
+static void tcpclient_send(t_tcpclient *x, t_symbol *s, int argc, t_atom *argv);
+static void tcpclient_rcv(t_tcpclient *x);
+static void tcpclient_poll(t_tcpclient *x);
+static void *tcpclient_new(t_floatarg udpflag);
+static void tcpclient_free(t_tcpclient *x);
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void tcpclient_setup(void);
+
+static void tcpclient_dump(t_tcpclient *x, t_float dump)
+{
+ x->x_dump = (dump == 0)?0:1;
+}
+
+static void tcp_client_hexdump(unsigned char *buf, long len)
+{
+#define BYTES_PER_LINE 16
+ char hexStr[(3*BYTES_PER_LINE)+1];
+ char ascStr[BYTES_PER_LINE+1];
+ long i, j, k = 0L;
+#ifdef DEBUG
+ post("tcp_client_hexdump %d", len);
+#endif
+ while (k < len)
+ {
+ for (i = j = 0; i < BYTES_PER_LINE; ++i, ++k, j+=3)
+ {
+ if (k < len)
+ {
+#ifdef MSW
+ sprintf_s(&hexStr[j], 4, "%02X ", buf[k]);
+ sprintf_s(&ascStr[i], 2, "%c", ((buf[k] >= 32) && (buf[k] <= 126))? buf[k]: '.');
+#else
+ snprintf(&hexStr[j], 4, "%02X ", buf[k]);
+ snprintf(&ascStr[i], 2, "%c", ((buf[k] >= 32) && (buf[k] <= 126))? buf[k]: '.');
+#endif
+ }
+ else
+ { // the last line
+#ifdef MSW
+ sprintf_s(&hexStr[j], 4, " ");
+ sprintf_s(&ascStr[i], 2, " ");
+#else
+ snprintf(&hexStr[j], 4, " ");
+ snprintf(&ascStr[i], 2, " ");
+#endif
+ }
+ }
+ post ("%s%s", hexStr, ascStr);
+ }
+}
+
+static void tcpclient_tick(t_tcpclient *x)
+{
+ outlet_float(x->x_outconnect, 1);
+}
+
+static void *tcpclient_child_connect(void *w)
+{
+ t_tcpclient *x = (t_tcpclient*) w;
+ struct sockaddr_in server;
+ struct hostent *hp;
+ int sockfd;
+
+ if (x->x_fd >= 0)
+ {
+ error("%s_connect: already connected", objName);
+ return (x);
+ }
+
+ /* create a socket */
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+#ifdef DEBUG
+ post("%s: send socket %d\n", objName, sockfd);
+#endif
+ if (sockfd < 0)
+ {
+ sys_sockerror("tcpclient: socket");
+ return (x);
+ }
+ /* connect socket using hostname provided in command line */
+ server.sin_family = AF_INET;
+ hp = gethostbyname(x->x_hostname);
+ if (hp == 0)
+ {
+ sys_sockerror("tcpclient: bad host?\n");
+ return (x);
+ }
+ memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+
+ /* assign client port number */
+ server.sin_port = htons((u_short)x->x_port);
+
+ post("%s: connecting socket %d to port %d", objName, sockfd, x->x_port);
+ /* try to connect */
+ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0)
+ {
+ sys_sockerror("tcpclient: connecting stream socket");
+ sys_closesocket(sockfd);
+ return (x);
+ }
+ x->x_fd = sockfd;
+ x->x_addr = ntohl(*(long *)hp->h_addr);
+ /* outlet_float is not threadsafe ! */
+ // outlet_float(x->x_obj.ob_outlet, 1);
+ x->x_connectstate = 1;
+ /* use callback instead to set outlet */
+ clock_delay(x->x_clock, 0);
+ return (x);
+}
+
+static void tcpclient_connect(t_tcpclient *x, t_symbol *hostname, t_floatarg fportno)
+{
+ /* we get hostname and port and pass them on
+ to the child thread that establishes the connection */
+ x->x_hostname = hostname->s_name;
+ x->x_port = fportno;
+ x->x_connectstate = 0;
+ /* start child thread */
+ if(pthread_create(&x->x_threadid, &x->x_threadattr, tcpclient_child_connect, x) < 0)
+ post("%s: could not create new thread", objName);
+}
+
+static void tcpclient_disconnect(t_tcpclient *x)
+{
+ if (x->x_fd >= 0)
+ {
+ sys_closesocket(x->x_fd);
+ x->x_fd = -1;
+ x->x_connectstate = 0;
+ outlet_float(x->x_outconnect, 0);
+ post("%s: disconnected", objName);
+ }
+ else post("%s: not connected", objName);
+}
+
+static void tcpclient_send(t_tcpclient *x, t_symbol *s, int argc, t_atom *argv)
+{
+ static char byte_buf[65536];// arbitrary maximum similar to max IP packet size
+ int i, d;
+ unsigned char c;
+ float f, e;
+ char *bp;
+ int length, sent;
+ int result;
+ static double lastwarntime;
+ static double pleasewarn;
+ double timebefore;
+ double timeafter;
+ int late;
+
+#ifdef DEBUG
+ post("s: %s", s->s_name);
+ post("argc: %d", argc);
+#endif
+
+ for (i = 0; i < argc; ++i)
+ {
+ if (argv[i].a_type == A_FLOAT)
+ {
+ f = argv[i].a_w.w_float;
+ d = (int)f;
+ e = f - d;
+#ifdef DEBUG
+ post("%s: argv[%d]: float:%f int:%d delta:%f", objName, i, f, d, e);
+#endif
+ if (e != 0)
+ {
+ error("%s_send: item %d (%f) is not an integer", objName, i, f);
+ return;
+ }
+ if ((d < 0) || (d > 255))
+ {
+ error("%s: item %d (%f) is not between 0 and 255", objName, i, f);
+ return;
+ }
+ c = (unsigned char)d;
+#ifdef DEBUG
+ post("%s_send: argv[%d]: %d", objName, i, c);
+#endif
+ byte_buf[i] = c;
+ }
+ else
+ {
+ error("%s_send: item %d is not a float", objName, i);
+ return;
+ }
+ }
+
+ length = i;
+ if ((x->x_fd >= 0) && (length > 0))
+ {
+ for (bp = byte_buf, sent = 0; sent < length;)
+ {
+ timebefore = sys_getrealtime();
+ result = send(x->x_fd, byte_buf, length-sent, 0);
+ timeafter = sys_getrealtime();
+ late = (timeafter - timebefore > 0.005);
+ if (late || pleasewarn)
+ {
+ if (timeafter > lastwarntime + 2)
+ {
+ post("%s_send blocked %d msec", objName,
+ (int)(1000 * ((timeafter - timebefore) + pleasewarn)));
+ pleasewarn = 0;
+ lastwarntime = timeafter;
+ }
+ else if (late) pleasewarn += timeafter - timebefore;
+ }
+ if (result <= 0)
+ {
+ sys_sockerror("tcpclient_send");
+ tcpclient_disconnect(x);
+ break;
+ }
+ else
+ {
+ sent += result;
+ bp += result;
+ }
+ }
+ }
+ else error("%s: not connected", objName);
+}
+
+static void tcpclient_rcv(t_tcpclient *x)
+{
+ int sockfd = x->x_fd;
+ int ret;
+ int i;
+ fd_set readset;
+ fd_set exceptset;
+ struct timeval ztout;
+
+ if(x->x_connectstate)
+ {
+ /* check if we can read/write from/to the socket */
+ FD_ZERO(&readset);
+ FD_ZERO(&exceptset);
+ FD_SET(x->x_fd, &readset );
+ FD_SET(x->x_fd, &exceptset );
+
+ ztout.tv_sec = 0;
+ ztout.tv_usec = 0;
+
+ ret = select(sockfd+1, &readset, NULL, &exceptset, &ztout);
+ if(ret < 0)
+ {
+ error("%s: unable to read from socket", objName);
+ sys_closesocket(sockfd);
+ return;
+ }
+ if(FD_ISSET(sockfd, &readset) || FD_ISSET(sockfd, &exceptset))
+ {
+ /* read from server */
+ ret = recv(sockfd, x->x_msginbuf, MAX_UDP_RECEIVE, 0);
+ if(ret > 0)
+ {
+#ifdef DEBUG
+ x->x_msginbuf[ret] = 0;
+ post("%s: received %d bytes ", objName, ret);
+#endif
+ if (x->x_dump)tcp_client_hexdump(x->x_msginbuf, ret);
+ for (i = 0; i < ret; ++i)
+ {
+ /* convert the bytes in the buffer to floats in a list */
+ x->x_msgoutbuf[i].a_w.w_float = (float)x->x_msginbuf[i];
+ }
+ /* find sender's ip address and output it */
+ x->x_addrbytes[0].a_w.w_float = (x->x_addr & 0xFF000000)>>24;
+ x->x_addrbytes[1].a_w.w_float = (x->x_addr & 0x0FF0000)>>16;
+ x->x_addrbytes[2].a_w.w_float = (x->x_addr & 0x0FF00)>>8;
+ x->x_addrbytes[3].a_w.w_float = (x->x_addr & 0x0FF);
+ outlet_list(x->x_addrout, &s_list, 4L, x->x_addrbytes);
+ /* send the list out the outlet */
+ if (ret > 1) outlet_list(x->x_msgout, &s_list, ret, x->x_msgoutbuf);
+ else outlet_float(x->x_msgout, x->x_msgoutbuf[0].a_w.w_float);
+ }
+ else
+ {
+ if (ret < 0)
+ {
+ sys_sockerror("tcpclient: recv");
+ tcpclient_disconnect(x);
+ }
+ else
+ {
+ post("%s: connection closed for socket %d\n", objName, sockfd);
+ tcpclient_disconnect(x);
+ }
+ }
+ }
+ }
+ else post("%s: not connected", objName);
+}
+
+static void tcpclient_poll(t_tcpclient *x)
+{
+ if(x->x_connectstate)
+ tcpclient_rcv(x); /* try to read in case we're connected */
+ clock_delay(x->x_poll, DEFPOLLTIME); /* see you later */
+}
+
+static void *tcpclient_new(t_floatarg udpflag)
+{
+ int i;
+
+ t_tcpclient *x = (t_tcpclient *)pd_new(tcpclient_class);
+ x->x_msgout = outlet_new(&x->x_obj, &s_anything); /* received data */
+ x->x_addrout = outlet_new(&x->x_obj, &s_list);
+ x->x_outconnect = outlet_new(&x->x_obj, &s_float); /* connection state */
+ x->x_clock = clock_new(x, (t_method)tcpclient_tick);
+ x->x_poll = clock_new(x, (t_method)tcpclient_poll);
+ x->x_fd = -1;
+ /* convert the bytes in the buffer to floats in a list */
+ for (i = 0; i < MAX_UDP_RECEIVE; ++i)
+ {
+ x->x_msgoutbuf[i].a_type = A_FLOAT;
+ x->x_msgoutbuf[i].a_w.w_float = 0;
+ }
+ for (i = 0; i < 4; ++i)
+ {
+ x->x_addrbytes[i].a_type = A_FLOAT;
+ x->x_addrbytes[i].a_w.w_float = 0;
+ }
+ x->x_addr = 0L;
+ /* prepare child thread */
+ if(pthread_attr_init(&x->x_threadattr) < 0)
+ post("%s: warning: could not prepare child thread", objName);
+ if(pthread_attr_setdetachstate(&x->x_threadattr, PTHREAD_CREATE_DETACHED) < 0)
+ post("%s: warning: could not prepare child thread", objName);
+ clock_delay(x->x_poll, 0); /* start polling the input */
+ return (x);
+}
+
+static void tcpclient_free(t_tcpclient *x)
+{
+ tcpclient_disconnect(x);
+ clock_free(x->x_poll);
+ clock_free(x->x_clock);
+}
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void tcpclient_setup(void)
+{
+ tcpclient_class = class_new(gensym(objName), (t_newmethod)tcpclient_new,
+ (t_method)tcpclient_free,
+ sizeof(t_tcpclient), 0, A_DEFFLOAT, 0);
+ class_addmethod(tcpclient_class, (t_method)tcpclient_connect, gensym("connect")
+ , A_SYMBOL, A_FLOAT, 0);
+ class_addmethod(tcpclient_class, (t_method)tcpclient_disconnect, gensym("disconnect"), 0);
+ class_addmethod(tcpclient_class, (t_method)tcpclient_send, gensym("send"), A_GIMME, 0);
+ class_addmethod(tcpclient_class, (t_method)tcpclient_rcv, gensym("receive"), 0);
+ class_addmethod(tcpclient_class, (t_method)tcpclient_rcv, gensym("rcv"), 0);
+ class_addmethod(tcpclient_class, (t_method)tcpclient_dump, gensym("dump"), A_FLOAT, 0);
+}
+
+/* end of x_net_tcp.c */
diff --git a/net/x_net_tcp_server.c b/net/x_net_tcp_server.c
new file mode 100755
index 0000000..4438215
--- /dev/null
+++ b/net/x_net_tcp_server.c
@@ -0,0 +1,600 @@
+/* x_net_tcpserver.c Martin Peach 20060511 working version 20060512 */
+/* x_net_tcpserver.c is based on netserver: */
+/* -------------------------- netserver ------------------------------------- */
+/* */
+/* A server for bidirectional communication from within Pd. */
+/* Allows to send back data to specific clients connected to the server. */
+/* Written by Olaf Matthes <olaf.matthes@gmx.de> */
+/* Get source at http://www.akustische-kunst.org/puredata/maxlib */
+/* */
+/* This program is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU General Public License */
+/* as published by the Free Software Foundation; either version 2 */
+/* of the License, or (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+/* */
+/* Based on PureData by Miller Puckette and others. */
+/* */
+/* ---------------------------------------------------------------------------- */
+//define DEBUG
+
+#include "m_pd.h"
+#include "m_imp.h"
+#include "s_stuff.h"
+
+//#include <sys/types.h>
+//#include <stdarg.h>
+//#include <signal.h>
+//#include <fcntl.h>
+//#include <errno.h>
+//#include <string.h>
+//#include <stdio.h>
+//#include <pthread.h>
+#if defined(UNIX) || defined(unix)
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#define SOCKET_ERROR -1
+#else
+//#include <io.h>
+//#include <fcntl.h>
+#include <winsock2.h>
+#endif
+
+#define MAX_CONNECT 32 /* maximum number of connections */
+#define INBUFSIZE 65536L /* was 4096: size of receiving data buffer */
+#define MAX_UDP_RECEIVE 65536L /* longer than data in maximum UDP packet */
+
+/* ----------------------------- tcpserver ------------------------- */
+
+static t_class *tcpserver_class;
+static t_binbuf *inbinbuf;
+static char objName[] = "tcpserver";
+
+typedef void (*t_tcpserver_socketnotifier)(void *x);
+typedef void (*t_tcpserver_socketreceivefn)(void *x, t_binbuf *b);
+
+typedef struct _tcpserver
+{
+ t_object x_obj;
+ t_outlet *x_msgout;
+ t_outlet *x_connectout;
+ t_outlet *x_sockout;
+ t_outlet *x_addrout;
+ t_symbol *x_host[MAX_CONNECT];
+ t_int x_fd[MAX_CONNECT];
+ u_long x_addr[MAX_CONNECT];
+ t_atom x_addrbytes[4];
+ t_int x_sock_fd;
+ t_int x_connectsocket;
+ t_int x_nconnections;
+ t_atom x_msgoutbuf[MAX_UDP_RECEIVE];
+ char x_msginbuf[MAX_UDP_RECEIVE];
+} t_tcpserver;
+
+typedef struct _tcpserver_socketreceiver
+{
+ unsigned char *sr_inbuf;
+ int sr_inhead;
+ int sr_intail;
+ void *sr_owner;
+ t_tcpserver_socketnotifier sr_notifier;
+ t_tcpserver_socketreceivefn sr_socketreceivefn;
+} t_tcpserver_socketreceiver;
+
+static t_tcpserver_socketreceiver *tcpserver_socketreceiver_new(void *owner, t_tcpserver_socketnotifier notifier,
+ t_tcpserver_socketreceivefn socketreceivefn);
+static int tcpserver_socketreceiver_doread(t_tcpserver_socketreceiver *x);
+static void tcpserver_socketreceiver_read(t_tcpserver_socketreceiver *x, int fd);
+static void tcpserver_socketreceiver_free(t_tcpserver_socketreceiver *x);
+static void tcpserver_send(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv);
+static void tcp_server_send_bytes(int sockfd, t_tcpserver *x, int argc, t_atom *argv);
+static void tcpserver_client_send(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv);
+static void tcpserver_broadcast(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv);
+static void tcpserver_notify(t_tcpserver *x);
+static void tcpserver_connectpoll(t_tcpserver *x);
+static void tcpserver_print(t_tcpserver *x);
+static void *tcpserver_new(t_floatarg fportno);
+static void tcpserver_free(t_tcpserver *x);
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void tcpserver_setup(void);
+
+static t_tcpserver_socketreceiver *tcpserver_socketreceiver_new(void *owner, t_tcpserver_socketnotifier notifier,
+ t_tcpserver_socketreceivefn socketreceivefn)
+{
+ t_tcpserver_socketreceiver *x = (t_tcpserver_socketreceiver *)getbytes(sizeof(*x));
+ if (!x)
+ {
+ error("%s_socketreceiver: unable to allocate %d bytes", objName, sizeof(*x));
+ }
+ else
+ {
+ x->sr_inhead = x->sr_intail = 0;
+ x->sr_owner = owner;
+ x->sr_notifier = notifier;
+ x->sr_socketreceivefn = socketreceivefn;
+ if (!(x->sr_inbuf = malloc(INBUFSIZE)))
+ {
+ freebytes(x, sizeof(*x));
+ x = NULL;
+ error("%s_socketreceiver: unable to allocate %d bytes", objName, INBUFSIZE);
+ }
+ }
+ return (x);
+}
+
+/* this is in a separately called subroutine so that the buffer isn't
+ sitting on the stack while the messages are getting passed. */
+static int tcpserver_socketreceiver_doread(t_tcpserver_socketreceiver *x)
+{
+ char messbuf[INBUFSIZE];
+ char *bp = messbuf;
+ int indx, i;
+ int inhead = x->sr_inhead;
+ int intail = x->sr_intail;
+ unsigned char c;
+ t_tcpserver *y = x->sr_owner;
+ unsigned char *inbuf = x->sr_inbuf;
+
+ if (intail == inhead) return (0);
+#ifdef DEBUG
+ post ("%s_socketreceiver_doread: intail=%d inhead=%d", objName, intail, inhead);
+#endif
+
+ for (indx = intail, i = 0; indx != inhead; indx = (indx+1)&(INBUFSIZE-1), ++i)
+ {
+ c = *bp++ = inbuf[indx];
+ y->x_msgoutbuf[i].a_w.w_float = (float)c;
+ }
+ if (i > 1) outlet_list(y->x_msgout, &s_list, i, y->x_msgoutbuf);
+ else outlet_float(y->x_msgout, y->x_msgoutbuf[0].a_w.w_float);
+
+ // intail = (indx+1)&(INBUFSIZE-1);
+ x->sr_inhead = inhead;
+ x->sr_intail = indx;//intail;
+ return (1);
+}
+
+static void tcpserver_socketreceiver_read(t_tcpserver_socketreceiver *x, int fd)
+{
+ char *semi;
+ int readto = (x->sr_inhead >= x->sr_intail ? INBUFSIZE : x->sr_intail-1);
+ int ret, i;
+ t_tcpserver *y = x->sr_owner;
+
+ y->x_sock_fd = fd;
+ /* the input buffer might be full. If so, drop the whole thing */
+ if (readto == x->sr_inhead)
+ {
+ post("%s: dropped message", objName);
+ x->sr_inhead = x->sr_intail = 0;
+ readto = INBUFSIZE;
+ }
+ else
+ {
+ ret = recv(fd, x->sr_inbuf + x->sr_inhead,
+ readto - x->sr_inhead, 0);
+ if (ret < 0)
+ {
+ sys_sockerror("tcpserver: recv");
+ if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner);
+ sys_rmpollfn(fd);
+ sys_closesocket(fd);
+ }
+ else if (ret == 0)
+ {
+ post("%s: connection closed on socket %d", objName, fd);
+ if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner);
+ sys_rmpollfn(fd);
+ sys_closesocket(fd);
+ }
+ else
+ {
+#ifdef DEBUG
+ post ("%s_socketreceiver_read: ret = %d", objName, ret);
+#endif
+ x->sr_inhead += ret;
+ if (x->sr_inhead >= INBUFSIZE) x->sr_inhead = 0;
+ /* output client's IP and socket no. */
+ for(i = 0; i < y->x_nconnections; i++) /* search for corresponding IP */
+ {
+ if(y->x_fd[i] == y->x_sock_fd)
+ {
+// outlet_symbol(x->x_connectionip, x->x_host[i]);
+ /* find sender's ip address and output it */
+ y->x_addrbytes[0].a_w.w_float = (y->x_addr[i] & 0xFF000000)>>24;
+ y->x_addrbytes[1].a_w.w_float = (y->x_addr[i] & 0x0FF0000)>>16;
+ y->x_addrbytes[2].a_w.w_float = (y->x_addr[i] & 0x0FF00)>>8;
+ y->x_addrbytes[3].a_w.w_float = (y->x_addr[i] & 0x0FF);
+ outlet_list(y->x_addrout, &s_list, 4L, y->x_addrbytes);
+ break;
+ }
+ }
+ outlet_float(y->x_sockout, y->x_sock_fd); /* the socket number */
+ tcpserver_socketreceiver_doread(x);
+ }
+ }
+}
+
+static void tcpserver_socketreceiver_free(t_tcpserver_socketreceiver *x)
+{
+ free(x->sr_inbuf);
+ freebytes(x, sizeof(*x));
+}
+
+/* ---------------- main tcpserver (send) stuff --------------------- */
+
+static void tcp_server_send_bytes(int client, t_tcpserver *x, int argc, t_atom *argv)
+{
+ static char byte_buf[MAX_UDP_RECEIVE];// arbitrary maximum similar to max IP packet size
+ int i, d;
+ unsigned char c;
+ float f, e;
+ char *bp;
+ int length, sent;
+ int result;
+ static double lastwarntime;
+ static double pleasewarn;
+ double timebefore;
+ double timeafter;
+ int late;
+ int sockfd = x->x_fd[client];
+
+ /* process & send data */
+ if(sockfd >= 0)
+ {
+ for (i = 0; i < argc; ++i)
+ {
+ if (argv[i].a_type == A_FLOAT)
+ {
+ f = argv[i].a_w.w_float;
+ d = (int)f;
+ e = f - d;
+#ifdef DEBUG
+ post("%s: argv[%d]: float:%f int:%d delta:%f", objName, i, f, d, e);
+#endif
+ if (e != 0)
+ {
+ error("%s: item %d (%f) is not an integer", objName, i, f);
+ return;
+ }
+ if ((d < 0) || (d > 255))
+ {
+ error("%s: item %d (%f) is not between 0 and 255", objName, i, f);
+ return;
+ }
+ c = (unsigned char)d; /* make sure it doesn't become negative; this only matters for post() */
+#ifdef DEBUG
+ post("%s: argv[%d]: %d", objName, i, c);
+#endif
+ byte_buf[i] = c;
+ }
+ else
+ {
+ error("%s: item %d is not a float", objName, i);
+ return;
+ }
+ }
+ length = i;
+ if (length > 0)
+ {
+ for (bp = byte_buf, sent = 0; sent < length;)
+ {
+ timebefore = sys_getrealtime();
+ result = send(sockfd, byte_buf, length-sent, 0);
+ timeafter = sys_getrealtime();
+ late = (timeafter - timebefore > 0.005);
+ if (late || pleasewarn)
+ {
+ if (timeafter > lastwarntime + 2)
+ {
+ post("%s: send blocked %d msec", objName,
+ (int)(1000 * ((timeafter - timebefore) + pleasewarn)));
+ pleasewarn = 0;
+ lastwarntime = timeafter;
+ }
+ else if (late) pleasewarn += timeafter - timebefore;
+ }
+ if (result <= 0)
+ {
+ sys_sockerror("tcpserver: send");
+ post("%s: could not send data to client %d", objName, client);
+ break;
+ }
+ else
+ {
+ sent += result;
+ bp += result;
+ }
+ }
+ }
+ }
+ else post("%s: not a valid socket number (%d)", objName, sockfd);
+}
+
+/* send message to client using socket number */
+static void tcpserver_send(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int i, sockfd;
+ int client = -1;
+
+ if(x->x_nconnections < 0)
+ {
+ post("%s: no clients connected", objName);
+ return;
+ }
+ if(argc < 2)
+ {
+ post("%s: nothing to send", objName);
+ return;
+ }
+ /* get socket number of connection (first element in list) */
+ if(argv[0].a_type == A_FLOAT)
+ {
+ sockfd = atom_getfloatarg(0, argc, argv);
+ for(i = 0; i < x->x_nconnections; i++) /* check if connection exists */
+ {
+ if(x->x_fd[i] == sockfd)
+ {
+ client = i; /* the client we're sending to */
+ break;
+ }
+ }
+ if(client == -1)
+ {
+ post("%s: no connection on socket %d", objName, sockfd);
+ return;
+ }
+ }
+ else
+ {
+ post("%s: no socket specified", objName);
+ return;
+ }
+ tcp_server_send_bytes(client, x, argc-1, &argv[1]);
+}
+
+/* send message to client using client number
+ note that the client numbers might change in case a client disconnects! */
+/* clients start at 1 but our index starts at 0 */
+static void tcpserver_client_send(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int sockfd, client;
+
+ if(x->x_nconnections < 0)
+ {
+ post("%s: no clients connected", objName);
+ return;
+ }
+ if(argc < 2)
+ {
+ post("%s: nothing to send", objName);
+ return;
+ }
+ /* get number of client (first element in list) */
+ if(argv[0].a_type == A_FLOAT)
+ client = atom_getfloatarg(0, argc, argv);
+ else
+ {
+ post("%s: no client specified", objName);
+ return;
+ }
+ if (!((client > 0) && (client < MAX_CONNECT)))
+ {
+ post("%s: client %d out of range [1..%d]", objName, client, MAX_CONNECT);
+ return;
+ }
+ --client;/* zero based index*/
+ tcp_server_send_bytes(client, x, argc-1, &argv[1]);
+}
+
+/* broadcasts a message to all connected clients */
+static void tcpserver_broadcast(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int client;
+
+ /* enumerate through the clients and send each the message */
+ for(client = 0; client < x->x_nconnections; client++) /* check if connection exists */
+ {
+ if(x->x_fd[client] >= 0)
+ { /* socket exists for this client */
+ tcp_server_send_bytes(client, x, argc, argv);
+ break;
+ }
+ }
+}
+
+/* ---------------- main tcpserver (receive) stuff --------------------- */
+
+static void tcpserver_notify(t_tcpserver *x)
+{
+ int i, k;
+
+ /* remove connection from list */
+ for(i = 0; i < x->x_nconnections; i++)
+ {
+ if(x->x_fd[i] == x->x_sock_fd)
+ {
+ x->x_nconnections--;
+ post("%s: \"%s\" removed from list of clients", objName, x->x_host[i]->s_name);
+ x->x_host[i] = NULL; /* delete entry */
+ x->x_fd[i] = -1;
+ /* rearrange list now: move entries to close the gap */
+ for(k = i; k < x->x_nconnections; k++)
+ {
+ x->x_host[k] = x->x_host[k + 1];
+ x->x_fd[k] = x->x_fd[k + 1];
+ }
+ }
+ }
+ outlet_float(x->x_connectout, x->x_nconnections);
+}
+
+static void tcpserver_connectpoll(t_tcpserver *x)
+{
+ struct sockaddr_in incomer_address;
+ int sockaddrl = (int) sizeof( struct sockaddr );
+ int fd = accept(x->x_connectsocket, (struct sockaddr*)&incomer_address, &sockaddrl);
+ int i;
+
+ if (fd < 0) post("%s: accept failed", objName);
+ else
+ {
+ t_tcpserver_socketreceiver *y = tcpserver_socketreceiver_new((void *)x,
+ (t_tcpserver_socketnotifier)tcpserver_notify, NULL);/* MP tcpserver_doit isn't used I think...*/
+ if (!y)
+ {
+#ifdef MSW
+ closesocket(fd);
+#else
+ close(fd);
+#endif
+ return;
+ }
+ sys_addpollfn(fd, (t_fdpollfn)tcpserver_socketreceiver_read, y);
+ x->x_nconnections++;
+ i = x->x_nconnections - 1;
+ x->x_host[i] = gensym(inet_ntoa(incomer_address.sin_addr));
+ x->x_fd[i] = fd;
+ post("%s: accepted connection from %s on socket %d",
+ objName, x->x_host[i]->s_name, x->x_fd[i]);
+ outlet_float(x->x_connectout, x->x_nconnections);
+ outlet_float(x->x_sockout, x->x_fd[i]); /* the socket number */
+ x->x_addr[i] = ntohl(incomer_address.sin_addr.s_addr);
+ x->x_addrbytes[0].a_w.w_float = (x->x_addr[i] & 0xFF000000)>>24;
+ x->x_addrbytes[1].a_w.w_float = (x->x_addr[i] & 0x0FF0000)>>16;
+ x->x_addrbytes[2].a_w.w_float = (x->x_addr[i] & 0x0FF00)>>8;
+ x->x_addrbytes[3].a_w.w_float = (x->x_addr[i] & 0x0FF);
+ outlet_list(x->x_addrout, &s_list, 4L, x->x_addrbytes);
+ }
+}
+
+static void tcpserver_print(t_tcpserver *x)
+{
+ int i;
+
+ if(x->x_nconnections > 0)
+ {
+ post("%s: %d open connections:", objName, x->x_nconnections);
+ for(i = 0; i < x->x_nconnections; i++)
+ {
+ post(" \"%s\" on socket %d",
+ x->x_host[i]->s_name, x->x_fd[i]);
+ }
+ }
+ else post("%s: no open connections", objName);
+}
+
+static void *tcpserver_new(t_floatarg fportno)
+{
+ t_tcpserver *x;
+ int i;
+ struct sockaddr_in server;
+ int sockfd, portno = fportno;
+
+ /* create a socket */
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+#ifdef DEBUG
+ post("%s: receive socket %d", objName, sockfd);
+#endif
+ if (sockfd < 0)
+ {
+ sys_sockerror("tcpserver: socket");
+ return (0);
+ }
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = INADDR_ANY;
+#ifdef IRIX
+ /* this seems to work only in IRIX but is unnecessary in
+ Linux. Not sure what NT needs in place of this. */
+ if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
+ post("setsockopt failed\n");
+#endif
+ /* 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("tcpserver: bind");
+ sys_closesocket(sockfd);
+ return (0);
+ }
+ x = (t_tcpserver *)pd_new(tcpserver_class);
+ x->x_msgout = outlet_new(&x->x_obj, &s_anything); /* 1st outlet for received data */
+ /* streaming protocol */
+ if (listen(sockfd, 5) < 0)
+ {
+ sys_sockerror("tcpserver: listen");
+ sys_closesocket(sockfd);
+ sockfd = -1;
+ }
+ else
+ {
+ sys_addpollfn(sockfd, (t_fdpollfn)tcpserver_connectpoll, x);
+ x->x_connectout = outlet_new(&x->x_obj, &s_float); /* 2nd outlet for number of connected clients */
+ x->x_sockout = outlet_new(&x->x_obj, &s_float); /* 3rd outlet for socket number of current client */
+ x->x_addrout = outlet_new(&x->x_obj, &s_list); /* 4th outlet for ip address of current client */
+ inbinbuf = binbuf_new();
+ }
+ x->x_connectsocket = sockfd;
+ x->x_nconnections = 0;
+ for(i = 0; i < MAX_CONNECT; i++) x->x_fd[i] = -1;
+ /* prepare to convert the bytes in the buffer to floats in a list */
+ for (i = 0; i < MAX_UDP_RECEIVE; ++i)
+ {
+ x->x_msgoutbuf[i].a_type = A_FLOAT;
+ x->x_msgoutbuf[i].a_w.w_float = 0;
+ }
+ for (i = 0; i < 4; ++i)
+ {
+ x->x_addrbytes[i].a_type = A_FLOAT;
+ x->x_addrbytes[i].a_w.w_float = 0;
+ }
+
+ return (x);
+}
+
+static void tcpserver_free(t_tcpserver *x)
+{
+ int i;
+
+ for(i = 0; i < x->x_nconnections; i++)
+ {
+ sys_rmpollfn(x->x_fd[i]);
+ sys_closesocket(x->x_fd[i]);
+ }
+ if (x->x_connectsocket >= 0)
+ {
+ sys_rmpollfn(x->x_connectsocket);
+ sys_closesocket(x->x_connectsocket);
+ }
+ binbuf_free(inbinbuf);
+}
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void tcpserver_setup(void)
+{
+ tcpserver_class = class_new(gensym(objName),(t_newmethod)tcpserver_new, (t_method)tcpserver_free,
+ sizeof(t_tcpserver), 0, A_DEFFLOAT, 0);
+ class_addmethod(tcpserver_class, (t_method)tcpserver_print, gensym("print"), 0);
+ class_addmethod(tcpserver_class, (t_method)tcpserver_send, gensym("send"), A_GIMME, 0);
+ class_addmethod(tcpserver_class, (t_method)tcpserver_client_send, gensym("client"), A_GIMME, 0);
+ class_addmethod(tcpserver_class, (t_method)tcpserver_broadcast, gensym("broadcast"), A_GIMME, 0);
+}
diff --git a/net/x_net_tcpclient.c b/net/x_net_tcpclient.c
new file mode 100755
index 0000000..366d421
--- /dev/null
+++ b/net/x_net_tcpclient.c
@@ -0,0 +1,443 @@
+/* x_net_tcp_client.c Martin Peach 20060508, working version 20060512 */
+/* linux version 20060515 */
+/* x_net_tcp_client.c is based on netclient: */
+/* -------------------------- netclient ------------------------------------- */
+/* */
+/* Extended 'netsend', connects to 'netserver'. */
+/* Uses child thread to connect to server. Thus needs pd0.35-test17 or later. */
+/* Written by Olaf Matthes (olaf.matthes@gmx.de) */
+/* Get source at http://www.akustische-kunst.org/puredata/maxlib/ */
+/* */
+/* This program is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU General Public License */
+/* as published by the Free Software Foundation; either version 2 */
+/* of the License, or (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+/* */
+/* Based on PureData by Miller Puckette and others. */
+/* */
+/* ---------------------------------------------------------------------------- */
+//define DEBUG
+
+#include "m_pd.h"
+#include "s_stuff.h"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <string.h>
+#include <pthread.h>
+#if defined(UNIX) || defined(unix)
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <unistd.h>
+#define SOCKET_ERROR -1
+#else
+#include <winsock2.h>
+#endif
+
+#define DEFPOLLTIME 20 /* check for input every 20 ms */
+
+static t_class *tcpclient_class;
+static char objName[] = "tcpclient";
+#define MAX_UDP_RECEIVE 65536L // longer than data in maximum UDP packet
+
+typedef struct _tcpclient
+{
+ t_object x_obj;
+ t_clock *x_clock;
+ t_clock *x_poll;
+ t_outlet *x_msgout;
+ t_outlet *x_addrout;
+ t_outlet *x_outconnect;
+ int x_dump; // 1 = hexdump received bytes
+ int x_fd; // the socket
+ char *x_hostname; // address we want to connect to as text
+ int x_connectstate; // 0 = not connected, 1 = connected
+ int x_port; // port we're connected to
+ long x_addr; // address we're connected to as 32bit int
+ t_atom x_addrbytes[4]; // address we're connected to as 4 bytes
+ t_atom x_msgoutbuf[MAX_UDP_RECEIVE]; // received data as float atoms
+ unsigned char x_msginbuf[MAX_UDP_RECEIVE]; // received data as bytes
+ /* multithread stuff */
+ pthread_t x_threadid; /* id of child thread */
+ pthread_attr_t x_threadattr; /* attributes of child thread */
+} t_tcpclient;
+
+static void tcpclient_dump(t_tcpclient *x, t_float dump);
+static void tcp_client_hexdump(unsigned char *buf, long len);
+static void tcpclient_tick(t_tcpclient *x);
+static void *tcpclient_child_connect(void *w);
+static void tcpclient_connect(t_tcpclient *x, t_symbol *hostname, t_floatarg fportno);
+static void tcpclient_disconnect(t_tcpclient *x);
+static void tcpclient_send(t_tcpclient *x, t_symbol *s, int argc, t_atom *argv);
+static void tcpclient_rcv(t_tcpclient *x);
+static void tcpclient_poll(t_tcpclient *x);
+static void *tcpclient_new(t_floatarg udpflag);
+static void tcpclient_free(t_tcpclient *x);
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void tcpclient_setup(void);
+
+static void tcpclient_dump(t_tcpclient *x, t_float dump)
+{
+ x->x_dump = (dump == 0)?0:1;
+}
+
+static void tcp_client_hexdump(unsigned char *buf, long len)
+{
+#define BYTES_PER_LINE 16
+ char hexStr[(3*BYTES_PER_LINE)+1];
+ char ascStr[BYTES_PER_LINE+1];
+ long i, j, k = 0L;
+#ifdef DEBUG
+ post("tcp_client_hexdump %d", len);
+#endif
+ while (k < len)
+ {
+ for (i = j = 0; i < BYTES_PER_LINE; ++i, ++k, j+=3)
+ {
+ if (k < len)
+ {
+#ifdef MSW
+ sprintf_s(&hexStr[j], 4, "%02X ", buf[k]);
+ sprintf_s(&ascStr[i], 2, "%c", ((buf[k] >= 32) && (buf[k] <= 126))? buf[k]: '.');
+#else
+ snprintf(&hexStr[j], 4, "%02X ", buf[k]);
+ snprintf(&ascStr[i], 2, "%c", ((buf[k] >= 32) && (buf[k] <= 126))? buf[k]: '.');
+#endif
+ }
+ else
+ { // the last line
+#ifdef MSW
+ sprintf_s(&hexStr[j], 4, " ");
+ sprintf_s(&ascStr[i], 2, " ");
+#else
+ snprintf(&hexStr[j], 4, " ");
+ snprintf(&ascStr[i], 2, " ");
+#endif
+ }
+ }
+ post ("%s%s", hexStr, ascStr);
+ }
+}
+
+static void tcpclient_tick(t_tcpclient *x)
+{
+ outlet_float(x->x_outconnect, 1);
+}
+
+static void *tcpclient_child_connect(void *w)
+{
+ t_tcpclient *x = (t_tcpclient*) w;
+ struct sockaddr_in server;
+ struct hostent *hp;
+ int sockfd;
+
+ if (x->x_fd >= 0)
+ {
+ error("%s_connect: already connected", objName);
+ return (x);
+ }
+
+ /* create a socket */
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+#ifdef DEBUG
+ post("%s: send socket %d\n", objName, sockfd);
+#endif
+ if (sockfd < 0)
+ {
+ sys_sockerror("tcpclient: socket");
+ return (x);
+ }
+ /* connect socket using hostname provided in command line */
+ server.sin_family = AF_INET;
+ hp = gethostbyname(x->x_hostname);
+ if (hp == 0)
+ {
+ sys_sockerror("tcpclient: bad host?\n");
+ return (x);
+ }
+ memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+
+ /* assign client port number */
+ server.sin_port = htons((u_short)x->x_port);
+
+ post("%s: connecting socket %d to port %d", objName, sockfd, x->x_port);
+ /* try to connect */
+ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0)
+ {
+ sys_sockerror("tcpclient: connecting stream socket");
+ sys_closesocket(sockfd);
+ return (x);
+ }
+ x->x_fd = sockfd;
+ x->x_addr = ntohl(*(long *)hp->h_addr);
+ /* outlet_float is not threadsafe ! */
+ // outlet_float(x->x_obj.ob_outlet, 1);
+ x->x_connectstate = 1;
+ /* use callback instead to set outlet */
+ clock_delay(x->x_clock, 0);
+ return (x);
+}
+
+static void tcpclient_connect(t_tcpclient *x, t_symbol *hostname, t_floatarg fportno)
+{
+ /* we get hostname and port and pass them on
+ to the child thread that establishes the connection */
+ x->x_hostname = hostname->s_name;
+ x->x_port = fportno;
+ x->x_connectstate = 0;
+ /* start child thread */
+ if(pthread_create(&x->x_threadid, &x->x_threadattr, tcpclient_child_connect, x) < 0)
+ post("%s: could not create new thread", objName);
+}
+
+static void tcpclient_disconnect(t_tcpclient *x)
+{
+ if (x->x_fd >= 0)
+ {
+ sys_closesocket(x->x_fd);
+ x->x_fd = -1;
+ x->x_connectstate = 0;
+ outlet_float(x->x_outconnect, 0);
+ post("%s: disconnected", objName);
+ }
+ else post("%s: not connected", objName);
+}
+
+static void tcpclient_send(t_tcpclient *x, t_symbol *s, int argc, t_atom *argv)
+{
+ static char byte_buf[65536];// arbitrary maximum similar to max IP packet size
+ int i, d;
+ unsigned char c;
+ float f, e;
+ char *bp;
+ int length, sent;
+ int result;
+ static double lastwarntime;
+ static double pleasewarn;
+ double timebefore;
+ double timeafter;
+ int late;
+
+#ifdef DEBUG
+ post("s: %s", s->s_name);
+ post("argc: %d", argc);
+#endif
+
+ for (i = 0; i < argc; ++i)
+ {
+ if (argv[i].a_type == A_FLOAT)
+ {
+ f = argv[i].a_w.w_float;
+ d = (int)f;
+ e = f - d;
+#ifdef DEBUG
+ post("%s: argv[%d]: float:%f int:%d delta:%f", objName, i, f, d, e);
+#endif
+ if (e != 0)
+ {
+ error("%s_send: item %d (%f) is not an integer", objName, i, f);
+ return;
+ }
+ if ((d < 0) || (d > 255))
+ {
+ error("%s: item %d (%f) is not between 0 and 255", objName, i, f);
+ return;
+ }
+ c = (unsigned char)d;
+#ifdef DEBUG
+ post("%s_send: argv[%d]: %d", objName, i, c);
+#endif
+ byte_buf[i] = c;
+ }
+ else
+ {
+ error("%s_send: item %d is not a float", objName, i);
+ return;
+ }
+ }
+
+ length = i;
+ if ((x->x_fd >= 0) && (length > 0))
+ {
+ for (bp = byte_buf, sent = 0; sent < length;)
+ {
+ timebefore = sys_getrealtime();
+ result = send(x->x_fd, byte_buf, length-sent, 0);
+ timeafter = sys_getrealtime();
+ late = (timeafter - timebefore > 0.005);
+ if (late || pleasewarn)
+ {
+ if (timeafter > lastwarntime + 2)
+ {
+ post("%s_send blocked %d msec", objName,
+ (int)(1000 * ((timeafter - timebefore) + pleasewarn)));
+ pleasewarn = 0;
+ lastwarntime = timeafter;
+ }
+ else if (late) pleasewarn += timeafter - timebefore;
+ }
+ if (result <= 0)
+ {
+ sys_sockerror("tcpclient_send");
+ tcpclient_disconnect(x);
+ break;
+ }
+ else
+ {
+ sent += result;
+ bp += result;
+ }
+ }
+ }
+ else error("%s: not connected", objName);
+}
+
+static void tcpclient_rcv(t_tcpclient *x)
+{
+ int sockfd = x->x_fd;
+ int ret;
+ int i;
+ fd_set readset;
+ fd_set exceptset;
+ struct timeval ztout;
+
+ if(x->x_connectstate)
+ {
+ /* check if we can read/write from/to the socket */
+ FD_ZERO(&readset);
+ FD_ZERO(&exceptset);
+ FD_SET(x->x_fd, &readset );
+ FD_SET(x->x_fd, &exceptset );
+
+ ztout.tv_sec = 0;
+ ztout.tv_usec = 0;
+
+ ret = select(sockfd+1, &readset, NULL, &exceptset, &ztout);
+ if(ret < 0)
+ {
+ error("%s: unable to read from socket", objName);
+ sys_closesocket(sockfd);
+ return;
+ }
+ if(FD_ISSET(sockfd, &readset) || FD_ISSET(sockfd, &exceptset))
+ {
+ /* read from server */
+ ret = recv(sockfd, x->x_msginbuf, MAX_UDP_RECEIVE, 0);
+ if(ret > 0)
+ {
+#ifdef DEBUG
+ x->x_msginbuf[ret] = 0;
+ post("%s: received %d bytes ", objName, ret);
+#endif
+ if (x->x_dump)tcp_client_hexdump(x->x_msginbuf, ret);
+ for (i = 0; i < ret; ++i)
+ {
+ /* convert the bytes in the buffer to floats in a list */
+ x->x_msgoutbuf[i].a_w.w_float = (float)x->x_msginbuf[i];
+ }
+ /* find sender's ip address and output it */
+ x->x_addrbytes[0].a_w.w_float = (x->x_addr & 0xFF000000)>>24;
+ x->x_addrbytes[1].a_w.w_float = (x->x_addr & 0x0FF0000)>>16;
+ x->x_addrbytes[2].a_w.w_float = (x->x_addr & 0x0FF00)>>8;
+ x->x_addrbytes[3].a_w.w_float = (x->x_addr & 0x0FF);
+ outlet_list(x->x_addrout, &s_list, 4L, x->x_addrbytes);
+ /* send the list out the outlet */
+ if (ret > 1) outlet_list(x->x_msgout, &s_list, ret, x->x_msgoutbuf);
+ else outlet_float(x->x_msgout, x->x_msgoutbuf[0].a_w.w_float);
+ }
+ else
+ {
+ if (ret < 0)
+ {
+ sys_sockerror("tcpclient: recv");
+ tcpclient_disconnect(x);
+ }
+ else
+ {
+ post("%s: connection closed for socket %d\n", objName, sockfd);
+ tcpclient_disconnect(x);
+ }
+ }
+ }
+ }
+ else post("%s: not connected", objName);
+}
+
+static void tcpclient_poll(t_tcpclient *x)
+{
+ if(x->x_connectstate)
+ tcpclient_rcv(x); /* try to read in case we're connected */
+ clock_delay(x->x_poll, DEFPOLLTIME); /* see you later */
+}
+
+static void *tcpclient_new(t_floatarg udpflag)
+{
+ int i;
+
+ t_tcpclient *x = (t_tcpclient *)pd_new(tcpclient_class);
+ x->x_msgout = outlet_new(&x->x_obj, &s_anything); /* received data */
+ x->x_addrout = outlet_new(&x->x_obj, &s_list);
+ x->x_outconnect = outlet_new(&x->x_obj, &s_float); /* connection state */
+ x->x_clock = clock_new(x, (t_method)tcpclient_tick);
+ x->x_poll = clock_new(x, (t_method)tcpclient_poll);
+ x->x_fd = -1;
+ /* convert the bytes in the buffer to floats in a list */
+ for (i = 0; i < MAX_UDP_RECEIVE; ++i)
+ {
+ x->x_msgoutbuf[i].a_type = A_FLOAT;
+ x->x_msgoutbuf[i].a_w.w_float = 0;
+ }
+ for (i = 0; i < 4; ++i)
+ {
+ x->x_addrbytes[i].a_type = A_FLOAT;
+ x->x_addrbytes[i].a_w.w_float = 0;
+ }
+ x->x_addr = 0L;
+ /* prepare child thread */
+ if(pthread_attr_init(&x->x_threadattr) < 0)
+ post("%s: warning: could not prepare child thread", objName);
+ if(pthread_attr_setdetachstate(&x->x_threadattr, PTHREAD_CREATE_DETACHED) < 0)
+ post("%s: warning: could not prepare child thread", objName);
+ clock_delay(x->x_poll, 0); /* start polling the input */
+ return (x);
+}
+
+static void tcpclient_free(t_tcpclient *x)
+{
+ tcpclient_disconnect(x);
+ clock_free(x->x_poll);
+ clock_free(x->x_clock);
+}
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void tcpclient_setup(void)
+{
+ tcpclient_class = class_new(gensym(objName), (t_newmethod)tcpclient_new,
+ (t_method)tcpclient_free,
+ sizeof(t_tcpclient), 0, A_DEFFLOAT, 0);
+ class_addmethod(tcpclient_class, (t_method)tcpclient_connect, gensym("connect")
+ , A_SYMBOL, A_FLOAT, 0);
+ class_addmethod(tcpclient_class, (t_method)tcpclient_disconnect, gensym("disconnect"), 0);
+ class_addmethod(tcpclient_class, (t_method)tcpclient_send, gensym("send"), A_GIMME, 0);
+ class_addmethod(tcpclient_class, (t_method)tcpclient_rcv, gensym("receive"), 0);
+ class_addmethod(tcpclient_class, (t_method)tcpclient_rcv, gensym("rcv"), 0);
+ class_addmethod(tcpclient_class, (t_method)tcpclient_dump, gensym("dump"), A_FLOAT, 0);
+}
+
+/* end of x_net_tcp.c */
diff --git a/net/x_net_tcpreceive.c b/net/x_net_tcpreceive.c
new file mode 100755
index 0000000..d5884e1
--- /dev/null
+++ b/net/x_net_tcpreceive.c
@@ -0,0 +1,313 @@
+/* x_net_tcpreceive.c 20060424. Martin Peach did it based on x_net.c. x_net.c header follows: */
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include "m_pd.h"
+#include "s_stuff.h"
+
+#ifdef MSW
+#include <winsock2.h>
+#include <ws2tcpip.h> /* for socklen_t */
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <stdio.h>
+#endif
+
+
+/* ----------------------------- tcpreceive ------------------------- */
+
+static t_class *tcpreceive_class;
+
+#define MAX_UDP_RECEIVE 65536L // longer than data in maximum UDP packet
+#define MAX_CONNECTIONS 128 // this is going to cause trouble down the line...:(
+
+typedef struct _tcpconnection
+{
+ long addr;
+ int socket;
+} t_tcpconnection;
+
+typedef struct _tcpreceive
+{
+ t_object x_obj;
+ t_outlet *x_msgout;
+ t_outlet *x_addrout;
+ t_outlet *x_connectout;
+ int x_connectsocket;
+ int x_nconnections;
+ t_tcpconnection x_connection[MAX_CONNECTIONS];
+ t_atom x_addrbytes[4];
+ t_atom x_msgoutbuf[MAX_UDP_RECEIVE];
+ char x_msginbuf[MAX_UDP_RECEIVE];
+} t_tcpreceive;
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void tcpreceive_setup(void);
+static void tcpreceive_free(t_tcpreceive *x);
+static void *tcpreceive_new(t_floatarg fportno);
+static void tcpreceive_read(t_tcpreceive *x, int sockfd);
+static void tcpreceive_connectpoll(t_tcpreceive *x);
+static int tcpreceive_addconnection(t_tcpreceive * x, int fd, long addr);
+static int tcpreceive_removeconnection(t_tcpreceive * x, int fd);
+static void tcpreceive_closeall(t_tcpreceive *x);
+static long tcpreceive_getconnection(t_tcpreceive * x, int fd);
+
+static void tcpreceive_read(t_tcpreceive *x, int sockfd)
+{
+ int i, read = 0;
+ long addr;
+
+// read = recvfrom(sockfd, x->x_msginbuf, MAX_UDP_RECEIVE, 0, (struct sockaddr *)&from, &fromlen);
+ read = recv(sockfd, x->x_msginbuf, MAX_UDP_RECEIVE, 0);
+#ifdef DEBUG
+ post("tcpreceive_read: read %lu x->x_connectsocket = %d",
+ read, x->x_connectsocket);
+#endif
+ if (read < 0)
+ {
+ sys_sockerror("tcpreceive_read: recv");
+ sys_rmpollfn(sockfd);
+ sys_closesocket(sockfd);
+ tcpreceive_removeconnection(x, sockfd);
+ outlet_float(x->x_connectout, --x->x_nconnections);
+ }
+ else if (read == 0)
+ {
+ post("tcpreceive: EOF on socket %d\n", sockfd);
+ sys_rmpollfn(sockfd);
+ sys_closesocket(sockfd);
+ tcpreceive_removeconnection(x, sockfd);
+ outlet_float(x->x_connectout, --x->x_nconnections);
+ }
+ else if (read > 0)
+ {
+ for (i = 0; i < read; ++i)
+ {
+ /* convert the bytes in the buffer to floats in a list */
+ x->x_msgoutbuf[i].a_w.w_float = (float)x->x_msginbuf[i];
+ }
+ /* find sender's ip address and output it */
+ addr = tcpreceive_getconnection(x, sockfd);
+ x->x_addrbytes[0].a_w.w_float = (addr & 0xFF000000)>>24;
+ x->x_addrbytes[1].a_w.w_float = (addr & 0x0FF0000)>>16;
+ x->x_addrbytes[2].a_w.w_float = (addr & 0x0FF00)>>8;
+ x->x_addrbytes[3].a_w.w_float = (addr & 0x0FF);
+ outlet_list(x->x_addrout, &s_list, 4L, x->x_addrbytes);
+ /* send the list out the outlet */
+ if (read > 1) outlet_list(x->x_msgout, &s_list, read, x->x_msgoutbuf);
+ else outlet_float(x->x_msgout, x->x_msgoutbuf[0].a_w.w_float);
+ }
+}
+
+static void *tcpreceive_new(t_floatarg fportno)
+{
+ t_tcpreceive *x;
+ struct sockaddr_in server;
+ int sockfd, portno = fportno;
+ int intarg, i;
+
+ /* create a socket */
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+#ifdef DEBUG
+ post("tcpreceive_new: socket %d port %d", sockfd, portno);
+#endif
+ if (sockfd < 0)
+ {
+ sys_sockerror("tcpreceive: socket");
+ return (0);
+ }
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = INADDR_ANY;
+
+ /* ask OS to allow another Pd to repoen this port after we close it. */
+ intarg = 1;
+ if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&intarg, sizeof(intarg)) < 0)
+ post("tcpreceive: setsockopt (SO_REUSEADDR) failed");
+ /* Stream (TCP) sockets are set NODELAY */
+ intarg = 1;
+ if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&intarg, sizeof(intarg)) < 0)
+ post("setsockopt (TCP_NODELAY) failed\n");
+
+ /* 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("tcpreceive: bind");
+ sys_closesocket(sockfd);
+ return (0);
+ }
+ x = (t_tcpreceive *)pd_new(tcpreceive_class);
+ x->x_msgout = outlet_new(&x->x_obj, &s_anything);
+ x->x_addrout = outlet_new(&x->x_obj, &s_list);
+ x->x_connectout = outlet_new(&x->x_obj, &s_float);
+ /* clear the connection list */
+ for (i = 0; i < MAX_CONNECTIONS; ++i)
+ {
+ x->x_connection[i].socket = -1;
+ x->x_connection[i].addr = 0L;
+ }
+ /* convert the bytes in the buffer to floats in a list */
+ for (i = 0; i < MAX_UDP_RECEIVE; ++i)
+ {
+ x->x_msgoutbuf[i].a_type = A_FLOAT;
+ x->x_msgoutbuf[i].a_w.w_float = 0;
+ }
+ for (i = 0; i < 4; ++i)
+ {
+ x->x_addrbytes[i].a_type = A_FLOAT;
+ x->x_addrbytes[i].a_w.w_float = 0;
+ }
+
+ /* streaming protocol */
+ if (listen(sockfd, 5) < 0)
+ {
+ sys_sockerror("tcpreceive: listen");
+ sys_closesocket(sockfd);
+ sockfd = -1;
+ }
+ else
+ {
+ sys_addpollfn(sockfd, (t_fdpollfn)tcpreceive_connectpoll, x);
+ }
+ x->x_connectsocket = sockfd;
+ x->x_nconnections = 0;
+
+//udp version... sys_addpollfn(x->x_connectsocket, (t_fdpollfn)tcpreceive_read, x);
+ return (x);
+}
+
+/* tcpreceive_connectpoll checks for incoming connection requests on the original socket */
+/* a new socket is assigned */
+static void tcpreceive_connectpoll(t_tcpreceive *x)
+{
+ struct sockaddr_in from;
+ socklen_t fromlen = sizeof(from);
+ long addr;
+ int fd;
+
+ fd = accept(x->x_connectsocket, (struct sockaddr *)&from, &fromlen);
+ if (fd < 0) post("tcpreceive: accept failed");
+ else
+ {
+ // t_socketreceiver *y = socketreceiver_new((void *)x,
+ // (t_socketnotifier)tcpreceive_notify,
+ // 0, 0);
+
+ /* get the sender's ip */
+ addr = ntohl(from.sin_addr.s_addr);
+ if (tcpreceive_addconnection(x, fd, addr))
+ {
+ sys_addpollfn(fd, (t_fdpollfn)tcpreceive_read, x);
+ outlet_float(x->x_connectout, ++x->x_nconnections);
+ x->x_addrbytes[0].a_w.w_float = (addr & 0xFF000000)>>24;
+ x->x_addrbytes[1].a_w.w_float = (addr & 0x0FF0000)>>16;
+ x->x_addrbytes[2].a_w.w_float = (addr & 0x0FF00)>>8;
+ x->x_addrbytes[3].a_w.w_float = (addr & 0x0FF);
+ outlet_list(x->x_addrout, &s_list, 4L, x->x_addrbytes);
+ }
+ else
+ {
+ error ("tcpreceive: Too many connections");
+ sys_closesocket(fd);
+ }
+ }
+}
+
+/* tcpreceive_addconnection tries to add the socket fd to the list */
+/* returns 1 on success, else 0 */
+static int tcpreceive_addconnection(t_tcpreceive *x, int fd, long addr)
+{
+ int i;
+ for (i = 0; i < MAX_CONNECTIONS; ++i)
+ {
+ if (x->x_connection[i].socket == -1)
+ {
+ x->x_connection[i].socket = fd;
+ x->x_connection[i].addr = addr;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* tcpreceive_closeall closes all open sockets and deletes them from the list */
+static void tcpreceive_closeall(t_tcpreceive *x)
+{
+ int i;
+
+ for (i = 0; ((i < MAX_CONNECTIONS) && (x->x_nconnections > 0)); ++i)
+ {
+ if (x->x_connection[i].socket != -1)
+ {
+ post ("tcpreceive: closing socket %d", x->x_connection[i].socket);
+ sys_rmpollfn(x->x_connection[i].socket);
+ sys_closesocket(x->x_connection[i].socket);
+ x->x_connection[i].socket = -1;
+ x->x_connection[i].addr = 0L;
+ outlet_float(x->x_connectout, --x->x_nconnections);
+ }
+ }
+}
+
+/* tcpreceive_removeconnection tries to delete the socket fd from the list */
+/* returns 1 on success, else 0 */
+static int tcpreceive_removeconnection(t_tcpreceive *x, int fd)
+{
+ int i;
+ for (i = 0; i < MAX_CONNECTIONS; ++i)
+ {
+ if (x->x_connection[i].socket == fd)
+ {
+ x->x_connection[i].socket = -1;
+ x->x_connection[i].addr = 0L;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* tcpreceive_getconnection tries to find the socket fd in the list */
+/* returns addr on success, else 0 */
+static long tcpreceive_getconnection(t_tcpreceive *x, int fd)
+{
+ int i;
+ for (i = 0; i < MAX_CONNECTIONS; ++i)
+ {
+ if (x->x_connection[i].socket == fd)
+ return x->x_connection[i].addr;
+ }
+ return 0;
+}
+
+static void tcpreceive_free(t_tcpreceive *x)
+{ /* is this ever called? */
+ if (x->x_connectsocket >= 0)
+ {
+ sys_rmpollfn(x->x_connectsocket);
+ sys_closesocket(x->x_connectsocket);
+ }
+ tcpreceive_closeall(x);
+}
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void tcpreceive_setup(void)
+{
+ tcpreceive_class = class_new(gensym("tcpreceive"),
+ (t_newmethod)tcpreceive_new, (t_method)tcpreceive_free,
+ sizeof(t_tcpreceive), CLASS_NOINLET, A_DEFFLOAT, 0);
+}
+
+/* end x_net_tcpreceive.c */
+
diff --git a/net/x_net_tcpsend.c b/net/x_net_tcpsend.c
new file mode 100755
index 0000000..1a9937b
--- /dev/null
+++ b/net/x_net_tcpsend.c
@@ -0,0 +1,221 @@
+/* x_net_tcpsend.c 20060424 Martin Peach did it based on x_net.c. x_net.c header follows: */
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* network */
+
+#include "m_pd.h"
+#include "s_stuff.h"
+
+#ifdef MSW
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#endif
+
+static t_class *tcpsend_class;
+
+typedef struct _tcpsend
+{
+ t_object x_obj;
+ int x_fd;
+} t_tcpsend;
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void tcpsend_setup(void);
+static void tcpsend_free(t_tcpsend *x);
+static void tcpsend_send(t_tcpsend *x, t_symbol *s, int argc, t_atom *argv);
+static void tcpsend_disconnect(t_tcpsend *x);
+static void tcpsend_connect(t_tcpsend *x, t_symbol *hostname, t_floatarg fportno);
+static void *tcpsend_new(void);
+
+static void *tcpsend_new(void)
+{
+ t_tcpsend *x = (t_tcpsend *)pd_new(tcpsend_class);
+ outlet_new(&x->x_obj, &s_float);
+ x->x_fd = -1;
+ return (x);
+}
+
+static void tcpsend_connect(t_tcpsend *x, t_symbol *hostname,
+ t_floatarg fportno)
+{
+ struct sockaddr_in server;
+ struct hostent *hp;
+ int sockfd;
+ int portno = fportno;
+ int intarg;
+
+ if (x->x_fd >= 0)
+ {
+ error("tcpsend: already connected");
+ return;
+ }
+
+ /* create a socket */
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+#ifdef DEBUG
+ fprintf(stderr, "tcpsend_connect: send socket %d\n", sockfd);
+#endif
+ if (sockfd < 0)
+ {
+ sys_sockerror("tcpsend: socket");
+ return;
+ }
+ /* connect socket using hostname provided in command line */
+ server.sin_family = AF_INET;
+ hp = gethostbyname(hostname->s_name);
+ if (hp == 0)
+ {
+ post("tcpsend: bad host?\n");
+ return;
+ }
+ /* for stream (TCP) sockets, specify "nodelay" */
+ intarg = 1;
+ if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY,
+ (char *)&intarg, sizeof(intarg)) < 0)
+ post("tcpsend: setsockopt (TCP_NODELAY) failed\n");
+
+ memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+
+ /* assign client port number */
+ server.sin_port = htons((u_short)portno);
+
+ post("tcpsend: connecting to port %d", portno);
+ /* try to connect. */
+ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0)
+ {
+ sys_sockerror("tcpsend: connecting stream socket");
+ sys_closesocket(sockfd);
+ return;
+ }
+ x->x_fd = sockfd;
+ outlet_float(x->x_obj.ob_outlet, 1);
+}
+
+static void tcpsend_disconnect(t_tcpsend *x)
+{
+ if (x->x_fd >= 0)
+ {
+ sys_closesocket(x->x_fd);
+ x->x_fd = -1;
+ outlet_float(x->x_obj.ob_outlet, 0);
+ post("tcpsend: disconnected");
+ }
+}
+
+static void tcpsend_send(t_tcpsend *x, t_symbol *s, int argc, t_atom *argv)
+{
+ static char byte_buf[65536];// arbitrary maximum similar to max IP packet size
+ int i, d;
+ char c;
+ float f, e;
+ char *bp;
+ int length, sent;
+ int result;
+ static double lastwarntime;
+ static double pleasewarn;
+ double timebefore;
+ double timeafter;
+ int late;
+
+#ifdef DEBUG
+ post("s: %s", s->s_name);
+ post("argc: %d", argc);
+#endif
+ for (i = 0; i < argc; ++i)
+ {
+ if (argv[i].a_type == A_FLOAT)
+ {
+ f = argv[i].a_w.w_float;
+ d = (int)f;
+ e = f - d;
+ if (e != 0)
+ {
+ error("tcpsend_send: item %d (%f) is not an integer", i, f);
+ return;
+ }
+ c = (unsigned char)d;
+ if (c != d)
+ {
+ error("tcpsend_send: item %d (%f) is not between 0 and 255", i, f);
+ return;
+ }
+#ifdef DEBUG
+ post("tcpsend_send: argv[%d]: %d", i, c);
+#endif
+ byte_buf[i] = c;
+ }
+ else
+ {
+ error("tcpsend_send: item %d is not a float", i);
+ return;
+ }
+ }
+
+ length = i;
+ if ((x->x_fd >= 0) && (length > 0))
+ {
+ for (bp = byte_buf, sent = 0; sent < length;)
+ {
+ timebefore = sys_getrealtime();
+ result = send(x->x_fd, byte_buf, length-sent, 0);
+ timeafter = sys_getrealtime();
+ late = (timeafter - timebefore > 0.005);
+ if (late || pleasewarn)
+ {
+ if (timeafter > lastwarntime + 2)
+ {
+ post("tcpsend blocked %d msec",
+ (int)(1000 * ((timeafter - timebefore) + pleasewarn)));
+ pleasewarn = 0;
+ lastwarntime = timeafter;
+ }
+ else if (late) pleasewarn += timeafter - timebefore;
+ }
+ if (result <= 0)
+ {
+ sys_sockerror("tcpsend");
+ tcpsend_disconnect(x);
+ break;
+ }
+ else
+ {
+ sent += result;
+ bp += result;
+ }
+ }
+ }
+ else error("tcpsend: not connected");
+}
+
+static void tcpsend_free(t_tcpsend *x)
+{
+ tcpsend_disconnect(x);
+}
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void tcpsend_setup(void)
+{
+ tcpsend_class = class_new(gensym("tcpsend"), (t_newmethod)tcpsend_new,
+ (t_method)tcpsend_free,
+ sizeof(t_tcpsend), 0, 0);
+ class_addmethod(tcpsend_class, (t_method)tcpsend_connect,
+ gensym("connect"), A_SYMBOL, A_FLOAT, 0);
+ class_addmethod(tcpsend_class, (t_method)tcpsend_disconnect,
+ gensym("disconnect"), 0);
+ class_addmethod(tcpsend_class, (t_method)tcpsend_send, gensym("send"),
+ A_GIMME, 0);
+}
+
+/* end x_net_tcpsend.c*/
diff --git a/net/x_net_tcpserver.c b/net/x_net_tcpserver.c
new file mode 100755
index 0000000..f6526c0
--- /dev/null
+++ b/net/x_net_tcpserver.c
@@ -0,0 +1,619 @@
+/* x_net_tcpserver.c Martin Peach 20060511 working version 20060512 */
+/* 20060515 works on linux too... */
+/* x_net_tcpserver.c is based on netserver: */
+/* -------------------------- netserver ------------------------------------- */
+/* */
+/* A server for bidirectional communication from within Pd. */
+/* Allows to send back data to specific clients connected to the server. */
+/* Written by Olaf Matthes <olaf.matthes@gmx.de> */
+/* Get source at http://www.akustische-kunst.org/puredata/maxlib */
+/* */
+/* This program is free software; you can redistribute it and/or */
+/* modify it under the terms of the GNU General Public License */
+/* as published by the Free Software Foundation; either version 2 */
+/* of the License, or (at your option) any later version. */
+/* */
+/* This program is distributed in the hope that it will be useful, */
+/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
+/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
+/* GNU General Public License for more details. */
+/* */
+/* You should have received a copy of the GNU General Public License */
+/* along with this program; if not, write to the Free Software */
+/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+/* */
+/* Based on PureData by Miller Puckette and others. */
+/* */
+/* ---------------------------------------------------------------------------- */
+//define DEBUG
+
+#include "m_pd.h"
+#include "m_imp.h"
+#include "s_stuff.h"
+
+//#include <sys/types.h>
+//#include <stdarg.h>
+//#include <signal.h>
+//#include <fcntl.h>
+//#include <errno.h>
+//#include <string.h>
+//#include <stdio.h>
+//#include <pthread.h>
+#if defined(UNIX) || defined(unix)
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#define SOCKET_ERROR -1
+#else
+//#include <io.h>
+//#include <fcntl.h>
+#include <winsock2.h>
+#endif
+
+#define MAX_CONNECT 32 /* maximum number of connections */
+#define INBUFSIZE 65536L /* was 4096: size of receiving data buffer */
+#define MAX_UDP_RECEIVE 65536L /* longer than data in maximum UDP packet */
+
+/* ----------------------------- tcpserver ------------------------- */
+
+static t_class *tcpserver_class;
+static t_binbuf *inbinbuf;
+static char objName[] = "tcpserver";
+
+typedef void (*t_tcpserver_socketnotifier)(void *x);
+typedef void (*t_tcpserver_socketreceivefn)(void *x, t_binbuf *b);
+
+typedef struct _tcpserver_socketreceiver
+{
+ unsigned char *sr_inbuf;
+ int sr_inhead;
+ int sr_intail;
+ void *sr_owner;
+ t_tcpserver_socketnotifier sr_notifier;
+ t_tcpserver_socketreceivefn sr_socketreceivefn;
+} t_tcpserver_socketreceiver;
+
+typedef struct _tcpserver
+{
+ t_object x_obj;
+ t_outlet *x_msgout;
+ t_outlet *x_connectout;
+ t_outlet *x_sockout;
+ t_outlet *x_addrout;
+ t_symbol *x_host[MAX_CONNECT];
+ t_int x_fd[MAX_CONNECT];
+ u_long x_addr[MAX_CONNECT];
+ t_tcpserver_socketreceiver *x_sr[MAX_CONNECT];
+ t_atom x_addrbytes[4];
+ t_int x_sock_fd;
+ t_int x_connectsocket;
+ t_int x_nconnections;
+ t_atom x_msgoutbuf[MAX_UDP_RECEIVE];
+ char x_msginbuf[MAX_UDP_RECEIVE];
+} t_tcpserver;
+
+static t_tcpserver_socketreceiver *tcpserver_socketreceiver_new(void *owner, t_tcpserver_socketnotifier notifier,
+ t_tcpserver_socketreceivefn socketreceivefn);
+static int tcpserver_socketreceiver_doread(t_tcpserver_socketreceiver *x);
+static void tcpserver_socketreceiver_read(t_tcpserver_socketreceiver *x, int fd);
+static void tcpserver_socketreceiver_free(t_tcpserver_socketreceiver *x);
+static void tcpserver_send(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv);
+static void tcp_server_send_bytes(int sockfd, t_tcpserver *x, int argc, t_atom *argv);
+static void tcpserver_client_send(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv);
+static void tcpserver_broadcast(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv);
+static void tcpserver_notify(t_tcpserver *x);
+static void tcpserver_connectpoll(t_tcpserver *x);
+static void tcpserver_print(t_tcpserver *x);
+static void *tcpserver_new(t_floatarg fportno);
+static void tcpserver_free(t_tcpserver *x);
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void tcpserver_setup(void);
+
+static t_tcpserver_socketreceiver *tcpserver_socketreceiver_new(void *owner, t_tcpserver_socketnotifier notifier,
+ t_tcpserver_socketreceivefn socketreceivefn)
+{
+ t_tcpserver_socketreceiver *x = (t_tcpserver_socketreceiver *)getbytes(sizeof(*x));
+ if (!x)
+ {
+ error("%s_socketreceiver: unable to allocate %d bytes", objName, sizeof(*x));
+ }
+ else
+ {
+ x->sr_inhead = x->sr_intail = 0;
+ x->sr_owner = owner;
+ x->sr_notifier = notifier;
+ x->sr_socketreceivefn = socketreceivefn;
+ if (!(x->sr_inbuf = malloc(INBUFSIZE)))
+ {
+ freebytes(x, sizeof(*x));
+ x = NULL;
+ error("%s_socketreceiver: unable to allocate %d bytes", objName, INBUFSIZE);
+ }
+ }
+ return (x);
+}
+
+/* this is in a separately called subroutine so that the buffer isn't
+ sitting on the stack while the messages are getting passed. */
+static int tcpserver_socketreceiver_doread(t_tcpserver_socketreceiver *x)
+{
+ char messbuf[INBUFSIZE];
+ char *bp = messbuf;
+ int indx, i;
+ int inhead = x->sr_inhead;
+ int intail = x->sr_intail;
+ unsigned char c;
+ t_tcpserver *y = x->sr_owner;
+ unsigned char *inbuf = x->sr_inbuf;
+
+ if (intail == inhead) return (0);
+#ifdef DEBUG
+ post ("%s_socketreceiver_doread: intail=%d inhead=%d", objName, intail, inhead);
+#endif
+
+ for (indx = intail, i = 0; indx != inhead; indx = (indx+1)&(INBUFSIZE-1), ++i)
+ {
+ c = *bp++ = inbuf[indx];
+ y->x_msgoutbuf[i].a_w.w_float = (float)c;
+ }
+ if (i > 1) outlet_list(y->x_msgout, &s_list, i, y->x_msgoutbuf);
+ else outlet_float(y->x_msgout, y->x_msgoutbuf[0].a_w.w_float);
+
+ // intail = (indx+1)&(INBUFSIZE-1);
+ x->sr_inhead = inhead;
+ x->sr_intail = indx;//intail;
+ return (1);
+}
+
+static void tcpserver_socketreceiver_read(t_tcpserver_socketreceiver *x, int fd)
+{
+ int readto = (x->sr_inhead >= x->sr_intail ? INBUFSIZE : x->sr_intail-1);
+ int ret, i;
+ t_tcpserver *y = x->sr_owner;
+
+ y->x_sock_fd = fd;
+ /* the input buffer might be full. If so, drop the whole thing */
+ if (readto == x->sr_inhead)
+ {
+ post("%s: dropped message", objName);
+ x->sr_inhead = x->sr_intail = 0;
+ readto = INBUFSIZE;
+ }
+ else
+ {
+ ret = recv(fd, x->sr_inbuf + x->sr_inhead,
+ readto - x->sr_inhead, 0);
+ if (ret < 0)
+ {
+ sys_sockerror("tcpserver: recv");
+ if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner);
+ sys_rmpollfn(fd);
+ sys_closesocket(fd);
+ }
+ else if (ret == 0)
+ {
+ post("%s: connection closed on socket %d", objName, fd);
+ if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner);
+ sys_rmpollfn(fd);
+ sys_closesocket(fd);
+ }
+ else
+ {
+#ifdef DEBUG
+ post ("%s_socketreceiver_read: ret = %d", objName, ret);
+#endif
+ x->sr_inhead += ret;
+ if (x->sr_inhead >= INBUFSIZE) x->sr_inhead = 0;
+ /* output client's IP and socket no. */
+ for(i = 0; i < y->x_nconnections; i++) /* search for corresponding IP */
+ {
+ if(y->x_fd[i] == y->x_sock_fd)
+ {
+// outlet_symbol(x->x_connectionip, x->x_host[i]);
+ /* find sender's ip address and output it */
+ y->x_addrbytes[0].a_w.w_float = (y->x_addr[i] & 0xFF000000)>>24;
+ y->x_addrbytes[1].a_w.w_float = (y->x_addr[i] & 0x0FF0000)>>16;
+ y->x_addrbytes[2].a_w.w_float = (y->x_addr[i] & 0x0FF00)>>8;
+ y->x_addrbytes[3].a_w.w_float = (y->x_addr[i] & 0x0FF);
+ outlet_list(y->x_addrout, &s_list, 4L, y->x_addrbytes);
+ break;
+ }
+ }
+ outlet_float(y->x_sockout, y->x_sock_fd); /* the socket number */
+ tcpserver_socketreceiver_doread(x);
+ }
+ }
+}
+
+static void tcpserver_socketreceiver_free(t_tcpserver_socketreceiver *x)
+{
+ if (x != NULL)
+ {
+ free(x->sr_inbuf);
+ freebytes(x, sizeof(*x));
+ }
+}
+
+/* ---------------- main tcpserver (send) stuff --------------------- */
+
+static void tcp_server_send_bytes(int client, t_tcpserver *x, int argc, t_atom *argv)
+{
+ static char byte_buf[MAX_UDP_RECEIVE];// arbitrary maximum similar to max IP packet size
+ int i, d;
+ unsigned char c;
+ float f, e;
+ char *bp;
+ int length, sent;
+ int result;
+ static double lastwarntime;
+ static double pleasewarn;
+ double timebefore;
+ double timeafter;
+ int late;
+ int sockfd = x->x_fd[client];
+
+ /* process & send data */
+ if(sockfd >= 0)
+ {
+ for (i = 0; i < argc; ++i)
+ {
+ if (argv[i].a_type == A_FLOAT)
+ {
+ f = argv[i].a_w.w_float;
+ d = (int)f;
+ e = f - d;
+#ifdef DEBUG
+ post("%s: argv[%d]: float:%f int:%d delta:%f", objName, i, f, d, e);
+#endif
+ if (e != 0)
+ {
+ error("%s: item %d (%f) is not an integer", objName, i, f);
+ return;
+ }
+ if ((d < 0) || (d > 255))
+ {
+ error("%s: item %d (%f) is not between 0 and 255", objName, i, f);
+ return;
+ }
+ c = (unsigned char)d; /* make sure it doesn't become negative; this only matters for post() */
+#ifdef DEBUG
+ post("%s: argv[%d]: %d", objName, i, c);
+#endif
+ byte_buf[i] = c;
+ }
+ else
+ {
+ error("%s: item %d is not a float", objName, i);
+ return;
+ }
+ }
+ length = i;
+ if (length > 0)
+ {
+ for (bp = byte_buf, sent = 0; sent < length;)
+ {
+ timebefore = sys_getrealtime();
+ result = send(sockfd, byte_buf, length-sent, 0);
+ timeafter = sys_getrealtime();
+ late = (timeafter - timebefore > 0.005);
+ if (late || pleasewarn)
+ {
+ if (timeafter > lastwarntime + 2)
+ {
+ post("%s: send blocked %d msec", objName,
+ (int)(1000 * ((timeafter - timebefore) + pleasewarn)));
+ pleasewarn = 0;
+ lastwarntime = timeafter;
+ }
+ else if (late) pleasewarn += timeafter - timebefore;
+ }
+ if (result <= 0)
+ {
+ sys_sockerror("tcpserver: send");
+ post("%s: could not send data to client %d", objName, client);
+ break;
+ }
+ else
+ {
+ sent += result;
+ bp += result;
+ }
+ }
+ }
+ }
+ else post("%s: not a valid socket number (%d)", objName, sockfd);
+}
+
+/* send message to client using socket number */
+static void tcpserver_send(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int i, sockfd;
+ int client = -1;
+
+ if(x->x_nconnections < 0)
+ {
+ post("%s: no clients connected", objName);
+ return;
+ }
+ if(argc < 2)
+ {
+ post("%s: nothing to send", objName);
+ return;
+ }
+ /* get socket number of connection (first element in list) */
+ if(argv[0].a_type == A_FLOAT)
+ {
+ sockfd = atom_getfloatarg(0, argc, argv);
+ for(i = 0; i < x->x_nconnections; i++) /* check if connection exists */
+ {
+ if(x->x_fd[i] == sockfd)
+ {
+ client = i; /* the client we're sending to */
+ break;
+ }
+ }
+ if(client == -1)
+ {
+ post("%s: no connection on socket %d", objName, sockfd);
+ return;
+ }
+ }
+ else
+ {
+ post("%s: no socket specified", objName);
+ return;
+ }
+ tcp_server_send_bytes(client, x, argc-1, &argv[1]);
+}
+
+/* send message to client using client number
+ note that the client numbers might change in case a client disconnects! */
+/* clients start at 1 but our index starts at 0 */
+static void tcpserver_client_send(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int client;
+
+ if(x->x_nconnections < 0)
+ {
+ post("%s: no clients connected", objName);
+ return;
+ }
+ if(argc < 2)
+ {
+ post("%s: nothing to send", objName);
+ return;
+ }
+ /* get number of client (first element in list) */
+ if(argv[0].a_type == A_FLOAT)
+ client = atom_getfloatarg(0, argc, argv);
+ else
+ {
+ post("%s: no client specified", objName);
+ return;
+ }
+ if (!((client > 0) && (client < MAX_CONNECT)))
+ {
+ post("%s: client %d out of range [1..%d]", objName, client, MAX_CONNECT);
+ return;
+ }
+ --client;/* zero based index*/
+ tcp_server_send_bytes(client, x, argc-1, &argv[1]);
+}
+
+/* broadcasts a message to all connected clients */
+static void tcpserver_broadcast(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int client;
+
+ /* enumerate through the clients and send each the message */
+ for(client = 0; client < x->x_nconnections; client++) /* check if connection exists */
+ {
+ if(x->x_fd[client] >= 0)
+ { /* socket exists for this client */
+ tcp_server_send_bytes(client, x, argc, argv);
+ break;
+ }
+ }
+}
+
+/* ---------------- main tcpserver (receive) stuff --------------------- */
+
+static void tcpserver_notify(t_tcpserver *x)
+{
+ int i, k;
+
+ /* remove connection from list */
+ for(i = 0; i < x->x_nconnections; i++)
+ {
+ if(x->x_fd[i] == x->x_sock_fd)
+ {
+ x->x_nconnections--;
+ post("%s: \"%s\" removed from list of clients", objName, x->x_host[i]->s_name);
+ x->x_host[i] = NULL; /* delete entry */
+ x->x_fd[i] = -1;
+ tcpserver_socketreceiver_free(x->x_sr[i]);
+ x->x_sr[i] = NULL;
+
+ /* rearrange list now: move entries to close the gap */
+ for(k = i; k < x->x_nconnections; k++)
+ {
+ x->x_host[k] = x->x_host[k + 1];
+ x->x_fd[k] = x->x_fd[k + 1];
+ }
+ }
+ }
+ outlet_float(x->x_connectout, x->x_nconnections);
+}
+
+static void tcpserver_connectpoll(t_tcpserver *x)
+{
+ struct sockaddr_in incomer_address;
+ int sockaddrl = (int) sizeof( struct sockaddr );
+ int fd = accept(x->x_connectsocket, (struct sockaddr*)&incomer_address, &sockaddrl);
+ int i;
+
+ if (fd < 0) post("%s: accept failed", objName);
+ else
+ {
+ t_tcpserver_socketreceiver *y = tcpserver_socketreceiver_new((void *)x,
+ (t_tcpserver_socketnotifier)tcpserver_notify, NULL);/* MP tcpserver_doit isn't used I think...*/
+ if (!y)
+ {
+#ifdef MSW
+ closesocket(fd);
+#else
+ close(fd);
+#endif
+ return;
+ }
+ sys_addpollfn(fd, (t_fdpollfn)tcpserver_socketreceiver_read, y);
+ x->x_nconnections++;
+ i = x->x_nconnections - 1;
+ x->x_host[i] = gensym(inet_ntoa(incomer_address.sin_addr));
+ x->x_fd[i] = fd;
+ x->x_sr[i] = y;
+ post("%s: accepted connection from %s on socket %d",
+ objName, x->x_host[i]->s_name, x->x_fd[i]);
+ outlet_float(x->x_connectout, x->x_nconnections);
+ outlet_float(x->x_sockout, x->x_fd[i]); /* the socket number */
+ x->x_addr[i] = ntohl(incomer_address.sin_addr.s_addr);
+ x->x_addrbytes[0].a_w.w_float = (x->x_addr[i] & 0xFF000000)>>24;
+ x->x_addrbytes[1].a_w.w_float = (x->x_addr[i] & 0x0FF0000)>>16;
+ x->x_addrbytes[2].a_w.w_float = (x->x_addr[i] & 0x0FF00)>>8;
+ x->x_addrbytes[3].a_w.w_float = (x->x_addr[i] & 0x0FF);
+ outlet_list(x->x_addrout, &s_list, 4L, x->x_addrbytes);
+ }
+}
+
+static void tcpserver_print(t_tcpserver *x)
+{
+ int i;
+
+ if(x->x_nconnections > 0)
+ {
+ post("%s: %d open connections:", objName, x->x_nconnections);
+ for(i = 0; i < x->x_nconnections; i++)
+ {
+ post(" \"%s\" on socket %d",
+ x->x_host[i]->s_name, x->x_fd[i]);
+ }
+ }
+ else post("%s: no open connections", objName);
+}
+
+static void *tcpserver_new(t_floatarg fportno)
+{
+ t_tcpserver *x;
+ int i;
+ struct sockaddr_in server;
+ int sockfd, portno = fportno;
+
+ /* create a socket */
+ sockfd = socket(AF_INET, SOCK_STREAM, 0);
+#ifdef DEBUG
+ post("%s: receive socket %d", objName, sockfd);
+#endif
+ if (sockfd < 0)
+ {
+ sys_sockerror("tcpserver: socket");
+ return (0);
+ }
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = INADDR_ANY;
+#ifdef IRIX
+ /* this seems to work only in IRIX but is unnecessary in
+ Linux. Not sure what NT needs in place of this. */
+ if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0)
+ post("setsockopt failed\n");
+#endif
+ /* 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("tcpserver: bind");
+ sys_closesocket(sockfd);
+ return (0);
+ }
+ x = (t_tcpserver *)pd_new(tcpserver_class);
+ x->x_msgout = outlet_new(&x->x_obj, &s_anything); /* 1st outlet for received data */
+ /* streaming protocol */
+ if (listen(sockfd, 5) < 0)
+ {
+ sys_sockerror("tcpserver: listen");
+ sys_closesocket(sockfd);
+ sockfd = -1;
+ }
+ else
+ {
+ sys_addpollfn(sockfd, (t_fdpollfn)tcpserver_connectpoll, x);
+ x->x_connectout = outlet_new(&x->x_obj, &s_float); /* 2nd outlet for number of connected clients */
+ x->x_sockout = outlet_new(&x->x_obj, &s_float); /* 3rd outlet for socket number of current client */
+ x->x_addrout = outlet_new(&x->x_obj, &s_list); /* 4th outlet for ip address of current client */
+ inbinbuf = binbuf_new();
+ }
+ x->x_connectsocket = sockfd;
+ x->x_nconnections = 0;
+ for(i = 0; i < MAX_CONNECT; i++)
+ {
+ x->x_fd[i] = -1;
+ x->x_sr[i] = NULL;
+ }
+ /* prepare to convert the bytes in the buffer to floats in a list */
+ for (i = 0; i < MAX_UDP_RECEIVE; ++i)
+ {
+ x->x_msgoutbuf[i].a_type = A_FLOAT;
+ x->x_msgoutbuf[i].a_w.w_float = 0;
+ }
+ for (i = 0; i < 4; ++i)
+ {
+ x->x_addrbytes[i].a_type = A_FLOAT;
+ x->x_addrbytes[i].a_w.w_float = 0;
+ }
+
+ return (x);
+}
+
+static void tcpserver_free(t_tcpserver *x)
+{
+ int i;
+
+ for(i = 0; i < MAX_CONNECT; i++)
+ {
+ if (x->x_fd[i] >= 0)
+ {
+ sys_rmpollfn(x->x_fd[i]);
+ sys_closesocket(x->x_fd[i]);
+ }
+ if (x->x_sr[i] != NULL) tcpserver_socketreceiver_free(x->x_sr[i]);
+ }
+ if (x->x_connectsocket >= 0)
+ {
+ sys_rmpollfn(x->x_connectsocket);
+ sys_closesocket(x->x_connectsocket);
+ }
+
+ binbuf_free(inbinbuf);
+}
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void tcpserver_setup(void)
+{
+ tcpserver_class = class_new(gensym(objName),(t_newmethod)tcpserver_new, (t_method)tcpserver_free,
+ sizeof(t_tcpserver), 0, A_DEFFLOAT, 0);
+ class_addmethod(tcpserver_class, (t_method)tcpserver_print, gensym("print"), 0);
+ class_addmethod(tcpserver_class, (t_method)tcpserver_send, gensym("send"), A_GIMME, 0);
+ class_addmethod(tcpserver_class, (t_method)tcpserver_client_send, gensym("client"), A_GIMME, 0);
+ class_addmethod(tcpserver_class, (t_method)tcpserver_broadcast, gensym("broadcast"), A_GIMME, 0);
+}
+
+/* end of x_net_tcpserver.c */
diff --git a/net/x_net_udpreceive.c b/net/x_net_udpreceive.c
new file mode 100755
index 0000000..1aa9058
--- /dev/null
+++ b/net/x_net_udpreceive.c
@@ -0,0 +1,161 @@
+/* x_net_udpreceive.c 20060424. Martin Peach did it based on x_net.c. x_net.c header follows: */
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+#include "m_pd.h"
+#include "s_stuff.h"
+
+#ifdef MSW
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#include <stdio.h>
+#endif
+
+
+/* ----------------------------- udpreceive ------------------------- */
+
+static t_class *udpreceive_class;
+
+#define MAX_UDP_RECEIVE 65536L // longer than data in maximum UDP packet
+
+typedef struct _udpreceive
+{
+ t_object x_obj;
+ t_outlet *x_msgout;
+ t_outlet *x_addrout;
+ int x_connectsocket;
+ t_atom x_addrbytes[4];
+ t_atom x_msgoutbuf[MAX_UDP_RECEIVE];
+ char x_msginbuf[MAX_UDP_RECEIVE];
+} t_udpreceive;
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void udpreceive_setup(void);
+static void udpreceive_free(t_udpreceive *x);
+static void *udpreceive_new(t_floatarg fportno);
+static void udpreceive_read(t_udpreceive *x, int sockfd);
+
+static void udpreceive_read(t_udpreceive *x, int sockfd)
+{
+ int i, read = 0;
+ struct sockaddr_in from;
+ socklen_t fromlen = sizeof(from);
+ long addr;
+
+ read = recvfrom(sockfd, x->x_msginbuf, MAX_UDP_RECEIVE, 0, (struct sockaddr *)&from, &fromlen);
+#ifdef DEBUG
+ post("udpreceive_read: read %lu x->x_connectsocket = %d",
+ read, x->x_connectsocket);
+#endif
+ /* get the sender's ip */
+ addr = ntohl(from.sin_addr.s_addr);
+ x->x_addrbytes[0].a_w.w_float = (addr & 0xFF000000)>>24;
+ x->x_addrbytes[1].a_w.w_float = (addr & 0x0FF0000)>>16;
+ x->x_addrbytes[2].a_w.w_float = (addr & 0x0FF00)>>8;
+ x->x_addrbytes[3].a_w.w_float = (addr & 0x0FF);
+ outlet_list(x->x_addrout, &s_list, 4L, x->x_addrbytes);
+
+ if (read < 0)
+ {
+ sys_sockerror("udpreceive_read");
+ sys_closesocket(x->x_connectsocket);
+ return;
+ }
+ if (read > 0)
+ {
+ for (i = 0; i < read; ++i)
+ {
+ /* convert the bytes in the buffer to floats in a list */
+ x->x_msgoutbuf[i].a_w.w_float = (float)x->x_msginbuf[i];
+ }
+ /* send the list out the outlet */
+ if (read > 1) outlet_list(x->x_msgout, &s_list, read, x->x_msgoutbuf);
+ else outlet_float(x->x_msgout, x->x_msgoutbuf[0].a_w.w_float);
+ }
+}
+
+static void *udpreceive_new(t_floatarg fportno)
+{
+ t_udpreceive *x;
+ struct sockaddr_in server;
+ int sockfd, portno = fportno;
+ int intarg, i;
+
+ /* create a socket */
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+#ifdef DEBUG
+ post("udpreceive_new: socket %d port %d", sockfd, portno);
+#endif
+ if (sockfd < 0)
+ {
+ sys_sockerror("udpreceive: socket");
+ return (0);
+ }
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = INADDR_ANY;
+
+ /* ask OS to allow another Pd to repoen this port after we close it. */
+ intarg = 1;
+ if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&intarg, sizeof(intarg)) < 0)
+ post("udpreceive: setsockopt (SO_REUSEADDR) failed");
+
+ /* 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("udpreceive: bind");
+ sys_closesocket(sockfd);
+ return (0);
+ }
+ x = (t_udpreceive *)pd_new(udpreceive_class);
+ x->x_msgout = outlet_new(&x->x_obj, &s_anything);
+ x->x_addrout = outlet_new(&x->x_obj, &s_list);
+ x->x_connectsocket = sockfd;
+
+ /* convert the bytes in the buffer to floats in a list */
+ for (i = 0; i < MAX_UDP_RECEIVE; ++i)
+ {
+ x->x_msgoutbuf[i].a_type = A_FLOAT;
+ x->x_msgoutbuf[i].a_w.w_float = 0;
+ }
+ for (i = 0; i < 4; ++i)
+ {
+ x->x_addrbytes[i].a_type = A_FLOAT;
+ x->x_addrbytes[i].a_w.w_float = 0;
+ }
+ sys_addpollfn(x->x_connectsocket, (t_fdpollfn)udpreceive_read, x);
+ return (x);
+}
+
+static void udpreceive_free(t_udpreceive *x)
+{
+ if (x->x_connectsocket >= 0)
+ {
+ sys_rmpollfn(x->x_connectsocket);
+ sys_closesocket(x->x_connectsocket);
+ }
+}
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void udpreceive_setup(void)
+{
+ udpreceive_class = class_new(gensym("udpreceive"),
+ (t_newmethod)udpreceive_new, (t_method)udpreceive_free,
+ sizeof(t_udpreceive), CLASS_NOINLET, A_DEFFLOAT, 0);
+}
+
+/* end x_net_udpreceive.c */
+
diff --git a/net/x_net_udpsend.c b/net/x_net_udpsend.c
new file mode 100755
index 0000000..03c1f18
--- /dev/null
+++ b/net/x_net_udpsend.c
@@ -0,0 +1,214 @@
+/* x_net_udpsend.c 20060424. Martin Peach did it based on x_net.c. x_net.c header follows: */
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* network */
+
+#include "m_pd.h"
+#include "s_stuff.h"
+
+#include <stdio.h>
+#include <string.h>
+#ifdef MSW
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#endif
+
+static t_class *udpsend_class;
+
+typedef struct _udpsend
+{
+ t_object x_obj;
+ int x_fd;
+} t_udpsend;
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void udpsend_setup(void);
+static void udpsend_free(t_udpsend *x);
+static void udpsend_send(t_udpsend *x, t_symbol *s, int argc, t_atom *argv);
+static void udpsend_disconnect(t_udpsend *x);
+static void udpsend_connect(t_udpsend *x, t_symbol *hostname, t_floatarg fportno);
+static void *udpsend_new(void);
+
+static void *udpsend_new(void)
+{
+ t_udpsend *x = (t_udpsend *)pd_new(udpsend_class);
+ outlet_new(&x->x_obj, &s_float);
+ x->x_fd = -1;
+ return (x);
+}
+
+static void udpsend_connect(t_udpsend *x, t_symbol *hostname,
+ t_floatarg fportno)
+{
+ struct sockaddr_in server;
+ struct hostent *hp;
+ int sockfd;
+ int portno = fportno;
+
+ if (x->x_fd >= 0)
+ {
+ error("udpsend: already connected");
+ return;
+ }
+
+ /* create a socket */
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+#ifdef DEBUG
+ fprintf(stderr, "udpsend_connect: send socket %d\n", sockfd);
+#endif
+ if (sockfd < 0)
+ {
+ sys_sockerror("udpsend: socket");
+ return;
+ }
+ /* connect socket using hostname provided in command line */
+ server.sin_family = AF_INET;
+ hp = gethostbyname(hostname->s_name);
+ if (hp == 0)
+ {
+ post("udpsend: bad host?\n");
+ return;
+ }
+ memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+
+ /* assign client port number */
+ server.sin_port = htons((u_short)portno);
+
+ post("udpsend: connecting to port %d", portno);
+ /* try to connect. */
+ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0)
+ {
+ sys_sockerror("udpsend: connecting stream socket");
+ sys_closesocket(sockfd);
+ return;
+ }
+ x->x_fd = sockfd;
+ outlet_float(x->x_obj.ob_outlet, 1);
+}
+
+static void udpsend_disconnect(t_udpsend *x)
+{
+ if (x->x_fd >= 0)
+ {
+ sys_closesocket(x->x_fd);
+ x->x_fd = -1;
+ outlet_float(x->x_obj.ob_outlet, 0);
+ }
+}
+
+static void udpsend_send(t_udpsend *x, t_symbol *s, int argc, t_atom *argv)
+{
+ static char byte_buf[65536];// arbitrary maximum similar to max IP packet size
+ int i, d;
+ char c;
+ float f, e;
+ char *bp;
+ int length, sent;
+ int result;
+ static double lastwarntime;
+ static double pleasewarn;
+ double timebefore;
+ double timeafter;
+ int late;
+
+#ifdef DEBUG
+ post("s: %s", s->s_name);
+ post("argc: %d", argc);
+#endif
+ for (i = 0; i < argc; ++i)
+ {
+ if (argv[i].a_type == A_FLOAT)
+ {
+ f = argv[i].a_w.w_float;
+ d = (int)f;
+ e = f - d;
+ if (e != 0)
+ {
+ error("udpsend_send: item %d (%f) is not an integer", i, f);
+ return;
+ }
+ c = (unsigned char)d;
+ if (c != d)
+ {
+ error("udpsend_send: item %d (%f) is not between 0 and 255", i, f);
+ return;
+ }
+#ifdef DEBUG
+ post("udpsend_send: argv[%d]: %d", i, c);
+#endif
+ byte_buf[i] = c;
+ }
+ else
+ {
+ error("udpsend_send: item %d is not a float", i);
+ return;
+ }
+ }
+
+ length = i;
+ if ((x->x_fd >= 0) && (length > 0))
+ {
+ for (bp = byte_buf, sent = 0; sent < length;)
+ {
+ timebefore = sys_getrealtime();
+ result = send(x->x_fd, byte_buf, length-sent, 0);
+ timeafter = sys_getrealtime();
+ late = (timeafter - timebefore > 0.005);
+ if (late || pleasewarn)
+ {
+ if (timeafter > lastwarntime + 2)
+ {
+ post("udpsend blocked %d msec",
+ (int)(1000 * ((timeafter - timebefore) + pleasewarn)));
+ pleasewarn = 0;
+ lastwarntime = timeafter;
+ }
+ else if (late) pleasewarn += timeafter - timebefore;
+ }
+ if (result <= 0)
+ {
+ sys_sockerror("udpsend");
+ udpsend_disconnect(x);
+ break;
+ }
+ else
+ {
+ sent += result;
+ bp += result;
+ }
+ }
+ }
+ else error("udpsend: not connected");
+}
+
+static void udpsend_free(t_udpsend *x)
+{
+ udpsend_disconnect(x);
+}
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void udpsend_setup(void)
+{
+ udpsend_class = class_new(gensym("udpsend"), (t_newmethod)udpsend_new,
+ (t_method)udpsend_free,
+ sizeof(t_udpsend), 0, 0);
+ class_addmethod(udpsend_class, (t_method)udpsend_connect,
+ gensym("connect"), A_SYMBOL, A_FLOAT, 0);
+ class_addmethod(udpsend_class, (t_method)udpsend_disconnect,
+ gensym("disconnect"), 0);
+ class_addmethod(udpsend_class, (t_method)udpsend_send, gensym("send"),
+ A_GIMME, 0);
+}
+
+/* end x_net_udpsend.c*/
+
diff --git a/osc/packOSC-help.pd b/osc/packOSC-help.pd
new file mode 100755
index 0000000..8518642
--- /dev/null
+++ b/osc/packOSC-help.pd
@@ -0,0 +1,30 @@
+#N canvas 611 6 488 317 12;
+#X obj 171 184 udpsend;
+#X msg 177 158 disconnect;
+#X msg 171 96 connect 127.0.0.1 9997;
+#X obj 71 96 packOSC;
+#X obj 71 128 prepend send;
+#X obj 171 207 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1
+1;
+#X msg 14 18 send /test/one/two/three zz 88 T;
+#X msg 71 65 send /test 1 2 3;
+#X msg 90 43 send /west 35;
+#X msg 201 41 send /*/left 22;
+#X msg 296 24 send /?est/ 1;
+#X msg 246 66 send /left one two;
+#X msg 339 43 send /right 88;
+#X text 4 226 packOSC is like sendOSC except that it outputs a list
+of floats instead of directly connecting to the network;
+#X text 158 276 2006/04/25 Martin Peach;
+#X connect 0 0 5 0;
+#X connect 1 0 0 0;
+#X connect 2 0 0 0;
+#X connect 3 0 4 0;
+#X connect 4 0 0 0;
+#X connect 6 0 3 0;
+#X connect 7 0 3 0;
+#X connect 8 0 3 0;
+#X connect 9 0 3 0;
+#X connect 10 0 3 0;
+#X connect 11 0 3 0;
+#X connect 12 0 3 0;
diff --git a/osc/packOSC.c b/osc/packOSC.c
new file mode 100755
index 0000000..eed5f0a
--- /dev/null
+++ b/osc/packOSC.c
@@ -0,0 +1,1000 @@
+/* packOSC is like sendOSC but outputs a list of floats which are the bytes making up the OSC packet. */
+/* This allows for the separation of the protocol and its transport. */
+/* Started by Martin Peach 20060403 */
+/* 20060425 version independent of libOSC */
+/* packOSC.c makes extensive use of code from OSC-client.c and sendOSC.c */
+/* as well as some from OSC-timetag.c. These files have the following header: */
+/*
+Written by Matt Wright, The Center for New Music and Audio Technologies,
+University of California, Berkeley. Copyright (c) 1996,97,98,99,2000,01,02,03
+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
+*/
+
+#define MAX_ARGS 2000
+#define SC_BUFFER_SIZE 64000
+
+#include "m_pd.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#ifdef MSW
+#include <winsock2.h>
+#else
+#include <ctype.h>
+#endif
+
+#ifdef unix
+#include <netinet/in.h>
+#endif
+/* This is from OSC-client.h :*/
+/*
+
+ OSC-client.h: library for constructing OpenSoundControl messages.
+ Derived from SynthControl.h
+ Author: Matt Wright
+ Version 0.1: 6/13/97
+ Version 0.2: 7/21/2000: Support for type-tagged messages
+
+
+ General notes:
+
+ This library abstracts away the data format for the OpenSoundControl
+ protocol. Users of this library can construct OpenSoundControl packets
+ with a function call interface instead of knowing how to lay out the bits.
+
+ All issues of memory allocation are deferred to the user of this library.
+ There are two data structures that the user must allocate. The first
+ is the actual buffer that the message will be written into. This buffer
+ can be any size, but if it's too small there's a possibility that it
+ will become overfull. The other data structure is called an OSCbuf,
+ and it holds all the state used by the library as it's constructing
+ a buffer.
+
+ All procedures that have the possibility of an error condition return int,
+ with 0 indicating no error and nonzero indicating an error. The variable
+ OSC_errorMessage will be set to point to a string containing an error
+ message explaining what the problem is.
+
+*/
+/*
+ OSC_timeTag.h: library for manipulating OSC time tags
+ Matt Wright, 5/29/97
+
+ Time tags in OSC have the same format as in NTP: 64 bit fixed point, with the
+ top 32 bits giving number of seconds sinve midnight 1/1/1900 and the bottom
+ 32 bits giving fractional parts of a second. We represent this by an 8-byte
+ unsigned long if possible, or else a struct.
+
+ NB: On many architectures with 8-byte ints, it's illegal (like maybe a bus error)
+ to dereference a pointer to an 8 byte int that's not 8-byte aligned.
+*/
+
+/* You may have to redefine this typedef if ints on your system
+ aren't 4 bytes. */
+typedef unsigned int uint4;
+typedef struct
+{
+ uint4 seconds;
+ uint4 fraction;
+} OSCTimeTag;
+
+/* Return the time tag 0x0000000000000001, indicating to the receiving device
+ that it should process the message immediately. */
+static OSCTimeTag OSCTT_Immediately(void);
+
+/* The int4byte type has to be a 4-byte integer. You may have to
+ change this to long or something else on your system. */
+typedef int int4byte;
+
+/* The maximum depth of bundles within bundles within bundles within...
+ This is the size of a static array. If you exceed this limit you'll
+ get an error message. */
+#define MAX_BUNDLE_NESTING 32
+
+/* Don't ever manipulate the data in the OSCbuf struct directly. (It's
+ declared here in the header file only so your program will be able to
+ declare variables of type OSCbuf and have the right amount of memory
+ be allocated.) */
+
+typedef struct OSCbuf_struct
+{
+ char *buffer; /* The buffer to hold the OSC packet */
+ int size; /* Size of the buffer */
+ char *bufptr; /* Current position as we fill the buffer */
+ int state; /* State of partially-constructed message */
+ int4byte *thisMsgSize; /* Pointer to count field before */
+ /* currently-being-written message */
+ int4byte *prevCounts[MAX_BUNDLE_NESTING]; /* Pointers to count */
+ /* field before each currently open bundle */
+ int bundleDepth; /* How many sub-sub-bundles are we in now? */
+ char *typeStringPtr; /* This pointer advances through the type */
+ /* tag string as you add arguments. */
+ int gettingFirstUntypedArg; /* nonzero if this message doesn't have */
+ /* a type tag and we're waiting for the 1st arg */
+} OSCbuf;
+
+/* Here are the possible values of the state field: */
+
+#define EMPTY 0 /* Nothing written to packet yet */
+#define ONE_MSG_ARGS 1 /* Packet has a single message; gathering arguments */
+#define NEED_COUNT 2 /* Just opened a bundle; must write message name or */
+ /* open another bundle */
+#define GET_ARGS 3 /* Getting arguments to a message. If we see a message */
+ /* name or a bundle open/close then the current message */
+ /* will end. */
+#define DONE 4 /* All open bundles have been closed, so can't write */
+ /* anything else */
+
+
+static int OSC_strlen(char *s);
+static int OSC_padString(char *dest, char *str);
+static int OSC_padStringWithAnExtraStupidComma(char *dest, char *str);
+static int OSC_WritePadding(char *dest, int i);
+static int CheckTypeTag(OSCbuf *buf, char expectedType);
+
+/* Initialize the given OSCbuf. The user of this module must pass in the
+ block of memory that this OSCbuf will use for a buffer, and the number of
+ bytes in that block. (It's the user's job to allocate the memory because
+ you do it differently in different systems.) */
+static void OSC_initBuffer(OSCbuf *buf, int size, char *byteArray);
+
+
+/* Reset the given OSCbuf. Do this after you send out the contents of
+ the buffer and want to start writing new data into it. */
+static void OSC_resetBuffer(OSCbuf *buf);
+
+
+/* Is the buffer empty? (I.e., would it be stupid to send the buffer
+ contents to the synth?) */
+static int OSC_isBufferEmpty(OSCbuf *buf);
+
+
+/* How much space is left in the buffer? */
+static int OSC_freeSpaceInBuffer(OSCbuf *buf);
+
+/* Does the buffer contain a valid OSC packet? (Returns nonzero if yes.) */
+static int OSC_isBufferDone(OSCbuf *buf);
+
+/* When you're ready to send out the buffer (i.e., when OSC_isBufferDone()
+ returns true), call these two procedures to get the OSC packet that's been
+ assembled and its size in bytes. (And then call OSC_resetBuffer() if you
+ want to re-use this OSCbuf for the next packet.) */
+static char *OSC_getPacket(OSCbuf *buf);
+static int OSC_packetSize(OSCbuf *buf);
+static int OSC_CheckOverflow(OSCbuf *buf, int bytesNeeded);
+
+/* Here's the basic model for building up OSC messages in an OSCbuf:
+ - Make sure the OSCbuf has been initialized with OSC_initBuffer().
+ - To open a bundle, call OSC_openBundle(). You can then write
+ messages or open new bundles within the bundle you opened.
+ Call OSC_closeBundle() to close the bundle. Note that a packet
+ does not have to have a bundle; it can instead consist of just a
+ single message.
+ - For each message you want to send:
+ - Call OSC_writeAddress() with the name of your message. (In
+ addition to writing your message name into the buffer, this
+ procedure will also leave space for the size count of this message.)
+ - Alternately, call OSC_writeAddressAndTypes() with the name of
+ your message and with a type string listing the types of all the
+ arguments you will be putting in this message.
+ - Now write each of the arguments into the buffer, by calling one of:
+ OSC_writeFloatArg()
+ OSC_writeIntArg()
+ OSC_writeStringArg()
+ - Now your message is complete; you can send out the buffer or you can
+ add another message to it.
+*/
+
+static int OSC_openBundle(OSCbuf *buf, OSCTimeTag tt);
+static int OSC_closeBundle(OSCbuf *buf);
+static int OSC_writeAddress(OSCbuf *buf, char *name);
+static int OSC_writeAddressAndTypes(OSCbuf *buf, char *name, char *types);
+static int OSC_writeFloatArg(OSCbuf *buf, float arg);
+static int OSC_writeIntArg(OSCbuf *buf, int4byte arg);
+static int OSC_writeStringArg(OSCbuf *buf, char *arg);
+
+/* How many bytes will be needed in the OSC format to hold the given
+ string? The length of the string, plus the null char, plus any padding
+ needed for 4-byte alignment. */
+static int OSC_effectiveStringLength(char *string);
+
+typedef struct
+{
+ enum {INT_osc, FLOAT_osc, STRING_osc, NOTYPE_osc} type;
+ union
+ {
+ int i;
+ float f;
+ char *s;
+ } datum;
+} typedArg;
+
+static int useTypeTags = 0;
+static char bufferForOSCbuf[SC_BUFFER_SIZE];
+static t_atom bufferForOSClist[SC_BUFFER_SIZE];
+
+static t_class *packOSC_class;
+
+typedef struct _packOSC
+{
+ t_object x_obj;
+ t_int x_typetags; // typetag flag
+ int x_bundle; // bundle open flag
+ OSCbuf x_oscbuf[1]; // OSCbuffer
+ t_atom *x_osclist;
+ t_outlet *x_bdpthout; // bundle-depth floatoutlet
+ t_outlet *x_listout; // OSC packet list ouput
+} t_packOSC;
+
+static void *packOSC_new(t_floatarg udpflag);
+static void packOSC_openbundle(t_packOSC *x);
+static void packOSC_closebundle(t_packOSC *x);
+static void packOSC_settypetags(t_packOSC *x, t_float *f);
+static void packOSC_sendtyped(t_packOSC *x, t_symbol *s, int argc, t_atom *argv);
+static void packOSC_send(t_packOSC *x, t_symbol *s, int argc, t_atom *argv);
+static void packOSC_free(t_packOSC *x);
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void packOSC_setup(void);
+static typedArg packOSC_parseatom(t_atom *a);
+static int packOSC_writemessage(OSCbuf *buf, char *messageName, int numArgs, typedArg *args);
+static void packOSC_sendbuffer(t_packOSC *x);
+
+static void *packOSC_new(t_floatarg udpflag)
+{
+ t_packOSC *x = (t_packOSC *)pd_new(packOSC_class);
+ // set typetags to 1 by default
+ x->x_typetags = 1;
+ // bundle is closed
+ x->x_bundle = 0;
+ OSC_initBuffer(x->x_oscbuf, SC_BUFFER_SIZE, bufferForOSCbuf);
+ x->x_osclist = bufferForOSClist;
+ x->x_listout = outlet_new(&x->x_obj, &s_list);
+ x->x_bdpthout = outlet_new(&x->x_obj, &s_float);
+ return (x);
+}
+
+
+static void packOSC_openbundle(t_packOSC *x)
+{
+ if (x->x_oscbuf->bundleDepth + 1 >= MAX_BUNDLE_NESTING ||
+ OSC_openBundle(x->x_oscbuf, OSCTT_Immediately()))
+ {
+ post("packOSC: Problem opening bundle.");
+ return;
+ }
+ x->x_bundle = 1;
+ outlet_float(x->x_bdpthout, (float)x->x_oscbuf->bundleDepth);
+}
+
+static void packOSC_closebundle(t_packOSC *x)
+{
+ if (OSC_closeBundle(x->x_oscbuf))
+ {
+ post("packOSC: Problem closing bundle.");
+ return;
+ }
+ outlet_float(x->x_bdpthout, (float)x->x_oscbuf->bundleDepth);
+ // in bundle mode we send when bundle is closed?
+ if(!OSC_isBufferEmpty(x->x_oscbuf) > 0 && OSC_isBufferDone(x->x_oscbuf))
+ {
+ packOSC_sendbuffer(x);
+ OSC_initBuffer(x->x_oscbuf, SC_BUFFER_SIZE, bufferForOSCbuf);
+ x->x_bundle = 0;
+ return;
+ }
+}
+
+static void packOSC_settypetags(t_packOSC *x, t_float *f)
+{
+ x->x_typetags = (f != 0)?1:0;
+ post("packOSC: setting typetags %d",x->x_typetags);
+}
+
+
+//////////////////////////////////////////////////////////////////////
+// this is the real and only sending routine now, for both typed and
+// undtyped mode.
+
+static void packOSC_sendtyped(t_packOSC *x, t_symbol *s, int argc, t_atom *argv)
+{
+ char messageName[MAXPDSTRING];
+ typedArg args[MAX_ARGS];
+ int i;
+
+ messageName[0] = '\0'; // empty
+
+ if(argc>MAX_ARGS)
+ {
+ post ("packOSC: too many arguments! (max: %d)", MAX_ARGS);
+ return;
+ }
+
+#ifdef DEBUG
+ post ("packOSC: type tags? %d", useTypeTags);
+#endif
+
+ atom_string(&argv[0], messageName, MAXPDSTRING);
+ for (i = 0; i < argc-1; i++)
+ {
+ args[i] = packOSC_parseatom(&argv[i+1]);
+#ifdef DEBUG
+ switch (args[i].type)
+ {
+ case INT_osc:
+ post("packOSC: cell-cont: %d\n", args[i].datum.i);
+ break;
+ case FLOAT_osc:
+ post("packOSC: cell-cont: %f\n", args[i].datum.f);
+ break;
+ case STRING_osc:
+ post("packOSC: cell-cont: %s\n", args[i].datum.s);
+ break;
+ case NOTYPE_osc:
+ post("packOSC: unknown type\n");
+ break;
+ }
+ post("packOSC: type-id: %d\n", args[i].type);
+#endif
+ }
+
+ if(packOSC_writemessage(x->x_oscbuf, messageName, i, args))
+ {
+ post("packOSC: usage error, write-msg failed.");
+ return;
+ }
+
+ if(!x->x_bundle)
+ {
+ packOSC_sendbuffer(x);
+ OSC_initBuffer(x->x_oscbuf, SC_BUFFER_SIZE, bufferForOSCbuf);
+ }
+}
+
+static void packOSC_send(t_packOSC *x, t_symbol *s, int argc, t_atom *argv)
+{
+ if(!argc)
+ {
+ post("packOSC: not sending empty message.");
+ return;
+ }
+ if(x->x_typetags)
+ {
+ useTypeTags = 1;
+ packOSC_sendtyped(x, s, argc, argv);
+ useTypeTags = 0;
+ }
+ else
+ {
+ packOSC_sendtyped(x, s, argc, argv);
+ }
+}
+
+static void packOSC_free(t_packOSC *x)
+{
+}
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void packOSC_setup(void)
+{
+ packOSC_class = class_new(gensym("packOSC"), (t_newmethod)packOSC_new,
+ (t_method)packOSC_free,
+ sizeof(t_packOSC), 0, A_DEFFLOAT, 0);
+ class_addmethod(packOSC_class, (t_method)packOSC_settypetags,
+ gensym("typetags"), A_FLOAT, 0);
+ class_addmethod(packOSC_class, (t_method)packOSC_send,
+ gensym("send"), A_GIMME, 0);
+ class_addmethod(packOSC_class, (t_method)packOSC_send,
+ gensym("senduntyped"), A_GIMME, 0);
+ class_addmethod(packOSC_class, (t_method)packOSC_send,
+ gensym("sendtyped"), A_GIMME, 0);
+ class_addmethod(packOSC_class, (t_method)packOSC_openbundle,
+ gensym("["), 0, 0);
+ class_addmethod(packOSC_class, (t_method)packOSC_closebundle,
+ gensym("]"), 0, 0);
+}
+
+static typedArg packOSC_parseatom(t_atom *a)
+{
+ typedArg returnVal;
+ t_float f;
+ t_int i;
+ t_symbol s;
+ char buf[MAXPDSTRING];
+
+ atom_string(a, buf, MAXPDSTRING);
+#ifdef DEBUG
+ post("packOSC: atom type %d (%s)", a->a_type, buf);
+#endif
+ /* It might be an int, a float, or a string */
+ switch (a->a_type)
+ {
+ case A_FLOAT:
+ f = atom_getfloat(a);
+ i = atom_getint(a);
+ if (f == (t_float)i)
+ { // assume that if the int and float are the same, it's an int
+ returnVal.type = INT_osc;
+ returnVal.datum.i = i;
+ }
+ else
+ {
+ returnVal.type = FLOAT_osc;
+ returnVal.datum.f = f;
+ }
+ return returnVal;
+ case A_SYMBOL:
+ s = *atom_getsymbol(a);
+ returnVal.type = STRING_osc;
+ returnVal.datum.s = s.s_name;
+ return returnVal;
+ default:
+ atom_string(a, buf, MAXPDSTRING);
+ error("packOSC: atom type %d not implemented (%s)", a->a_type, buf);
+ returnVal.type = NOTYPE_osc;
+ returnVal.datum.s = NULL;
+ return returnVal;
+ }
+}
+
+static int packOSC_writemessage(OSCbuf *buf, char *messageName, int numArgs, typedArg *args)
+{
+ int j, returnVal;
+
+ returnVal = 0;
+#ifdef DEBUG
+ post("packOSC: packOSC_writemessage: %s ", messageName);
+
+ for (j = 0; j < numArgs; j++)
+ {
+ switch (args[j].type)
+ {
+ case INT_osc:
+ post("packOSC: %d ", args[j].datum.i);
+ break;
+ case FLOAT_osc:
+ post("packOSC: %f ", args[j].datum.f);
+ break;
+ case STRING_osc:
+ post("packOSC: %s ", args[j].datum.s);
+ break;
+ default:
+ post("packOSC: Unrecognized arg type %d", args[j].type);
+ break;
+ }
+ }
+#endif
+
+ if (!useTypeTags)
+ {
+ returnVal = OSC_writeAddress(buf, messageName);
+ if (returnVal)
+ {
+ post("packOSC: Problem writing address.");
+ }
+ }
+ else
+ {
+ /* First figure out the type tags */
+ char typeTags[MAX_ARGS+2];
+
+ typeTags[0] = ',';
+
+ for (j = 0; j < numArgs; ++j)
+ {
+ switch (args[j].type)
+ {
+ case INT_osc:
+ typeTags[j+1] = 'i';
+ break;
+ case FLOAT_osc:
+ typeTags[j+1] = 'f';
+ break;
+ case STRING_osc:
+ typeTags[j+1] = 's';
+ break;
+ default:
+ post("packOSC: arg %d type is unrecognized(%d)", j, args[j].type);
+ break;
+ }
+ }
+ typeTags[j+1] = '\0';
+ returnVal = OSC_writeAddressAndTypes(buf, messageName, typeTags);
+ if (returnVal)
+ {
+ post("packOSC: Problem writing address.");
+ }
+ }
+
+ for (j = 0; j < numArgs; j++)
+ {
+ switch (args[j].type)
+ {
+ case INT_osc:
+ if ((returnVal = OSC_writeIntArg(buf, args[j].datum.i)) != 0)
+ {
+ return returnVal;
+ }
+ break;
+ case FLOAT_osc:
+ if ((returnVal = OSC_writeFloatArg(buf, args[j].datum.f)) != 0)
+ {
+ return returnVal;
+ }
+ break;
+ case STRING_osc:
+ if ((returnVal = OSC_writeStringArg(buf, args[j].datum.s)) != 0)
+ {
+ return returnVal;
+ }
+ break;
+ default:
+ break; // just skip bad types (which we won't get anyway unless this code is buggy)
+ }
+ }
+ return returnVal;
+}
+
+static void packOSC_sendbuffer(t_packOSC *x)
+{
+ int i;
+ int length;
+ char *buf;
+#ifdef DEBUG
+ post("packOSC_sendbuffer: Sending buffer...\n");
+#endif
+ if (OSC_isBufferEmpty(x->x_oscbuf))
+ {
+ post("packOSC_sendbuffer() called but buffer empty");
+ return;
+ }
+ if (!OSC_isBufferDone(x->x_oscbuf))
+ {
+ post("packOSC_sendbuffer() called but buffer not ready!, not exiting");
+ return; //{{raf}}
+ }
+ length = OSC_packetSize(x->x_oscbuf);
+ buf = OSC_getPacket(x->x_oscbuf);
+#ifdef DEBUG
+ post ("packOSC_sendbuffer: length: %lu", length);
+#endif
+ /* convert the bytes in the buffer to floats in a list */
+ for (i = 0; i < length; ++i) SETFLOAT(&x->x_osclist[i], buf[i]);
+ /* send the list out the outlet */
+ outlet_list(x->x_listout, &s_list, length, x->x_osclist);
+}
+
+/* The next part is copied and morphed from OSC-client.c. */
+/*
+ Author: Matt Wright
+ Version 2.2: Calls htonl in the right places 20000620
+ Version 2.3: Gets typed messages right.
+ */
+
+/*
+ pd
+ -------------
+
+ raf@interaccess.com:
+ rev. for Win32 build (verified under Win-2ooo) 11-April-2002
+
+ -- changed licence part (20040820) jdl
+ -- Version 2.4 changes not in here (20040820) jdl
+
+*/
+
+
+static void OSC_initBuffer(OSCbuf *buf, int size, char *byteArray)
+{
+ buf->buffer = byteArray;
+ buf->size = size;
+ OSC_resetBuffer(buf);
+}
+
+static void OSC_resetBuffer(OSCbuf *buf)
+{
+ buf->bufptr = buf->buffer;
+ buf->state = EMPTY;
+ buf->bundleDepth = 0;
+ buf->prevCounts[0] = 0;
+ buf->gettingFirstUntypedArg = 0;
+ buf->typeStringPtr = 0;
+}
+
+static int OSC_isBufferEmpty(OSCbuf *buf)
+{
+ return buf->bufptr == buf->buffer;
+}
+
+static int OSC_freeSpaceInBuffer(OSCbuf *buf)
+{
+ return buf->size - (buf->bufptr - buf->buffer);
+}
+
+static int OSC_isBufferDone(OSCbuf *buf)
+{
+ return (buf->state == DONE || buf->state == ONE_MSG_ARGS);
+}
+
+static char *OSC_getPacket(OSCbuf *buf)
+{
+ return buf->buffer;
+}
+
+static int OSC_packetSize(OSCbuf *buf)
+{
+ return (buf->bufptr - buf->buffer);
+}
+
+static int OSC_CheckOverflow(OSCbuf *buf, int bytesNeeded)
+{
+ if ((bytesNeeded) > OSC_freeSpaceInBuffer(buf))
+ {
+ post("packOSC: buffer overflow");
+ return 1;
+ }
+ return 0;
+}
+
+static void PatchMessageSize(OSCbuf *buf)
+{
+ int4byte size = buf->bufptr - ((char *) buf->thisMsgSize) - 4;
+ *(buf->thisMsgSize) = htonl(size);
+}
+
+static int OSC_openBundle(OSCbuf *buf, OSCTimeTag tt)
+{
+ if (buf->state == ONE_MSG_ARGS)
+ {
+ post("packOSC: Can't open a bundle in a one-message packet");
+ return 3;
+ }
+
+ if (buf->state == DONE)
+ {
+ post("packOSC: This packet is finished; can't open a new bundle");
+ return 4;
+ }
+
+ if (++(buf->bundleDepth) >= MAX_BUNDLE_NESTING)
+ {
+ post("packOSC: Bundles nested too deeply; change MAX_BUNDLE_NESTING");
+ return 2;
+ }
+
+ if (CheckTypeTag(buf, '\0')) return 9;
+
+ if (buf->state == GET_ARGS)
+ {
+ PatchMessageSize(buf);
+ }
+
+ if (buf->state == EMPTY)
+ {
+ /* Need 16 bytes for "#bundle" and time tag */
+ if(OSC_CheckOverflow(buf, 16)) return 1;
+ }
+ else
+ {
+ /* This bundle is inside another bundle, so we need to leave
+ a blank size count for the size of this current bundle. */
+ if(OSC_CheckOverflow(buf, 20))return 1;
+ *((int4byte *)buf->bufptr) = 0xaaaaaaaa;
+ buf->prevCounts[buf->bundleDepth] = (int4byte *)buf->bufptr;
+
+ buf->bufptr += 4;
+ }
+
+ buf->bufptr += OSC_padString(buf->bufptr, "#bundle");
+
+
+ *((OSCTimeTag *) buf->bufptr) = tt;
+
+ if (htonl(1) != 1)
+ {
+ /* Byte swap the 8-byte integer time tag */
+ int4byte *intp = (int4byte *)buf->bufptr;
+ intp[0] = htonl(intp[0]);
+ intp[1] = htonl(intp[1]);
+
+ /* tt is a struct of two 32-bit words, and even though
+ each word was wrong-endian, they were in the right order
+ in the struct.) */
+ }
+
+ buf->bufptr += sizeof(OSCTimeTag);
+
+ buf->state = NEED_COUNT;
+
+ buf->gettingFirstUntypedArg = 0;
+ buf->typeStringPtr = 0;
+ return 0;
+}
+
+
+static int OSC_closeBundle(OSCbuf *buf)
+{
+ if (buf->bundleDepth == 0)
+ {
+ /* This handles EMPTY, ONE_MSG, ARGS, and DONE */
+ post("packOSC: Can't close bundle; no bundle is open!");
+ return 5;
+ }
+
+ if (CheckTypeTag(buf, '\0')) return 9;
+
+ if (buf->state == GET_ARGS)
+ {
+ PatchMessageSize(buf);
+ }
+
+ if (buf->bundleDepth == 1)
+ {
+ /* Closing the last bundle: No bundle size to patch */
+ buf->state = DONE;
+ }
+ else
+ {
+ /* Closing a sub-bundle: patch bundle size */
+ int size = buf->bufptr - ((char *) buf->prevCounts[buf->bundleDepth]) - 4;
+ *(buf->prevCounts[buf->bundleDepth]) = htonl(size);
+ buf->state = NEED_COUNT;
+ }
+
+ --buf->bundleDepth;
+ buf->gettingFirstUntypedArg = 0;
+ buf->typeStringPtr = 0;
+ return 0;
+}
+
+static int OSC_writeAddress(OSCbuf *buf, char *name)
+{
+ int4byte paddedLength;
+
+ if (buf->state == ONE_MSG_ARGS)
+ {
+ post("packOSC: This packet is not a bundle, so you can't write another address");
+ return 7;
+ }
+
+ if (buf->state == DONE)
+ {
+ post("packOSC: This packet is finished; can't write another address");
+ return 8;
+ }
+
+ if (CheckTypeTag(buf, '\0')) return 9;
+
+ paddedLength = OSC_effectiveStringLength(name);
+
+ if (buf->state == EMPTY)
+ {
+ /* This will be a one-message packet, so no sizes to worry about */
+ if(OSC_CheckOverflow(buf, paddedLength))return 1;
+ buf->state = ONE_MSG_ARGS;
+ }
+ else
+ {
+ /* GET_ARGS or NEED_COUNT */
+ if(OSC_CheckOverflow(buf, 4+paddedLength))return 1;
+ if (buf->state == GET_ARGS)
+ {
+ /* Close the old message */
+ PatchMessageSize(buf);
+ }
+ buf->thisMsgSize = (int4byte *)buf->bufptr;
+ *(buf->thisMsgSize) = 0xbbbbbbbb;
+ buf->bufptr += 4;
+ buf->state = GET_ARGS;
+ }
+
+ /* Now write the name */
+ buf->bufptr += OSC_padString(buf->bufptr, name);
+ buf->typeStringPtr = 0;
+ buf->gettingFirstUntypedArg = 1;
+
+ return 0;
+}
+
+static int OSC_writeAddressAndTypes(OSCbuf *buf, char *name, char *types)
+{
+ int result;
+ int4byte paddedLength;
+
+ if (CheckTypeTag(buf, '\0')) return 9;
+
+ result = OSC_writeAddress(buf, name);
+
+ if (result) return result;
+
+ paddedLength = OSC_effectiveStringLength(types);
+
+ if(OSC_CheckOverflow(buf, paddedLength))return 1;
+
+ buf->typeStringPtr = buf->bufptr + 1; /* skip comma */
+ buf->bufptr += OSC_padString(buf->bufptr, types);
+
+ buf->gettingFirstUntypedArg = 0;
+ return 0;
+}
+
+static int CheckTypeTag(OSCbuf *buf, char expectedType)
+{
+ if (buf->typeStringPtr)
+ {
+ if (*(buf->typeStringPtr) != expectedType)
+ {
+ if (expectedType == '\0')
+ {
+ post("packOSC: According to the type tag I expected more arguments.");
+ }
+ else if (*(buf->typeStringPtr) == '\0')
+ {
+ post("packOSC: According to the type tag I didn't expect any more arguments.");
+ }
+ else
+ {
+ post("packOSC: According to the type tag I expected an argument of a different type.");
+ post("* Expected %c, string now %s\n", expectedType, buf->typeStringPtr);
+ }
+ return 9;
+ }
+ ++(buf->typeStringPtr);
+ }
+ return 0;
+}
+
+static int OSC_writeFloatArg(OSCbuf *buf, float arg)
+{
+ int4byte *intp;
+
+ if(OSC_CheckOverflow(buf, 4))return 1;
+
+ if (CheckTypeTag(buf, 'f')) return 9;
+
+ /* Pretend arg is a long int so we can use htonl() */
+ intp = ((int4byte *) &arg);
+ *((int4byte *) buf->bufptr) = htonl(*intp);
+
+ buf->bufptr += 4;
+
+ buf->gettingFirstUntypedArg = 0;
+ return 0;
+}
+
+static int OSC_writeIntArg(OSCbuf *buf, int4byte arg)
+{
+ if(OSC_CheckOverflow(buf, 4))return 1;
+ if (CheckTypeTag(buf, 'i')) return 9;
+
+ *((int4byte *) buf->bufptr) = htonl(arg);
+ buf->bufptr += 4;
+
+ buf->gettingFirstUntypedArg = 0;
+ return 0;
+}
+
+static int OSC_writeStringArg(OSCbuf *buf, char *arg)
+{
+ int len;
+
+ if (CheckTypeTag(buf, 's')) return 9;
+
+ len = OSC_effectiveStringLength(arg);
+
+ if (buf->gettingFirstUntypedArg && arg[0] == ',')
+ {
+ /* This un-type-tagged message starts with a string
+ that starts with a comma, so we have to escape it
+ (with a double comma) so it won't look like a type
+ tag string. */
+
+ if(OSC_CheckOverflow(buf, len+4))return 1; /* Too conservative */
+ buf->bufptr +=
+ OSC_padStringWithAnExtraStupidComma(buf->bufptr, arg);
+
+ }
+ else
+ {
+ if(OSC_CheckOverflow(buf, len))return 1;
+ buf->bufptr += OSC_padString(buf->bufptr, arg);
+ }
+
+ buf->gettingFirstUntypedArg = 0;
+ return 0;
+
+}
+
+/* String utilities */
+
+static int OSC_strlen(char *s)
+{
+ int i;
+ for (i = 0; s[i] != '\0'; i++) /* Do nothing */ ;
+ return i;
+}
+
+#define STRING_ALIGN_PAD 4
+static int OSC_effectiveStringLength(char *string)
+{
+ int len = OSC_strlen(string) + 1; /* We need space for the null char. */
+
+ /* Round up len to next multiple of STRING_ALIGN_PAD to account for alignment padding */
+ if ((len % STRING_ALIGN_PAD) != 0)
+ {
+ len += STRING_ALIGN_PAD - (len % STRING_ALIGN_PAD);
+ }
+ return len;
+}
+
+static int OSC_padString(char *dest, char *str)
+{
+ int i;
+
+ for (i = 0; str[i] != '\0'; i++) dest[i] = str[i];
+
+ return OSC_WritePadding(dest, i);
+}
+
+static int OSC_padStringWithAnExtraStupidComma(char *dest, char *str)
+{
+ int i;
+
+ dest[0] = ',';
+ for (i = 0; str[i] != '\0'; i++) dest[i+1] = str[i];
+
+ return OSC_WritePadding(dest, i+1);
+}
+
+static int OSC_WritePadding(char *dest, int i)
+{
+ dest[i] = '\0';
+ i++;
+
+ for (; (i % STRING_ALIGN_PAD) != 0; i++) dest[i] = '\0';
+
+ return i;
+}
+
+/* The next bit is modified from OSC-timetag.c. */
+/*
+
+ OSC_timeTag.c: library for manipulating OSC time tags
+ Matt Wright, 5/29/97
+
+ Version 0.2 (9/11/98): cleaned up so no explicit type names in the .c file.
+
+*/
+
+static OSCTimeTag OSCTT_Immediately(void)
+{
+ OSCTimeTag tt;
+ tt.fraction = 1;
+ tt.seconds = 0;
+ return tt;
+}
+/* end packOSC.c*/
diff --git a/osc/routeOSC-help.pd b/osc/routeOSC-help.pd
new file mode 100755
index 0000000..080ce50
--- /dev/null
+++ b/osc/routeOSC-help.pd
@@ -0,0 +1,35 @@
+#N canvas 0 0 574 322 12;
+#X obj 58 82 udpreceive 9997;
+#X obj 173 106 unpack 0 0 0 0;
+#X floatatom 173 129 3 0 0 0 - - -;
+#X floatatom 208 129 3 0 0 0 - - -;
+#X floatatom 244 129 3 0 0 0 - - -;
+#X floatatom 280 129 3 0 0 0 - - -;
+#X text 137 128 from;
+#X obj 58 114 unpackOSC;
+#X obj 56 158 print;
+#X obj 70 206 routeOSC /test /west;
+#X obj 70 241 print a;
+#X obj 147 241 print b;
+#X obj 225 241 print c;
+#X msg 203 171 set /left;
+#X msg 294 171 set /left /right;
+#X text 10 7 routeOSC;
+#X text 10 25 accepts lists of floats that are interpreted as OSC packets
+;
+#X text 10 43 set message reassigns outputs;
+#X text 244 206 arguments are OSC addresses to route;
+#X text 296 284 2006/04/25 Martin Peach;
+#X connect 0 0 7 0;
+#X connect 0 1 1 0;
+#X connect 1 0 2 0;
+#X connect 1 1 3 0;
+#X connect 1 2 4 0;
+#X connect 1 3 5 0;
+#X connect 7 0 8 0;
+#X connect 7 0 9 0;
+#X connect 9 0 10 0;
+#X connect 9 1 11 0;
+#X connect 9 2 12 0;
+#X connect 13 0 9 0;
+#X connect 14 0 9 0;
diff --git a/osc/routeOSC.c b/osc/routeOSC.c
new file mode 100755
index 0000000..50c7872
--- /dev/null
+++ b/osc/routeOSC.c
@@ -0,0 +1,510 @@
+/* routeOSC.c 20060424 by Martin Peach, based on OSCroute and OSC-pattern-match.c. */
+/* 20060425 cleaned up some more */
+/* OSCroute.c header follows: */
+/*
+Written by 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
+*/
+
+/* OSC-route.c
+ Max object for OSC-style dispatching
+
+ To-do:
+
+ Match a pattern against a pattern?
+ [Done: Only Slash-Star is allowed, see MyPatternMatch.]
+ Declare outlet types / distinguish leaf nodes from other children
+ More sophisticated (2-pass?) allmessages scheme
+ set message?
+
+
+ pd
+ -------------
+ -- tweaks for Win32 www.zeggz.com/raf 13-April-2002
+
+*/
+/* OSC-pattern-match.c header follows: */
+/*
+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-pattern-match.c
+ Matt Wright, 3/16/98
+ Adapted from oscpattern.c, by Matt Wright and Amar Chaudhury
+*/
+
+#if HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* the required include files */
+#include "m_pd.h"
+
+#define MAX_NUM 128 // maximum number of paths (prefixes) we can route
+
+typedef struct _routeOSC
+{
+ t_object x_obj; // required header
+ t_int x_num; // Number of prefixes we store
+ char *x_prefixes[MAX_NUM];
+ void *x_outlets[MAX_NUM+1]; // one for each prefix plus one for everything else
+} t_routeOSC;
+
+/* prototypes */
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void routeOSC_setup(void);
+static int MyPatternMatch (const char *pattern, const char *test);
+static void routeOSC_doanything(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv);
+static void routeOSC_list(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv);
+static void *routeOSC_new(t_symbol *s, int argc, t_atom *argv);
+static void routeOSC_set(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv);
+static char *NextSlashOrNull(char *p);
+static void StrCopyUntilSlash(char *target, const char *source);
+
+/* from
+ OSC-pattern-match.c
+*/
+static const char *theWholePattern; /* Just for warning messages */
+static int MatchBrackets (const char *pattern, const char *test);
+static int MatchList (const char *pattern, const char *test);
+static int PatternMatch (const char * pattern, const char * test);
+
+static t_class *routeOSC_class;
+t_symbol *ps_list, *ps_complain, *ps_emptySymbol;
+
+static int MyPatternMatch (const char *pattern, const char *test)
+{
+ // This allows the special case of "routeOSC /* " to be an outlet that
+ // matches anything; i.e., it always outputs the input with the first level
+ // of the address stripped off.
+
+ if (test[0] == '*' && test[1] == '\0') return 1;
+ else return PatternMatch(pattern, test);
+}
+
+static void routeOSC_free(t_routeOSC *x)
+{
+}
+
+/* initialization routine */
+// setup
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void routeOSC_setup(void)
+{
+ routeOSC_class = class_new(gensym("routeOSC"), (t_newmethod)routeOSC_new,
+ (t_method)routeOSC_free,sizeof(t_routeOSC), 0, A_GIMME, 0);
+ class_addlist(routeOSC_class, routeOSC_list);
+ class_addanything(routeOSC_class, routeOSC_doanything);
+ class_addmethod(routeOSC_class, (t_method)routeOSC_set, gensym("set"), A_GIMME, 0);
+ class_sethelpsymbol(routeOSC_class, gensym("routeOSC-help.pd"));
+
+ ps_emptySymbol = gensym("");
+
+ post("routeOSC object version 1.0 by Martin Peach, based on OSCroute by Matt Wright. pd: jdl Win32 raf.");
+ post("OSCroute Copyright © 1999 Regents of the Univ. of California. All Rights Reserved.");
+}
+
+/* instance creation routine */
+static void *routeOSC_new(t_symbol *s, int argc, t_atom *argv)
+{
+
+ t_routeOSC *x = (t_routeOSC *)pd_new(routeOSC_class); // get memory for a new object & initialize
+ int i;
+
+ if (argc > MAX_NUM)
+ {
+ post("* routeOSC: too many arguments: %ld (max %ld)", argc, MAX_NUM);
+ return 0;
+ }
+ x->x_num = 0;
+ for (i = 0; i < argc; ++i)
+ {
+ if (argv[i].a_type == A_SYMBOL)
+ {
+ if (argv[i].a_w.w_symbol->s_name[0] == '/')
+ { /* Now that's a nice prefix */
+ x->x_prefixes[i] = argv[i].a_w.w_symbol->s_name;
+ ++(x->x_num);
+ }
+#if 0
+/* this doesn't make sense to me MP20060425 */
+ else if (argv[i].a_w.w_symbol->s_name[0] == '#' &&
+ argv[i].a_w.w_symbol->s_name[1] >= '1' &&
+ argv[i].a_w.w_symbol->s_name[1] <= '9')
+ {
+ /* The Max programmer is trying to make a patch that will be
+ a subpatch with arguments. We have to make an outlet for this
+ argument. */
+ x->x_prefixes[i] = "dummy";
+ ++(x->x_num);
+ }
+#endif /* 0 */
+ }
+ else if (argv[i].a_type == A_FLOAT)
+ {
+ post("* routeOSC: float arguments are not OK.");
+ return 0;
+ }
+ else
+ {
+ post("* routeOSC: unrecognized argument type!");
+ return 0;
+ }
+ }
+ /* Have to create the outlets in reverse order */
+ /* well, not in pd ? */
+ for (i = 0; i <= x->x_num; i++)
+ {
+ x->x_outlets[i] = outlet_new(&x->x_obj, &s_list);
+ }
+ return (x);
+}
+
+static void routeOSC_set(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int i;
+
+ if (argc > x->x_num)
+ {
+ post ("routeOSC: too many paths");
+ return;
+ }
+ for (i = 0; i < argc; ++i)
+ {
+ if (argv[i].a_type != A_SYMBOL)
+ {
+ post ("routeOSC: path %d not a symbol", i);
+ return;
+ }
+ if (argv[i].a_w.w_symbol->s_name[0] != '/')
+ {
+ post ("routeOSC: path %d doesn't start with /", i);
+ return;
+ }
+ }
+ for (i = 0; i < argc; ++i)
+ {
+ if (argv[i].a_w.w_symbol->s_name[0] == '/')
+ { /* Now that's a nice prefix */
+ x->x_prefixes[i] = argv[i].a_w.w_symbol->s_name;
+ }
+ }
+}
+
+static void routeOSC_list(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv)
+{
+ if (argc > 0 && argv[0].a_type == A_SYMBOL)
+ {
+ /* Ignore the fact that this is a "list" */
+ routeOSC_doanything(x, argv[0].a_w.w_symbol, argc-1, argv+1);
+ }
+ else
+ {
+ // post("* OSC-route: invalid list beginning with a number");
+ // output on unmatched outlet jdl 20020908
+ if (argv[0].a_type == A_FLOAT)
+ {
+ outlet_float(x->x_outlets[x->x_num], argv[0].a_w.w_float);
+ }
+ else
+ {
+ post("* routeOSC: unrecognized atom type!");
+ }
+ }
+}
+
+static void routeOSC_doanything(t_routeOSC *x, t_symbol *s, int argc, t_atom *argv)
+{
+ char *pattern, *nextSlash;
+ int i;
+ int matchedAnything;
+ // post("*** routeOSC_anything(s %s, argc %ld)", s->s_name, (long) argc);
+
+ pattern = s->s_name;
+ if (pattern[0] != '/')
+ {
+ post("* routeOSC: invalid message pattern %s does not begin with /", s->s_name);
+ outlet_anything(x->x_outlets[x->x_num], s, argc, argv);
+ return;
+ }
+ matchedAnything = 0;
+
+ nextSlash = NextSlashOrNull(pattern+1);
+ if (*nextSlash == '\0')
+ {
+ /* last level of the address, so we'll output the argument list */
+
+ for (i = 0; i < x->x_num; ++i)
+ {
+ if (MyPatternMatch(pattern+1, x->x_prefixes[i]+1))
+ {
+ ++matchedAnything;
+ // I hate stupid Max lists with a special first element
+ if (argc == 0)
+ {
+ outlet_bang(x->x_outlets[i]);
+ }
+ else if (argv[0].a_type == A_SYMBOL)
+ {
+ // Promote the symbol that was argv[0] to the special symbol
+ outlet_anything(x->x_outlets[i], argv[0].a_w.w_symbol, argc-1, argv+1);
+ }
+ else if (argc > 1)
+ {
+ // Multiple arguments starting with a number, so naturally we have
+ // to use a special function to output this "list", since it's what
+ // Max originally meant by "list".
+ outlet_list(x->x_outlets[i], 0L, argc, argv);
+ }
+ else
+ {
+ // There was only one argument, and it was a number, so we output it
+ // not as a list
+ if (argv[0].a_type == A_FLOAT)
+ {
+ outlet_float(x->x_outlets[i], argv[0].a_w.w_float);
+ }
+ else
+ {
+ post("* routeOSC: unrecognized atom type!");
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ /* There's more address after this part, so our output list will begin with
+ the next slash. */
+ t_symbol *restOfPattern = 0; /* avoid the gensym unless we have to output */
+ char patternBegin[1000];
+
+ /* Get the first level of the incoming pattern to match against all our prefixes */
+ StrCopyUntilSlash(patternBegin, pattern+1);
+
+ for (i = 0; i < x->x_num; ++i)
+ {
+ if (MyPatternMatch(patternBegin, x->x_prefixes[i]+1))
+ {
+ ++matchedAnything;
+ if (restOfPattern == 0) restOfPattern = gensym(nextSlash);
+ outlet_anything(x->x_outlets[i], restOfPattern, argc, argv);
+ }
+ }
+ }
+
+ if (!matchedAnything)
+ {
+ // output unmatched data on rightmost outlet a la normal 'route' object, jdl 20020908
+ outlet_anything(x->x_outlets[x->x_num], s, argc, argv);
+ }
+}
+
+static char *NextSlashOrNull(char *p)
+{
+ while (*p != '/' && *p != '\0') p++;
+ return p;
+}
+
+static void StrCopyUntilSlash(char *target, const char *source)
+{
+ while (*source != '/' && *source != '\0')
+ {
+ *target = *source;
+ ++target;
+ ++source;
+ }
+ *target = 0;
+}
+
+/* from
+ OSC-pattern-match.c
+ Matt Wright, 3/16/98
+ Adapted from oscpattern.c, by Matt Wright and Amar Chaudhury
+*/
+
+static int PatternMatch (const char * pattern, const char * test)
+{
+ theWholePattern = pattern;
+
+ if (pattern == 0 || pattern[0] == 0) return test[0] == 0;
+
+ if (test[0] == 0)
+ {
+ if (pattern[0] == '*') return PatternMatch (pattern+1, test);
+ return 0;
+ }
+
+ switch (pattern[0])
+ {
+ case 0:
+ return test[0] == 0;
+ case '?':
+ return PatternMatch (pattern+1, test+1);
+ case '*':
+ if (PatternMatch (pattern+1, test)) return 1;
+ return PatternMatch (pattern, test+1);
+ case ']':
+ case '}':
+ post("routeOSC: Spurious %c in pattern \".../%s/...\"",pattern[0], theWholePattern);
+ return 0;
+ case '[':
+ return MatchBrackets (pattern,test);
+ case '{':
+ return MatchList (pattern,test);
+ case '\\':
+ if (pattern[1] == 0) return test[0] == 0;
+ if (pattern[1] == test[0]) return PatternMatch (pattern+2,test+1);
+ return 0;
+ default:
+ if (pattern[0] == test[0]) return PatternMatch (pattern+1,test+1);
+ return 0;
+ }
+}
+
+/* we know that pattern[0] == '[' and test[0] != 0 */
+
+static int MatchBrackets (const char *pattern, const char *test)
+{
+ int result;
+ int negated = 0;
+ const char *p = pattern;
+
+ if (pattern[1] == 0)
+ {
+ post("routeOSC: Unterminated [ in pattern \".../%s/...\"", theWholePattern);
+ return 0;
+ }
+ if (pattern[1] == '!')
+ {
+ negated = 1;
+ p++;
+ }
+ while (*p != ']')
+ {
+ if (*p == 0)
+ {
+ post("Unterminated [ in pattern \".../%s/...\"", theWholePattern);
+ return 0;
+ }
+ if (p[1] == '-' && p[2] != 0)
+ {
+ if (test[0] >= p[0] && test[0] <= p[2])
+ {
+ result = !negated;
+ goto advance;
+ }
+ }
+ if (p[0] == test[0])
+ {
+ result = !negated;
+ goto advance;
+ }
+ p++;
+ }
+ result = negated;
+advance:
+ if (!result) return 0;
+ while (*p != ']')
+ {
+ if (*p == 0)
+ {
+ post("Unterminated [ in pattern \".../%s/...\"", theWholePattern);
+ return 0;
+ }
+ p++;
+ }
+ return PatternMatch (p+1,test+1);
+}
+
+static int MatchList (const char *pattern, const char *test)
+{
+ const char *restOfPattern, *tp = test;
+
+ for(restOfPattern = pattern; *restOfPattern != '}'; restOfPattern++)
+ {
+ if (*restOfPattern == 0)
+ {
+ post("Unterminated { in pattern \".../%s/...\"", theWholePattern);
+ return 0;
+ }
+ }
+ restOfPattern++; /* skip close curly brace */
+ pattern++; /* skip open curly brace */
+ while (1)
+ {
+ if (*pattern == ',')
+ {
+ if (PatternMatch (restOfPattern, tp)) return 1;
+ tp = test;
+ ++pattern;
+ }
+ else if (*pattern == '}') return PatternMatch (restOfPattern, tp);
+ else if (*pattern == *tp)
+ {
+ ++pattern;
+ ++tp;
+ }
+ else
+ {
+ tp = test;
+ while (*pattern != ',' && *pattern != '}') pattern++;
+ if (*pattern == ',') pattern++;
+ }
+ }
+}
+
+/* end of routeOSC.c */
diff --git a/osc/unpackOSC.c b/osc/unpackOSC.c
new file mode 100755
index 0000000..acdec07
--- /dev/null
+++ b/osc/unpackOSC.c
@@ -0,0 +1,604 @@
+/* unpackOSC is like dumpOSC but outputs two lists: a list of symbols for the path */
+/* and a list of floats and/or symbols for the data */
+/* This allows for the separation of the protocol and its transport. */
+/* Started by Martin Peach 20060420 */
+/* This version tries to be standalone from LIBOSC MP 20060425 */
+/* MP 20060505 fixed a bug (line 209) where bytes are wrongly interpreted as negative */
+/* dumpOSC.c header follows: */
+/*
+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"
+
+/* declarations */
+
+
+#ifdef _WIN32
+ #ifdef _MSC_VER
+// #include "OSC-common.h"
+ #endif /* _MSC_VER */
+ #include <string.h>
+ #include <stdlib.h>
+ #include <winsock2.h>
+ #include <stdio.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 /* _WIN32 */
+
+/* Declarations */
+#ifdef WIN32
+ typedef unsigned __int64 osc_time_t;
+#else
+ typedef unsigned long long osc_time_t;
+#endif
+
+#define MAX_MESG 65536 // was 32768 MP: make same as MAX_UDP_PACKET
+
+/* ----------------------------- was dumpOSC ------------------------- */
+
+#define MAX_PATH_AT 50 // maximum nuber of elements in OSC path
+
+static t_class *unpackOSC_class;
+
+typedef struct _unpackOSC
+{
+ t_object x_obj;
+ t_outlet *x_data_out;
+ t_atom x_data_at[MAX_MESG];// symbols making up the path + payload
+ int x_data_atc;// number of symbols to be output
+ char x_raw[MAX_MESG];// bytes making up the entire OSC message
+ int x_raw_c;// number of bytes in OSC message
+} t_unpackOSC;
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void unpackOSC_setup(void);
+static void *unpackOSC_new(void);
+static void unpackOSC_free(t_unpackOSC *x);
+void unpackOSC_setup(void);
+static void unpackOSC_list(t_unpackOSC *x, t_symbol *s, int argc, t_atom *argv);
+static int unpackOSC_path(t_unpackOSC *x, char *path);
+static void unpackOSC_Smessage(t_unpackOSC *x, void *v, int n);
+static void unpackOSC_PrintTypeTaggedArgs(t_unpackOSC *x, void *v, int n);
+static void unpackOSC_PrintHeuristicallyTypeGuessedArgs(t_unpackOSC *x, void *v, int n, int skipComma);
+static char *unpackOSC_DataAfterAlignedString(char *string, char *boundary);
+static int unpackOSC_IsNiceString(char *string, char *boundary);
+
+static void *unpackOSC_new(void)
+{
+ t_unpackOSC *x;
+
+ x = (t_unpackOSC *)pd_new(unpackOSC_class);
+ x->x_data_out = outlet_new(&x->x_obj, &s_list);
+ x->x_raw_c = x->x_data_atc = 0;
+ return (x);
+}
+
+static void unpackOSC_free(t_unpackOSC *x)
+{
+}
+
+#ifdef MSW
+__declspec(dllexport)
+#endif
+void unpackOSC_setup(void)
+{
+ unpackOSC_class = class_new(gensym("unpackOSC"),
+ (t_newmethod)unpackOSC_new, (t_method)unpackOSC_free,
+ sizeof(t_unpackOSC), 0, 0);
+ class_addlist(unpackOSC_class, (t_method)unpackOSC_list);
+}
+
+/* unpackOSC_list expects an OSC packet in the form of a list of floats on [0..255] */
+static void unpackOSC_list(t_unpackOSC *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int size, messageLen, i, j;
+ char *messageName, *args, *buf;
+
+ if ((argc%4) != 0)
+ {
+ post("unpackOSC: packet size (%d) not a multiple of 4 bytes: dropping packet", argc);
+ return;
+ }
+ /* copy the list to a byte buffer, checking for bytes only */
+ for (i = 0; i < argc; ++i)
+ {
+ if (argv[i].a_type == A_FLOAT)
+ {
+ j = (int)argv[i].a_w.w_float;
+// if ((j == argv[i].a_w.w_float) && (j >= 0) && (j <= 255))
+// this can miss bytes between 128 and 255 because they are interpreted somewhere as negative
+// , so change to this:
+ if ((j == argv[i].a_w.w_float) && (j >= -128) && (j <= 255))
+ {
+ x->x_raw[i] = (char)j;
+ }
+ else
+ {
+ post("unpackOSC: data out of range (%d), dropping packet", argv[i].a_w.w_float);
+ return;
+ }
+ }
+ else
+ {
+ post("unpackOSC: data not float, dropping packet");
+ return;
+ }
+ }
+ x->x_raw_c = argc;
+ buf = x->x_raw;
+
+ if ((argc >= 8) && (strncmp(buf, "#bundle", 8) == 0))
+ { /* This is a bundle message. */
+#ifdef DEBUG
+ post("unpackOSC: bundle msg: bundles not yet supported\n");
+#endif
+
+ if (argc < 16)
+ {
+ post("unpackOSC: Bundle message too small (%d bytes) for time tag", argc);
+ return;
+ }
+
+ /* Print the time tag */
+#ifdef DEBUG
+ printf("unpackOSC: [ %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 < argc)
+ {
+ size = ntohl(*((int *) (buf + i)));
+ if ((size % 4) != 0)
+ {
+ post("unpackOSC: Bad size count %d in bundle (not a multiple of 4)", size);
+ return;
+ }
+ if ((size + i + 4) > argc)
+ {
+ post("unpackOSC: Bad size count %d in bundle (only %d bytes left in entire bundle)",
+ size, argc-i-4);
+ return;
+ }
+
+ /* Recursively handle element of bundle */
+ unpackOSC_list(x, s, size, &argv[i+4]);
+ i += 4 + size;
+ }
+
+ if (i != argc)
+ {
+ post("unpackOSC: This can't happen");
+ }
+#ifdef DEBUG
+ printf("]\n");
+#endif
+
+ }
+ else if ((argc == 24) && (strcmp(buf, "#time") == 0))
+ {
+ post("unpackOSC: Time message: %s\n :).\n", buf);
+ return;
+ }
+ else
+ { /* This is not a bundle message or a time message */
+
+ messageName = buf;
+ args = unpackOSC_DataAfterAlignedString(messageName, buf+x->x_raw_c);
+ if (args == 0)
+ {
+ post("unpackOSC: Bad message name string: (%s) Dropping entire message.",
+ messageName);
+ return;
+ }
+#ifdef DEBUG
+ post("unpackOSC: message name string: %s", messageName);
+#endif
+ messageLen = args-messageName;
+ /* put the OSC path into a single symbol */
+ x->x_data_atc = unpackOSC_path(x, messageName);
+ if (x->x_data_atc == 1) unpackOSC_Smessage(x, (void *)args, x->x_raw_c-messageLen);
+ }
+ if (x->x_data_atc >= 1) outlet_list(x->x_data_out, &s_list, x->x_data_atc, x->x_data_at);
+ x->x_data_atc = 0;
+}
+
+static int unpackOSC_path(t_unpackOSC *x, char *path)
+{
+ int i;
+
+ if (path[0] != '/')
+ {
+ post("unpackOSC: bad path (%s)", path);
+ return 0;
+ }
+ for (i = 1; i < MAX_MESG; ++i)
+ {
+ if (path[i] == '\0')
+ { /* the end of the path: turn path into a symbol */
+ SETSYMBOL(&x->x_data_at[0],gensym(path));
+ return 1;
+ }
+ }
+ post("unpackOSC: path too long");
+ return 0;
+}
+#define SMALLEST_POSITIVE_FLOAT 0.000001f
+
+static void unpackOSC_Smessage(t_unpackOSC *x, void *v, int n)
+{
+ char *chars = v;
+
+ if (n != 0)
+ {
+ if (chars[0] == ',')
+ {
+ if (chars[1] != ',')
+ {
+ /* This message begins with a type-tag string */
+ unpackOSC_PrintTypeTaggedArgs(x, v, n);
+ }
+ else
+ {
+ /* Double comma means an escaped real comma, not a type string */
+ unpackOSC_PrintHeuristicallyTypeGuessedArgs(x, v, n, 1);
+ }
+ }
+ else
+ {
+ unpackOSC_PrintHeuristicallyTypeGuessedArgs(x, v, n, 0);
+ }
+ }
+}
+
+static void unpackOSC_PrintTypeTaggedArgs(t_unpackOSC *x, void *v, int n)
+{
+ char *typeTags, *thisType, *p;
+ int myargc = x->x_data_atc;
+ t_atom *mya = x->x_data_at;
+
+ typeTags = v;
+
+ if (!unpackOSC_IsNiceString(typeTags, typeTags+n))
+ {
+ /* No null-termination, so maybe it wasn't a type tag
+ string after all */
+ unpackOSC_PrintHeuristicallyTypeGuessedArgs(x, v, n, 0);
+ return;
+ }
+
+ p = unpackOSC_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
+ 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 (!unpackOSC_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++;
+ p = unpackOSC_DataAfterAlignedString(p, typeTags+n);
+
+ }
+ 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);
+ }
+ }
+ x->x_data_atc = myargc;
+}
+
+static void unpackOSC_PrintHeuristicallyTypeGuessedArgs(t_unpackOSC *x, void *v, int n, int skipComma)
+{
+ int i, thisi;
+ int *ints;
+ float thisf;
+ char *chars, *string, *nextString;
+ int myargc= x->x_data_atc;
+ t_atom* mya = x->x_data_at;
+
+
+ /* 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
+ SETFLOAT(mya+myargc,(t_float) (thisi));
+ myargc++;
+ i++;
+ }
+ else if (thisf >= -1000.f && thisf <= 1000000.f &&
+ (thisf <=0.0f || thisf >= SMALLEST_POSITIVE_FLOAT))
+ {
+#ifdef DEBUG
+ printf("%f ", thisf);
+#endif
+ SETFLOAT(mya+myargc,thisf);
+ myargc++;
+ i++;
+ }
+ else if (unpackOSC_IsNiceString(string, chars+n))
+ {
+ nextString = unpackOSC_DataAfterAlignedString(string, chars+n);
+#ifdef DEBUG
+ printf("\"%s\" ", (i == 0 && skipComma) ? string +1 : string);
+#endif
+ SETSYMBOL(mya+myargc,gensym(string));
+ myargc++;
+ i += (nextString-string) / 4;
+ }
+ else
+ {
+ // unhandled .. ;)
+#ifdef DEBUG
+ post("unpackOSC: indeterminate type: 0x%x xx", ints[i]);
+#endif
+ i++;
+ }
+ x->x_data_atc = myargc;
+ }
+}
+
+#define STRING_ALIGN_PAD 4
+
+static char *unpackOSC_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)
+ {
+ post("unpackOSC: DataAfterAlignedString: bad boundary");
+ return 0;
+ }
+
+ for (i = 0; string[i] != '\0'; i++)
+ {
+ if (string + i >= boundary)
+ {
+ post("unpackOSC: 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)
+ {
+ post("unpackOSC: DataAfterAlignedString: Unreasonably long string");
+ return 0;
+ }
+ if (string[i] != '\0')
+ {
+ post("unpackOSC:DataAfterAlignedString: Incorrectly padded string");
+ return 0;
+ }
+ }
+
+ return string+i;
+}
+
+static int unpackOSC_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?
+ Returns 1 if true, else 0. */
+
+ 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])) || (string + i >= boundary)) return 0;
+
+ /* 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 0;
+
+ return 1;
+}
+
+/* end of unpackOSC.c */
diff --git a/sqosc~/sqosc-help.pd b/sqosc~/sqosc-help.pd
new file mode 100755
index 0000000..1a5cf6d
--- /dev/null
+++ b/sqosc~/sqosc-help.pd
@@ -0,0 +1,116 @@
+#N canvas 166 14 572 824 12;
+#X obj 149 229 dac~;
+#X obj 257 179 tabwrite~ wave;
+#X obj 15 319 table wave 512;
+#X msg 257 149 bang;
+#X text 297 148 graph;
+#X floatatom 230 32 5 0 1 0 - - -;
+#X msg 225 -2 0.001;
+#X obj 7 48 osc~ 1;
+#X obj 46 160 *~ 100;
+#X floatatom 7 25 5 0 0 0 - - -;
+#X floatatom 184 53 5 0 0 0 - - -;
+#X floatatom 78 79 5 0 0 0 - - -;
+#X obj 77 47 line;
+#X msg 93 22 0 10000;
+#X msg 81 -13 20000 10000;
+#X obj 7 79 +~ 1;
+#X msg 313 24 0.1;
+#X floatatom 275 67 5 0 0 0 - - -;
+#X obj 359 46 line;
+#X msg 346 -8 0.01 10000;
+#X msg 382 17 0.99 10000;
+#X msg 296 -8 0.5;
+#X obj 379 127 metro 100;
+#X obj 379 96 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 1 1
+;
+#X obj 184 30 mtof;
+#X obj 184 -33 counter 127;
+#X obj 184 -76 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 184 -57 metro 100;
+#X floatatom 251 -78 5 0 0 0 - - -;
+#X msg 250 -102 10000;
+#X floatatom 364 -67 5 0 0 0 - - -;
+#X obj 362 -149 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X obj 361 -90 counter 2;
+#X obj 403 -32 sel 1 2;
+#X obj 362 -118 metro 10000;
+#X text 18 255 creation arguments: frequency pulsewidth bandwidth;
+#X text 17 274 inlets: frequency phase pulsewidth;
+#X text 22 295 outlet: pulse;
+#X obj 24 -91 random 1000;
+#X obj 24 -65 * 0.001;
+#X obj 24 -115 metro 100;
+#X obj 24 -134 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0
+1;
+#X floatatom 91 -136 5 0 0 0 - - -;
+#X msg 91 -161 100;
+#X obj 163 96 sqosc~ 440 0.5 2000;
+#X obj 146 -171 osc~ 1;
+#X obj 147 -148 +~ 1;
+#X obj 146 -125 *~ 100;
+#X floatatom 211 -152 5 0 0 1 fm - -;
+#X floatatom 146 -192 5 0 0 0 - - -;
+#X obj 379 151 spigot;
+#X obj 422 96 tgl 15 0 empty empty sync_graph 20 7 0 8 -262144 -1 -1
+0 1;
+#X msg 379 176 0;
+#X obj 342 71 loadbang;
+#X obj 164 72 +~;
+#X floatatom 431 -162 5 0 0 0 - - -;
+#X connect 3 0 1 0;
+#X connect 5 0 44 1;
+#X connect 6 0 5 0;
+#X connect 7 0 15 0;
+#X connect 9 0 7 0;
+#X connect 10 0 54 1;
+#X connect 11 0 8 1;
+#X connect 12 0 11 0;
+#X connect 13 0 12 0;
+#X connect 14 0 12 0;
+#X connect 15 0 8 0;
+#X connect 16 0 17 0;
+#X connect 17 0 44 2;
+#X connect 18 0 17 0;
+#X connect 19 0 18 0;
+#X connect 20 0 18 0;
+#X connect 21 0 17 0;
+#X connect 22 0 1 0;
+#X connect 22 0 50 0;
+#X connect 23 0 22 0;
+#X connect 24 0 10 0;
+#X connect 25 0 24 0;
+#X connect 26 0 27 0;
+#X connect 27 0 25 0;
+#X connect 28 0 27 1;
+#X connect 29 0 28 0;
+#X connect 30 0 33 0;
+#X connect 31 0 34 0;
+#X connect 32 0 30 0;
+#X connect 33 0 21 0;
+#X connect 33 1 16 0;
+#X connect 34 0 32 0;
+#X connect 38 0 39 0;
+#X connect 39 0 17 0;
+#X connect 40 0 38 0;
+#X connect 41 0 40 0;
+#X connect 42 0 40 1;
+#X connect 43 0 42 0;
+#X connect 44 0 1 0;
+#X connect 44 0 0 1;
+#X connect 44 0 0 0;
+#X connect 45 0 46 0;
+#X connect 46 0 47 0;
+#X connect 47 0 54 0;
+#X connect 48 0 47 1;
+#X connect 49 0 45 0;
+#X connect 50 0 52 0;
+#X connect 51 0 50 1;
+#X connect 52 0 5 0;
+#X connect 53 0 23 0;
+#X connect 53 0 3 0;
+#X connect 53 0 48 0;
+#X connect 54 0 44 0;
+#X connect 55 0 34 1;
diff --git a/sqosc~/sqosc.c b/sqosc~/sqosc.c
new file mode 100755
index 0000000..f832633
--- /dev/null
+++ b/sqosc~/sqosc.c
@@ -0,0 +1,461 @@
+/* sqosc.c Martin Peach 20060613 based on d_osc.c */
+/* 20060707 using x-x^3/3 to smooth the ramp */
+/* Copyright (c) 1997-1999 Miller Puckette.
+* For information on usage and redistribution, and for a DISCLAIMER OF ALL
+* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */
+
+/* sinusoidal oscillator and table lookup; see also tabosc4~ in d_array.c.
+*/
+
+#include "m_pd.h"
+#include "math.h"
+#include <stdio.h> /* for file io */
+
+#define UNITBIT32 1572864. /* 3*2^19; bit 32 has place value 1 */
+
+ /* machine-dependent definitions. These ifdefs really
+ should have been by CPU type and not by operating system! */
+#ifdef IRIX
+ /* big-endian. Most significant byte is at low address in memory */
+#define HIOFFSET 0 /* word offset to find MSB */
+#define LOWOFFSET 1 /* word offset to find LSB */
+#define int32 long /* a data type that has 32 bits */
+#endif /* IRIX */
+
+#ifdef MSW
+ /* little-endian; most significant byte is at highest address */
+#define HIOFFSET 1
+#define LOWOFFSET 0
+#define int32 long
+#endif
+
+#if defined(__FreeBSD__) || defined(__APPLE__)
+#include <machine/endian.h>
+#endif
+
+#ifdef __APPLE__
+#define __BYTE_ORDER BYTE_ORDER
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#endif
+
+#ifdef __linux__
+#include <endian.h>
+#endif
+
+#if defined(__unix__) || defined(__APPLE__)
+#if !defined(__BYTE_ORDER) || !defined(__LITTLE_ENDIAN)
+#error No byte order defined
+#endif
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define HIOFFSET 1
+#define LOWOFFSET 0
+#else
+#define HIOFFSET 0 /* word offset to find MSB */
+#define LOWOFFSET 1 /* word offset to find LSB */
+#endif /* __BYTE_ORDER */
+#include <sys/types.h>
+#define int32 int32_t
+#endif /* __unix__ or __APPLE__*/
+
+union tabfudge
+{
+ double tf_d;
+ int32 tf_i[2];
+};
+
+static t_class *sqosc_class, *scalarsqosc_class;
+
+static float *sqosc_table;
+/* COSTABSIZE is 512 in m_pd.h, we start with that... */
+#define LOGSQOSCTABSIZE 9
+#define SQOSCTABSIZE 512
+#define HALFSQOSCTABSIZE 256
+
+typedef struct _sqosc
+{
+ t_object x_obj;
+ double x_phase;
+ float x_conv;
+ float x_f; /* frequency if scalar */
+ float x_pw; /* pulse width 0-1, default 0.5 */
+ float x_bw; /* bandwidth */
+ float x_slew; /* slew time in samples */
+ double x_dpw; /* pulse width in this pulse */
+ int x_pulse_ended; /* nonzero if pulse has finished */
+// FILE *x_logfp;
+// int x_logcount;
+} t_sqosc;
+
+static void sqosc_maketable(void);
+void sqosc_tilde_setup(void);
+static void sqosc_ft1(t_sqosc *x, t_float f);
+static void sqosc_pw(t_sqosc *x, t_float pw);
+static void sqosc_dsp(t_sqosc *x, t_signal **sp);
+static t_int *sqosc_perform(t_int *w);
+static void *sqosc_new(t_floatarg f, t_floatarg pw, t_float bw);
+
+static void sqosc_maketable(void)
+{
+ int i;
+ float *fp, phase, phsinc = (2. * 3.14159) / SQOSCTABSIZE;
+ union tabfudge tf;
+ //FILE *cosfp;
+
+ if (sqosc_table) return;
+ //cosfp = fopen("sqosctable.txt", "wb");
+ sqosc_table = (float *)getbytes(sizeof(float) * (SQOSCTABSIZE+1));
+ for (i = SQOSCTABSIZE + 1, fp = sqosc_table, phase = 0; i--;
+ fp++, phase += phsinc)
+ {
+ *fp = cos(phase);
+ //fprintf(cosfp, "%f: %f\n", phase, *fp);
+ }
+ //fclose(cosfp);
+ /* here we check at startup whether the byte alignment
+ is as we declared it. If not, the code has to be
+ recompiled the other way. */
+ tf.tf_d = UNITBIT32 + 0.5;
+ if ((unsigned)tf.tf_i[LOWOFFSET] != 0x80000000)
+ bug("cos~: unexpected machine alignment");
+}
+
+static void *sqosc_new(t_floatarg f, t_floatarg pw, t_floatarg bw)
+{
+ t_sqosc *x = (t_sqosc *)pd_new(sqosc_class);
+
+ x->x_f = f; /* the initial frequency in Hz */
+ outlet_new(&x->x_obj, gensym("signal"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("pw"));
+ post("sqosc_new frequency %f, pulsewidth %f bandwidth %f", f, pw, bw);
+ x->x_phase = 0;
+ x->x_conv = 0;
+ if ((pw <= 0)||(pw >= 1))
+ {
+ post("sqosc: second argument (pulse width) must be greater than 0 and less than 1, using 0.5");
+ x->x_pw = 0.5 * SQOSCTABSIZE;
+ }
+ else x->x_pw = pw * SQOSCTABSIZE;
+ if (bw < 0)
+ {
+ post("sqosc: third argument (bandwidth) must be greater than 0, using 10000");
+ x->x_bw = 10000;
+ }
+ else x->x_bw = bw;
+ x->x_slew = HALFSQOSCTABSIZE/x->x_bw;/* slew = time of half bandwidth cycle */
+ x->x_dpw = x->x_pw; /* pulse width in this pulse */
+ x->x_pulse_ended = 1; /* nonzero if pulse has finished */
+// x->x_logfp = fopen("sqosclog.txt", "wb");
+// x->x_logcount = 0;
+ return (x);
+}
+
+/*
+This is corrected from Chun Lee's explanation in an email at:
+http://music.columbia.edu/pipermail/music-dsp/2004-November/028814.html
+
+1-> a double variable UNITBIT32 is assigned to 1572864. This is what it
+looks like in memory in a little endian machine:
+byte 7 byte 6 byte 5 byte 4 byte 3 byte 2 byte 1 byte 0
+00000000 00000000 00000000 00000000 00000000 00000000 0001 1100 1000001 0
+|<------fraction------------------>|<----1572864--------->|exponent |sign|
+
+The reason for 1572864 (1.5 X 2^20) is that its representation in IEEE754 format (double)
+has 51 zeros to the right of a placeholding 1 bit, which can then be used as
+a wide fixed-point register with the binary point at bit 32, so the lower 32 bits
+can be used as a fractional accumulator with 32-bit precision.
+Adding 1 to INITBIT32 adds a 1 at bit 32, with no change in exponent,
+so the lower 32 bits act as a fraction of one. The 31 zero bits in the integer part
+can be set as high as 2147483647 (31 1s) without affecting the precision.
+
+The upper 32 bits are 1094189056, or 01000001 00111000 00000000 00000000 in binary.
+The sign bit is zero, or positive. The upper bit of the exponent is one, meaning
+the number is normalized, or full precision.The exponent is 1043 minus the bias
+of 1023, or 20.
+
+This is the representation of the number 1.5 X 2^20 = 1572864:
+
+IEEE Standard 754 Double Precision Storage Format (double):
+63 62 52 51 32 31 0
++--------+----------------+-------------------+------------------------------+
+| s 1bit | e[62:52] 11bit | f[51:32] 20bit | f[31:0] 32bit |
++--------+----------------+-------------------+------------------------------+
+B0-------------->B1---------->B2----->B3----->B4----->B5----->B6----->B7----->
+0 10000010011 1000000000000000000000000000000000000000000000000000
+
+2-> there is the tabfudge like:
+
+Union tabfudge
+{
+ double tf_d;
+ int32 tf_i[2];
+}
+
+In dsp_perfrom:
+
+Union tabfudge tf;
+
+tf.tf_d = UNITBIT32;
+
+3-> the phase is accumulated into tf_d plus the UNITBIT32
+
+Double dphase = x_phase + UNITBIT32;
+
+4-> byte 0 to byte 3 is stored as a separate variable as:
+
+int normhipart = tf.tf_I[HIOFFSET]; //where HIOFFSET is used to find MSB
+
+5-> in the actual dsp block, although the phase is accumulated into
+tf.tf_d, bytes 0 to 3 of tf.tf_d are constantly forced to be a
+constant by:
+
+tf.tf_i[HIOFFSET] = normhipart;
+
+6-> because of this, to get the fraction part out of the tf.tf_d, simply do:
+
+tf.tf_d - UNITBIT32;
+
+7-> mission accomplished!!!!!
+
+*/
+
+static t_int *sqosc_perform(t_int *w)
+{
+ t_sqosc *x = (t_sqosc *)(w[1]);
+ t_float *in = (t_float *)(w[2]);
+ t_float *out = (t_float *)(w[3]);
+ t_float sample;
+ int n = (int)(w[4]);
+ float *tab = sqosc_table, *addr, f1, f2, frac;
+ int index;
+ double dphase = x->x_phase + UNITBIT32;
+ int normhipart;
+ union tabfudge tf;
+ float conv = x->x_conv;
+ double lastin, findex, slewindex;
+ static double twothirds = 2.0/3.0;
+ static double onethird = 1.0/3.0;
+
+ tf.tf_d = UNITBIT32; /* set the phase accumulator to (1.5 X 2^20) making it effectively fixed-point */
+ normhipart = tf.tf_i[HIOFFSET]; /* save the sign, exponent, and integer part of a fixed accumulator */
+ tf.tf_d = dphase; /* the current phase plus the "frame" */
+ lastin = *in++; /* latest frequency */
+ if (lastin < 0) lastin = -lastin;/* negative frequency is the same as positive here */
+ if (lastin > x->x_bw) lastin = x->x_bw;// limit frequency to bandwidth
+ slewindex = x->x_slew*lastin;
+ dphase += lastin * conv; /* new phase is old phase + (frequency * table period) */
+ //addr = tab + (tf.tf_i[HIOFFSET] & (SQOSCTABSIZE-1)); /* point to the current sample in the table */
+ index = tf.tf_i[HIOFFSET] & (SQOSCTABSIZE-1);
+ tf.tf_i[HIOFFSET] = normhipart; /* zero the non-fractional part of the phase */
+ frac = tf.tf_d - UNITBIT32; /* extract the fractional part of the phase */
+ while (--n)
+ {
+ tf.tf_d = dphase;
+ //f1 = addr[0]; /* first sample */
+ if (index <= slewindex)
+ { /* rising phase */
+ if(x->x_pulse_ended)
+ {/* set pw for this pulse once only*/
+ if(x->x_pw < slewindex)x->x_dpw = slewindex;
+ else if (x->x_pw > SQOSCTABSIZE-slewindex)x->x_dpw = SQOSCTABSIZE-slewindex;
+ else x->x_dpw = x->x_pw;
+ x->x_pulse_ended = 0;
+ }
+ //findex = (index/(x->x_slew*lastin))*HALFSQOSCTABSIZE;
+ //addr = tab + HALFSQOSCTABSIZE + (int)findex;
+ f1 = 1.0-2.0*(slewindex-index)/slewindex;// a ramp from -1 to +1 // addr[0];
+ f1 = f1 - pow(f1, 3.0)*onethird;// smooth the ramp
+// if (x->x_logcount < 1000)
+// {
+// fprintf(x->x_logfp, "rise index %d slewindex %f f1 %f frac %f\n", index, slewindex, f1, frac);
+// ++x->x_logcount;
+// if (x->x_logcount >= 1000) fclose(x->x_logfp);
+// }
+ }
+ else if (index < x->x_dpw) f1 = twothirds; /* risen */
+ else if (index <= slewindex+x->x_dpw)
+ { /* falling phase */
+// findex = ((index-HALFSQOSCTABSIZE)/(x->x_slew*lastin))*HALFSQOSCTABSIZE;
+// addr = tab + (int)findex;
+ f1 = -1.0+2.0*(slewindex-index+x->x_dpw)/slewindex;// a ramp from +1 to -1 // addr[0];
+ f1 = f1 - pow(f1, 3.0)*onethird;// smooth the ramp
+ x->x_pulse_ended = 1;
+// if (x->x_logcount < 1000)
+// {
+// fprintf(x->x_logfp, "fall index %d slewindex %f f1 %f frac %f\n", index, slewindex, f1, frac);
+// ++x->x_logcount;
+// if (x->x_logcount >= 1000) fclose(x->x_logfp);
+// }
+ }
+ else
+ { /* fallen */
+ f1 = -twothirds;
+ }
+ lastin = *in++;
+ if (lastin < 0) lastin = -lastin;/* negative frequency is the same as positive here */
+ if (lastin > x->x_bw) lastin = x->x_bw;// limit frequency to bandwidth
+ slewindex = x->x_slew*lastin;
+ dphase += lastin * conv; /* next phase */
+ //f2 = addr[1]; /* second sample */
+ if (index+1 <= slewindex)
+ {
+ f2 = 1.0-2.0*(slewindex-index-1)/slewindex;// addr[1];
+ f2 = f2 - pow(f2, 3.0)*onethird;
+// if (x->x_logcount < 1000)
+// {
+// fprintf(x->x_logfp, "rise index %d slewindex %f f2 %f frac %f\n", index+1, slewindex, f2, frac);
+// ++x->x_logcount;
+// if (x->x_logcount >= 1000) fclose(x->x_logfp);
+// }
+ }
+ else if (index+1 < x->x_dpw) f2 = twothirds;
+ else if (index+1 <= slewindex+x->x_dpw)
+ {
+ f2 = -1.0+2.0*(slewindex-index-1+x->x_dpw)/slewindex;// addr[1];
+ f2 = f2 - pow(f2, 3.0)*onethird;
+// if (x->x_logcount < 1000)
+// {
+// fprintf(x->x_logfp, "fall index %d slewindex %f f2 %f frac %f\n", index+1, slewindex, f2, frac);
+// ++x->x_logcount;
+// if (x->x_logcount >= 1000) fclose(x->x_logfp);
+// }
+ }
+ else f2 = -twothirds;
+
+ sample = f1 + frac * (f2 - f1); /* output first sample plus fraction of second sample (linear interpolation) */
+ *out++ = sample;
+// if (x->x_logcount < 1000)
+// {
+// fprintf(x->x_logfp, "index %ld f1 %f f2 %f frac %f out %f\n", index, f1, f2, frac, sample);
+// ++x->x_logcount;
+// if (x->x_logcount >= 1000) fclose(x->x_logfp);
+// }
+ //addr = tab + (tf.tf_i[HIOFFSET] & (SQOSCTABSIZE-1)); /* point to the next sample */
+ index = tf.tf_i[HIOFFSET] & (SQOSCTABSIZE-1);
+ tf.tf_i[HIOFFSET] = normhipart; /* zero the non-fractional part */
+
+ frac = tf.tf_d - UNITBIT32; /* get next fractional part */
+ }
+ //f1 = addr[0];
+ if (index <= slewindex)
+ {
+ if(x->x_pulse_ended)
+ {/* set pw for this pulse once only*/
+ if(x->x_pw < slewindex)x->x_dpw = slewindex;
+ else if (x->x_pw > SQOSCTABSIZE-slewindex)x->x_dpw = SQOSCTABSIZE-slewindex;
+ else x->x_dpw = x->x_pw;
+ x->x_pulse_ended = 0;
+ }
+ //findex = (index/(x->x_slew*lastin))*HALFSQOSCTABSIZE;
+ //addr = tab + HALFSQOSCTABSIZE + (int)findex;
+ f1 = 1.0-2.0*(slewindex-index)/slewindex;// addr[0];
+ f1 = f1 - pow(f1, 3.0)*onethird;
+// if (x->x_logcount < 1000)
+// {
+// fprintf(x->x_logfp, "rise2 index %d slewindex %f f1 %f frac %f\n", index, slewindex, f1, frac);
+// ++x->x_logcount;
+// if (x->x_logcount >= 1000) fclose(x->x_logfp);
+// }
+ }
+ else if (index < x->x_dpw) f1 = twothirds; /* risen */
+ else if (index <= slewindex+x->x_dpw)
+ { /* falling phase */
+// findex = ((index-HALFSQOSCTABSIZE)/(x->x_slew*lastin))*HALFSQOSCTABSIZE;
+// addr = tab + (int)findex;
+ f1 = -1.0+2.0*(slewindex-index+x->x_dpw)/slewindex;// addr[0];
+ f1 = f1 - pow(f1, 3.0)*onethird;
+ x->x_pulse_ended = 1;
+// if (x->x_logcount < 1000)
+// {
+// fprintf(x->x_logfp, "fall2 index %d slewindex %f f1 %f frac %f\n", index, slewindex, f1, frac);
+// ++x->x_logcount;
+/// if (x->x_logcount >= 1000) fclose(x->x_logfp);
+// }
+ }
+ else
+ { /* fallen */
+ f1 = -twothirds;
+ }
+ //f2 = addr[1]; /* second sample */
+ if (index+1 <= slewindex)
+ {
+ f2 = 1.0-2.0*(slewindex-index-1)/slewindex;// addr[1];
+ f2 = f2 - pow(f2, 3.0)*onethird;
+// if (x->x_logcount < 1000)
+// {
+// fprintf(x->x_logfp, "rise2 index %d slewindex %f f2 %f frac %f\n", index+1, slewindex, f2, frac);
+// ++x->x_logcount;
+// if (x->x_logcount >= 1000) fclose(x->x_logfp);
+// }
+ }
+ else if (index+1 < x->x_dpw) f2 = twothirds;
+ else if (index+1 <= slewindex+x->x_dpw)
+ {
+ f2 = -1.0+2.0*(slewindex-index-1+x->x_dpw)/slewindex;// addr[1];
+ f2 = f2 - pow(f2, 3.0)*onethird;
+// if (x->x_logcount < 1000)
+// {
+// fprintf(x->x_logfp, "fall2 index %d slewindex %f f2 %f frac %f\n", index+1, slewindex, f2, frac);
+// ++x->x_logcount;
+// if (x->x_logcount >= 1000) fclose(x->x_logfp);
+// }
+ }
+ else f2 = -twothirds;
+ sample = f1 + frac * (f2 - f1); /* the final sample */
+ *out++ = sample;
+// if (x->x_logcount < 1000)
+// {
+// fprintf(x->x_logfp, "*index %ld f1 %f f2 %f frac %f out %f\n", index, f1, f2, frac, sample);
+// ++x->x_logcount;
+// if (x->x_logcount >= 1000) fclose(x->x_logfp);
+// }
+
+ tf.tf_d = UNITBIT32 * SQOSCTABSIZE; /* this just changes the exponent if the table size is a power of 2 */
+ normhipart = tf.tf_i[HIOFFSET]; /* ...so we get more integer digits but fewer fractional ones */
+ tf.tf_d = dphase + (UNITBIT32 * SQOSCTABSIZE - UNITBIT32); /* subtract one UNITBIT32 we added at the beginning */
+ tf.tf_i[HIOFFSET] = normhipart; /* wrap any overflow to the table size */
+ x->x_phase = tf.tf_d - UNITBIT32 * SQOSCTABSIZE; /* extract just the phase */
+ return (w+5);
+}
+static void sqosc_dsp(t_sqosc *x, t_signal **sp)
+{
+// static int once = 0;
+
+ x->x_conv = SQOSCTABSIZE/sp[0]->s_sr;
+/* conv = table period = (samples/cycle)/(samples/sec) = sec/cycle = 0.011610sec for 512/44100 */
+
+// if (once == 0)
+// {
+// ++once;
+// post ("x->x_slew = %f, x->x_bw = %f, sp[0]->s_sr = %f", x->x_slew, x->x_bw, sp[0]->s_sr);
+// }
+ dsp_add(sqosc_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
+}
+
+static void sqosc_ft1(t_sqosc *x, t_float f)
+{
+ x->x_phase = SQOSCTABSIZE * f;
+}
+
+static void sqosc_pw(t_sqosc *x, t_float pw)
+{
+ if ((pw <= 0)||(pw >= 1)) return;
+ //post("sqosc: pulse width must be greater than 0 and less than 1");// this is an annoying message...
+ x->x_pw = pw * SQOSCTABSIZE;
+}
+
+void sqosc_tilde_setup(void)
+{
+ sqosc_class = class_new(gensym("sqosc~"), (t_newmethod)sqosc_new, 0,
+ sizeof(t_sqosc), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0);
+ CLASS_MAINSIGNALIN(sqosc_class, t_sqosc, x_f);/* x_f is used when no signal is input */
+ class_addmethod(sqosc_class, (t_method)sqosc_dsp, gensym("dsp"), 0);
+ class_addmethod(sqosc_class, (t_method)sqosc_ft1, gensym("ft1"), A_FLOAT, 0);
+ class_addmethod(sqosc_class, (t_method)sqosc_pw, gensym("pw"), A_FLOAT, 0);
+
+ sqosc_maketable(); /* make the same table as cos_table */
+}
+
+
+/* end of sqosc.c */