A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
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}