aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIOhannes m zmölnig <zmoelnig@users.sourceforge.net>2010-03-23 11:54:21 +0000
committerIOhannes m zmölnig <zmoelnig@users.sourceforge.net>2010-03-23 11:54:21 +0000
commit010723cb02b26c608537fc537d5e963ff6402f9e (patch)
tree6cddfb30ce4d78777d45fabcda928cc1d88af458
forked mrpeach's "net" svn2git-root
svn path=/trunk/externals/iem/iemnet/; revision=13240
-rw-r--r--tcpclient-help.pd108
-rw-r--r--tcpclient.c552
-rw-r--r--tcpreceive-help.pd22
-rw-r--r--tcpreceive.c333
-rw-r--r--tcpsend-help.pd31
-rw-r--r--tcpsend.c254
-rw-r--r--tcpserver-help.pd181
-rw-r--r--tcpserver.c1065
-rw-r--r--test.txt1
-rw-r--r--udpreceive-help.pd19
-rw-r--r--udpreceive.c160
-rw-r--r--udpreceive~.c805
-rw-r--r--udpsend-help.pd31
-rw-r--r--udpsend.c257
-rw-r--r--udpsend~-help.pd173
-rw-r--r--udpsend~.c701
-rw-r--r--udpsend~.h102
17 files changed, 4795 insertions, 0 deletions
diff --git a/tcpclient-help.pd b/tcpclient-help.pd
new file mode 100644
index 0000000..1803d01
--- /dev/null
+++ b/tcpclient-help.pd
@@ -0,0 +1,108 @@
+#N canvas 184 70 1096 649 12;
+#X msg -112 56 disconnect;
+#X obj 219 439 unpack 0 0 0 0;
+#X floatatom 219 462 3 0 0 0 - - -;
+#X floatatom 250 462 3 0 0 0 - - -;
+#X floatatom 282 462 3 0 0 0 - - -;
+#X floatatom 314 462 3 0 0 0 - - -;
+#X text 179 461 from;
+#X msg -175 -7 connect 132.205.142.12 80;
+#X obj 199 387 tcpclient;
+#X obj 239 413 tgl 15 0 empty empty connected 18 7 0 8 -24198 -241291
+-1 0 1;
+#X msg -88 80 dump \$1;
+#X obj -140 65 tgl 15 0 empty empty empty 0 -6 0 8 -4034 -257985 -1
+1 1;
+#X msg 11 179 receive;
+#X msg 35 203 recv;
+#X text -59 -58 connect with an IP address and port number;
+#X msg -200 -32 connect www.concordia.ca 80;
+#X text -17 79 print received messages to main window in hexdump format
+;
+#X text 270 386 tcpclient opens a tcp socket to send and receive bytes
+on;
+#X text -217 305 See also:;
+#X obj -212 329 netclient;
+#X msg -225 -57 connect 127.0.0.1 9997;
+#X obj -212 352 tcpreceive;
+#X text -214 374 can receive messages from tcpclient;
+#X text -136 328 is what tcpclient is based on;
+#X text 250 513 Received messages are output as a list of bytes;
+#X text 77 176 get any received data (not useful unless you need it
+faster than once per 20ms);
+#X text 220 225 semicolon-terminated string for netserver or netreceive
+;
+#X msg 59 227 send 49 127 128 51 59;
+#X obj -84 352 tcpserver;
+#X text -118 351 and;
+#X text 347 -55 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 msg -63 105 send ../doc/5.reference/test.txt;
+#X obj -15 129 openpanel;
+#X msg -15 153 send \$1;
+#X obj -101 114 bng 15 250 50 0 empty empty empty 17 7 0 10 -24198
+-241291 -1;
+#X text 172 105 send a file;
+#X text 62 128 ...any file;
+#X msg 99 251 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 529 257 'send' prefix is optional;
+#X obj 199 514 spigot;
+#X obj 238 491 tgl 15 0 empty empty enable_print 18 7 0 8 -24198 -241291
+-1 0 1;
+#X obj 199 543 print >>>;
+#X text 272 24 GET http:///index.phpCRLF;
+#X floatatom 374 433 9 0 0 0 - - -;
+#X text 173 291 set send-buffer size;
+#X obj 374 407 route sent buf;
+#X floatatom 421 457 9 0 0 0 - - -;
+#X text 491 456 Size of the send buffer;
+#X text 448 432 Number of bytes sent (may still be in buffer);
+#X msg 147 315 buf;
+#X text 177 314 get send-buffer size;
+#X msg 123 291 buf 10;
+#X msg 170 338 timeout 100;
+#X text 258 337 set send timeout in microseconds (default is 1000)
+;
+#X msg 194 362 verbosity \$1;
+#X obj 139 347 tgl 15 1 empty empty empty 0 -6 0 8 -4034 -257985 -1
+0 1;
+#X text 289 362 print connection status messages to main window (default)
+;
+#X msg -136 16 send 71 69 84 32 104 116 116 112 58 47 47 47 105 110
+100 101 120 46 112 104 112 13 10 13 10;
+#X text -208 540 2010/03/01 Martin Peach;
+#X text 271 542 Attempting to print long messages can hang Pd!;
+#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 39 0;
+#X connect 8 1 1 0;
+#X connect 8 2 9 0;
+#X connect 8 3 45 0;
+#X connect 10 0 8 0;
+#X connect 11 0 10 0;
+#X connect 12 0 8 0;
+#X connect 13 0 8 0;
+#X connect 15 0 8 0;
+#X connect 20 0 8 0;
+#X connect 27 0 8 0;
+#X connect 31 0 8 0;
+#X connect 32 0 33 0;
+#X connect 33 0 8 0;
+#X connect 34 0 32 0;
+#X connect 37 0 8 0;
+#X connect 39 0 41 0;
+#X connect 40 0 39 1;
+#X connect 45 0 43 0;
+#X connect 45 1 46 0;
+#X connect 49 0 8 0;
+#X connect 51 0 8 0;
+#X connect 52 0 8 0;
+#X connect 54 0 8 0;
+#X connect 55 0 54 0;
+#X connect 57 0 8 0;
diff --git a/tcpclient.c b/tcpclient.c
new file mode 100644
index 0000000..93fbb91
--- /dev/null
+++ b/tcpclient.c
@@ -0,0 +1,552 @@
+/* tcpclient.c Martin Peach 20060508, working version 20060512 */
+/* linux version 20060515 */
+/* tcpclient.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>
+#include <ws2tcpip.h> /* for socklen_t */
+#endif
+
+#ifdef _MSC_VER
+#define snprintf sprintf_s
+#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_connectout;
+ t_outlet *x_statusout;
+ int x_dump; // 1 = hexdump received bytes
+ int x_verbosity; // 1 = post connection state changes to main window
+ int x_fd; // the socket
+ int x_fdbuf; // the socket's buffer size
+ t_int x_timeout_us; /* send timeout in microseconds */
+ 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_timeout(t_tcpclient *x, t_float timeout);
+static void tcpclient_verbosity(t_tcpclient *x, t_float verbosity);
+static void tcpclient_dump(t_tcpclient *x, t_float dump);
+static void tcp_client_hexdump(t_tcpclient *x, 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);
+int tcpclient_send_byte(t_tcpclient *x, char byte);
+static int tcpclient_get_socket_send_buf_size(t_tcpclient *x);
+static int tcpclient_set_socket_send_buf_size(t_tcpclient *x, int size);
+static void tcpclient_buf_size(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);
+void tcpclient_setup(void);
+
+static void tcpclient_timeout(t_tcpclient *x, t_float timeout)
+{
+ /* set the timeout on the select call in tcpclient_send_byte */
+ /* this is the maximum time in microseconds to wait */
+ /* before abandoning attempt to send */
+
+ t_int timeout_us = 0;
+ if ((timeout >= 0)&&(timeout < 1000000))
+ {
+ timeout_us = (t_int)timeout;
+ x->x_timeout_us = timeout_us;
+ }
+}
+
+static void tcpclient_dump(t_tcpclient *x, t_float dump)
+{
+ x->x_dump = (dump == 0)?0:1;
+}
+
+static void tcpclient_verbosity(t_tcpclient *x, t_float verbosity)
+{
+ x->x_verbosity = (verbosity == 0)?0:1; /* only two states so far */
+}
+
+static void tcp_client_hexdump(t_tcpclient *x, 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;
+ unsigned char *buf = x->x_msginbuf;
+
+ if (x->x_verbosity) post("%s_hexdump %d:", objName, len);
+ while (k < len)
+ {
+ for (i = j = 0; i < BYTES_PER_LINE; ++i, ++k, j+=3)
+ {
+ if (k < len)
+ {
+ snprintf(&hexStr[j], 4, "%02X ", buf[k]);
+ snprintf(&ascStr[i], 2, "%c", ((buf[k] >= 32) && (buf[k] <= 126))? buf[k]: '.');
+ }
+ else
+ { // the last line
+ snprintf(&hexStr[j], 4, " ");
+ snprintf(&ascStr[i], 2, " ");
+ }
+ }
+ post ("%s%s", hexStr, ascStr);
+ }
+}
+
+static void tcpclient_tick(t_tcpclient *x)
+{
+ outlet_float(x->x_connectout, 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);
+
+ if (x->x_verbosity) 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_connectout, 0);
+ if (x->x_verbosity) 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)
+{
+//#define BYTE_BUF_LEN 65536 // arbitrary maximum similar to max IP packet size
+// static char byte_buf[BYTE_BUF_LEN];
+ int i, j, d;
+ unsigned char c;
+ float f, e;
+ char *bp;
+ int length;
+ size_t sent;
+ int result;
+ char fpath[FILENAME_MAX];
+ FILE *fptr;
+ t_atom output_atom;
+
+#ifdef DEBUG
+ post("s: %s", s->s_name);
+ post("argc: %d", argc);
+#endif
+
+ if (x->x_fd < 0)
+ {
+ error("%s: not connected", objName);
+ return;
+ }
+ for (i = j = 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("%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;
+ if (0 == tcpclient_send_byte(x, c)) break;
+ ++j;
+ }
+ else if (argv[i].a_type == A_SYMBOL)
+ {
+
+ atom_string(&argv[i], fpath, FILENAME_MAX);
+ fptr = fopen(fpath, "rb");
+ if (fptr == NULL)
+ {
+ post("%s_send: unable to open \"%s\"", objName, fpath);
+ return;
+ }
+ rewind(fptr);
+ while ((d = fgetc(fptr)) != EOF)
+ {
+ c = (char)(d & 0x0FF);
+ if (0 == tcpclient_send_byte(x, c)) break;
+ ++j;
+ }
+ fclose(fptr);
+ fptr = NULL;
+ if (x->x_verbosity) post("%s_send: read \"%s\" length %d byte%s", objName, fpath, j, ((d==1)?"":"s"));
+ }
+ else
+ {
+ error("%s_send: item %d is not a float or a file name", objName, i);
+ return;
+ }
+ }
+ sent = j;
+ SETFLOAT(&output_atom, sent);
+ outlet_anything( x->x_statusout, gensym("sent"), 1, &output_atom);
+}
+
+int tcpclient_send_byte(t_tcpclient *x, char byte)
+{
+ int result = 0;
+ fd_set wfds;
+ struct timeval timeout;
+
+ FD_ZERO(&wfds);
+ FD_SET(x->x_fd, &wfds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = x->x_timeout_us; /* give it about a millisecond to clear buffer */
+ result = select(x->x_fd+1, NULL, &wfds, NULL, &timeout);
+ if (result == -1)
+ {
+ post("%s_send_byte: select returned error %d", objName, errno);
+ return 0;
+ }
+ if (FD_ISSET(x->x_fd, &wfds))
+ {
+ result = send(x->x_fd, &byte, 1, 0);
+ if (result <= 0)
+ {
+ sys_sockerror("tcpclient: send");
+ post("%s_send_byte: could not send data ", objName);
+ return 0;
+ }
+ }
+ return result;
+}
+
+/* Return the send buffer size of socket, also output it on status outlet */
+static int tcpclient_get_socket_send_buf_size(t_tcpclient *x)
+{
+ int optVal = 0;
+ socklen_t optLen = sizeof(int);
+ t_atom output_atom;
+#ifdef _WIN32
+ if (getsockopt(x->x_fd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optLen) == SOCKET_ERROR)
+ post("%_get_socket_send_buf_size: getsockopt returned %d\n", objName, WSAGetLastError());
+#else
+ if (getsockopt(x->x_fd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optLen) == -1)
+ post("%_get_socket_send_buf_size: getsockopt returned %d\n", objName, errno);
+#endif
+ SETFLOAT(&output_atom, optVal);
+ outlet_anything( x->x_statusout, gensym("buf"), 1, &output_atom);
+ return optVal;
+}
+
+/* Set the send buffer size of socket, returns actual size */
+static int tcpclient_set_socket_send_buf_size(t_tcpclient *x, int size)
+{
+ int optVal = size;
+ int optLen = sizeof(int);
+#ifdef _WIN32
+ if (setsockopt(x->x_fd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, optLen) == SOCKET_ERROR)
+ {
+ post("%s_set_socket_send_buf_size: setsockopt returned %d\n", objName, WSAGetLastError());
+#else
+ if (setsockopt(x->x_fd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, optLen) == -1)
+ {
+ post("%s_set_socket_send_buf_size: setsockopt returned %d\n", objName, errno);
+#endif
+ return 0;
+ }
+ else return (tcpclient_get_socket_send_buf_size(x));
+}
+
+/* Get/set the send buffer size of client socket */
+static void tcpclient_buf_size(t_tcpclient *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int client = -1;
+ float buf_size = 0;
+ t_atom output_atom[3];
+
+ if(x->x_connectstate == 0)
+ {
+ post("%s_buf_size: no clients connected", objName);
+ return;
+ }
+ /* get size of buffer (first element in list) */
+ if (argc > 0)
+ {
+ if (argv[0].a_type != A_FLOAT)
+ {
+ post("%s_buf_size: specify buffer size with a float", objName);
+ return;
+ }
+ buf_size = atom_getfloatarg(0, argc, argv);
+ x->x_fdbuf = tcpclient_set_socket_send_buf_size(x, (int)buf_size);
+ if (x->x_verbosity) post("%s_buf_size: set to %d", objName, x->x_fdbuf);
+ return;
+ }
+ x->x_fdbuf = tcpclient_get_socket_send_buf_size(x);
+ return;
+}
+
+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, 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
+ {
+ if (x->x_verbosity) 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_connectout = outlet_new(&x->x_obj, &s_float); /* connection state */
+ x->x_statusout = outlet_new(&x->x_obj, &s_anything);/* last outlet for everything else */
+ x->x_clock = clock_new(x, (t_method)tcpclient_tick);
+ x->x_poll = clock_new(x, (t_method)tcpclient_poll);
+ x->x_verbosity = 1; /* default post status changes to main window */
+ 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;
+ x->x_timeout_us = 1000; /* set a default 1ms send timeout */
+ /* 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);
+}
+
+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_buf_size, gensym("buf"), 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_verbosity, gensym("verbosity"), A_FLOAT, 0);
+ class_addmethod(tcpclient_class, (t_method)tcpclient_dump, gensym("dump"), A_FLOAT, 0);
+ class_addmethod(tcpclient_class, (t_method)tcpclient_timeout, gensym("timeout"), A_FLOAT, 0);
+ class_addlist(tcpclient_class, (t_method)tcpclient_send);
+}
+
+/* end of tcpclient.c */
diff --git a/tcpreceive-help.pd b/tcpreceive-help.pd
new file mode 100644
index 0000000..8cfbbe4
--- /dev/null
+++ b/tcpreceive-help.pd
@@ -0,0 +1,22 @@
+#N canvas 713 11 478 294 12;
+#X floatatom 206 144 3 0 0 0 - - -;
+#X floatatom 233 144 3 0 0 0 - - -;
+#X floatatom 260 144 3 0 0 0 - - -;
+#X floatatom 287 144 3 0 0 0 - - -;
+#X text 163 143 from;
+#X obj 155 185 print message;
+#X obj 155 57 tcpreceive 9997;
+#X floatatom 257 96 5 0 0 0 - - -;
+#X text 303 94 connections;
+#X text 32 16 tcpreceive receives bytes over a tcp connection.;
+#X floatatom 315 144 5 0 0 0 - - -;
+#X obj 206 117 unpack 0 0 0 0 0;
+#X text 265 235 Martin Peach 2008/11/05;
+#X connect 6 0 5 0;
+#X connect 6 1 11 0;
+#X connect 6 2 7 0;
+#X connect 11 0 0 0;
+#X connect 11 1 1 0;
+#X connect 11 2 2 0;
+#X connect 11 3 3 0;
+#X connect 11 4 10 0;
diff --git a/tcpreceive.c b/tcpreceive.c
new file mode 100644
index 0000000..418e77b
--- /dev/null
+++ b/tcpreceive.c
@@ -0,0 +1,333 @@
+/* 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 _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h> /* for socklen_t */
+#else
+#include <sys/types.h>
+#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;
+ unsigned short port;
+ 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[5];
+ t_atom x_msgoutbuf[MAX_UDP_RECEIVE];
+ char x_msginbuf[MAX_UDP_RECEIVE];
+} t_tcpreceive;
+
+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, unsigned short port);
+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 unsigned short tcpreceive_getconnectionport(t_tcpreceive * x, int fd);
+
+static void tcpreceive_read(t_tcpreceive *x, int sockfd)
+{
+ int i, read = 0;
+ long addr;
+ unsigned short port;
+
+// 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);
+ port = tcpreceive_getconnectionport(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);
+ x->x_addrbytes[4].a_w.w_float = port;
+ outlet_list(x->x_addrout, &s_list, 5L, 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;
+ x->x_connection[i].port = 0;
+ }
+ /* 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 < 5; ++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;
+ unsigned short port;
+ 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);
+ port = ntohs(from.sin_port);
+ if (tcpreceive_addconnection(x, fd, addr, port))
+ {
+ 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);
+ x->x_addrbytes[4].a_w.w_float = port;
+ outlet_list(x->x_addrout, &s_list, 5L, 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, unsigned short port)
+{
+ 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;
+ x->x_connection[i].port = port;
+ 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;
+ x->x_connection[i].port = 0;
+ 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;
+ x->x_connection[i].port = 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* tcpreceive_getconnectionport tries to find the socket fd in the list */
+/* returns port on success, else 0 */
+static u_short tcpreceive_getconnectionport(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].port;
+ }
+ 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);
+}
+
+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/tcpsend-help.pd b/tcpsend-help.pd
new file mode 100644
index 0000000..8b3ee6d
--- /dev/null
+++ b/tcpsend-help.pd
@@ -0,0 +1,31 @@
+#N canvas 0 94 559 320 12;
+#X msg 85 175 disconnect;
+#X msg 13 59 connect 127.0.0.1 9997;
+#X obj 60 253 tgl 15 0 empty empty connected 20 7 0 8 -24198 -241291
+-1 0 1;
+#X obj 60 230 tcpsend;
+#X text 217 60 <--first;
+#X msg 32 83 send 0 1 2 3;
+#X text 8 7 tcpsend sends bytes over a tcp connection.;
+#X text 8 30 Used in conjunction with packOSC will send OSC over tcp
+;
+#X msg 50 106 send ../doc/5.reference/test.txt;
+#X obj 385 144 openpanel;
+#X msg 385 168 send \$1;
+#X obj 385 125 bng 15 250 50 0 empty empty empty 17 7 0 10 -24198 -241291
+-1;
+#X text 344 105 send a file;
+#X text 407 124 ...any file;
+#X text 148 84 send raw data;
+#X text 137 134 'send' prefix is optional;
+#X msg 61 133 99 98 97;
+#X text 291 260 Martin Peach 2007/06/20;
+#X connect 0 0 3 0;
+#X connect 1 0 3 0;
+#X connect 3 0 2 0;
+#X connect 5 0 3 0;
+#X connect 8 0 3 0;
+#X connect 9 0 10 0;
+#X connect 10 0 3 0;
+#X connect 11 0 9 0;
+#X connect 16 0 3 0;
diff --git a/tcpsend.c b/tcpsend.c
new file mode 100644
index 0000000..b576eae
--- /dev/null
+++ b/tcpsend.c
@@ -0,0 +1,254 @@
+/* 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"
+#include <stdio.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include <winsock2.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netdb.h>
+#endif
+
+static t_class *tcpsend_class;
+
+typedef struct _tcpsend
+{
+ t_object x_obj;
+ int x_fd;
+} t_tcpsend;
+
+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)
+{
+#define BYTE_BUF_LEN 65536 // arbitrary maximum similar to max IP packet size
+ static char byte_buf[BYTE_BUF_LEN];
+ int i, j;
+ unsigned int 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;
+ char fpath[FILENAME_MAX];
+ FILE *fptr;
+
+#ifdef DEBUG
+ post("s: %s", s->s_name);
+ post("argc: %d", argc);
+#endif
+ for (i = j = 0; i < argc; ++i)
+ {
+ if (argv[i].a_type == A_FLOAT)
+ {
+ f = argv[i].a_w.w_float;
+ d = (unsigned 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[j++] = c;
+ }
+ else if (argv[i].a_type == A_SYMBOL)
+ {
+
+ atom_string(&argv[i], fpath, FILENAME_MAX);
+#ifdef DEBUG
+ post ("tcpsend fname: %s", fpath);
+#endif
+ fptr = fopen(fpath, "rb");
+ if (fptr == NULL)
+ {
+ post("tcpsend: unable to open \"%s\"", fpath);
+ return;
+ }
+ rewind(fptr);
+#ifdef DEBUG
+ post("tcpsend: d is %d", d);
+#endif
+ while ((d = fgetc(fptr)) != EOF)
+ {
+ byte_buf[j++] = (char)(d & 0x0FF);
+#ifdef DEBUG
+ post("tcpsend: byte_buf[%d] = %d", j-1, byte_buf[j-1]);
+#endif
+ if (j >= BYTE_BUF_LEN)
+ {
+ post ("tcpsend: file too long, truncating at %lu", BYTE_BUF_LEN);
+ break;
+ }
+ }
+ fclose(fptr);
+ fptr = NULL;
+ post("tcpsend: read \"%s\" length %d byte%s", fpath, j, ((d==1)?"":"s"));
+ }
+ else
+ {
+ error("tcpsend_send: item %d is not a float or a file name", i);
+ return;
+ }
+ }
+
+ length = j;
+ 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);
+}
+
+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);
+ class_addlist(tcpsend_class, (t_method)tcpsend_send);
+}
+
+/* end tcpsend.c */
diff --git a/tcpserver-help.pd b/tcpserver-help.pd
new file mode 100644
index 0000000..5e7b041
--- /dev/null
+++ b/tcpserver-help.pd
@@ -0,0 +1,181 @@
+#N canvas 284 35 1256 779 12;
+#X msg 143 -36 print;
+#X floatatom 448 290 5 0 0 0 - - -;
+#X floatatom 472 340 5 0 0 0 - - -;
+#X obj 496 291 unpack 0 0 0 0;
+#X floatatom 496 314 3 0 0 0 - - -;
+#X floatatom 527 314 3 0 0 0 - - -;
+#X floatatom 559 314 3 0 0 0 - - -;
+#X floatatom 593 314 3 0 0 0 - - -;
+#X text 453 313 from;
+#X text 364 290 connections;
+#X obj 425 415 print received;
+#X msg 239 60 client 1 1 2 3;
+#X msg 167 -12 broadcast 1 2 3;
+#X text 195 -35 list of connections;
+#X text 290 -12 send to all clients;
+#X text 402 340 on socket;
+#X msg 93 -86 send 504 1 2 3;
+#X text 204 -91 send to client on socket 504;
+#X msg 777 -209 client 1 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
+18 19;
+#X msg 777 -186 client 1 20 21 22 23 24 25 26 27 28 29 30 31 32 33
+34 35 36 37 38 39;
+#X msg 777 -148 client 1 40 41 42 43 44 45 46 47 48 49 50 51 52 53
+54 55 56 57 58 59;
+#X msg 777 -110 client 1 60 61 62 63 64 65 66 67 68 69 70 71 72 73
+74 75 76 77 78 79;
+#X msg 777 -72 client 1 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
+95 96 97 98 99;
+#X msg 777 -34 client 1 100 101 102 103 104 105 106 107 108 109 110
+111 112 113 114 115 116 117 118 119;
+#X msg 777 4 client 1 120 121 122 123 124 125 126 127 138 139 140 141
+142 143 144 145 146 147 148 149;
+#X msg 777 42 client 1 150 151 152 153 154 155 156 157 158 159 160
+161 162 163 164 165 166 167 168 169;
+#X msg 777 80 client 1 170 171 172 173 174 175 176 177 178 179 180
+181 182 183 184 185 186 187 188 189;
+#X msg 777 118 client 1 190 191 192 193 194 195 196 197 198 199 200
+201 202 203 204 205 206 207 208 209;
+#X msg 777 156 client 1 210 211 212 213 214 215 216 217 218 219 220
+221 222 223 224 225 226 227 228 229;
+#X msg 777 194 client 1 230 231 232 233 234 235 236 237 238 239 240
+241 242 243 244 245 246 247 248 249;
+#X msg 777 232 client 1 250 251 252 253 254 255;
+#X obj 144 -203 openpanel;
+#X obj 144 -222 bng 15 250 50 0 empty empty choose_a_file 17 7 0 10
+-24198 -241291 -1;
+#X text 128 -183 ...any file;
+#X msg -4 -183 client 1 \$1;
+#X msg 118 -61 10 1 2 43;
+#X text 202 -74 'send' prefix is optional;
+#X text 192 -57 (the first number is socket number);
+#X msg 63 -116 disconnectsocket \$1;
+#X msg 20 -159 disconnectclient \$1;
+#X obj 777 257 s toserver;
+#X obj 425 238 r toserver;
+#X floatatom 247 -198 5 0 0 0 - - -;
+#X obj 225 -179 f;
+#X obj 225 -198 bng 15 250 50 0 empty empty empty 17 7 0 10 -258699
+-241291 -1;
+#X floatatom 289 -155 5 0 0 0 - - -;
+#X obj 267 -136 f;
+#X obj 267 -155 bng 15 250 50 0 empty empty empty 17 7 0 10 -258699
+-241291 -1;
+#X text -5 -136 disconnect by socket or client number;
+#X text 532 265 argument is port number;
+#X text 96 267 Received messages are output as lists of bytes;
+#X text 35 371 ***WARNING*** Attempting to print long messages can
+hang pd!;
+#X obj 425 393 spigot;
+#X obj 464 373 tgl 15 0 empty empty enable_print 17 7 0 10 -24198 -258699
+-45076 0 1;
+#X msg 263 84 dump \$1;
+#X obj 87 69 tgl 15 0 empty empty enable_dump 17 7 0 10 -4034 -257985
+-1 0 1;
+#X text 19 19 dump received;
+#X text 19 35 characters to main;
+#X text 19 50 window in hexdump;
+#X text 19 66 format:;
+#X obj 425 266 tcpserver 9997;
+#X msg -28 -207 client 1 test.txt;
+#X text 777 -234 [tcpserver] sends and receives bytes on [0...255]
+;
+#X text 262 -243 [tcpserver] waits for clients to connect to its port.
+;
+#X text -33 -228 send a file to client 1;
+#X msg 287 108 client 1 72 101 108 108 111 13 10;
+#X floatatom 682 351 9 0 0 0 - - -;
+#X obj 758 313 print status;
+#X floatatom 642 374 5 0 0 0 - - -;
+#X text 572 373 to_client;
+#X msg 215 36 client;
+#X msg 192 13 client 1;
+#X text 260 12 get state of client 1;
+#X text 353 60 send (binary) 1 2 3 to client 1;
+#X obj 642 286 route sent client;
+#X floatatom 817 443 5 0 0 0 - - -;
+#X symbolatom 849 416 20 0 0 0 - - -;
+#X floatatom 786 416 5 0 0 0 - - -;
+#X text 749 350 bytes;
+#X text 642 350 sent;
+#X msg 313 134 clientbuf 1 65536;
+#X obj 786 392 unpack 0 0 s 0;
+#X floatatom 881 464 7 0 0 0 - - -;
+#X text 726 464 length of send buffer:;
+#X text 766 442 socket:;
+#X text 735 415 client:;
+#X text 826 415 ip:;
+#X text 443 133 set send-buffer size for client 1;
+#X text 267 36 get state of all clients (list on right outlet);
+#X obj 642 325 unpack 0 0 0;
+#X floatatom 723 392 5 0 0 0 - - -;
+#X text 653 391 on_socket;
+#X msg 363 184 send;
+#X msg 388 209 1156;
+#X text 406 183 output 'client' message for all sockets;
+#X text 428 209 output 'client' message for socket 1156;
+#X msg 338 159 timeout 100;
+#X text 425 159 microsecond timeout for send (default is 1000);
+#X text 35 450 2009/04/08 Martin Peach;
+#X connect 0 0 60 0;
+#X connect 3 0 4 0;
+#X connect 3 1 5 0;
+#X connect 3 2 6 0;
+#X connect 3 3 7 0;
+#X connect 11 0 60 0;
+#X connect 12 0 60 0;
+#X connect 16 0 60 0;
+#X connect 18 0 40 0;
+#X connect 19 0 40 0;
+#X connect 20 0 40 0;
+#X connect 21 0 40 0;
+#X connect 22 0 40 0;
+#X connect 23 0 40 0;
+#X connect 24 0 40 0;
+#X connect 25 0 40 0;
+#X connect 26 0 40 0;
+#X connect 27 0 40 0;
+#X connect 28 0 40 0;
+#X connect 29 0 40 0;
+#X connect 30 0 40 0;
+#X connect 31 0 34 0;
+#X connect 32 0 31 0;
+#X connect 34 0 60 0;
+#X connect 35 0 60 0;
+#X connect 38 0 60 0;
+#X connect 39 0 60 0;
+#X connect 41 0 60 0;
+#X connect 42 0 43 1;
+#X connect 43 0 39 0;
+#X connect 44 0 43 0;
+#X connect 45 0 46 1;
+#X connect 46 0 38 0;
+#X connect 47 0 46 0;
+#X connect 52 0 10 0;
+#X connect 53 0 52 1;
+#X connect 54 0 60 0;
+#X connect 55 0 54 0;
+#X connect 60 0 52 0;
+#X connect 60 1 1 0;
+#X connect 60 2 2 0;
+#X connect 60 3 3 0;
+#X connect 60 4 74 0;
+#X connect 61 0 60 0;
+#X connect 65 0 60 0;
+#X connect 70 0 60 0;
+#X connect 71 0 60 0;
+#X connect 74 0 89 0;
+#X connect 74 1 81 0;
+#X connect 74 2 67 0;
+#X connect 80 0 60 0;
+#X connect 81 0 77 0;
+#X connect 81 1 75 0;
+#X connect 81 2 76 0;
+#X connect 81 3 82 0;
+#X connect 89 0 68 0;
+#X connect 89 1 66 0;
+#X connect 89 2 90 0;
+#X connect 92 0 60 0;
+#X connect 93 0 60 0;
+#X connect 96 0 60 0;
diff --git a/tcpserver.c b/tcpserver.c
new file mode 100644
index 0000000..5272029
--- /dev/null
+++ b/tcpserver.c
@@ -0,0 +1,1065 @@
+/* tcpserver.c Martin Peach 20060511 working version 20060512 */
+/* 20060515 works on linux too... */
+/* 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 <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>
+#include <errno.h>
+#include <sys/select.h>
+#include <sys/ioctl.h> /* linux has the SIOCOUTQ ioctl */
+#define SOCKET_ERROR -1
+#else
+#include <winsock2.h>
+#endif
+
+
+#ifdef _MSC_VER
+#define snprintf sprintf_s
+#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 char objName[] = "tcpserver";
+
+typedef void (*t_tcpserver_socketnotifier)(void *x);
+typedef void (*t_tcpserver_socketreceivefn)(void *x, t_binbuf *b);
+
+typedef struct _tcpserver_socketreceiver
+{
+ t_symbol *sr_host;
+ t_int sr_fd;
+ t_int sr_fdbuf;
+ u_long sr_addr;
+ 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_send_params
+{
+ int client;
+ int sockfd;
+ char *byte_buf;
+ size_t length;
+ t_int timeout_us;
+} t_tcpserver_send_params;
+
+typedef struct _tcpserver
+{
+ t_object x_obj;
+ t_outlet *x_msgout;
+ t_outlet *x_connectout;
+ t_outlet *x_sockout;
+ t_outlet *x_addrout;
+ t_outlet *x_status_outlet;
+ t_int x_dump; // 1 = hexdump received bytes
+
+ 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_int x_timeout_us;
+ 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 tcpserver_send_bytes(int sockfd, t_tcpserver *x, int argc, t_atom *argv);
+#ifdef SIOCOUTQ
+static int tcpserver_send_buffer_avaliable_for_client(t_tcpserver *x, int client);
+#endif
+static void *tcpserver_send_buf_thread(void *arg);
+static size_t tcpserver_send_buf(int client, int sockfd, char *byte_buf, size_t length, t_int timeout_us);
+static void tcpserver_client_send(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv);
+static void tcpserver_output_client_state(t_tcpserver *x, int client);
+static int tcpserver_get_socket_send_buf_size(int sockfd);
+static int tcpserver_set_socket_send_buf_size(int sockfd, int size);
+static void tcpserver_buf_size(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv);
+static void tcpserver_disconnect(t_tcpserver *x);
+static void tcpserver_client_disconnect(t_tcpserver *x, t_floatarg fclient);
+static void tcpserver_socket_disconnect(t_tcpserver *x, t_floatarg fsocket);
+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);
+void tcpserver_setup(void);
+static void tcpserver_dump(t_tcpserver *x, t_float dump);
+static void tcpserver_timeout(t_tcpserver *x, t_float timeout);
+static void tcpserver_hexdump(unsigned char *buf, long len);
+
+static void tcpserver_timeout(t_tcpserver *x, t_float timeout)
+{
+ /* set the timeout on the select call in tcpserver_send_buf */
+ /* this is the maximum time in microseconds to wait */
+ /* before abandoning attempt to send */
+
+ t_int timeout_us = 0;
+ if ((timeout >= 0)&&(timeout < 1000000))
+ {
+ timeout_us = (t_int)timeout;
+ x->x_timeout_us = timeout_us;
+ }
+}
+
+static void tcpserver_dump(t_tcpserver *x, t_float dump)
+{
+ x->x_dump = (dump == 0)?0:1;
+}
+
+static void tcpserver_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("tcpserver_hexdump %d", len);
+#endif
+ while (k < len)
+ {
+ for (i = j = 0; i < BYTES_PER_LINE; ++i, ++k, j+=3)
+ {
+ if (k < len)
+ {
+ snprintf(&hexStr[j], 4, "%02X ", buf[k]);
+ snprintf(&ascStr[i], 2, "%c", ((buf[k] >= 32) && (buf[k] <= 126))? buf[k]: '.');
+ }
+ else
+ { // the last line
+ snprintf(&hexStr[j], 4, " ");
+ snprintf(&ascStr[i], 2, " ");
+ }
+ }
+ post ("%s%s", hexStr, ascStr);
+ }
+}
+
+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 %ld 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 (y->x_dump)tcpserver_hexdump(&inbuf[intail], i);
+
+ 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_sr[i]->sr_fd == y->x_sock_fd)
+ {
+// outlet_symbol(x->x_connectionip, x->x_sr[i].sr_host);
+ /* find sender's ip address and output it */
+ y->x_addrbytes[0].a_w.w_float = (y->x_sr[i]->sr_addr & 0xFF000000)>>24;
+ y->x_addrbytes[1].a_w.w_float = (y->x_sr[i]->sr_addr & 0x0FF0000)>>16;
+ y->x_addrbytes[2].a_w.w_float = (y->x_sr[i]->sr_addr & 0x0FF00)>>8;
+ y->x_addrbytes[3].a_w.w_float = (y->x_sr[i]->sr_addr & 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 tcpserver_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, j, d;
+ unsigned char c;
+ float f, e;
+ int length;
+ size_t flen = 0;
+ int sockfd = x->x_sr[client]->sr_fd;
+ char fpath[FILENAME_MAX];
+ FILE *fptr;
+ t_atom output_atom[3];
+ t_tcpserver_send_params *ttsp;
+ pthread_t sender_thread;
+ pthread_attr_t sender_attr;
+ int sender_thread_result;
+
+ /* process & send data */
+ if(sockfd >= 0)
+ {
+ /* sender thread should start out detached so its resouces will be freed when it is done */
+ if (0!= (sender_thread_result = pthread_attr_init(&sender_attr)))
+ {
+ error("%s: pthread_attr_init failed: %d", objName, sender_thread_result);
+ goto failed;
+ }
+ if(0!= (sender_thread_result = pthread_attr_setdetachstate(&sender_attr, PTHREAD_CREATE_DETACHED)))
+ {
+ error("%s: pthread_attr_setdetachstate failed: %d", objName, sender_thread_result);
+ goto failed;
+ }
+ for (i = j = 0; i < argc; ++i)
+ {
+ if (argv[i].a_type == A_FLOAT)
+ { /* load floats into buffer as long as they are integers on [0..255]*/
+ 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);
+ goto failed;
+ }
+ if ((d < 0) || (d > 255))
+ {
+ error("%s: item %d (%f) is not between 0 and 255", objName, i, f);
+ goto failed;
+ }
+ 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[j++] = c;
+ if (j >= MAX_UDP_RECEIVE)
+ { /* if the argument list is longer than our buffer, send the buffer whenever it's full */
+#ifdef SIOCOUTQ
+ if (tcpserver_send_buffer_avaliable_for_client(x, client) < j)
+ {
+ error("%s: buffer too small for client(%d)", objName, client);
+ goto failed;
+ }
+#endif // SIOCOUTQ
+ ttsp = (t_tcpserver_send_params *)getbytes(sizeof(t_tcpserver_send_params));
+ if (ttsp == NULL)
+ {
+ error("%s: unable to allocate %d bytes for t_tcpserver_send_params", objName, sizeof(t_tcpserver_send_params));
+ goto failed;
+ }
+ ttsp->client = client;
+ ttsp->sockfd = sockfd;
+ ttsp->byte_buf = byte_buf;
+ ttsp->length = j;
+ ttsp->timeout_us = x->x_timeout_us;
+ if (0 != (sender_thread_result = pthread_create(&sender_thread, &sender_attr, tcpserver_send_buf_thread, (void *)ttsp)))
+ {
+ error("%s: couldn't create sender thread (%d)", objName, sender_thread_result);
+ goto failed;
+ }
+ flen += j;
+ j = 0;
+ }
+ }
+ else if (argv[i].a_type == A_SYMBOL)
+ { /* symbols are interpreted to be file names; attempt to load the file and send it */
+
+ atom_string(&argv[i], fpath, FILENAME_MAX);
+#ifdef DEBUG
+ post ("%s: fname: %s", objName, fpath);
+#endif
+ fptr = fopen(fpath, "rb");
+ if (fptr == NULL)
+ {
+ error("%s: unable to open \"%s\"", objName, fpath);
+ goto failed;
+ }
+ rewind(fptr);
+#ifdef DEBUG
+ post("%s: d is %d", objName, d);
+#endif
+ while ((d = fgetc(fptr)) != EOF)
+ {
+ byte_buf[j++] = (char)(d & 0x0FF);
+#ifdef DEBUG
+ post("%s: byte_buf[%d] = %d", objName, j-1, byte_buf[j-1]);
+#endif
+ if (j >= MAX_UDP_RECEIVE)
+ { /* if the file is longer than our buffer, send the buffer whenever it's full */
+ /* this might be better than allocating huge amounts of memory */
+#ifdef SIOCOUTQ
+ if (tcpserver_send_buffer_avaliable_for_client(x, client) < j)
+ {
+ error("%s: buffer too small for client(%d)", objName, client);
+ goto failed;
+ }
+#endif // SIOCOUTQ
+ ttsp = (t_tcpserver_send_params *)getbytes(sizeof(t_tcpserver_send_params));
+ if (ttsp == NULL)
+ {
+ error("%s: unable to allocate %d bytes for t_tcpserver_send_params", objName, sizeof(t_tcpserver_send_params));
+ goto failed;
+ }
+ ttsp->client = client;
+ ttsp->sockfd = sockfd;
+ ttsp->byte_buf = byte_buf;
+ ttsp->length = j;
+ ttsp->timeout_us = x->x_timeout_us;
+ if ( 0!= (sender_thread_result = pthread_create(&sender_thread, &sender_attr, tcpserver_send_buf_thread, (void *)ttsp)))
+ {
+ error("%s: couldn't create sender thread (%d)", objName, sender_thread_result);
+ goto failed;
+ }
+ flen += j;
+ j = 0;
+ }
+ }
+ flen += j;
+ fclose(fptr);
+ fptr = NULL;
+ post("%s: read \"%s\" length %d byte%s", objName, fpath, flen, ((d==1)?"":"s"));
+ }
+ else
+ { /* arg was neither a float nor a valid file name */
+ error("%s: item %d is not a float or a file name", objName, i);
+ goto failed;
+ }
+ }
+ length = j;
+ if (length > 0)
+ { /* send whatever remains in our buffer */
+#ifdef SIOCOUTQ
+ if (tcpserver_send_buffer_avaliable_for_client(x, client) < length)
+ {
+ error("%s: buffer too small for client(%d)", objName, client);
+ goto failed;
+ }
+#endif // SIOCOUTQ
+ ttsp = (t_tcpserver_send_params *)getbytes(sizeof(t_tcpserver_send_params));
+ if (ttsp == NULL)
+ {
+ error("%s: unable to allocate %d bytes for t_tcpserver_send_params", objName, sizeof(t_tcpserver_send_params));
+ goto failed;
+ }
+ ttsp->client = client;
+ ttsp->sockfd = sockfd;
+ ttsp->byte_buf = byte_buf;
+ ttsp->length = length;
+ ttsp->timeout_us = x->x_timeout_us;
+ if ( 0!= (sender_thread_result = pthread_create(&sender_thread, &sender_attr, tcpserver_send_buf_thread, (void *)ttsp)))
+ {
+ error("%s: couldn't create sender thread (%d)", objName, sender_thread_result);
+ goto failed;
+ }
+ flen += length;
+ }
+ }
+ else post("%s: not a valid socket number (%d)", objName, sockfd);
+failed:
+ SETFLOAT(&output_atom[0], client+1);
+ SETFLOAT(&output_atom[1], flen);
+ SETFLOAT(&output_atom[2], sockfd);
+ outlet_anything( x->x_status_outlet, gensym("sent"), 3, output_atom);
+}
+
+#ifdef SIOCOUTQ
+/* SIOCOUTQ exists only(?) on linux, returns remaining space in the socket's output buffer */
+static int tcpserver_send_buffer_avaliable_for_client(t_tcpserver *x, int client)
+{
+ int sockfd = x->x_sr[client].sr_fd;
+ int result = 0L;
+
+ ioctl(sockfd, SIOCOUTQ, &result);
+ return result;
+}
+#endif // SIOCOUTQ
+
+// send a buffer in its own thread
+static void *tcpserver_send_buf_thread(void *arg)
+{
+ t_tcpserver_send_params *ttsp = (t_tcpserver_send_params *)arg;
+ int result;
+
+ result = send(ttsp->sockfd, ttsp->byte_buf, ttsp->length, 0);
+ if (result <= 0)
+ {
+ sys_sockerror("tcpserver: send");
+ post("%s_send_buf: could not send data to client %d", objName, ttsp->client+1);
+ }
+ freebytes (arg, sizeof (t_tcpserver_send_params));
+ return NULL;
+}
+
+// send a buffer one byte at a time, no thread
+static size_t tcpserver_send_buf(int client, int sockfd, char *byte_buf, size_t length, t_int timeout_us)
+{
+ char *bp;
+ size_t sent = 0;
+ int result;
+ fd_set wfds;
+ struct timeval timeout;
+
+ for (bp = byte_buf, sent = 0; sent < length;)
+ {
+ FD_ZERO(&wfds);
+ FD_SET(sockfd, &wfds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = timeout_us; /* give it a short time to clear buffer */
+ result = select(sockfd+1, NULL, &wfds, NULL, &timeout);
+ if (result == -1)
+ {
+ post("%s_send_buf: select returned error %d", objName, errno);
+ break;
+ }
+ if (FD_ISSET(sockfd, &wfds))
+ {
+ result = send(sockfd, bp, 1, 0);/*(sockfd, bp, (int)(length-sent), 0);*/
+ if (result <= 0)
+ {
+ sys_sockerror("tcpserver: send");
+ post("%s_send_buf: could not send data to client %d", objName, client+1);
+ break;
+ }
+ else
+ {
+ sent += result;
+ bp += result;
+ }
+ }
+ else
+ {
+ post ("%s_send_buf: can't send right now, sent %lu of %lu", objName, sent, length);
+ return sent;/* abandon any further attempts to send so we don't block */
+ }
+ }
+ return sent;
+}
+
+/* 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_send: no clients connected", objName);
+ return;
+ }
+ if(argc == 0) /* no socket specified: output state of all sockets */
+ {
+ tcpserver_output_client_state(x, client);
+ 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_sr[i]->sr_fd == sockfd)
+ {
+ client = i; /* the client we're sending to */
+ break;
+ }
+ }
+ if(client == -1)
+ {
+ post("%s_send: no connection on socket %d", objName, sockfd);
+ return;
+ }
+ }
+ else
+ {
+ post("%s_send: no socket specified", objName);
+ return;
+ }
+ if (argc < 2) /* nothing to send: output state of this socket */
+ {
+ tcpserver_output_client_state(x, client+1);
+ return;
+ }
+ tcpserver_send_bytes(client, x, argc-1, &argv[1]);
+}
+
+/* disconnect the client at x_sock_fd */
+static void tcpserver_disconnect(t_tcpserver *x)
+{
+ int i, fd;
+ t_tcpserver_socketreceiver *y;
+
+ if (x->x_sock_fd >= 0)
+ {
+ /* find the socketreceiver for this socket */
+ for(i = 0; i < x->x_nconnections; i++)
+ {
+ if(x->x_sr[i]->sr_fd == x->x_sock_fd)
+ {
+ y = x->x_sr[i];
+ fd = y->sr_fd;
+ if (y->sr_notifier) (*y->sr_notifier)(x);
+ sys_rmpollfn(fd);
+ sys_closesocket(fd);
+ x->x_sock_fd = -1;
+ return;
+ }
+ }
+ }
+ post("%s__disconnect: no connection on socket %d", objName, x->x_sock_fd);
+}
+
+/* disconnect a client by socket */
+static void tcpserver_socket_disconnect(t_tcpserver *x, t_floatarg fsocket)
+{
+ int sock = (int)fsocket;
+
+ if(x->x_nconnections <= 0)
+ {
+ post("%s_socket_disconnect: no clients connected", objName);
+ return;
+ }
+ x->x_sock_fd = sock;
+ tcpserver_disconnect(x);
+}
+
+/* disconnect a client by number */
+static void tcpserver_client_disconnect(t_tcpserver *x, t_floatarg fclient)
+{
+ int client = (int)fclient;
+
+ if(x->x_nconnections <= 0)
+ {
+ post("%s_client_disconnect: no clients connected", 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*/
+ x->x_sock_fd = x->x_sr[client]->sr_fd;
+ tcpserver_disconnect(x);
+}
+
+
+/* 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 = -1;
+
+ if(x->x_nconnections <= 0)
+ {
+ post("%s_client_send: no clients connected", objName);
+ return;
+ }
+ if(argc > 0)
+ {
+ /* get number of client (first element in list) */
+ if(argv[0].a_type == A_FLOAT)
+ client = atom_getfloatarg(0, argc, argv);
+ else
+ {
+ post("%s_client_send: specify client by number", objName);
+ return;
+ }
+ if (!((client > 0) && (client < MAX_CONNECT)))
+ {
+ post("%s_client_send: client %d out of range [1..%d]", objName, client, MAX_CONNECT);
+ return;
+ }
+ }
+ if (argc > 1)
+ {
+ --client;/* zero based index*/
+ tcpserver_send_bytes(client, x, argc-1, &argv[1]);
+ return;
+ }
+ tcpserver_output_client_state(x, client);
+}
+
+static void tcpserver_output_client_state(t_tcpserver *x, int client)
+{
+ t_atom output_atom[4];
+
+ if (client == -1)
+ {
+ /* output parameters of all connections via status outlet */
+ for(client = 0; client < x->x_nconnections; client++)
+ {
+ x->x_sr[client]->sr_fdbuf = tcpserver_get_socket_send_buf_size(x->x_sr[client]->sr_fd);
+ SETFLOAT(&output_atom[0], client+1);
+ SETFLOAT(&output_atom[1], x->x_sr[client]->sr_fd);
+ output_atom[2].a_type = A_SYMBOL;
+ output_atom[2].a_w.w_symbol = x->x_sr[client]->sr_host;
+ SETFLOAT(&output_atom[3], x->x_sr[client]->sr_fdbuf);
+ outlet_anything( x->x_status_outlet, gensym("client"), 4, output_atom);
+ }
+ }
+ else
+ {
+ client -= 1;/* zero-based client index conflicts with 1-based user index !!! */
+ /* output client parameters via status outlet */
+ x->x_sr[client]->sr_fdbuf = tcpserver_get_socket_send_buf_size(x->x_sr[client]->sr_fd);
+ SETFLOAT(&output_atom[0], client+1);/* user sees client 0 as 1 */
+ SETFLOAT(&output_atom[1], x->x_sr[client]->sr_fd);
+ output_atom[2].a_type = A_SYMBOL;
+ output_atom[2].a_w.w_symbol = x->x_sr[client]->sr_host;
+ SETFLOAT(&output_atom[3], x->x_sr[client]->sr_fdbuf);
+ outlet_anything( x->x_status_outlet, gensym("client"), 4, output_atom);
+ }
+}
+
+/* Return the send buffer size of socket */
+static int tcpserver_get_socket_send_buf_size(int sockfd)
+{
+ int optVal = 0;
+ unsigned int optLen = sizeof(int);
+#ifdef _WIN32
+ if (getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optLen) == SOCKET_ERROR)
+ post("%_get_socket_send_buf_size: getsockopt returned %d\n", objName, WSAGetLastError());
+#else
+ if (getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optLen) == -1)
+ post("%_get_socket_send_buf_size: getsockopt returned %d\n", objName, errno);
+#endif
+ return optVal;
+}
+
+/* Set the send buffer size of socket, returns actual size */
+static int tcpserver_set_socket_send_buf_size(int sockfd, int size)
+{
+ int optVal = size;
+ int optLen = sizeof(int);
+#ifdef _WIN32
+ if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, optLen) == SOCKET_ERROR)
+ {
+ post("%s_set_socket_send_buf_size: setsockopt returned %d\n", objName, WSAGetLastError());
+#else
+ if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, optLen) == -1)
+ {
+ post("%s_set_socket_send_buf_size: setsockopt returned %d\n", objName, errno);
+#endif
+ return 0;
+ }
+ else return (tcpserver_get_socket_send_buf_size(sockfd));
+}
+
+/* Get/set the send buffer of client socket */
+static void tcpserver_buf_size(t_tcpserver *x, t_symbol *s, int argc, t_atom *argv)
+{
+ int client = -1;
+ float buf_size = 0;
+ t_atom output_atom[3];
+
+ if(x->x_nconnections <= 0)
+ {
+ post("%s_buf_size: no clients connected", objName);
+ return;
+ }
+ /* get number of client (first element in list) */
+ if (argc > 0)
+ {
+ if (argv[0].a_type == A_FLOAT)
+ client = atom_getfloatarg(0, argc, argv);
+ else
+ {
+ post("%s_buf_size: specify client by number", objName);
+ return;
+ }
+ if (!((client > 0) && (client < MAX_CONNECT)))
+ {
+ post("%s__buf_size: client %d out of range [1..%d]", objName, client, MAX_CONNECT);
+ return;
+ }
+ }
+ if (argc > 1)
+ {
+ if (argv[1].a_type != A_FLOAT)
+ {
+ post("%s_buf_size: specify buffer size with a float", objName);
+ return;
+ }
+ buf_size = atom_getfloatarg(1, argc, argv);
+ --client;/* zero based index*/
+ x->x_sr[client]->sr_fdbuf = tcpserver_set_socket_send_buf_size(x->x_sr[client]->sr_fd, (int)buf_size);
+ post("%s_buf_size: client %d set to %d", objName, client+1, x->x_sr[client]->sr_fdbuf);
+ return;
+ }
+ post("%s_buf_size: specify client and buffer size", objName);
+ return;
+}
+
+/* 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_sr[client]->sr_fd >= 0)
+ { /* socket exists for this client */
+ tcpserver_send_bytes(client, x, argc, argv);
+ }
+ }
+}
+
+/* ---------------- 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_sr[i]->sr_fd == x->x_sock_fd)
+ {
+ x->x_nconnections--;
+ post("%s: \"%s\" removed from list of clients", objName, x->x_sr[i]->sr_host->s_name);
+ 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_sr[k] = x->x_sr[k + 1];
+ }
+ }
+ }
+ outlet_float(x->x_connectout, x->x_nconnections);
+}
+
+static void tcpserver_connectpoll(t_tcpserver *x)
+{
+ struct sockaddr_in incomer_address;
+ unsigned int sockaddrl = sizeof( struct sockaddr );
+ int fd = accept(x->x_connectsocket, (struct sockaddr*)&incomer_address, &sockaddrl);
+ int i;
+ int optVal;
+ unsigned int optLen = sizeof(int);
+
+ 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 _WIN32
+ 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_sr[i] = y;
+ x->x_sr[i]->sr_host = gensym(inet_ntoa(incomer_address.sin_addr));
+ x->x_sr[i]->sr_fd = fd;
+ post("%s: accepted connection from %s on socket %d",
+ objName, x->x_sr[i]->sr_host->s_name, x->x_sr[i]->sr_fd);
+/* see how big the send buffer is on this socket */
+ x->x_sr[i]->sr_fdbuf = 0;
+#ifdef _WIN32
+ if (getsockopt(x->x_sr[i]->sr_fd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optLen) != SOCKET_ERROR)
+ {
+ /* post("%s_connectpoll: send buffer is %ld\n", objName, optVal); */
+ x->x_sr[i]->sr_fdbuf = optVal;
+ }
+ else post("%s_connectpoll: getsockopt returned %d\n", objName, WSAGetLastError());
+#else
+ if (getsockopt(x->x_sr[i]->sr_fd, SOL_SOCKET, SO_SNDBUF, (char*)&optVal, &optLen) == 0)
+ {
+ /* post("%s_connectpoll: send buffer is %ld\n", objName, optVal); */
+ x->x_sr[i]->sr_fdbuf = optVal;
+ }
+ else post("%s_connectpoll: getsockopt returned %d\n", objName, errno);
+#endif
+ outlet_float(x->x_connectout, x->x_nconnections);
+ outlet_float(x->x_sockout, x->x_sr[i]->sr_fd); /* the socket number */
+ x->x_sr[i]->sr_addr = ntohl(incomer_address.sin_addr.s_addr);
+ x->x_addrbytes[0].a_w.w_float = (x->x_sr[i]->sr_addr & 0xFF000000)>>24;
+ x->x_addrbytes[1].a_w.w_float = (x->x_sr[i]->sr_addr & 0x0FF0000)>>16;
+ x->x_addrbytes[2].a_w.w_float = (x->x_sr[i]->sr_addr & 0x0FF00)>>8;
+ x->x_addrbytes[3].a_w.w_float = (x->x_sr[i]->sr_addr & 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_sr[i]->sr_host->s_name, x->x_sr[i]->sr_fd);
+ }
+ }
+ 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 */
+ x->x_status_outlet = outlet_new(&x->x_obj, &s_anything);/* 5th outlet for everything else */
+ }
+ x->x_connectsocket = sockfd;
+ x->x_nconnections = 0;
+ for(i = 0; i < MAX_CONNECT; i++)
+ {
+ 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;
+ }
+ x->x_timeout_us = 1000;/* default 1 ms for select call timeout when sending */
+ return (x);
+}
+
+static void tcpserver_free(t_tcpserver *x)
+{
+ int i;
+
+ for(i = 0; i < MAX_CONNECT; i++)
+ {
+
+ if (NULL!=x->x_sr[i]) {
+ tcpserver_socketreceiver_free(x->x_sr[i]);
+ if (x->x_sr[i]->sr_fd >= 0)
+ {
+ sys_rmpollfn(x->x_sr[i]->sr_fd);
+ sys_closesocket(x->x_sr[i]->sr_fd);
+ }
+ }
+ }
+ if (x->x_connectsocket >= 0)
+ {
+ sys_rmpollfn(x->x_connectsocket);
+ sys_closesocket(x->x_connectsocket);
+ }
+
+}
+
+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_buf_size, gensym("clientbuf"), A_GIMME, 0);
+ class_addmethod(tcpserver_class, (t_method)tcpserver_client_disconnect, gensym("disconnectclient"), A_DEFFLOAT, 0);
+ class_addmethod(tcpserver_class, (t_method)tcpserver_socket_disconnect, gensym("disconnectsocket"), A_DEFFLOAT, 0);
+ class_addmethod(tcpserver_class, (t_method)tcpserver_dump, gensym("dump"), A_FLOAT, 0);
+ class_addmethod(tcpserver_class, (t_method)tcpserver_broadcast, gensym("broadcast"), A_GIMME, 0);
+ class_addmethod(tcpserver_class, (t_method)tcpserver_timeout, gensym("timeout"), A_FLOAT, 0);
+ class_addlist(tcpserver_class, (t_method)tcpserver_send);
+}
+
+/* end of tcpserver.c */
diff --git a/test.txt b/test.txt
new file mode 100644
index 0000000..e36d4ff
--- /dev/null
+++ b/test.txt
@@ -0,0 +1 @@
+testing one two three
diff --git a/udpreceive-help.pd b/udpreceive-help.pd
new file mode 100644
index 0000000..a906e52
--- /dev/null
+++ b/udpreceive-help.pd
@@ -0,0 +1,19 @@
+#N canvas 187 585 478 280 12;
+#X floatatom 209 142 3 0 0 0 - - -;
+#X floatatom 236 142 3 0 0 0 - - -;
+#X floatatom 263 142 3 0 0 0 - - -;
+#X floatatom 290 142 3 0 0 0 - - -;
+#X text 166 141 from;
+#X obj 107 186 print message;
+#X obj 107 91 udpreceive 9997;
+#X text 32 16 udpreceive receives bytes over a udp connection.;
+#X floatatom 318 142 5 0 0 0 - - -;
+#X obj 209 116 unpack 0 0 0 0 0;
+#X text 265 235 Martin Peach 2008/11/05;
+#X connect 6 0 5 0;
+#X connect 6 1 9 0;
+#X connect 9 0 0 0;
+#X connect 9 1 1 0;
+#X connect 9 2 2 0;
+#X connect 9 3 3 0;
+#X connect 9 4 8 0;
diff --git a/udpreceive.c b/udpreceive.c
new file mode 100644
index 0000000..a9ac791
--- /dev/null
+++ b/udpreceive.c
@@ -0,0 +1,160 @@
+/* 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 _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#else
+#include <sys/types.h>
+#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[5];
+ t_atom x_msgoutbuf[MAX_UDP_RECEIVE];
+ char x_msginbuf[MAX_UDP_RECEIVE];
+} t_udpreceive;
+
+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;
+ unsigned short port;
+
+ 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);
+ port = ntohs(from.sin_port);
+
+ 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);
+ x->x_addrbytes[4].a_w.w_float = port;
+ outlet_list(x->x_addrout, &s_list, 5L, 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)(unsigned char)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;
+
+ /* enable delivery of all multicast or broadcast (but not unicast)
+ * UDP datagrams to all sockets bound to the same port */
+ 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 < 5; ++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);
+ }
+}
+
+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 udpreceive.c */
diff --git a/udpreceive~.c b/udpreceive~.c
new file mode 100644
index 0000000..42bf7c7
--- /dev/null
+++ b/udpreceive~.c
@@ -0,0 +1,805 @@
+/* udpreceive~ started 20100110 by Martin Peach based on netreceive~: */
+/* ------------------------ netreceive~ --------------------------------------- */
+/* */
+/* Tilde object to receive uncompressed audio data from netsend~. */
+/* Written by Olaf Matthes <olaf.matthes@gmx.de>. */
+/* Based on streamin~ by Guenter Geiger. */
+/* Get source at http://www.akustische-kunst.org/ */
+/* */
+/* 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. */
+/* */
+/* See file LICENSE for further informations on licensing terms. */
+/* */
+/* 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. */
+/* */
+/* This project was commissioned by the Society for Arts and Technology [SAT], */
+/* Montreal, Quebec, Canada, http://www.sat.qc.ca/. */
+/* */
+/* ---------------------------------------------------------------------------- */
+
+
+#include "m_pd.h"
+
+#include "udpsend~.h"
+
+#include <sys/types.h>
+#include <string.h>
+#if defined(UNIX) || defined(unix)
+#include <sys/socket.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#define SOCKET_ERROR -1
+#endif
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h> /* for socklen_t */
+#endif
+
+#ifndef SOL_IP
+#define SOL_IP IPPROTO_IP
+#endif
+
+#define DEFAULT_AUDIO_BUFFER_FRAMES 16 /* a small circ. buffer for 16 frames */
+#define DEFAULT_AVERAGE_NUMBER 10 /* number of values we store for average history */
+#define DEFAULT_NETWORK_POLLTIME 1 /* interval in ms for polling for input data (Max/MSP only) */
+#define DEFAULT_QUEUE_LENGTH 3 /* min. number of buffers that can be used reliably on your hardware */
+
+#if defined(UNIX) || defined(unix)
+#define CLOSESOCKET(fd) close(fd)
+#endif
+#ifdef _WIN32
+#define CLOSESOCKET(fd) closesocket(fd)
+#endif
+
+/* ------------------------ udpreceive~ ----------------------------- */
+
+typedef struct _udpreceive_tilde
+{
+ t_object x_obj;
+ t_outlet *x_outlet1;
+ t_outlet *x_outlet2;
+ t_outlet *x_addrout;
+ t_clock *x_clock;
+ t_atom x_addrbytes[5];
+ int x_socket;
+ int x_connectsocket;
+ int x_nconnections;
+ long x_addr;
+ unsigned short x_port;
+ t_symbol *x_hostname;
+ int x_error;
+ int x_buffering;
+ char x_msg[256];
+
+ /* buffering */
+ int x_framein;// index of next empty frame in x_frames[]
+ int x_frameout;// index of next frame to play back from x_frames[]
+ t_frame x_frames[DEFAULT_AUDIO_BUFFER_FRAMES];
+ int x_maxframes;
+ long x_tag_errors;
+ int x_sync;// zero if we didn't receive a tag when we expected one
+ int x_blocksize;
+ int x_blocksperrecv;
+ int x_blockssincerecv;
+
+ int x_nbytes;
+ int x_counter;// count of received frames
+ int x_average[DEFAULT_AVERAGE_NUMBER];
+ int x_averagecur;
+ int x_underflow;
+ int x_overflow;
+ int x_valid;
+ long x_samplerate;
+ int x_noutlets;
+ int x_vecsize;
+ t_int **x_myvec; /* vector we pass on to the DSP routine */
+} t_udpreceive_tilde;
+
+/* function prototypes */
+static void udpreceive_tilde_closesocket(t_udpreceive_tilde* x);
+static void udpreceive_tilde_reset(t_udpreceive_tilde* x, t_floatarg buffer);
+static void udpreceive_tilde_datapoll(t_udpreceive_tilde *x);
+static void udpreceive_tilde_connectpoll(t_udpreceive_tilde *x);
+static int udpreceive_tilde_createsocket(t_udpreceive_tilde* x, int portno);
+static t_int *udpreceive_tilde_perform(t_int *w);
+static void udpreceive_tilde_dsp(t_udpreceive_tilde *x, t_signal **sp);
+static void udpreceive_tilde_info(t_udpreceive_tilde *x);
+static void udpreceive_tilde_tick(t_udpreceive_tilde *x);
+static void *udpreceive_tilde_new(t_floatarg fportno, t_floatarg outlets, t_floatarg blocksize);
+static void udpreceive_tilde_free(t_udpreceive_tilde *x);
+void udpreceive_tilde_setup(void);
+static int udpreceive_tilde_sockerror(char *s);
+static int udpreceive_tilde_setsocketoptions(int sockfd);
+/* these would require to include some headers that are different
+ between pd 0.36 and later, so it's easier to do it like this! */
+EXTERN void sys_rmpollfn(int fd);
+EXTERN void sys_addpollfn(int fd, void* fn, void *ptr);
+
+static t_class *udpreceive_tilde_class;
+static t_symbol *ps_format, *ps_channels, *ps_framesize, *ps_overflow, *ps_underflow, *ps_packets,
+ *ps_queuesize, *ps_average, *ps_sf_float, *ps_sf_16bit, *ps_sf_8bit,
+ *ps_sf_mp3, *ps_sf_aac, *ps_sf_unknown, *ps_bitrate, *ps_hostname, *ps_nothing,
+ *ps_tag_errors;
+
+/* remove all pollfunctions and close socket */
+static void udpreceive_tilde_closesocket(t_udpreceive_tilde* x)
+{
+ sys_rmpollfn(x->x_socket);
+ outlet_float(x->x_outlet1, 0);
+ CLOSESOCKET(x->x_socket);
+ x->x_socket = -1;
+}
+
+static void udpreceive_tilde_reset(t_udpreceive_tilde* x, t_floatarg buffer)
+{
+ int i;
+
+ x->x_counter = 0;
+ x->x_nbytes = 0;
+ x->x_framein = 0;
+ x->x_frameout = 0;
+ x->x_blockssincerecv = 0;
+ x->x_blocksperrecv = x->x_blocksize / x->x_vecsize;
+ x->x_tag_errors = 0;
+
+ for (i = 0; i < DEFAULT_AVERAGE_NUMBER; i++)
+ x->x_average[i] = x->x_maxframes;
+ x->x_averagecur = 0;
+
+ i = (int)buffer;
+ if ((i > 0)&&(i < DEFAULT_AUDIO_BUFFER_FRAMES))
+ {
+ x->x_maxframes = i;
+ post("udpreceive~: set buffer to %d frames)", x->x_maxframes);
+ }
+ else if (i != 0) /* special case of 0 leaves buffer size unchanged */
+ {
+ post("udpreceive~: buffer must be between 1 and %d frames)", DEFAULT_AUDIO_BUFFER_FRAMES-1);
+ }
+ x->x_underflow = 0;
+ x->x_overflow = 0;
+ x->x_buffering = 1;
+}
+
+static void udpreceive_tilde_datapoll(t_udpreceive_tilde *x)
+{
+ int ret;
+ int n;
+ long addr;
+ unsigned short port;
+ struct sockaddr_in from;
+ socklen_t fromlen = sizeof(from);
+ t_tag *tag_ptr;
+
+ n = x->x_nbytes;
+
+ if (x->x_nbytes == 0) /* we ate all the samples and need a new header tag */
+ {
+ tag_ptr = &x->x_frames[x->x_framein].tag;
+ /* receive header tag */
+ ret = recvfrom(x->x_socket, (char*)tag_ptr, sizeof(t_tag), 0,
+ (struct sockaddr *)&from, &fromlen);
+ /* get the sender's ip */
+ addr = ntohl(from.sin_addr.s_addr);
+ port = ntohs(from.sin_port);
+ /* output addr/port only if changed */
+ if (!((addr == x->x_addr)&&(port == x->x_port)))
+ {
+ x->x_addr = addr;
+ x->x_port = port;
+ 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);
+ x->x_addrbytes[4].a_w.w_float = x->x_port;
+ outlet_list(x->x_addrout, &s_list, 5L, x->x_addrbytes);
+ }
+ if (ret <= 0) /* error */
+ {
+ if (0 == udpreceive_tilde_sockerror("recv tag")) return;
+ udpreceive_tilde_reset(x, 0);
+ return;
+ }
+ else if (ret != sizeof(t_tag))
+ {
+ /* incomplete header tag: return and try again later */
+ /* in the hope that more data will be available */
+ error("udpreceive~: got incomplete header tag");
+ return;
+ }
+ /* make sure this is really a tag */
+ if (!((tag_ptr->tag[0] == 'T')&&(tag_ptr->tag[1] == 'A')&&(tag_ptr->tag[2] == 'G')&&(tag_ptr->tag[3] == '!')))
+ {
+ ++x->x_tag_errors;
+ if (x->x_sync) error("udpreceive~: bad header tag (%d)", x->x_tag_errors);
+ x->x_sync = 0;
+ /* tag length is 16 bytes, a multiple of the data frame size, so eventually we should resync on a tag */
+ return;
+ }
+ /* adjust byte order if necessary */
+ tag_ptr->count = ntohl(tag_ptr->count);
+ tag_ptr->framesize = ntohl(tag_ptr->framesize);
+
+ /* get info from header tag */
+ if (tag_ptr->channels > (x->x_noutlets-1))
+ {
+ error("udpreceive~: incoming stream has too many channels (%d)", tag_ptr->channels);
+ x->x_counter = 0;
+ return;
+ }
+ x->x_nbytes = n = tag_ptr->framesize;
+ x->x_sync = 1;
+ }
+ else /* we already have header tag or some data and need more */
+ {
+ ret = recvfrom(x->x_socket, (char*)x->x_frames[x->x_framein].data + x->x_frames[x->x_framein].tag.framesize - n,
+ n, 0, (struct sockaddr *)&from, &fromlen);
+ if (ret > 0)
+ {
+ n -= ret;
+ }
+ else if (ret < 0) /* error */
+ {
+ if ( 0 == (ret = udpreceive_tilde_sockerror("recv data"))) return;
+#ifdef _WIN32
+ if ( ret == WSAEFAULT)
+#else
+ if ( ret == EFAULT)
+#endif
+ {
+ post ("udpreceive~: EFAULT: %p %lu %d", x->x_frames[x->x_framein].data, x->x_frames[x->x_framein].tag.framesize, n);
+ return;
+ }
+ udpreceive_tilde_reset(x, 0);
+ return;
+ }
+
+ x->x_nbytes = n;
+ if (n == 0) /* a complete packet is received */
+ {
+ if (x->x_frames[x->x_framein].tag.format == SF_AAC)
+ {
+ error("udpreceive~: don't know how to decode AAC format");
+ return;
+ }
+ x->x_counter++;
+ x->x_framein++;
+ x->x_framein %= DEFAULT_AUDIO_BUFFER_FRAMES;
+
+ /* check for buffer overflow */
+ if (x->x_framein == x->x_frameout)
+ {
+ x->x_overflow++;
+ }
+ }
+ }
+}
+
+static void udpreceive_tilde_connectpoll(t_udpreceive_tilde *x)
+{
+ socklen_t sockaddrlen = sizeof(struct sockaddr);
+ struct sockaddr_in incomer_address;
+ int fd = accept(x->x_connectsocket, (struct sockaddr*)&incomer_address, &sockaddrlen);
+
+ if (fd < 0)
+ {
+ post("udpreceive~: accept failed");
+ return;
+ }
+ if (x->x_socket != -1)
+ {
+ post("udpreceive~: new connection");
+ udpreceive_tilde_closesocket(x);
+ }
+
+ udpreceive_tilde_reset(x, 0);
+ x->x_socket = fd;
+ x->x_nbytes = 0;
+ x->x_hostname = gensym(inet_ntoa(incomer_address.sin_addr));
+ sys_addpollfn(fd, udpreceive_tilde_datapoll, x);
+ outlet_float(x->x_outlet1, 1);
+}
+
+static int udpreceive_tilde_createsocket(t_udpreceive_tilde* x, int portno)
+{
+ struct sockaddr_in server;
+ int sockfd;
+
+ /* create a socket */
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (sockfd < 0)
+ {
+ udpreceive_tilde_sockerror("socket");
+ return 0;
+ }
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = INADDR_ANY;
+
+ /* assign server port number */
+
+ server.sin_port = htons((u_short)portno);
+ post("udpreceive~: listening to port number %d", portno);
+
+ udpreceive_tilde_setsocketoptions(sockfd);
+
+ /* name the socket */
+ if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
+ {
+ udpreceive_tilde_sockerror("bind");
+ CLOSESOCKET(sockfd);
+ return 0;
+ }
+
+ x->x_socket = sockfd;
+ x->x_nbytes = 0;
+ sys_addpollfn(sockfd, udpreceive_tilde_datapoll, x);
+ return 1;
+}
+
+/* Queue is 1 to 16 frames long */
+#define QUEUESIZE (int)((x->x_framein + DEFAULT_AUDIO_BUFFER_FRAMES - x->x_frameout) % DEFAULT_AUDIO_BUFFER_FRAMES)
+/* Block is a set of sample vectors inside a frame, one vector per channel */
+#define BLOCKOFFSET (x->x_blockssincerecv * x->x_vecsize * x->x_frames[x->x_frameout].tag.channels)
+
+static t_int *udpreceive_tilde_perform(t_int *w)
+{
+ t_udpreceive_tilde *x = (t_udpreceive_tilde*) (w[1]);
+ int n = (int)(w[2]);
+ t_float *out[DEFAULT_AUDIO_CHANNELS];
+ const int offset = 3;
+ const int channels = x->x_frames[x->x_frameout].tag.channels;
+ int i = 0;
+
+ x->x_valid = 0;
+ for (i = 0; i < x->x_noutlets; i++)
+ {
+ out[i] = (t_float *)(w[offset + i]);
+ }
+
+ /* set our vector size to the local vector size */
+ if (n != x->x_vecsize)
+ {
+ x->x_vecsize = n;
+ x->x_blocksperrecv = x->x_blocksize / x->x_vecsize;
+ x->x_blockssincerecv = 0;
+ }
+
+ /* check whether there is enough data in buffer */
+ if (x->x_buffering && (x->x_counter < x->x_maxframes))
+ {
+ goto bail;
+ }
+ x->x_buffering = 0;
+
+ /* check for buffer underflow */
+ if (x->x_framein == x->x_frameout)
+ {
+ x->x_underflow++;
+ goto bail;
+ }
+
+ x->x_valid = 1;
+ /* queue balancing */
+ x->x_average[x->x_averagecur] = QUEUESIZE;
+ if (++x->x_averagecur >= DEFAULT_AVERAGE_NUMBER)
+ x->x_averagecur = 0;
+
+ switch (x->x_frames[x->x_frameout].tag.format)
+ {
+ case SF_FLOAT:
+ {
+ int32_t* buf = (int32_t *)x->x_frames[x->x_frameout].data + BLOCKOFFSET;
+ flint fl;
+
+ /* swap bytes if necessary */
+ while (n--)
+ {
+ for (i = 0; i < channels; i++)
+ {
+ fl.i32 = ntohl(*buf++);
+ *out[i]++ = fl.f32;
+ }
+ for (i = channels; i < (x->x_noutlets-1); i++)
+ {
+ *out[i]++ = 0.;
+ }
+ *out[i]++ = x->x_valid;
+ }
+ x->x_error = 0;
+ break;
+ }
+ case SF_16BIT:
+ {
+ short* buf = (short *)x->x_frames[x->x_frameout].data + BLOCKOFFSET;
+ /* swap bytes if necessary */
+ while (n--)
+ {
+ for (i = 0; i < channels; i++)
+ {
+ *out[i]++ = (t_float)((short)(ntohs(*buf++)) * 3.051850e-05);
+ }
+ for (i = channels; i < (x->x_noutlets-1); i++)
+ {
+ *out[i]++ = 0.;
+ }
+ *out[i]++ = x->x_valid;
+ }
+ x->x_error = 0;
+ break;
+ }
+ case SF_8BIT:
+ {
+ unsigned char* buf = (unsigned char *)x->x_frames[x->x_frameout].data + BLOCKOFFSET;
+
+ while (n--)
+ {
+ for (i = 0; i < channels; i++)
+ {
+ *out[i]++ = (t_float)((0.0078125 * (*buf++)) - 1.0);
+ }
+ for (i = channels; i < (x->x_noutlets-1); i++)
+ {
+ *out[i]++ = 0.;
+ }
+ *out[i]++ = x->x_valid;
+ }
+ x->x_error = 0;
+ break;
+ }
+ case SF_MP3:
+ {
+ if (x->x_error != 1)
+ {
+ x->x_error = 1;
+ sprintf(x->x_msg, "udpreceive~: mp3 format not supported");
+ clock_delay(x->x_clock, 0);
+ }
+ break;
+ }
+ case SF_AAC:
+ {
+ if (x->x_error != 2)
+ {
+ x->x_error = 2;
+ sprintf(x->x_msg, "udpreceive~: aac format not supported");
+ clock_delay(x->x_clock, 0);
+ }
+ break;
+ }
+ default:
+ if (x->x_error != 3)
+ {
+ x->x_error = 3;
+ sprintf(x->x_msg, "udpreceive~: unknown format (%d)",x->x_frames[x->x_frameout].tag.format);
+ clock_delay(x->x_clock, 0);
+ }
+ break;
+ }
+
+ if (!(x->x_blockssincerecv < x->x_blocksperrecv - 1))
+ {
+ x->x_blockssincerecv = 0;
+ x->x_frameout++;
+ x->x_frameout %= DEFAULT_AUDIO_BUFFER_FRAMES;
+ }
+ else x->x_blockssincerecv++;
+
+ return (w + offset + x->x_noutlets);
+
+bail:
+ /* set output to zero */
+ while (n--) for (i = 0; i < x->x_noutlets; i++) *(out[i]++) = 0.;
+ return (w + offset + x->x_noutlets);
+}
+
+static void udpreceive_tilde_dsp(t_udpreceive_tilde *x, t_signal **sp)
+{
+ int i;
+
+ x->x_myvec[0] = (t_int*)x;
+ x->x_myvec[1] = (t_int*)sp[0]->s_n;
+
+ x->x_samplerate = (long)sp[0]->s_sr;
+
+ if (x->x_blocksize % sp[0]->s_n)
+ {
+ error("udpreceive~: signal vector size too large (needs to be even divisor of %d)", x->x_blocksize);
+ }
+ else
+ {
+ for (i = 0; i < x->x_noutlets; i++)
+ {
+ x->x_myvec[2 + i] = (t_int*)sp[i + 1]->s_vec;
+ }
+ dsp_addv(udpreceive_tilde_perform, x->x_noutlets + 2, (t_int*)x->x_myvec);
+ }
+}
+
+/* send stream info */
+static void udpreceive_tilde_info(t_udpreceive_tilde *x)
+{
+ t_atom list[2];
+ t_symbol *sf_format;
+ t_float bitrate;
+ int i, avg = 0;
+
+ for (i = 0; i < DEFAULT_AVERAGE_NUMBER; i++)
+ avg += x->x_average[i];
+
+ bitrate = (t_float)((SF_SIZEOF(x->x_frames[x->x_frameout].tag.format) * x->x_samplerate * 8 * x->x_frames[x->x_frameout].tag.channels) / 1000.);
+
+ switch (x->x_frames[x->x_frameout].tag.format)
+ {
+ case SF_FLOAT:
+ {
+ sf_format = ps_sf_float;
+ break;
+ }
+ case SF_16BIT:
+ {
+ sf_format = ps_sf_16bit;
+ break;
+ }
+ case SF_8BIT:
+ {
+ sf_format = ps_sf_8bit;
+ break;
+ }
+ case SF_MP3:
+ {
+ sf_format = ps_sf_mp3;
+ break;
+ }
+ case SF_AAC:
+ {
+ sf_format = ps_sf_aac;
+ break;
+ }
+ default:
+ {
+ sf_format = ps_sf_unknown;
+ break;
+ }
+ }
+
+ /* --- stream information (t_tag) --- */
+ /* audio format */
+ SETSYMBOL(list, (t_symbol *)sf_format);
+ outlet_anything(x->x_outlet2, ps_format, 1, list);
+
+ /* channels */
+ SETFLOAT(list, (t_float)x->x_frames[x->x_frameout].tag.channels);
+ outlet_anything(x->x_outlet2, ps_channels, 1, list);
+
+ /* framesize */
+ SETFLOAT(list, (t_float)x->x_frames[x->x_frameout].tag.framesize);
+ outlet_anything(x->x_outlet2, ps_framesize, 1, list);
+
+ /* bitrate */
+ SETFLOAT(list, (t_float)bitrate);
+ outlet_anything(x->x_outlet2, ps_bitrate, 1, list);
+
+ /* --- internal info (buffer and network) --- */
+ /* overflow */
+ SETFLOAT(list, (t_float)x->x_overflow);
+ outlet_anything(x->x_outlet2, ps_overflow, 1, list);
+
+ /* underflow */
+ SETFLOAT(list, (t_float)x->x_underflow);
+ outlet_anything(x->x_outlet2, ps_underflow, 1, list);
+
+ /* queuesize */
+ SETFLOAT(list, (t_float)QUEUESIZE);
+ outlet_anything(x->x_outlet2, ps_queuesize, 1, list);
+
+ /* average queuesize */
+ SETFLOAT(list, (t_float)((t_float)avg / (t_float)DEFAULT_AVERAGE_NUMBER));
+ outlet_anything(x->x_outlet2, ps_average, 1, list);
+
+ /* total packets */
+ SETFLOAT(list, (t_float)x->x_counter);
+ outlet_anything(x->x_outlet2, ps_packets, 1, list);
+
+ /* total tag errors */
+ SETFLOAT(list, (t_float)x->x_tag_errors);
+ outlet_anything(x->x_outlet2, ps_tag_errors, 1, list);
+
+ outlet_list(x->x_addrout, &s_list, 5L, x->x_addrbytes);
+}
+
+static void udpreceive_tilde_tick(t_udpreceive_tilde *x)
+{
+/* post a message once, outside of perform routine */
+ post("%s", x->x_msg);
+}
+
+static void *udpreceive_tilde_new(t_floatarg fportno, t_floatarg outlets, t_floatarg blocksize)
+{
+ t_udpreceive_tilde *x;
+ int i;
+
+ if (fportno == 0) fportno = DEFAULT_PORT;
+
+ x = (t_udpreceive_tilde *)pd_new(udpreceive_tilde_class);
+ if (x)
+ {
+ for (i = sizeof(t_object); i < (int)sizeof(t_udpreceive_tilde); i++)
+ ((char *)x)[i] = 0;
+
+ if ((int)outlets < 1 || (int)outlets > DEFAULT_AUDIO_CHANNELS)
+ {
+ error("udpreceive~: Number of channels must be between 1 and %d", DEFAULT_AUDIO_CHANNELS);
+ return NULL;
+ }
+
+ x->x_noutlets = (int)outlets + 1; // extra outlet for valid flag
+ for (i = 0; i < x->x_noutlets; i++)
+ outlet_new(&x->x_obj, &s_signal);
+ x->x_outlet2 = outlet_new(&x->x_obj, &s_anything);
+ x->x_addrout = outlet_new(&x->x_obj, &s_list);
+ for (i = 0; i < 5; ++i)
+ {
+ x->x_addrbytes[i].a_type = A_FLOAT;
+ x->x_addrbytes[i].a_w.w_float = 0;
+ }
+ x->x_addr = 0;
+ x->x_port = 0;
+ x->x_myvec = (t_int **)t_getbytes(sizeof(t_int *) * (x->x_noutlets + 3));
+ if (!x->x_myvec)
+ {
+ error("udpreceive~: out of memory");
+ return NULL;
+ }
+
+ x->x_connectsocket = x->x_socket = -1;
+ x->x_nconnections = x->x_underflow = x->x_overflow = 0;
+ x->x_hostname = ps_nothing;
+/* allocate space for 16 frames of 1024 X numchannels floats*/
+ for (i = 0; i < DEFAULT_AUDIO_BUFFER_FRAMES; i++)
+ {
+ x->x_frames[i].data = (char *)t_getbytes(DEFAULT_AUDIO_BUFFER_SIZE * (x->x_noutlets-1) * sizeof(t_float));
+ }
+ x->x_clock = clock_new(&x->x_obj.ob_pd, (t_method)udpreceive_tilde_tick);
+
+ x->x_sync = 1;
+ x->x_tag_errors = x->x_framein = x->x_frameout = x->x_valid = 0;
+ x->x_maxframes = DEFAULT_QUEUE_LENGTH;
+ x->x_vecsize = 64; /* we'll update this later */
+ if (blocksize == 0) x->x_blocksize = DEFAULT_AUDIO_BUFFER_SIZE;
+ else if (DEFAULT_AUDIO_BUFFER_SIZE%(int)blocksize)
+ {
+ error("udpreceive~: blocksize must fit snugly in %d", DEFAULT_AUDIO_BUFFER_SIZE);
+ return NULL;
+ }
+ else x->x_blocksize = (int)blocksize; //DEFAULT_AUDIO_BUFFER_SIZE; /* <-- the only place blocksize is set */
+ x->x_blockssincerecv = 0;
+ x->x_blocksperrecv = x->x_blocksize / x->x_vecsize;
+ x->x_buffering = 1;
+
+ if (!udpreceive_tilde_createsocket(x, (int)fportno))
+ {
+ error("udpreceive~: failed to create listening socket");
+ return (NULL);
+ }
+ }
+ return (x);
+}
+
+static void udpreceive_tilde_free(t_udpreceive_tilde *x)
+{
+ int i;
+
+ if (x->x_connectsocket != -1)
+ {
+ sys_rmpollfn(x->x_connectsocket);
+ CLOSESOCKET(x->x_connectsocket);
+ }
+ if (x->x_socket != -1)
+ {
+ sys_rmpollfn(x->x_socket);
+ CLOSESOCKET(x->x_socket);
+ }
+
+ /* free memory */
+ t_freebytes(x->x_myvec, sizeof(t_int *) * (x->x_noutlets + 3));
+ for (i = 0; i < DEFAULT_AUDIO_BUFFER_FRAMES; i++)
+ {
+ t_freebytes(x->x_frames[i].data, DEFAULT_AUDIO_BUFFER_SIZE * (x->x_noutlets-1) * sizeof(t_float));
+ }
+ clock_free(x->x_clock);
+}
+
+void udpreceive_tilde_setup(void)
+{
+ udpreceive_tilde_class = class_new(gensym("udpreceive~"),
+ (t_newmethod) udpreceive_tilde_new, (t_method) udpreceive_tilde_free,
+ sizeof(t_udpreceive_tilde), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_NULL);
+
+ class_addmethod(udpreceive_tilde_class, nullfn, gensym("signal"), 0);
+ class_addmethod(udpreceive_tilde_class, (t_method)udpreceive_tilde_info, gensym("info"), 0);
+ class_addmethod(udpreceive_tilde_class, (t_method)udpreceive_tilde_dsp, gensym("dsp"), 0);
+ class_addmethod(udpreceive_tilde_class, (t_method)udpreceive_tilde_reset, gensym("reset"), A_DEFFLOAT, 0);
+ class_addmethod(udpreceive_tilde_class, (t_method)udpreceive_tilde_reset, gensym("buffer"), A_DEFFLOAT, 0);
+ post("udpreceive~ v%s, (c) 2004 Olaf Matthes, 2010 Martin Peach", VERSION);
+
+ ps_format = gensym("format");
+ ps_tag_errors = gensym("tag_errors");
+ ps_channels = gensym("channels");
+ ps_framesize = gensym("framesize");
+ ps_bitrate = gensym("bitrate");
+ ps_overflow = gensym("overflow");
+ ps_underflow = gensym("underflow");
+ ps_queuesize = gensym("queuesize");
+ ps_average = gensym("average");
+ ps_packets = gensym("packets");
+ ps_hostname = gensym("ipaddr");
+ ps_sf_float = gensym("_float_");
+ ps_sf_16bit = gensym("_16bit_");
+ ps_sf_8bit = gensym("_8bit_");
+ ps_sf_mp3 = gensym("_mp3_");
+ ps_sf_aac = gensym("_aac_");
+ ps_sf_unknown = gensym("_unknown_");
+ ps_nothing = gensym("");
+}
+
+/* error handlers */
+static int udpreceive_tilde_sockerror(char *s)
+{
+#ifdef _WIN32
+ int err = WSAGetLastError();
+ if (err == 10054) return 1;
+ else if (err == 10040) post("udpreceive~: %s: message too long (%d)", s, err);
+ else if (err == 10053) post("udpreceive~: %s: software caused connection abort (%d)", s, err);
+ else if (err == 10055) post("udpreceive~: %s: no buffer space available (%d)", s, err);
+ else if (err == 10060) post("udpreceive~: %s: connection timed out (%d)", s, err);
+ else if (err == 10061) post("udpreceive~: %s: connection refused (%d)", s, err);
+ else post("udpreceive~: %s: %s (%d)", s, strerror(err), err);
+#else
+ int err = errno;
+ post("udpreceive~: %s: %s (%d)", s, strerror(err), err);
+#endif
+#ifdef _WIN32
+ if (err == WSAEWOULDBLOCK)
+#endif
+#if defined(UNIX) || defined(unix)
+ if (err == EAGAIN)
+#endif
+ {
+ return 0; /* recoverable error */
+ }
+ return err; /* indicate non-recoverable error */
+}
+
+static int udpreceive_tilde_setsocketoptions(int sockfd)
+{
+ int sockopt = 1;
+ if (setsockopt(sockfd, SOL_IP, TCP_NODELAY, (const char*)&sockopt, sizeof(int)) < 0)
+ post("udpreceive~: setsockopt NODELAY failed");
+
+ sockopt = 1;
+ if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&sockopt, sizeof(int)) < 0)
+ post("udpreceive~: setsockopt REUSEADDR failed");
+ return 0;
+}
+
+/* fin udpreceive~.c */
diff --git a/udpsend-help.pd b/udpsend-help.pd
new file mode 100644
index 0000000..0114f07
--- /dev/null
+++ b/udpsend-help.pd
@@ -0,0 +1,31 @@
+#N canvas 609 110 590 348 12;
+#X msg 72 182 disconnect;
+#X msg 16 59 connect 127.0.0.1 9997;
+#X obj 16 306 tgl 15 0 empty empty connected 20 7 0 8 -24198 -241291
+-1 1 1;
+#X text 220 60 <--first;
+#X msg 25 81 send 0 1 2 3;
+#X text 8 5 udpsend sends bytes over a udp connection.;
+#X text 8 28 Used in conjunction with packOSC will send OSC over udp
+;
+#X obj 16 283 udpsend;
+#X msg 32 103 send ../doc/5.reference/test.txt;
+#X obj 387 151 openpanel;
+#X msg 387 175 send \$1;
+#X obj 387 132 bng 15 250 50 0 empty empty empty 17 7 0 10 -24198 -241291
+-1;
+#X text 327 104 send a file;
+#X text 141 81 send raw data;
+#X text 410 130 ...any file;
+#X msg 40 126 99 98 97;
+#X text 361 311 Martin Peach 2007/06/20;
+#X text 120 126 'send' prefix is optional;
+#X connect 0 0 7 0;
+#X connect 1 0 7 0;
+#X connect 4 0 7 0;
+#X connect 7 0 2 0;
+#X connect 8 0 7 0;
+#X connect 9 0 10 0;
+#X connect 10 0 7 0;
+#X connect 11 0 9 0;
+#X connect 15 0 7 0;
diff --git a/udpsend.c b/udpsend.c
new file mode 100644
index 0000000..98db672
--- /dev/null
+++ b/udpsend.c
@@ -0,0 +1,257 @@
+/* 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 _WIN32
+#include <winsock2.h>
+#else
+#include <sys/types.h>
+#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;
+
+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;
+ int broadcast = 1;/* nonzero is true */
+
+ 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;
+ }
+/* Based on zmoelnig's patch 2221504:
+Enable sending of broadcast messages (if hostname is a broadcast address)*/
+#ifdef SO_BROADCAST
+ if( 0 != setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (const void *)&broadcast, sizeof(broadcast)))
+ {
+ pd_error(x, "couldn't switch to broadcast mode");
+ }
+#endif /* SO_BROADCAST */
+
+ /* 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)
+{
+#define BYTE_BUF_LEN 65536 // arbitrary maximum similar to max IP packet size
+ static char byte_buf[BYTE_BUF_LEN];
+ int d;
+ int i, j;
+ 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;
+ char fpath[FILENAME_MAX];
+ FILE *fptr;
+
+#ifdef DEBUG
+ post("s: %s", s->s_name);
+ post("argc: %d", argc);
+#endif
+ for (i = j = 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[j++] = c;
+ }
+ else if (argv[i].a_type == A_SYMBOL)
+ {
+
+ atom_string(&argv[i], fpath, FILENAME_MAX);
+#ifdef DEBUG
+ post ("udpsend fname: %s", fpath);
+#endif
+ fptr = fopen(fpath, "rb");
+ if (fptr == NULL)
+ {
+ post("udpsend: unable to open \"%s\"", fpath);
+ return;
+ }
+ rewind(fptr);
+#ifdef DEBUG
+ post("udpsend: d is %d", d);
+#endif
+ while ((d = fgetc(fptr)) != EOF)
+ {
+ byte_buf[j++] = (char)(d & 0x0FF);
+#ifdef DEBUG
+ post("udpsend: byte_buf[%d] = %d", j-1, byte_buf[j-1]);
+#endif
+ if (j >= BYTE_BUF_LEN)
+ {
+ post ("udpsend: file too long, truncating at %lu", BYTE_BUF_LEN);
+ break;
+ }
+ }
+ fclose(fptr);
+ fptr = NULL;
+ post("udpsend: read \"%s\" length %d byte%s", fpath, j, ((d==1)?"":"s"));
+ }
+ else
+ {
+ error("udpsend_send: item %d is not a float or a file name", i);
+ return;
+ }
+ }
+
+ length = j;
+ 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);
+}
+
+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);
+ class_addlist(udpsend_class, (t_method)udpsend_send);
+}
+
+/* end udpsend.c*/
+
diff --git a/udpsend~-help.pd b/udpsend~-help.pd
new file mode 100644
index 0000000..bd03c81
--- /dev/null
+++ b/udpsend~-help.pd
@@ -0,0 +1,173 @@
+#N canvas 197 27 785 803 10;
+#X obj -178 225 osc~ 440;
+#X msg -398 60 disconnect;
+#X msg -353 105 format float;
+#X msg -332 126 format 16bit;
+#X msg -311 147 format 8bit;
+#X msg -440 18 connect localhost 8008;
+#X text -244 103 float is the most expensive with the best resolution
+(32bit) \, default is 16bit;
+#X msg -419 39 connect 255.255.255.255 8008;
+#X obj 52 687 print udpreceive~;
+#X obj 224 369 print udpsend~;
+#X obj -178 320 tgl 15 0 empty empty empty 17 7 0 10 -4034 -1 -1 0
+1;
+#X symbolatom -97 435 10 0 0 0 - - -;
+#X floatatom -44 369 5 0 0 0 - - -;
+#X floatatom 10 391 9 0 0 0 - - -;
+#X floatatom 63 411 9 0 0 0 - - -;
+#X symbolatom 117 390 10 0 0 0 - - -;
+#X obj -97 411 prepend set;
+#X obj 117 367 prepend set;
+#X text -102 368 channels:;
+#X text -144 442 format:;
+#X text 13 411 bitrate:;
+#X text -51 391 framesize:;
+#X text 98 389 to:;
+#X msg -216 242 info;
+#X symbolatom -293 774 10 0 0 0 - - -;
+#X floatatom -259 708 5 0 0 0 - - -;
+#X floatatom -224 687 7 0 0 0 - - -;
+#X floatatom -190 752 9 0 0 0 - - -;
+#X obj -293 733 prepend set;
+#X text -323 707 channels:;
+#X text -339 773 format:;
+#X text -239 751 bitrate:;
+#X text -288 686 framesize:;
+#X floatatom -155 709 9 0 0 0 - - -;
+#X floatatom -121 687 9 0 0 0 - - -;
+#X floatatom -86 730 5 0 0 0 - - -;
+#X floatatom -52 709 5 0 0 0 - - -;
+#X text -212 708 overflow:;
+#X text -181 686 underflow:;
+#X text -147 729 queuesize:;
+#X text -100 708 average:;
+#X msg -435 478 info;
+#X text -237 34 broadcast to everybody on your local subnet listening
+on the specified port;
+#X msg -455 458 reset;
+#X text -402 477 status info to rightmost outlet;
+#X text -415 241 status info to rightmost outlet;
+#X text -417 457 reset underflow & overflow counters;
+#X floatatom -260 589 3 0 0 0 - - -;
+#X floatatom -237 589 3 0 0 0 - - -;
+#X floatatom -214 589 3 0 0 0 - - -;
+#X floatatom -191 589 3 0 0 0 - - -;
+#X floatatom -167 589 5 0 0 0 - - -;
+#X obj -260 563 unpack 0 0 0 0 0;
+#X text -297 588 from:;
+#X obj -179 268 *~;
+#X floatatom -164 150 5 0 0 0 - - -;
+#X text -66 148 Framesize = (blocksize) X (number of channels) X (bytes
+per sample);
+#X obj -97 339 route format channels framesize bitrate ipaddr vecsize
+;
+#X floatatom 170 430 9 0 0 0 - - -;
+#X text 70 430 dsp vector size:;
+#X msg -258 200 channels \$1;
+#X obj -412 185 hradio 15 1 0 4 empty empty empty 0 -8 0 10 -4034 -1
+-1 0;
+#X obj -178 297 udpsend~ 2 512;
+#X text -88 296 sends 2 dsp-channels using 512-sample blocks;
+#X obj -389 541 udpreceive~ 8008 2 512;
+#X obj -388 572 dac~ 1 2;
+#X text 68 448 (blocksize must be a multiple of this);
+#X text -160 318 1 = transmitting;
+#X obj -97 246 noise~;
+#X obj -98 268 *~;
+#X obj -164 170 / 100;
+#X text -455 338 Based on: [netreceive~] and [netsend~]by Olaf Matthes
+;
+#X floatatom -17 752 9 0 0 0 - - -;
+#X text -69 751 packets:;
+#X text -50 177 Default blocksize is 2048 The number of samples per
+block must be an integer multiple of the number of samples in one signal
+vector.;
+#X text -28 225 Arguments: (1st required \, 2nd optional) 1:number
+of channels to send. 2:blocksize = number of samples per channel per
+frame. (Blocksize of sender and receiver must be the same.);
+#X text -127 554 To communicate \, a [udpreceive~] and [udpsend~] pair
+must have the same number of channels and the same blocksize. Also
+[udpsend~] must [connect( to the port on which [udpreceive~] is listening.
+;
+#X text -329 59 stop transmitting;
+#X text -355 76 format defines the resolution of the sent signal and
+may be changed on-the-fly;
+#X text -450 200 number of channels to transmit;
+#X msg -414 499 buffer 2;
+#X text -352 498 set number of frames to buffer before playback;
+#X text -458 365 [udpsend~] transmits dsp vectors ("audio") via UDP.
+UDP is a connectionless protocol \, so [udpsend~] will transmit even
+if nothing is receiving.;
+#X text -299 18 connect to <hostname> <port> and begin transmitting
+;
+#X text -456 313 [udpreceive~] and [udpsend~];
+#X text -248 540 receives 2 channels on port 8008 Same blocksize as
+udpsend~;
+#X floatatom -412 216 5 0 0 0 - - -;
+#X obj -288 172 tgl 15 0 empty empty toggle_connection 17 7 0 10 -4034
+-1 -1 0 1;
+#X obj -293 650 route format channels framesize bitrate overflow underflow
+queuesize average packets tag_errors;
+#X floatatom 17 730 9 0 0 0 - - -;
+#X text -49 729 tag errors:;
+#X obj -326 601 env~;
+#X floatatom -326 624 9 0 0 0 - - -;
+#X text -266 613 The rightmost signal outlet outputs 1 if the stream
+is valid \, else 0;
+#X text -455 326 Author: Martin Peach 2010/03/22;
+#X connect 0 0 54 0;
+#X connect 1 0 62 0;
+#X connect 2 0 62 0;
+#X connect 3 0 62 0;
+#X connect 4 0 62 0;
+#X connect 5 0 62 0;
+#X connect 7 0 62 0;
+#X connect 16 0 11 0;
+#X connect 17 0 15 0;
+#X connect 23 0 62 0;
+#X connect 28 0 24 0;
+#X connect 41 0 64 0;
+#X connect 43 0 64 0;
+#X connect 52 0 47 0;
+#X connect 52 1 48 0;
+#X connect 52 2 49 0;
+#X connect 52 3 50 0;
+#X connect 52 4 51 0;
+#X connect 54 0 62 0;
+#X connect 55 0 70 0;
+#X connect 57 0 16 0;
+#X connect 57 1 12 0;
+#X connect 57 2 13 0;
+#X connect 57 3 14 0;
+#X connect 57 4 17 0;
+#X connect 57 5 58 0;
+#X connect 57 6 9 0;
+#X connect 60 0 62 0;
+#X connect 61 0 60 0;
+#X connect 61 0 86 0;
+#X connect 62 0 10 0;
+#X connect 62 1 57 0;
+#X connect 64 0 65 0;
+#X connect 64 1 65 1;
+#X connect 64 2 91 0;
+#X connect 64 3 88 0;
+#X connect 64 4 52 0;
+#X connect 68 0 69 0;
+#X connect 69 0 62 1;
+#X connect 70 0 54 1;
+#X connect 70 0 69 1;
+#X connect 80 0 64 0;
+#X connect 87 0 62 0;
+#X connect 88 0 28 0;
+#X connect 88 1 25 0;
+#X connect 88 2 26 0;
+#X connect 88 3 27 0;
+#X connect 88 4 33 0;
+#X connect 88 5 34 0;
+#X connect 88 6 35 0;
+#X connect 88 7 36 0;
+#X connect 88 8 72 0;
+#X connect 88 9 89 0;
+#X connect 88 10 8 0;
+#X connect 91 0 92 0;
diff --git a/udpsend~.c b/udpsend~.c
new file mode 100644
index 0000000..aa556c7
--- /dev/null
+++ b/udpsend~.c
@@ -0,0 +1,701 @@
+/* udpsend~ started by Martin Peach on 20100110, based on netsend~ */
+/* udpsend~ sends audio via udp only.*/
+/* It is a PD external, all Max stuff has been removed from the source */
+/* ------------------------ netsend~ ------------------------------------------ */
+/* */
+/* Tilde object to send uncompressed audio data to netreceive~. */
+/* Written by Olaf Matthes <olaf.matthes@gmx.de>. */
+/* Based on streamout~ by Guenter Geiger. */
+/* Get source at http://www.akustische-kunst.org/ */
+/* */
+/* 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. */
+/* */
+/* See file LICENSE for further informations on licensing terms. */
+/* */
+/* 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. */
+/* */
+/* This project was commissioned by the Society for Arts and Technology [SAT], */
+/* Montreal, Quebec, Canada, http://www.sat.qc.ca/. */
+/* */
+/* ---------------------------------------------------------------------------- */
+
+#include "m_pd.h"
+
+#include "udpsend~.h"
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#if defined(UNIX) || defined(unix)
+#include <sys/socket.h>
+#include <errno.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <pthread.h>
+#define SOCKET_ERROR -1
+#endif
+#ifdef _WIN32
+#include <winsock2.h>
+#include "pthread.h"
+#endif
+
+#ifdef MSG_NOSIGNAL
+#define SEND_FLAGS /*MSG_DONTWAIT|*/MSG_NOSIGNAL
+#else
+#define SEND_FLAGS 0
+#endif
+
+#ifndef SOL_IP
+#define SOL_IP IPPROTO_IP
+#endif
+
+
+/* ------------------------ udpsend~ ----------------------------- */
+
+static t_class *udpsend_tilde_class;
+
+static t_symbol *ps_nothing, *ps_localhost, *ps_vecsize;
+static t_symbol *ps_format, *ps_channels, *ps_framesize;
+static t_symbol *ps_sf_float, *ps_sf_16bit, *ps_sf_8bit;
+static t_symbol *ps_sf_unknown, *ps_bitrate, *ps_hostname;
+
+typedef struct _udpsend_tilde
+{
+ t_object x_obj;
+ t_outlet *x_outlet;
+ t_outlet *x_outlet2;
+ t_clock *x_clock;
+ int x_fd;
+ t_tag x_tag;
+ t_symbol* x_hostname;
+ int x_portno;
+ int x_connectstate;
+ char *x_cbuf;
+ int x_cbufsize;
+ int x_blocksize; /* set to DEFAULT_AUDIO_BUFFER_SIZE or user-supplied argument 3 in udpsend_tilde_new() */
+ int x_blockspersend; /* set to x->x_blocksize / x->x_vecsize in udpsend_tilde_perform() */
+ int x_blockssincesend;
+
+ long x_samplerate; /* samplerate we're running at */
+ int x_vecsize; /* current DSP signal vector size */
+ int x_ninlets; /* number of inlets */
+ int x_channels; /* number of channels we want to stream */
+ int x_format; /* format of streamed audio data */
+ int x_count; /* total number of audio frames */
+ t_int **x_myvec; /* vector we pass on in the DSP routine */
+
+ pthread_mutex_t x_mutex;
+ pthread_t x_childthread; /* a thread to initiate a connection to the remote port */
+ pthread_attr_t x_childthread_attr; /* child thread should have detached attribute so it can be cleaned up after it exits. */
+ int x_childthread_result; /* result from pthread_create. Zero if x_childthread represents a valid thread. */
+} t_udpsend_tilde;
+
+#define NO_CHILDTHREAD 1 /* not zero */
+
+/* function prototypes */
+static int udpsend_tilde_sockerror(char *s);
+static void udpsend_tilde_closesocket(int fd);
+static void udpsend_tilde_notify(t_udpsend_tilde *x);
+static void udpsend_tilde_disconnect(t_udpsend_tilde *x);
+static void *udpsend_tilde_doconnect(void *zz);
+static void udpsend_tilde_connect(t_udpsend_tilde *x, t_symbol *host, t_floatarg fportno);
+static t_int *udpsend_tilde_perform(t_int *w);
+static void udpsend_tilde_dsp(t_udpsend_tilde *x, t_signal **sp);
+static void udpsend_tilde_channels(t_udpsend_tilde *x, t_floatarg channels);
+static void udpsend_tilde_format(t_udpsend_tilde *x, t_symbol* form, t_floatarg bitrate);
+static void udpsend_tilde_float(t_udpsend_tilde* x, t_floatarg arg);
+static void udpsend_tilde_info(t_udpsend_tilde *x);
+static void *udpsend_tilde_new(t_floatarg inlets, t_floatarg blocksize);
+static void udpsend_tilde_free(t_udpsend_tilde* x);
+void udpsend_tilde_setup(void);
+
+/* functions */
+static void udpsend_tilde_notify(t_udpsend_tilde *x)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_childthread_result = NO_CHILDTHREAD; /* connection thread has ended */
+ outlet_float(x->x_outlet, x->x_connectstate); /* we should be connected */
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+static void udpsend_tilde_disconnect(t_udpsend_tilde *x)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ if (x->x_fd != -1)
+ {
+ udpsend_tilde_closesocket(x->x_fd);
+ x->x_fd = -1;
+ x->x_connectstate = 0;
+ outlet_float(x->x_outlet, 0);
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+/* udpsend_tilde_doconnect runs in the child thread, which terminates as soon as a connection is */
+static void *udpsend_tilde_doconnect(void *zz)
+{
+ t_udpsend_tilde *x = (t_udpsend_tilde *)zz;
+ struct sockaddr_in server;
+ struct hostent *hp;
+ int intarg = 1;
+ int sockfd;
+ int portno;
+ int broadcast = 1;/* nonzero is true */
+ t_symbol *hostname;
+
+ pthread_mutex_lock(&x->x_mutex);
+ hostname = x->x_hostname;
+ portno = x->x_portno;
+ pthread_mutex_unlock(&x->x_mutex);
+
+ /* create a socket */
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0)
+ {
+ post("udpsend~: connection to %s on port %d failed", hostname->s_name,portno);
+ udpsend_tilde_sockerror("socket");
+ x->x_childthread_result = NO_CHILDTHREAD;
+ return (0);
+ }
+
+#ifdef SO_BROADCAST
+ if( 0 != setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, (const void *)&broadcast, sizeof(broadcast)))
+ {
+ udpsend_tilde_sockerror("setting SO_BROADCAST");
+ }
+#endif /* SO_BROADCAST */
+
+ /* connect socket using hostname provided in command line */
+ server.sin_family = AF_INET;
+ hp = gethostbyname(x->x_hostname->s_name);
+ if (hp == 0)
+ {
+ post("udpsend~: bad host?");
+ x->x_childthread_result = NO_CHILDTHREAD;
+ return (0);
+ }
+
+#ifdef SO_PRIORITY
+ /* set high priority, LINUX only */
+ intarg = 6; /* select a priority between 0 and 7 */
+ if (setsockopt(sockfd, SOL_SOCKET, SO_PRIORITY, (const char*)&intarg, sizeof(int)) < 0)
+ {
+ udpsend_tilde_sockerror("setting SO_PRIORITY");
+ }
+#endif
+
+ memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+
+ /* assign client port number */
+ server.sin_port = htons((unsigned short)portno);
+
+ /* try to connect */
+ if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0)
+ {
+ udpsend_tilde_sockerror("connecting stream socket");
+ udpsend_tilde_closesocket(sockfd);
+ x->x_childthread_result = NO_CHILDTHREAD;
+ return (0);
+ }
+
+ post("udpsend~: connected host %s on port %d", hostname->s_name, portno);
+
+ pthread_mutex_lock(&x->x_mutex);
+ x->x_fd = sockfd;
+ x->x_connectstate = 1;
+ clock_delay(x->x_clock, 0);/* udpsend_tilde_notify is called in next clock tick */
+ pthread_mutex_unlock(&x->x_mutex);
+ return (0);
+}
+
+static void udpsend_tilde_connect(t_udpsend_tilde *x, t_symbol *host, t_floatarg fportno)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ if (x->x_childthread_result == 0)
+ {
+ pthread_mutex_unlock(&x->x_mutex);
+ post("udpsend~: already trying to connect");
+ return;
+ }
+ if (x->x_fd != -1)
+ {
+ pthread_mutex_unlock(&x->x_mutex);
+ post("udpsend~: already connected");
+ return;
+ }
+
+ if (host != ps_nothing)
+ x->x_hostname = host;
+ else
+ x->x_hostname = ps_localhost; /* default host */
+
+ if (!fportno)
+ x->x_portno = DEFAULT_PORT;
+ else
+ x->x_portno = (int)fportno;
+ x->x_count = 0;
+
+ /* start child thread to connect */
+ /* sender thread should start out detached so its resouces will be freed when it is done */
+ if (0!= (x->x_childthread_result = pthread_attr_init(&x->x_childthread_attr)))
+ {
+ pthread_mutex_unlock(&x->x_mutex);
+ post("udpsend~: pthread_attr_init failed: %d", x->x_childthread_result);
+ return;
+ }
+ if(0!= (x->x_childthread_result = pthread_attr_setdetachstate(&x->x_childthread_attr, PTHREAD_CREATE_DETACHED)))
+ {
+ pthread_mutex_unlock(&x->x_mutex);
+ post("udpsend~: pthread_attr_setdetachstate failed: %d", x->x_childthread_result);
+ return;
+ }
+ if (0 != (x->x_childthread_result = pthread_create(&x->x_childthread, &x->x_childthread_attr, udpsend_tilde_doconnect, x)))
+ {
+ pthread_mutex_unlock(&x->x_mutex);
+ post("udpsend~: couldn't create sender thread (%d)", x->x_childthread_result);
+ return;
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+static t_int *udpsend_tilde_perform(t_int *w)
+{
+ t_udpsend_tilde* x = (t_udpsend_tilde*) (w[1]);
+ int n = (int)(w[2]);
+ t_float *in[DEFAULT_AUDIO_CHANNELS];
+ const int offset = 3;
+ char* bp = NULL;
+ int i, length = x->x_blocksize * SF_SIZEOF(x->x_tag.format) * x->x_tag.channels;
+ int sent = 0;
+
+ pthread_mutex_lock(&x->x_mutex);
+
+ for (i = 0; i < x->x_ninlets; i++)
+ in[i] = (t_float *)(w[offset + i]);
+
+ if (n != x->x_vecsize) /* resize buffer */
+ {
+ x->x_vecsize = n;
+ x->x_blockspersend = x->x_blocksize / x->x_vecsize;
+ x->x_blockssincesend = 0;
+ length = x->x_blocksize * SF_SIZEOF(x->x_tag.format) * x->x_tag.channels;
+ }
+
+ /* format the buffer */
+ switch (x->x_tag.format)
+ {
+ case SF_FLOAT:
+ {
+ int32_t* fbuf = (int32_t *)x->x_cbuf + (x->x_blockssincesend * x->x_vecsize * x->x_tag.channels);
+ flint fl;
+
+ while (n--)
+ for (i = 0; i < x->x_tag.channels; i++)
+ {
+ fl.f32 = *(in[i]++);
+ *fbuf++ = htonl(fl.i32);
+ }
+ break;
+ }
+ case SF_16BIT:
+ {
+ short* cibuf = (short *)x->x_cbuf + (x->x_blockssincesend * x->x_vecsize * x->x_tag.channels);
+
+ while (n--)
+ for (i = 0; i < x->x_tag.channels; i++)
+ *cibuf++ = htons((short)floor(32767.5 * *(in[i]++)));/* signed binary */
+ break;
+ }
+ case SF_8BIT:
+ {
+ unsigned char* cbuf = (unsigned char*)x->x_cbuf + (x->x_blockssincesend * x->x_vecsize * x->x_tag.channels);
+
+ while (n--)
+ for (i = 0; i < x->x_tag.channels; i++)
+ *cbuf++ = (unsigned char)floor(128. * (1.0 + *(in[i]++))); /* offset binary */
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (!(x->x_blockssincesend < x->x_blockspersend - 1)) /* time to send the buffer */
+ {
+ x->x_blockssincesend = 0;
+ x->x_count++; /* count data packet we're going to send */
+
+ if (x->x_fd != -1)
+ {
+ bp = (char *)x->x_cbuf;
+ /* fill in the header tag */
+ x->x_tag.tag[0] = 'T';
+ x->x_tag.tag[1] = 'A';
+ x->x_tag.tag[2] = 'G';
+ x->x_tag.tag[3] = '!';
+ x->x_tag.framesize = htonl(length);
+ x->x_tag.count = htonl(x->x_count);
+ /* send the format tag */
+ if (send(x->x_fd, (char*)&x->x_tag, sizeof(t_tag), SEND_FLAGS) < 0)
+ {
+ udpsend_tilde_sockerror("send tag");
+ pthread_mutex_unlock(&x->x_mutex);
+ udpsend_tilde_disconnect(x);
+ return (w + offset + x->x_ninlets);
+ }
+ if (length != 0)
+/* UDP: max. packet size is 64k (incl. headers) so we have to split */
+ {
+#ifdef __APPLE__
+ /* WARNING: due to a 'bug' (maybe Apple would call it a feature?) in OS X
+ send calls with data packets larger than 16k fail with error number 40!
+ Thus we have to split the data packets into several packets that are
+ 16k in size. The other side will reassemble them again. */
+ int size = DEFAULT_UDP_PACKT_SIZE;
+ if (length < size) /* maybe data fits into one packet? */
+ size = length;
+ /* send the buffer */
+ for (sent = 0; sent < length;)
+ {
+ int ret = 0;
+ ret = send(x->x_fd, bp, size, SEND_FLAGS);
+ if (ret <= 0)
+ {
+ udpsend_tilde_sockerror("send data");
+ pthread_mutex_unlock(&x->x_mutex);
+ udpsend_tilde_disconnect(x);
+ return (w + offset + x->x_ninlets);
+ }
+ else
+ {
+ bp += ret;
+ sent += ret;
+ if ((length - sent) < size)
+ size = length - sent;
+ }
+ }
+#else
+ /* If there is any data, send the buffer, the OS might segment it into smaller packets */
+ int ret = send(x->x_fd, bp, length, SEND_FLAGS);
+ if (ret <= 0)
+ {
+ post ("udpsend~: sending length %ld", length);
+ udpsend_tilde_sockerror("send data");
+ pthread_mutex_unlock(&x->x_mutex);
+ udpsend_tilde_disconnect(x);
+ return (w + offset + x->x_ninlets);
+ }
+#endif
+ }
+ }
+
+/* check whether user has updated any parameters */
+ if (x->x_tag.channels != x->x_channels)
+ {
+ x->x_tag.channels = x->x_channels;
+ }
+ if (x->x_tag.format != x->x_format)
+ {
+ x->x_tag.format = x->x_format;
+ }
+ }
+ else
+ {
+ x->x_blockssincesend++;
+ }
+ pthread_mutex_unlock(&x->x_mutex);
+ return (w + offset + x->x_ninlets);
+}
+
+static void udpsend_tilde_dsp(t_udpsend_tilde *x, t_signal **sp)
+{
+ int i;
+
+ pthread_mutex_lock(&x->x_mutex);
+
+ x->x_myvec[0] = (t_int*)x;
+ x->x_myvec[1] = (t_int*)sp[0]->s_n;
+
+ x->x_samplerate = sp[0]->s_sr;
+
+ for (i = 0; i < x->x_ninlets; i++)
+ {
+ x->x_myvec[2 + i] = (t_int*)sp[i]->s_vec;
+ }
+
+ pthread_mutex_unlock(&x->x_mutex);
+
+ if (DEFAULT_AUDIO_BUFFER_SIZE % sp[0]->s_n)
+ {
+ error("udpsend~: signal vector size too large (needs to be even divisor of %d)", DEFAULT_AUDIO_BUFFER_SIZE);
+ }
+ else
+ {
+ dsp_addv(udpsend_tilde_perform, x->x_ninlets + 2, (t_int*)x->x_myvec);
+ }
+}
+
+static void udpsend_tilde_channels(t_udpsend_tilde *x, t_floatarg channels)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ if (channels >= 0 && channels <= x->x_ninlets)
+ {
+ x->x_channels = (int)channels;
+ post("udpsend~: channels set to %d", (int)channels);
+ }
+ else post ("udpsend~ number of channels must be between 0 and %d", x->x_ninlets);
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+static void udpsend_tilde_format(t_udpsend_tilde *x, t_symbol* form, t_floatarg bitrate)
+{
+ pthread_mutex_lock(&x->x_mutex);
+ if (!strncmp(form->s_name,"float", 5) && x->x_tag.format != SF_FLOAT)
+ {
+ x->x_format = (int)SF_FLOAT;
+ }
+ else if (!strncmp(form->s_name,"16bit", 5) && x->x_tag.format != SF_16BIT)
+ {
+ x->x_format = (int)SF_16BIT;
+ }
+ else if (!strncmp(form->s_name,"8bit", 4) && x->x_tag.format != SF_8BIT)
+ {
+ x->x_format = (int)SF_8BIT;
+ }
+
+ post("udpsend~: format set to %s", form->s_name);
+ pthread_mutex_unlock(&x->x_mutex);
+}
+
+static void udpsend_tilde_float(t_udpsend_tilde* x, t_floatarg arg)
+{
+ if (arg == 0.0)
+ udpsend_tilde_disconnect(x);
+ else
+ udpsend_tilde_connect(x,x->x_hostname,(float) x->x_portno);
+}
+
+/* send stream info */
+static void udpsend_tilde_info(t_udpsend_tilde *x)
+{
+ t_atom list[2];
+ t_symbol *sf_format;
+ t_float bitrate;
+
+ bitrate = (t_float)((SF_SIZEOF(x->x_tag.format) * x->x_samplerate * 8 * x->x_tag.channels) / 1000.);
+
+ switch (x->x_tag.format)
+ {
+ case SF_FLOAT:
+ {
+ sf_format = ps_sf_float;
+ break;
+ }
+ case SF_16BIT:
+ {
+ sf_format = ps_sf_16bit;
+ break;
+ }
+ case SF_8BIT:
+ {
+ sf_format = ps_sf_8bit;
+ break;
+ }
+ default:
+ {
+ sf_format = ps_sf_unknown;
+ break;
+ }
+ }
+
+ /* --- stream information (t_tag) --- */
+
+ /* audio format */
+ SETSYMBOL(list, (t_symbol *)sf_format);
+ outlet_anything(x->x_outlet2, ps_format, 1, list);
+
+ /* channels */
+ SETFLOAT(list, (t_float)x->x_tag.channels);
+ outlet_anything(x->x_outlet2, ps_channels, 1, list);
+
+ /* current signal vector size */
+ SETFLOAT(list, (t_float)x->x_vecsize);
+ outlet_anything(x->x_outlet2, ps_vecsize, 1, list);
+
+ /* framesize */
+ SETFLOAT(list, (t_float)(ntohl(x->x_tag.framesize)));
+ outlet_anything(x->x_outlet2, ps_framesize, 1, list);
+
+ /* bitrate */
+ SETFLOAT(list, (t_float)bitrate);
+ outlet_anything(x->x_outlet2, ps_bitrate, 1, list);
+
+ /* IP address */
+ SETSYMBOL(list, (t_symbol *)x->x_hostname);
+ outlet_anything(x->x_outlet2, ps_hostname, 1, list);
+}
+
+static void *udpsend_tilde_new(t_floatarg inlets, t_floatarg blocksize)
+{
+ int i;
+
+ t_udpsend_tilde *x = (t_udpsend_tilde *)pd_new(udpsend_tilde_class);
+ if (x)
+ {
+ for (i = sizeof(t_object); i < (int)sizeof(t_udpsend_tilde); i++)
+ ((char *)x)[i] = 0;
+
+ if ((int)inlets < 1 || (int)inlets > DEFAULT_AUDIO_CHANNELS)
+ {
+ error("udpsend~: Number of channels must be between 1 and %d", DEFAULT_AUDIO_CHANNELS);
+ return NULL;
+ }
+ x->x_ninlets = (int)inlets;
+ for (i = 1; i < x->x_ninlets; i++)
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal);
+
+ x->x_outlet = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet2 = outlet_new(&x->x_obj, &s_list);
+ x->x_clock = clock_new(x, (t_method)udpsend_tilde_notify);
+
+ x->x_myvec = (t_int **)t_getbytes(sizeof(t_int *) * (x->x_ninlets + 3));
+ if (!x->x_myvec)
+ {
+ error("udpsend~: out of memory");
+ return NULL;
+ }
+
+ pthread_mutex_init(&x->x_mutex, 0);
+
+ x->x_hostname = ps_localhost;
+ x->x_portno = DEFAULT_PORT;
+ x->x_connectstate = 0;
+ x->x_childthread_result = NO_CHILDTHREAD;
+ x->x_fd = -1;
+
+ x->x_tag.format = x->x_format = SF_FLOAT;
+ x->x_tag.channels = x->x_channels = x->x_ninlets;
+ x->x_vecsize = 64; /* this is updated in the perform routine udpsend_tilde_perform */
+ x->x_cbuf = NULL;
+ if (blocksize == 0) x->x_blocksize = DEFAULT_AUDIO_BUFFER_SIZE;
+ else if (DEFAULT_AUDIO_BUFFER_SIZE%(int)blocksize)
+ {
+ error("udpsend~: blocksize must fit snugly in %d", DEFAULT_AUDIO_BUFFER_SIZE);
+ return NULL;
+ }
+ else x->x_blocksize = (int)blocksize; //DEFAULT_AUDIO_BUFFER_SIZE; /* <-- the only place blocksize is set */
+ x->x_blockspersend = x->x_blocksize / x->x_vecsize; /* 1024/64 = 16 blocks */
+ x->x_blockssincesend = 0;
+ x->x_cbufsize = x->x_blocksize * sizeof(t_float) * x->x_ninlets;
+ x->x_cbuf = (char *)t_getbytes(x->x_cbufsize);
+
+#if defined(UNIX) || defined(unix)
+ /* we don't want to get signaled in case send() fails */
+ signal(SIGPIPE, SIG_IGN);
+#endif
+ }
+
+ return (x);
+}
+
+static void udpsend_tilde_free(t_udpsend_tilde* x)
+{
+ udpsend_tilde_disconnect(x);
+
+ /* free the memory */
+ if (x->x_cbuf)t_freebytes(x->x_cbuf, x->x_cbufsize);
+ if (x->x_myvec)t_freebytes(x->x_myvec, sizeof(t_int) * (x->x_ninlets + 3));
+
+ clock_free(x->x_clock);
+
+ pthread_mutex_destroy(&x->x_mutex);
+}
+
+void udpsend_tilde_setup(void)
+{
+ udpsend_tilde_class = class_new(gensym("udpsend~"), (t_newmethod)udpsend_tilde_new, (t_method)udpsend_tilde_free,
+ sizeof(t_udpsend_tilde), 0, A_DEFFLOAT, A_DEFFLOAT, A_NULL);
+ class_addmethod(udpsend_tilde_class, nullfn, gensym("signal"), 0);
+ class_addmethod(udpsend_tilde_class, (t_method)udpsend_tilde_dsp, gensym("dsp"), 0);
+ class_addfloat(udpsend_tilde_class, udpsend_tilde_float);
+ class_addmethod(udpsend_tilde_class, (t_method)udpsend_tilde_info, gensym("info"), 0);
+ class_addmethod(udpsend_tilde_class, (t_method)udpsend_tilde_connect, gensym("connect"), A_DEFSYM, A_DEFFLOAT, 0);
+ class_addmethod(udpsend_tilde_class, (t_method)udpsend_tilde_disconnect, gensym("disconnect"), 0);
+ class_addmethod(udpsend_tilde_class, (t_method)udpsend_tilde_channels, gensym("channels"), A_FLOAT, 0);
+ class_addmethod(udpsend_tilde_class, (t_method)udpsend_tilde_format, gensym("format"), A_SYMBOL, A_DEFFLOAT, 0);
+ class_sethelpsymbol(udpsend_tilde_class, gensym("udpsend~"));
+ post("udpsend~ v%s, (c) 2004-2005 Olaf Matthes, 2010 Martin Peach", VERSION);
+ post("udpsend~ Default blocksize is %d", DEFAULT_AUDIO_BUFFER_SIZE);
+
+ ps_nothing = gensym("");
+ ps_localhost = gensym("localhost");
+ ps_hostname = gensym("ipaddr");
+ ps_format = gensym("format");
+ ps_channels = gensym("channels");
+ ps_vecsize = gensym("vecsize");
+ ps_framesize = gensym("framesize");
+ ps_bitrate = gensym("bitrate");
+ ps_sf_float = gensym("_float_");
+ ps_sf_16bit = gensym("_16bit_");
+ ps_sf_8bit = gensym("_8bit_");
+ ps_sf_unknown = gensym("_unknown_");
+}
+
+/* Utility functions */
+
+static int udpsend_tilde_sockerror(char *s)
+{
+#ifdef _WIN32
+ int err = WSAGetLastError();
+ if (err == 10054) return 1;
+ else if (err == 10053) post("udpsend~: %s: software caused connection abort (%d)", s, err);
+ else if (err == 10055) post("udpsend~: %s: no buffer space available (%d)", s, err);
+ else if (err == 10060) post("udpsend~: %s: connection timed out (%d)", s, err);
+ else if (err == 10061) post("udpsend~: %s: connection refused (%d)", s, err);
+ else post("udpsend~: %s: %s (%d)", s, strerror(err), err);
+#else
+ int err = errno;
+ post("udpsend~: %s: %s (%d)", s, strerror(err), err);
+#endif
+#ifdef _WIN32
+ if (err == WSAEWOULDBLOCK)
+#endif
+#if defined(UNIX) || defined(unix)
+ if (err == EAGAIN)
+#endif
+ {
+ return 1; /* recoverable error */
+ }
+ return 0; /* indicate non-recoverable error */
+}
+
+static void udpsend_tilde_closesocket(int fd)
+{
+#if defined(UNIX) || defined(unix)
+ close(fd);
+#endif
+#ifdef _WIN32
+ closesocket(fd);
+#endif
+}
+
+/* fin udpsend~.c */
diff --git a/udpsend~.h b/udpsend~.h
new file mode 100644
index 0000000..386b363
--- /dev/null
+++ b/udpsend~.h
@@ -0,0 +1,102 @@
+/* udpsend~.h modified by Martin Peach from netsend~.h: */
+/* ------------------------ netsend~ ------------------------------------------ */
+/* */
+/* Tilde object to send uncompressed audio data to netreceive~. */
+/* Written by Olaf Matthes <olaf.matthes@gmx.de>. */
+/* Based on streamout~ by Guenter Geiger. */
+/* Get source at http://www.akustische-kunst.org/ */
+/* */
+/* 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. */
+/* */
+/* See file LICENSE for further informations on licensing terms. */
+/* */
+/* 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. */
+/* */
+/* This project was commissioned by the Society for Arts and Technology [SAT], */
+/* Montreal, Quebec, Canada, http://www.sat.qc.ca/. */
+/* */
+/* ---------------------------------------------------------------------------- */
+
+
+/* This file is based on and inspired by stream.h (C) Guenter Geiger 1999. */
+/* Some enhancements have been made with the goal of keeping compatibility */
+/* between the stream formats of streamout~/in~ and netsend~/receive~. */
+
+#define VERSION "0.34"
+
+#define DEFAULT_AUDIO_CHANNELS 32 /* nax. number of audio channels we support */
+#define DEFAULT_AUDIO_BUFFER_SIZE 2048 /*1024*/ /* number of samples in one audio block */
+#define DEFAULT_UDP_PACKT_SIZE 8192 /* number of bytes we send in one UDP datagram (OS X only) */
+#define DEFAULT_PORT 8000 /* default network port number */
+
+#ifdef _WIN32
+#ifndef HAVE_INT32_T
+typedef int int32_t;
+#define HAVE_INT32_T
+#endif
+#ifndef HAVE_INT16_T
+typedef short int16_t;
+#define HAVE_INT16_T
+#endif
+#ifndef HAVE_U_INT32_T
+typedef unsigned int u_int32_t;
+#define HAVE_U_INT32_T
+#endif
+#ifndef HAVE_U_INT16_T
+typedef unsigned short u_int16_t;
+#define HAVE_U_INT16_T
+#endif
+#endif
+
+typedef union _flint
+{
+ int i32;
+ t_float f32;
+} flint;
+
+/* format specific stuff */
+
+#define SF_FLOAT 1
+#define SF_DOUBLE 2 /* not implemented */
+#define SF_8BIT 10
+#define SF_16BIT 11
+#define SF_32BIT 12 /* not implemented */
+#define SF_ALAW 20 /* not implemented */
+#define SF_MP3 30 /* not implemented */
+#define SF_AAC 31 /* AAC encoding using FAAC */
+#define SF_VORBIS 40 /* not implemented */
+#define SF_FLAC 50 /* not implemented */
+
+#define SF_SIZEOF(a) (a == SF_FLOAT ? sizeof(t_float) : \
+ a == SF_16BIT ? sizeof(short) : 1)
+
+typedef struct _tag
+{ /* size (bytes) */
+ char tag[4]; /* 4 */ /*"TAG!"*/
+ char format; /* 1 */
+ long count; /* 4 */
+ char channels; /* 1 */
+ long framesize; /* 4 */
+ char reserved[2]; /* 2 */ /* pad to 16 bytes */
+} t_tag; /*-----*/
+ /* 16 */
+
+typedef struct _frame
+{
+ t_tag tag;
+ char *data;
+} t_frame;
+
+/* fin udpsend~.h */