From 9ce05217d923c35c41e9c19e96ab9cdc5a7e42ea Mon Sep 17 00:00:00 2001 From: Martin Peach Date: Thu, 28 Oct 2010 19:04:07 +0000 Subject: Added multicast support svn path=/trunk/externals/mrpeach/; revision=14245 --- net/udpreceive~.c | 284 ++++++++++++++++++++++++++----------- net/udpsend~-help.pd | 384 ++++++++++++++++++++++++++++----------------------- net/udpsend~.c | 312 ++++++++++++++++++++++++++++++++++++++++- 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 #include #include #if defined(UNIX) || defined(unix) @@ -52,6 +53,7 @@ #ifdef _WIN32 #include #include /* 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 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 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 #include #include +#include // for SIOCGIFCONF +#include // for SIOCGIFCONF #include #include #include @@ -56,6 +58,8 @@ #endif #ifdef _WIN32 #include +#include +#include // 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 -- cgit v1.2.1