A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 477 lines 11 kB view raw
1/* Copyright (c) 1997-1999 Miller Puckette. 2* For information on usage and redistribution, and for a DISCLAIMER OF ALL 3* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ 4 5#ifdef ROCKBOX 6#include "plugin.h" 7#include "../../pdbox.h" 8#endif 9 10/* network */ 11 12#include "m_pd.h" 13#include "s_stuff.h" 14 15#ifndef ROCKBOX 16#include <sys/types.h> 17#include <string.h> 18#ifdef UNIX 19#include <sys/socket.h> 20#include <netinet/in.h> 21#include <netinet/tcp.h> 22#include <netdb.h> 23#include <stdio.h> 24#define SOCKET_ERROR -1 25#else 26#include <winsock.h> 27#endif 28#endif /* ROCKBOX */ 29 30static t_class *netsend_class; 31 32typedef struct _netsend 33{ 34 t_object x_obj; 35 int x_fd; 36 int x_protocol; 37} t_netsend; 38 39static void *netsend_new(t_floatarg udpflag) 40{ 41#ifdef ROCKBOX 42 (void) udpflag; 43#endif 44 45 t_netsend *x = (t_netsend *)pd_new(netsend_class); 46 outlet_new(&x->x_obj, &s_float); 47 x->x_fd = -1; 48#ifndef ROCKBOX 49 x->x_protocol = (udpflag != 0 ? SOCK_DGRAM : SOCK_STREAM); 50#endif 51 return (x); 52} 53 54static void netsend_connect(t_netsend *x, t_symbol *hostname, 55 t_floatarg fportno) 56{ 57#ifdef ROCKBOX 58 (void) x; 59 (void) hostname; 60 (void) fportno; 61#else /* ROCKBOX */ 62 struct sockaddr_in server; 63 struct hostent *hp; 64 int sockfd; 65 int portno = fportno; 66 int intarg; 67 if (x->x_fd >= 0) 68 { 69 error("netsend_connect: already connected"); 70 return; 71 } 72 73 /* create a socket */ 74 sockfd = socket(AF_INET, x->x_protocol, 0); 75#if 0 76 fprintf(stderr, "send socket %d\n", sockfd); 77#endif 78 if (sockfd < 0) 79 { 80 sys_sockerror("socket"); 81 return; 82 } 83 /* connect socket using hostname provided in command line */ 84 server.sin_family = AF_INET; 85 hp = gethostbyname(hostname->s_name); 86 if (hp == 0) 87 { 88 post("bad host?\n"); 89 return; 90 } 91#if 0 92 intarg = 0; 93 if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, 94 &intarg, sizeof(intarg)) < 0) 95 post("setsockopt (SO_RCVBUF) failed\n"); 96#endif 97 /* for stream (TCP) sockets, specify "nodelay" */ 98 if (x->x_protocol == SOCK_STREAM) 99 { 100 intarg = 1; 101 if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, 102 &intarg, sizeof(intarg)) < 0) 103 post("setsockopt (TCP_NODELAY) failed\n"); 104 } 105 memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); 106 107 /* assign client port number */ 108 server.sin_port = htons((u_short)portno); 109 110 post("connecting to port %d", portno); 111 /* try to connect. LATER make a separate thread to do this 112 because it might block */ 113 if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) 114 { 115 sys_sockerror("connecting stream socket"); 116 sys_closesocket(sockfd); 117 return; 118 } 119 x->x_fd = sockfd; 120 outlet_float(x->x_obj.ob_outlet, 1); 121#endif /* ROCKBOX */ 122} 123 124static void netsend_disconnect(t_netsend *x) 125{ 126 if (x->x_fd >= 0) 127 { 128#ifndef ROCKBOX 129 sys_closesocket(x->x_fd); 130#endif 131 x->x_fd = -1; 132 outlet_float(x->x_obj.ob_outlet, 0); 133 } 134} 135 136static void netsend_send(t_netsend *x, t_symbol *s, int argc, t_atom *argv) 137{ 138#ifdef ROCKBOX 139 (void) x; 140 (void) s; 141 (void) argc; 142 (void) argv; 143#else /* ROCKBOX */ 144 if (x->x_fd >= 0) 145 { 146 t_binbuf *b = binbuf_new(); 147 char *buf, *bp; 148 int length, sent; 149 t_atom at; 150 binbuf_add(b, argc, argv); 151 SETSEMI(&at); 152 binbuf_add(b, 1, &at); 153 binbuf_gettext(b, &buf, &length); 154 for (bp = buf, sent = 0; sent < length;) 155 { 156 static double lastwarntime; 157 static double pleasewarn; 158 double timebefore = sys_getrealtime(); 159 int res = send(x->x_fd, buf, length-sent, 0); 160 double timeafter = sys_getrealtime(); 161 int late = (timeafter - timebefore > 0.005); 162 if (late || pleasewarn) 163 { 164 if (timeafter > lastwarntime + 2) 165 { 166 post("netsend blocked %d msec", 167 (int)(1000 * ((timeafter - timebefore) + pleasewarn))); 168 pleasewarn = 0; 169 lastwarntime = timeafter; 170 } 171 else if (late) pleasewarn += timeafter - timebefore; 172 } 173 if (res <= 0) 174 { 175 sys_sockerror("netsend"); 176 netsend_disconnect(x); 177 break; 178 } 179 else 180 { 181 sent += res; 182 bp += res; 183 } 184 } 185 t_freebytes(buf, length); 186 binbuf_free(b); 187 } 188 else error("netsend: not connected"); 189#endif /* ROCKBOX */ 190} 191 192static void netsend_free(t_netsend *x) 193{ 194#ifdef ROCKBOX 195 (void) x; 196#else 197 netsend_disconnect(x); 198#endif 199} 200 201static void netsend_setup(void) 202{ 203 netsend_class = class_new(gensym("netsend"), (t_newmethod)netsend_new, 204 (t_method)netsend_free, 205 sizeof(t_netsend), 0, A_DEFFLOAT, 0); 206 class_addmethod(netsend_class, (t_method)netsend_connect, 207 gensym("connect"), A_SYMBOL, A_FLOAT, 0); 208 class_addmethod(netsend_class, (t_method)netsend_disconnect, 209 gensym("disconnect"), 0); 210 class_addmethod(netsend_class, (t_method)netsend_send, gensym("send"), 211 A_GIMME, 0); 212} 213 214/* ----------------------------- netreceive ------------------------- */ 215 216static t_class *netreceive_class; 217 218typedef struct _netreceive 219{ 220 t_object x_obj; 221 t_outlet *x_msgout; 222 t_outlet *x_connectout; 223 int x_connectsocket; 224 int x_nconnections; 225 int x_udp; 226} t_netreceive; 227 228#ifdef ROCKBOX 229static t_netreceive* receiver; 230static int receiver_port; 231#endif /* ROCKBOX */ 232 233#ifndef ROCKBOX 234static void netreceive_notify(t_netreceive *x) 235{ 236 outlet_float(x->x_connectout, --x->x_nconnections); 237} 238#endif /* ROCKBOX */ 239 240static void netreceive_doit(void *z, t_binbuf *b) 241{ 242#ifndef ROCKBOX 243 t_atom messbuf[1024]; 244#endif 245 t_netreceive *x = (t_netreceive *)z; 246 int msg, natom = binbuf_getnatom(b); 247 t_atom *at = binbuf_getvec(b); 248 for (msg = 0; msg < natom;) 249 { 250 int emsg; 251 for (emsg = msg; emsg < natom && at[emsg].a_type != A_COMMA 252 && at[emsg].a_type != A_SEMI; emsg++) 253 ; 254 if (emsg > msg) 255 { 256 int i; 257 for (i = msg; i < emsg; i++) 258 if (at[i].a_type == A_DOLLAR || at[i].a_type == A_DOLLSYM) 259 { 260 pd_error(x, "netreceive: got dollar sign in message"); 261 goto nodice; 262 } 263 if (at[msg].a_type == A_FLOAT) 264 { 265 if (emsg > msg + 1) 266 outlet_list(x->x_msgout, 0, emsg-msg, at + msg); 267 else outlet_float(x->x_msgout, at[msg].a_w.w_float); 268 } 269 else if (at[msg].a_type == A_SYMBOL) 270 outlet_anything(x->x_msgout, at[msg].a_w.w_symbol, 271 emsg-msg-1, at + msg + 1); 272 } 273 nodice: 274 msg = emsg + 1; 275 } 276} 277 278#ifndef ROCKBOX 279static void netreceive_connectpoll(t_netreceive *x) 280{ 281 int fd = accept(x->x_connectsocket, 0, 0); 282 if (fd < 0) post("netreceive: accept failed"); 283 else 284 { 285 t_socketreceiver *y = socketreceiver_new((void *)x, 286 (t_socketnotifier)netreceive_notify, 287 (x->x_msgout ? netreceive_doit : 0), 0); 288 sys_addpollfn(fd, (t_fdpollfn)socketreceiver_read, y); 289 outlet_float(x->x_connectout, ++x->x_nconnections); 290 } 291} 292#endif 293 294static void *netreceive_new(t_symbol *compatflag, 295 t_floatarg fportno, t_floatarg udpflag) 296{ 297 t_netreceive *x; 298 299#ifdef ROCKBOX 300 int portno = fportno, udp = (udpflag != 0); 301 302 (void) compatflag; 303 304 /* Look whether callback is already taken. */ 305 if(receiver) 306 { 307 post("Receiver callback already taken!\n"); 308 return NULL; 309 } 310 311 /* Look whether TCP sockets are thought to exist. */ 312 if(!udp) 313 { 314 post("Trying to create TCP socket!\n"); 315 return NULL; 316 } 317 318 x = (t_netreceive *) pd_new(netreceive_class); 319 x->x_msgout = outlet_new(&x->x_obj, &s_anything); 320 x->x_connectout = 0; 321 x->x_nconnections = 0; 322 x->x_udp = udp; 323 324 receiver = x; 325 receiver_port = portno; 326 327#else /* ROCKBOX */ 328 struct sockaddr_in server; 329 int sockfd, portno = fportno, udp = (udpflag != 0); 330 int old = !strcmp(compatflag->s_name , "old"); 331 int intarg; 332 /* create a socket */ 333 sockfd = socket(AF_INET, (udp ? SOCK_DGRAM : SOCK_STREAM), 0); 334#if 0 335 fprintf(stderr, "receive socket %d\n", sockfd); 336#endif 337 if (sockfd < 0) 338 { 339 sys_sockerror("socket"); 340 return (0); 341 } 342 server.sin_family = AF_INET; 343 server.sin_addr.s_addr = INADDR_ANY; 344 345#if 1 346 /* ask OS to allow another Pd to repoen this port after we close it. */ 347 intarg = 1; 348 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 349 &intarg, sizeof(intarg)) < 0) 350 post("setsockopt (SO_REUSEADDR) failed\n"); 351#endif 352#if 0 353 intarg = 0; 354 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, 355 &intarg, sizeof(intarg)) < 0) 356 post("setsockopt (SO_RCVBUF) failed\n"); 357#endif 358 /* Stream (TCP) sockets are set NODELAY */ 359 if (!udp) 360 { 361 intarg = 1; 362 if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, 363 &intarg, sizeof(intarg)) < 0) 364 post("setsockopt (TCP_NODELAY) failed\n"); 365 } 366 /* assign server port number */ 367 server.sin_port = htons((u_short)portno); 368 369 /* name the socket */ 370 if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) 371 { 372 sys_sockerror("bind"); 373 sys_closesocket(sockfd); 374 return (0); 375 } 376 x = (t_netreceive *)pd_new(netreceive_class); 377 if (old) 378 { 379 /* old style, nonsecure version */ 380 x->x_msgout = 0; 381 } 382 else x->x_msgout = outlet_new(&x->x_obj, &s_anything); 383 384 if (udp) /* datagram protocol */ 385 { 386 t_socketreceiver *y = socketreceiver_new((void *)x, 387 (t_socketnotifier)netreceive_notify, 388 (x->x_msgout ? netreceive_doit : 0), 1); 389 sys_addpollfn(sockfd, (t_fdpollfn)socketreceiver_read, y); 390 x->x_connectout = 0; 391 } 392 else /* streaming protocol */ 393 { 394 if (listen(sockfd, 5) < 0) 395 { 396 sys_sockerror("listen"); 397 sys_closesocket(sockfd); 398 sockfd = -1; 399 } 400 else 401 { 402 sys_addpollfn(sockfd, (t_fdpollfn)netreceive_connectpoll, x); 403 x->x_connectout = outlet_new(&x->x_obj, &s_float); 404 } 405 } 406 x->x_connectsocket = sockfd; 407 x->x_nconnections = 0; 408 x->x_udp = udp; 409#endif /* ROCKBOX */ 410 411 return (x); 412} 413 414static void netreceive_free(t_netreceive *x) 415{ 416#ifdef ROCKBOX 417 if(receiver && receiver == x) 418 receiver = NULL; 419#else /* ROCKBOX */ 420 /* LATER make me clean up open connections */ 421 if (x->x_connectsocket >= 0) 422 { 423 sys_rmpollfn(x->x_connectsocket); 424 sys_closesocket(x->x_connectsocket); 425 } 426#endif /* ROCKBOX */ 427} 428 429#ifdef ROCKBOX 430/* Basically a reimplementation of socketreceiver_getudp() 431 from s_inter.c */ 432extern t_binbuf* inbinbuf; 433extern void outlet_setstacklim(void); 434 435void rockbox_receive_callback(struct datagram* dg) 436{ 437 /* Check whether there is a receiver. */ 438 if(!receiver) 439 return; 440 441 /* Limit string. */ 442 dg->data[dg->size] = '\0'; 443 444 /* If complete line... */ 445 if(dg->data[dg->size-1] == '\n') 446 { 447 char* semi = strchr(dg->data, ';'); 448 449 /* Limit message. */ 450 if(semi) 451 *semi = '\0'; 452 453 /* Create binary buffer. */ 454 binbuf_text(inbinbuf, dg->data, strlen(dg->data)); 455 456 /* Limit outlet stack. */ 457 outlet_setstacklim(); 458 459 /* Execute receive function. */ 460 netreceive_doit(receiver, inbinbuf); 461 } 462} 463#endif /* ROCKBOX */ 464 465static void netreceive_setup(void) 466{ 467 netreceive_class = class_new(gensym("netreceive"), 468 (t_newmethod)netreceive_new, (t_method)netreceive_free, 469 sizeof(t_netreceive), CLASS_NOINLET, A_DEFFLOAT, A_DEFFLOAT, 470 A_DEFSYM, 0); 471} 472 473void x_net_setup(void) 474{ 475 netsend_setup(); 476 netreceive_setup(); 477}