aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Peach <mrpeach@users.sourceforge.net>2010-10-28 19:04:07 +0000
committerMartin Peach <mrpeach@users.sourceforge.net>2010-10-28 19:04:07 +0000
commit9ce05217d923c35c41e9c19e96ab9cdc5a7e42ea (patch)
tree77c925cf27278c0796f641ec1f36d388de1a3585
parentc3f5005f943153ef5219f94ea3aee47a372a5156 (diff)
Added multicast support
svn path=/trunk/externals/mrpeach/; revision=14245
-rw-r--r--net/udpreceive~.c284
-rw-r--r--net/udpsend~-help.pd384
-rw-r--r--net/udpsend~.c312
3 files changed, 725 insertions, 255 deletions
diff --git a/net/udpreceive~.c b/net/udpreceive~.c
index 42bf7c7..5790869 100644
--- a/net/udpreceive~.c
+++ b/net/udpreceive~.c
@@ -31,9 +31,10 @@
#include "m_pd.h"
-
+#include "s_stuff.h"
#include "udpsend~.h"
+#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#if defined(UNIX) || defined(unix)
@@ -52,6 +53,7 @@
#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h> /* for socklen_t */
+#define snprintf sprintf_s
#endif
#ifndef SOL_IP
@@ -82,13 +84,16 @@ typedef struct _udpreceive_tilde
t_atom x_addrbytes[5];
int x_socket;
int x_connectsocket;
+ int x_multicast_joined;
int x_nconnections;
long x_addr;
unsigned short x_port;
t_symbol *x_hostname;
int x_error;
int x_buffering;
- char x_msg[256];
+#define XMSG_SIZE 256
+ char x_msg[XMSG_SIZE];
+ char x_addr_name[256]; // a multicast address or 0
/* buffering */
int x_framein;// index of next empty frame in x_frames[]
@@ -119,20 +124,21 @@ 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 int udpreceive_tilde_createsocket(t_udpreceive_tilde* x, char *address, 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_new(t_symbol *s, int argc, t_atom *argv);
static void udpreceive_tilde_free(t_udpreceive_tilde *x);
void udpreceive_tilde_setup(void);
+static void udpreceive_tilde_sock_err(t_udpreceive_tilde *x, char *err_string);
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);
+//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,
@@ -294,6 +300,7 @@ static void udpreceive_tilde_datapoll(t_udpreceive_tilde *x)
}
}
+/*
static void udpreceive_tilde_connectpoll(t_udpreceive_tilde *x)
{
socklen_t sockaddrlen = sizeof(struct sockaddr);
@@ -315,44 +322,110 @@ static void udpreceive_tilde_connectpoll(t_udpreceive_tilde *x)
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);
+ sys_addpollfn(fd, (t_fdpollfn)udpreceive_tilde_datapoll, x);
outlet_float(x->x_outlet1, 1);
}
+*/
-static int udpreceive_tilde_createsocket(t_udpreceive_tilde* x, int portno)
+static int udpreceive_tilde_createsocket(t_udpreceive_tilde* x, char *address, int portno)
{
struct sockaddr_in server;
+ struct hostent *hp;
int sockfd;
+ int intarg;
+ int multicast_joined = 0;
+#if defined __APPLE__ || defined _WIN32
+ struct ip_mreq mreq;
+#else
+ struct ip_mreqn mreq;
+#endif
+
+ if (x->x_socket >= 0)
+ {
+ // close the existing socket first
+ sys_rmpollfn(x->x_socket);
+ sys_closesocket(x->x_socket);
+ }
/* create a socket */
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0)
{
- udpreceive_tilde_sockerror("socket");
+ udpreceive_tilde_sock_err(x, "udpreceive~: socket");
return 0;
}
server.sin_family = AF_INET;
- server.sin_addr.s_addr = INADDR_ANY;
+ if (address[0] == 0) server.sin_addr.s_addr = INADDR_ANY;
+ else
+ {
+ hp = gethostbyname(address);
+ if (hp == 0)
+ {
+ pd_error(x, "udpreceive~: bad host?\n");
+ return 0;
+ }
+ memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+ }
+ /* 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)
+ udpreceive_tilde_sock_err(x, "udpreceive~: setsockopt (SO_REUSEADDR) failed");
/* 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)
+ /* if a multicast address was specified, join the multicast group */
+ /* hop count defaults to 1 so we won't leave the subnet*/
+ if (0xE0000000 == (ntohl(server.sin_addr.s_addr) & 0xF0000000))
{
- udpreceive_tilde_sockerror("bind");
- CLOSESOCKET(sockfd);
- return 0;
- }
+ server.sin_addr.s_addr = INADDR_ANY;
+ /* first bind the socket to INADDR_ANY */
+ if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
+ {
+ udpreceive_tilde_sock_err(x, "udpreceive~: bind");
+ sys_closesocket(sockfd);
+ return 0;
+ }
+ /* second join the multicast group */
+ memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+#if defined __APPLE__ || defined _WIN32
+ mreq.imr_multiaddr.s_addr = server.sin_addr.s_addr;
+ mreq.imr_interface.s_addr = INADDR_ANY;/* can put a specific local IP address here if host is multihomed */
+#else
+ mreq.imr_multiaddr.s_addr = server.sin_addr.s_addr;
+ mreq.imr_address.s_addr = INADDR_ANY;
+ mreq.imr_ifindex = 0;
+#endif //__APPLE__ || _WIN32
+ if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) < 0)
+ udpreceive_tilde_sock_err(x, "udpreceive~: setsockopt IP_ADD_MEMBERSHIP");
+ else
+ {
+ multicast_joined = 1;
+ post ("udpreceive~: added to multicast group");
+ }
+ }
+ else
+ {
+ /* name the socket */
+ if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
+ {
+ udpreceive_tilde_sock_err(x, "udpreceive~: bind");
+ CLOSESOCKET(sockfd);
+ return 0;
+ }
+ }
+ x->x_multicast_joined = multicast_joined;
x->x_socket = sockfd;
x->x_nbytes = 0;
- sys_addpollfn(sockfd, udpreceive_tilde_datapoll, x);
+ sys_addpollfn(x->x_socket, (t_fdpollfn)udpreceive_tilde_datapoll, x);
return 1;
}
@@ -471,7 +544,7 @@ static t_int *udpreceive_tilde_perform(t_int *w)
if (x->x_error != 1)
{
x->x_error = 1;
- sprintf(x->x_msg, "udpreceive~: mp3 format not supported");
+ snprintf(x->x_msg, XMSG_SIZE, "udpreceive~: mp3 format not supported");
clock_delay(x->x_clock, 0);
}
break;
@@ -481,7 +554,7 @@ static t_int *udpreceive_tilde_perform(t_int *w)
if (x->x_error != 2)
{
x->x_error = 2;
- sprintf(x->x_msg, "udpreceive~: aac format not supported");
+ snprintf(x->x_msg, XMSG_SIZE, "udpreceive~: aac format not supported");
clock_delay(x->x_clock, 0);
}
break;
@@ -490,7 +563,7 @@ static t_int *udpreceive_tilde_perform(t_int *w)
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);
+ snprintf(x->x_msg, XMSG_SIZE, "udpreceive~: unknown format (%d)",x->x_frames[x->x_frameout].tag.format);
clock_delay(x->x_clock, 0);
}
break;
@@ -633,74 +706,96 @@ static void udpreceive_tilde_tick(t_udpreceive_tilde *x)
post("%s", x->x_msg);
}
-static void *udpreceive_tilde_new(t_floatarg fportno, t_floatarg outlets, t_floatarg blocksize)
+static void *udpreceive_tilde_new(t_symbol *s, int argc, t_atom *argv)
{
t_udpreceive_tilde *x;
- int i;
+ int i, j = 0, portno = 0, outlets = 0, blocksize = 0;
- 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;
- }
+ if (NULL == x) return NULL;
+ for (i = sizeof(t_object); i < (int)sizeof(t_udpreceive_tilde); i++)
+ ((char *)x)[i] = 0; /* do we need to do this?*/
- 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;
+#ifdef DEBUG
+ post("udpreceive_tilde_new:argc is %d s is %s", argc, s->s_name);
+#endif
+ for (i = 0; i < argc ;++i)
+ {
+ if (argv[i].a_type == A_FLOAT)
+ { // float is taken to be a port number, a channel count, then a buffer size in that order
+#ifdef DEBUG
+ post ("argv[%d] is a float: %f", i, argv[i].a_w.w_float);
+#endif
+ if (j == 0) portno = (int)argv[i].a_w.w_float;
+ else if (j == 1) outlets = (int)argv[i].a_w.w_float;
+ else if (j == 2) blocksize = (int)argv[i].a_w.w_float;
+ ++j;
}
- 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;
+ else if (argv[i].a_type == A_SYMBOL)
+ { // symbol is taken to be an ip address (for multicast)
+#ifdef DEBUG
+ post ("argv[%d] is a symbol: %s", i, argv[i].a_w.w_symbol->s_name);
+#endif
+ atom_string(&argv[i], x->x_addr_name, 256);
}
+ }
+#ifdef DEBUG
+ post("Setting port %d, address %s", portno, x->addr);
+#endif
- 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);
- }
+ if (outlets < 1 || outlets > DEFAULT_AUDIO_CHANNELS)
+ {
+ error("udpreceive~: Number of channels must be between 1 and %d", DEFAULT_AUDIO_CHANNELS);
+ return NULL;
+ }
+
+ x->x_noutlets = 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 = 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, x->x_addr_name, portno))
+ {
+ error("udpreceive~: failed to create listening socket");
+ return NULL;
}
return (x);
}
@@ -733,7 +828,7 @@ 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);
+ sizeof(t_udpreceive_tilde), CLASS_DEFAULT, A_GIMME, 0);
class_addmethod(udpreceive_tilde_class, nullfn, gensym("signal"), 0);
class_addmethod(udpreceive_tilde_class, (t_method)udpreceive_tilde_info, gensym("info"), 0);
@@ -763,6 +858,35 @@ void udpreceive_tilde_setup(void)
}
/* error handlers */
+static void udpreceive_tilde_sock_err(t_udpreceive_tilde *x, char *err_string)
+{
+/* prints the last error from errno or WSAGetLastError() */
+#ifdef _WIN32
+ LPVOID lpMsgBuf;
+ DWORD dwRetVal = WSAGetLastError();
+ int len = 0, i;
+ char *cp;
+
+ if (len = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS
+ , NULL, dwRetVal, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&lpMsgBuf, 0, NULL))
+ {
+ cp = (char *)lpMsgBuf;
+ for(i = 0; i < len; ++i)
+ {
+ if (cp[i] < 0x20)
+ { /* end string at first weird character */
+ cp[i] = 0;
+ break;
+ }
+ }
+ pd_error(x, "%s: %s (%d)", err_string, lpMsgBuf, dwRetVal);
+ LocalFree(lpMsgBuf);
+ }
+#else
+ pd_error(x, "%s: %s (%d)", err_string, strerror(errno), errno);
+#endif
+}
+
static int udpreceive_tilde_sockerror(char *s)
{
#ifdef _WIN32
diff --git a/net/udpsend~-help.pd b/net/udpsend~-help.pd
index bd03c81..29c4164 100644
--- a/net/udpsend~-help.pd
+++ b/net/udpsend~-help.pd
@@ -1,173 +1,211 @@
-#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;
+#N canvas 289 56 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 1
+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 2;
+#X obj -178 297 udpsend~ 2 512;
+#X text -88 296 sends 2 dsp-channels using 512-sample blocks;
+#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 343 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 -122 562 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 368 [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 -249 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;
+#N canvas 335 200 657 334 multicast 0;
+#X msg 91 87 multicast_interface 192.168.0.88;
+#X msg 66 62 multicast_interface eth1;
+#X text 229 13 send to a multicast address;
+#X text 201 38 specify an interface to use with multicast;
+#X msg 43 39 multicast_interface 1;
+#X text 251 61 by index \, name or address;
+#X floatatom 183 112 5 0 256 0 - - -;
+#X obj 165 135 f 1;
+#X obj 165 115 bng 15 250 50 0 empty empty empty 17 7 0 10 -4034 -1
+-1;
+#X text 17 136 set multicast ttl:;
+#X msg 165 161 multicast_ttl \$1;
+#X msg 215 211 multicast_loopback \$1;
+#X obj 215 189 tgl 15 0 empty empty empty 20 7 0 8 -24198 -241291 -1
+1 1;
+#X text 26 187 enable multicast loopback:;
+#X obj 244 257 outlet;
+#X msg 18 14 connect 239.200.200.200 8008;
+#X text 262 278 [udpreceive~ 8008 2 512 239.200.200.200];
+#X text -70 278 Will be received by any udpreceive~ declared like this:
+;
+#X text 347 295 (2 and 512 may be changed);
+#X connect 0 0 14 0;
+#X connect 1 0 14 0;
+#X connect 4 0 14 0;
+#X connect 6 0 7 1;
+#X connect 7 0 10 0;
+#X connect 8 0 7 0;
+#X connect 10 0 14 0;
+#X connect 11 0 14 0;
+#X connect 12 0 11 0;
+#X connect 15 0 14 0;
+#X restore -347 280 pd multicast;
+#X obj -389 541 udpreceive~ 8008 2 512;
+#X text -50 497 Arguments: port \, channels \, blocksize \, multicast_address.
+Blocksize should match [udpsend~]. multicast_address is optional. Arguments
+must be in that order.;
+#X text -455 328 Author: Martin Peach 2010/10/28;
+#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 94 0;
+#X connect 43 0 94 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 69 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 85 0;
+#X connect 62 0 10 0;
+#X connect 62 1 57 0;
+#X connect 67 0 68 0;
+#X connect 68 0 62 1;
+#X connect 69 0 54 1;
+#X connect 69 0 68 1;
+#X connect 79 0 94 0;
+#X connect 86 0 62 0;
+#X connect 87 0 28 0;
+#X connect 87 1 25 0;
+#X connect 87 2 26 0;
+#X connect 87 3 27 0;
+#X connect 87 4 33 0;
+#X connect 87 5 34 0;
+#X connect 87 6 35 0;
+#X connect 87 7 36 0;
+#X connect 87 8 71 0;
+#X connect 87 9 88 0;
+#X connect 87 10 8 0;
+#X connect 90 0 91 0;
+#X connect 93 0 62 0;
+#X connect 94 0 64 0;
+#X connect 94 1 64 1;
+#X connect 94 2 90 0;
+#X connect 94 3 87 0;
+#X connect 94 4 52 0;
diff --git a/net/udpsend~.c b/net/udpsend~.c
index aa556c7..c452034 100644
--- a/net/udpsend~.c
+++ b/net/udpsend~.c
@@ -44,6 +44,8 @@
#include <errno.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
+#include <sys/ioctl.h> // for SIOCGIFCONF
+#include <net/if.h> // for SIOCGIFCONF
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/time.h>
@@ -56,6 +58,8 @@
#endif
#ifdef _WIN32
#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <iphlpapi.h> // for interface addresses
#include "pthread.h"
#endif
@@ -86,6 +90,8 @@ typedef struct _udpsend_tilde
t_outlet *x_outlet2;
t_clock *x_clock;
int x_fd;
+ unsigned int x_multicast_loop_state;
+ unsigned int x_multicast_ttl; /* time to live for multicast */
t_tag x_tag;
t_symbol* x_hostname;
int x_portno;
@@ -114,11 +120,15 @@ typedef struct _udpsend_tilde
/* function prototypes */
static int udpsend_tilde_sockerror(char *s);
+static void udpsend_tilde_sock_err(t_udpsend_tilde *x, char *err_string);
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 void udpsend_tilde_set_multicast_loopback(t_udpsend_tilde *x, t_floatarg loop_state);
+static void udpsend_tilde_set_multicast_ttl(t_udpsend_tilde *x, t_floatarg ttl_hops);
+static void udpsend_tilde_set_multicast_interface (t_udpsend_tilde *x, t_symbol *s, int argc, t_atom *argv);
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);
@@ -161,7 +171,10 @@ static void *udpsend_tilde_doconnect(void *zz)
int sockfd;
int portno;
int broadcast = 1;/* nonzero is true */
+ unsigned char multicast_loop_state;
+ unsigned char multicast_ttl;
t_symbol *hostname;
+ unsigned int size;
pthread_mutex_lock(&x->x_mutex);
hostname = x->x_hostname;
@@ -173,7 +186,7 @@ static void *udpsend_tilde_doconnect(void *zz)
if (sockfd < 0)
{
post("udpsend~: connection to %s on port %d failed", hostname->s_name,portno);
- udpsend_tilde_sockerror("socket");
+ udpsend_tilde_sock_err(x, "udpsend~ socket");
x->x_childthread_result = NO_CHILDTHREAD;
return (0);
}
@@ -206,13 +219,22 @@ static void *udpsend_tilde_doconnect(void *zz)
memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+ if (0xE0000000 == (ntohl(server.sin_addr.s_addr) & 0xF0000000))
+ post ("udpsend~: connecting to a multicast address");
+ size = sizeof(multicast_loop_state);
+ getsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &multicast_loop_state, &size);
+ size = sizeof(multicast_ttl);
+ getsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &multicast_ttl, &size);
+ x->x_multicast_loop_state = multicast_loop_state;
+ x->x_multicast_ttl = multicast_ttl;
+
/* 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_sock_err(x, "udpsend~ connect");
udpsend_tilde_closesocket(sockfd);
x->x_childthread_result = NO_CHILDTHREAD;
return (0);
@@ -278,6 +300,260 @@ static void udpsend_tilde_connect(t_udpsend_tilde *x, t_symbol *host, t_floatarg
pthread_mutex_unlock(&x->x_mutex);
}
+static void udpsend_tilde_set_multicast_loopback(t_udpsend_tilde *x, t_floatarg loop_state)
+{
+ int sockfd = x->x_fd;
+ unsigned char multicast_loop_state = loop_state;
+ unsigned int size;
+
+ if (x->x_fd < 0)
+ {
+ pd_error(x, "udpsend_tilde_set_multicast_loopback: not connected");
+ return;
+ }
+ if (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP,
+ &multicast_loop_state, sizeof(multicast_loop_state)) < 0)
+ udpsend_tilde_sock_err(x, "udpsend_tilde setsockopt IP_MULTICAST_LOOP");
+ getsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &multicast_loop_state, &size);
+ x->x_multicast_loop_state = multicast_loop_state;
+}
+
+static void udpsend_tilde_set_multicast_ttl(t_udpsend_tilde *x, t_floatarg ttl_hops)
+{
+ int sockfd = x->x_fd;
+ unsigned char multicast_ttl = ttl_hops;
+ unsigned int size;
+
+ if (x->x_fd < 0)
+ {
+ pd_error(x, "udpsend_tilde_set_multicast_ttl: not connected");
+ return;
+ }
+ if (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL,
+ &multicast_ttl, sizeof(multicast_ttl)) < 0)
+ udpsend_tilde_sock_err(x, "udpsend_tilde setsockopt IP_MULTICAST_TTL");
+ getsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_TTL, &multicast_ttl, &size);
+ x->x_multicast_ttl = multicast_ttl;
+}
+
+static void udpsend_tilde_set_multicast_interface (t_udpsend_tilde *x, t_symbol *s, int argc, t_atom *argv)
+{
+#ifdef _WIN32
+ int i, n_ifaces = 32;
+ PMIB_IPADDRTABLE pIPAddrTable;
+ DWORD dwSize;
+ IN_ADDR IPAddr;
+ int if_index = -1;
+ int found = 0;
+ t_symbol *interfacename = gensym("none");
+ struct hostent *hp = 0;
+ struct sockaddr_in server;
+
+ if (x->x_fd < 0)
+ {
+ pd_error(x, "udpsend_tilde_set_multicast_interface: not connected");
+ return;
+ }
+ switch (argv[0].a_type)
+ {
+ case A_FLOAT:
+ if_index = (int)atom_getfloat(&argv[0]);
+ break;
+ case A_SYMBOL:
+ interfacename = atom_getsymbol(&argv[0]);
+ break;
+ default:
+ pd_error(x, "udpsend_tilde_set_multicast_interface: argument not float or symbol");
+ return;
+ }
+ if (if_index == -1)
+ {
+ hp = gethostbyname(interfacename->s_name); // if interface is a dotted or named IP address (192.168.0.88)
+ }
+ if (hp != 0) memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+ else // maybe interface is its index (1) (names aren't available in _WIN32)
+ {
+ /* get the list of interfaces, IPv4 only */
+ dwSize = sizeof(MIB_IPADDRTABLE)*n_ifaces;
+ if ((pIPAddrTable = (MIB_IPADDRTABLE *) getbytes(dwSize)) == NULL)
+ {
+ post("udpsend_tilde: unable to allocate %lu bytes for GetIpAddrTable", dwSize);
+ return;
+ }
+ if (GetIpAddrTable(pIPAddrTable, &dwSize, 0))
+ {
+ udpsend_tilde_sock_err(x, "udpsend_tilde_set_multicast_interface: GetIpAddrTable");
+ return;
+ }
+
+ n_ifaces = pIPAddrTable->dwNumEntries;
+ post("udpsend_tilde: %d interface%s available:", n_ifaces, (n_ifaces == 1)?"":"s");
+ for (i = 0; i < n_ifaces; i++)
+ {
+ IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
+ post("[%d]: %s", pIPAddrTable->table[i].dwIndex, inet_ntoa(IPAddr));
+ if (pIPAddrTable->table[i].dwIndex == if_index)
+ {
+ server.sin_addr = IPAddr;
+ found = 1;
+ }
+ }
+
+ if (pIPAddrTable)
+ {
+ freebytes(pIPAddrTable, dwSize);
+ pIPAddrTable = NULL;
+ }
+ if (! found)
+ {
+ post("udpsend_tilde_set_multicast_interface: bad host name? (%s)\n", interfacename->s_name);
+ return;
+ }
+ }
+ if (setsockopt(x->x_fd, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&server.sin_addr, sizeof(struct in_addr)) == SOCKET_ERROR)
+ udpsend_tilde_sock_err(x, "udpsend_tilde setsockopt IP_MULTICAST_IF");
+ else post("udpsend_tilde multicast interface is %s", inet_ntoa(server.sin_addr));
+
+#elif defined __APPLE__
+ int if_index = -1;
+ int found = 0;
+ t_symbol *interfacename = gensym("none");
+ struct ifaddrs *ifap;
+ int i = 0;
+ int n_ifaces = 0;
+ struct hostent *hp = 0;
+ struct sockaddr_in server;
+ struct sockaddr *sa;
+ char ifname[IFNAMSIZ]; /* longest possible interface name */
+
+ if (x->x_fd < 0)
+ {
+ pd_error(x, "udpsend_tilde_set_multicast_interface: not connected");
+ return;
+ }
+ switch (argv[0].a_type)
+ {
+ case A_FLOAT:
+ if_index = (int)atom_getfloat(&argv[0]);
+ break;
+ case A_SYMBOL:
+ interfacename = atom_getsymbol(&argv[0]);
+ break;
+ default:
+ pd_error(x, "udpsend_tilde_set_multicast_interface: argument not float or symbol");
+ return;
+ }
+ if (if_index == -1)
+ {
+ hp = gethostbyname(interfacename->s_name); // if interface is a dotted or named IP address (192.168.0.88)
+ }
+ if (hp != 0) memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+ else // maybe interface is its name (eth0) or index (1)
+ { // scan all the interfaces to get the IP address of interface
+ if (getifaddrs(&ifap)) udpsend_tilde_sock_err(x, "udpsend_tilde getifaddrs");
+ i = found = n_ifaces = 0;
+ while (NULL != ifap)
+ {
+ sa = ifap->ifa_addr;
+ if (AF_INET == sa->sa_family)
+ {
+ ++n_ifaces;
+ strncpy (ifname, ifap->ifa_name, IFNAMSIZ);
+ post("[%d]: %s: %s", i, ifname, inet_ntoa(((struct sockaddr_in *)sa)->sin_addr));
+ if((i == if_index) || ((if_index == -1) && (!strncmp(interfacename->s_name, ifname, IFNAMSIZ))))
+ { // either the index or the name match
+ server.sin_addr = ((struct sockaddr_in *)sa)->sin_addr;
+ found = 1;
+ }
+ }
+ i++;
+ ifap = ifap->ifa_next; // next record or NULL
+ }
+ freeifaddrs(ifap);
+ post ("udpsend_tilde: %d interfaces", n_ifaces);
+ if (!found) return;
+ }
+ if (setsockopt(x->x_fd, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&server.sin_addr, sizeof(struct in_addr)))
+ udpsend_tilde_sock_err(x, "udpsend_tilde setsockopt IP_MULTICAST_IF");
+ else post("udpsend_tilde multicast interface is %s", inet_ntoa(server.sin_addr));
+ return;
+#else // __linux__
+ struct sockaddr_in server;
+ struct sockaddr *sa;
+ struct hostent *hp = 0;
+ struct ifconf ifc;
+ int n_ifaces = 32, i, origbuflen, found = 0;
+ char ifname[IFNAMSIZ]; /* longest possible interface name */
+ t_symbol *interface = gensym("none");
+ int if_index = -1;
+
+ if (x->x_fd < 0)
+ {
+ pd_error(x, "udpsend_tilde_set_multicast_interface: not connected");
+ return;
+ }
+ switch (argv[0].a_type)
+ {
+ case A_FLOAT:
+ if_index = (int)atom_getfloat(&argv[0]);
+ break;
+ case A_SYMBOL:
+ interface = atom_getsymbol(&argv[0]);
+ break;
+ default:
+ pd_error(x, "udpsend_tilde_set_multicast_interface: argument not float or symbol");
+ return;
+ }
+ if (if_index == -1)
+ {
+ hp = gethostbyname(interface->s_name); // if interface is a dotted or named IP address (192.168.0.88)
+ }
+ if (hp != 0) memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length);
+ else // maybe interface is its name (eth0) or index (1)
+ { // scan all the interfaces to get the IP address of interface
+ // find the number of interfaces
+ origbuflen = n_ifaces * sizeof (struct ifreq);// save maximum length for free()
+ ifc.ifc_len = origbuflen; // SIOCGIFCONF changes it to valid length
+ ifc.ifc_buf = (char*)getzbytes(origbuflen);
+ if (ifc.ifc_buf != NULL)
+ { //
+ if (ioctl(x->x_fd, SIOCGIFCONF, &ifc) < 0) // get list of interfaces
+ udpsend_tilde_sock_err(x, "udpsend_tilde_set_multicast_interface: getting list of interfaces");
+ else
+ {
+ n_ifaces = ifc.ifc_len/sizeof(struct ifreq);
+ post("udpsend_tilde: %d interface%s available:", n_ifaces, (n_ifaces == 1)?"":"s");
+ for(i = 0; i < n_ifaces; i++)
+ {
+ sa = (struct sockaddr *)&(ifc.ifc_req[i].ifr_addr);
+ strncpy (ifname, ifc.ifc_req[i].ifr_name, IFNAMSIZ);
+ post("[%d]: %s: %s", i, ifname, inet_ntoa(((struct sockaddr_in *)sa)->sin_addr));
+ if
+ (
+ (i == if_index) ||
+ ((if_index == -1) && (!strncmp(interface->s_name, ifname, IFNAMSIZ)))
+ )
+ {
+ server.sin_addr = ((struct sockaddr_in *)sa)->sin_addr;
+ found = 1;
+ }
+ }
+ }
+ }
+ freebytes(ifc.ifc_buf, origbuflen);
+
+ if (! found)
+ {
+ post("udpsend_tilde_set_multicast_interface: bad host name? (%s)\n", interface->s_name);
+ return;
+ }
+ }
+ if (setsockopt(x->x_fd, IPPROTO_IP, IP_MULTICAST_IF, &server.sin_addr, sizeof(struct in_addr)) < 0)
+ udpsend_tilde_sock_err(x, "udpsend_tilde_set_multicast_interface: setsockopt");
+ else post("udpsend_tilde multicast interface is %s", inet_ntoa(server.sin_addr));
+#endif // _WIN32
+}
+
static t_int *udpsend_tilde_perform(t_int *w)
{
t_udpsend_tilde* x = (t_udpsend_tilde*) (w[1]);
@@ -639,6 +915,9 @@ void udpsend_tilde_setup(void)
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_set_multicast_ttl, gensym("multicast_ttl"), A_DEFFLOAT, 0);
+ class_addmethod(udpsend_tilde_class, (t_method)udpsend_tilde_set_multicast_loopback, gensym("multicast_loopback"), A_DEFFLOAT, 0);
+ class_addmethod(udpsend_tilde_class, (t_method)udpsend_tilde_set_multicast_interface, gensym("multicast_interface"), A_GIMME, 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);
@@ -662,6 +941,35 @@ void udpsend_tilde_setup(void)
/* Utility functions */
+static void udpsend_tilde_sock_err(t_udpsend_tilde *x, char *err_string)
+{
+/* prints the last error from errno or WSAGetLastError() */
+#ifdef _WIN32
+ void *lpMsgBuf;
+ unsigned long errornumber = WSAGetLastError();
+ int len = 0, i;
+ char *cp;
+
+ if (len = FormatMessageA((FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS)
+ , NULL, errornumber, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&lpMsgBuf, 0, NULL))
+ {
+ cp = (char *)lpMsgBuf;
+ for(i = 0; i < len; ++i)
+ {
+ if (cp[i] < 0x20)
+ { /* end string at first weird character */
+ cp[i] = 0;
+ break;
+ }
+ }
+ pd_error(x, "%s: %s (%d)", err_string, lpMsgBuf, errornumber);
+ LocalFree(lpMsgBuf);
+ }
+#else
+ pd_error(x, "%s: %s (%d)", err_string, strerror(errno), errno);
+#endif
+}
+
static int udpsend_tilde_sockerror(char *s)
{
#ifdef _WIN32