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/* this file handles Max-style patchable objects, i.e., objects which
6can interconnect via inlets and outlets; also, the (terse) generic
7behavior for "gobjs" appears at the end of this file. */
8
9#include "m_pd.h"
10#include "m_imp.h"
11
12union inletunion
13{
14 t_symbol *iu_symto;
15 t_gpointer *iu_pointerslot;
16 t_float *iu_floatslot;
17 t_symbol **iu_symslot;
18 t_sample iu_floatsignalvalue;
19};
20
21struct _inlet
22{
23 t_pd i_pd;
24 struct _inlet *i_next;
25 t_object *i_owner;
26 t_pd *i_dest;
27 t_symbol *i_symfrom;
28 union inletunion i_un;
29};
30
31#define i_symto i_un.iu_symto
32#define i_pointerslot i_un.iu_pointerslot
33#define i_floatslot i_un.iu_floatslot
34#define i_symslot i_un.iu_symslot
35
36static t_class *inlet_class, *pointerinlet_class, *floatinlet_class,
37 *symbolinlet_class;
38
39#define ISINLET(pd) ((*(pd) == inlet_class) || \
40 (*(pd) == pointerinlet_class) || \
41 (*(pd) == floatinlet_class) || \
42 (*(pd) == symbolinlet_class))
43
44/* --------------------- generic inlets ala max ------------------ */
45
46t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1, t_symbol *s2)
47{
48 t_inlet *x = (t_inlet *)pd_new(inlet_class), *y, *y2;
49 x->i_owner = owner;
50 x->i_dest = dest;
51 if (s1 == &s_signal)
52 x->i_un.iu_floatsignalvalue = 0;
53 else x->i_symto = s2;
54 x->i_symfrom = s1;
55 x->i_next = 0;
56 if((y = owner->ob_inlet))
57 {
58 while((y2 = y->i_next)) y = y2;
59 y->i_next = x;
60 }
61 else owner->ob_inlet = x;
62 return (x);
63}
64
65static void inlet_wrong(t_inlet *x, t_symbol *s)
66{
67 pd_error(x->i_owner, "inlet: expected '%s' but got '%s'",
68 x->i_symfrom->s_name, s->s_name);
69}
70
71 /* LATER figure out how to make these efficient: */
72static void inlet_bang(t_inlet *x)
73{
74 if (x->i_symfrom == &s_bang)
75 pd_vmess(x->i_dest, x->i_symto, "");
76 else if (!x->i_symfrom) pd_bang(x->i_dest);
77 else inlet_wrong(x, &s_bang);
78}
79
80static void inlet_pointer(t_inlet *x, t_gpointer *gp)
81{
82 if (x->i_symfrom == &s_pointer)
83 pd_vmess(x->i_dest, x->i_symto, "p", gp);
84 else if (!x->i_symfrom) pd_pointer(x->i_dest, gp);
85 else inlet_wrong(x, &s_pointer);
86}
87
88static void inlet_float(t_inlet *x, t_float f)
89{
90 if (x->i_symfrom == &s_float)
91 pd_vmess(x->i_dest, x->i_symto, "f", (t_floatarg)f);
92 else if (x->i_symfrom == &s_signal)
93 x->i_un.iu_floatsignalvalue = ftofix(f);
94 else if (!x->i_symfrom)
95 pd_float(x->i_dest, f);
96 else inlet_wrong(x, &s_float);
97}
98
99static void inlet_symbol(t_inlet *x, t_symbol *s)
100{
101 if (x->i_symfrom == &s_symbol)
102 pd_vmess(x->i_dest, x->i_symto, "s", s);
103 else if (!x->i_symfrom) pd_symbol(x->i_dest, s);
104 else inlet_wrong(x, &s_symbol);
105}
106
107static void inlet_list(t_inlet *x, t_symbol *s, int argc, t_atom *argv)
108{
109#ifndef ROCKBOX
110 t_atom at;
111#endif
112 if (x->i_symfrom == &s_list || x->i_symfrom == &s_float
113 || x->i_symfrom == &s_symbol || x->i_symfrom == &s_pointer)
114 typedmess(x->i_dest, x->i_symto, argc, argv);
115 else if (!x->i_symfrom) pd_list(x->i_dest, s, argc, argv);
116 else inlet_wrong(x, &s_list);
117}
118
119static void inlet_anything(t_inlet *x, t_symbol *s, int argc, t_atom *argv)
120{
121 if (x->i_symfrom == s)
122 typedmess(x->i_dest, x->i_symto, argc, argv);
123 else if (!x->i_symfrom)
124 typedmess(x->i_dest, s, argc, argv);
125 else inlet_wrong(x, s);
126}
127
128void inlet_free(t_inlet *x)
129{
130 t_object *y = x->i_owner;
131 t_inlet *x2;
132 if (y->ob_inlet == x) y->ob_inlet = x->i_next;
133 else for (x2 = y->ob_inlet; x2; x2 = x2->i_next)
134 if (x2->i_next == x)
135 {
136 x2->i_next = x->i_next;
137 break;
138 }
139 t_freebytes(x, sizeof(*x));
140}
141
142/* ----- pointerinlets, floatinlets, syminlets: optimized inlets ------- */
143
144static void pointerinlet_pointer(t_inlet *x, t_gpointer *gp)
145{
146 gpointer_unset(x->i_pointerslot);
147 *(x->i_pointerslot) = *gp;
148 if (gp->gp_stub) gp->gp_stub->gs_refcount++;
149}
150
151t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp)
152{
153 t_inlet *x = (t_inlet *)pd_new(pointerinlet_class), *y, *y2;
154 x->i_owner = owner;
155 x->i_dest = 0;
156 x->i_symfrom = &s_pointer;
157 x->i_pointerslot = gp;
158 x->i_next = 0;
159 if((y = owner->ob_inlet))
160 {
161 while((y2 = y->i_next)) y = y2;
162 y->i_next = x;
163 }
164 else owner->ob_inlet = x;
165 return (x);
166}
167
168static void floatinlet_float(t_inlet *x, t_float f)
169{
170 *(x->i_floatslot) = f;
171}
172
173t_inlet *floatinlet_new(t_object *owner, t_float *fp)
174{
175 t_inlet *x = (t_inlet *)pd_new(floatinlet_class), *y, *y2;
176 x->i_owner = owner;
177 x->i_dest = 0;
178 x->i_symfrom = &s_float;
179 x->i_floatslot = fp;
180 x->i_next = 0;
181 if((y = owner->ob_inlet))
182 {
183 while((y2 = y->i_next)) y = y2;
184 y->i_next = x;
185 }
186 else owner->ob_inlet = x;
187 return (x);
188}
189
190static void symbolinlet_symbol(t_inlet *x, t_symbol *s)
191{
192 *(x->i_symslot) = s;
193}
194
195t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp)
196{
197 t_inlet *x = (t_inlet *)pd_new(symbolinlet_class), *y, *y2;
198 x->i_owner = owner;
199 x->i_dest = 0;
200 x->i_symfrom = &s_symbol;
201 x->i_symslot = sp;
202 x->i_next = 0;
203 if((y = owner->ob_inlet))
204 {
205 while((y2 = y->i_next)) y = y2;
206 y->i_next = x;
207 }
208 else owner->ob_inlet = x;
209 return (x);
210}
211
212/* ---------------------- routine to handle lists ---------------------- */
213
214 /* objects interpret lists by feeding them to the individual inlets.
215 Before you call this check that the object doesn't have a more
216 specific way to handle lists. */
217void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv)
218{
219 t_atom *ap;
220 int count;
221 t_inlet *ip = ((t_object *)x)->ob_inlet;
222
223#ifdef ROCKBOX
224 (void) s;
225#endif
226
227 if (!argc) return;
228 for (count = argc-1, ap = argv+1; ip && count--; ap++, ip = ip->i_next)
229 {
230 if (ap->a_type == A_POINTER) pd_pointer(&ip->i_pd, ap->a_w.w_gpointer);
231 else if (ap->a_type == A_FLOAT) pd_float(&ip->i_pd, ap->a_w.w_float);
232 else pd_symbol(&ip->i_pd, ap->a_w.w_symbol);
233 }
234 if (argv->a_type == A_POINTER) pd_pointer(&x->ob_pd, argv->a_w.w_gpointer);
235 else if (argv->a_type == A_FLOAT) pd_float(&x->ob_pd, argv->a_w.w_float);
236 else pd_symbol(&x->ob_pd, argv->a_w.w_symbol);
237}
238
239void obj_init(void)
240{
241 inlet_class = class_new(gensym("inlet"), 0, 0,
242 sizeof(t_inlet), CLASS_PD, 0);
243 class_addbang(inlet_class, inlet_bang);
244 class_addpointer(inlet_class, inlet_pointer);
245 class_addfloat(inlet_class, inlet_float);
246 class_addsymbol(inlet_class, inlet_symbol);
247 class_addlist(inlet_class, inlet_list);
248 class_addanything(inlet_class, inlet_anything);
249
250 pointerinlet_class = class_new(gensym("inlet"), 0, 0,
251 sizeof(t_inlet), CLASS_PD, 0);
252 class_addpointer(pointerinlet_class, pointerinlet_pointer);
253
254 floatinlet_class = class_new(gensym("inlet"), 0, 0,
255 sizeof(t_inlet), CLASS_PD, 0);
256 class_addfloat(floatinlet_class, (t_method)floatinlet_float);
257
258 symbolinlet_class = class_new(gensym("inlet"), 0, 0,
259 sizeof(t_inlet), CLASS_PD, 0);
260 class_addsymbol(symbolinlet_class, symbolinlet_symbol);
261
262}
263
264/* --------------------------- outlets ------------------------------ */
265
266static char *stacklimit, *topstack;
267#define STACKSIZE 1000000
268static int outlet_eventno;
269
270 /* set a stack limit (on each incoming event that can set off messages)
271 for the outlet functions to check to prevent stack overflow from message
272 recursion */
273#pragma GCC diagnostic push
274#pragma GCC diagnostic ignored "-Warray-bounds"
275#if __GNUC__ >= 13
276#pragma GCC diagnostic ignored "-Wdangling-pointer"
277#endif
278void outlet_setstacklim(void)
279{
280 char c;
281 topstack = &c;
282 stacklimit = (&c) - STACKSIZE;
283 outlet_eventno++;
284}
285#pragma GCC diagnostic pop
286
287 /* get a number unique to the (clock, MIDI, GUI, etc.) event we're on */
288int sched_geteventno( void)
289{
290 return (outlet_eventno);
291}
292
293struct _outconnect
294{
295 struct _outconnect *oc_next;
296 t_pd *oc_to;
297};
298
299struct _outlet
300{
301 t_object *o_owner;
302 struct _outlet *o_next;
303 t_outconnect *o_connections;
304 t_symbol *o_sym;
305};
306
307t_outlet *outlet_new(t_object *owner, t_symbol *s)
308{
309 t_outlet *x = (t_outlet *)getbytes(sizeof(*x)), *y, *y2;
310 x->o_owner = owner;
311 x->o_next = 0;
312 if((y = owner->ob_outlet))
313 {
314 while((y2 = y->o_next)) y = y2;
315 y->o_next = x;
316 }
317 else owner->ob_outlet = x;
318 x->o_connections = 0;
319 x->o_sym = s;
320 return (x);
321}
322
323static void outlet_stackerror(t_outlet *x)
324{
325 pd_error(x->o_owner, "stack overflow");
326 stacklimit = topstack;
327}
328
329void outlet_bang(t_outlet *x)
330{
331 t_outconnect *oc;
332 char c;
333 if (&c < stacklimit)
334 outlet_stackerror(x);
335 else for (oc = x->o_connections; oc; oc = oc->oc_next)
336 pd_bang(oc->oc_to);
337}
338
339void outlet_pointer(t_outlet *x, t_gpointer *gp)
340{
341 t_outconnect *oc;
342 t_gpointer gpointer;
343 char c;
344 if (&c < stacklimit)
345 outlet_stackerror(x);
346 else
347 {
348#if 0
349 gpointer_copy(gp, &gpointer);
350 for (oc = x->o_connections; oc; oc = oc->oc_next)
351 pd_pointer(oc->oc_to, &gpointer);
352 gpointer_unset(&gpointer);
353#else
354 gpointer = *gp;
355 for (oc = x->o_connections; oc; oc = oc->oc_next)
356 pd_pointer(oc->oc_to, &gpointer);
357#endif
358 }
359}
360
361void outlet_float(t_outlet *x, t_float f)
362{
363 t_outconnect *oc;
364 char c;
365 if (&c < stacklimit)
366 outlet_stackerror(x);
367 else for (oc = x->o_connections; oc; oc = oc->oc_next)
368 pd_float(oc->oc_to, f);
369}
370
371void outlet_symbol(t_outlet *x, t_symbol *s)
372{
373 t_outconnect *oc;
374 char c;
375 if (&c < stacklimit)
376 outlet_stackerror(x);
377 else for (oc = x->o_connections; oc; oc = oc->oc_next)
378 pd_symbol(oc->oc_to, s);
379}
380
381void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv)
382{
383 t_outconnect *oc;
384 char c;
385 if (&c < stacklimit)
386 outlet_stackerror(x);
387 else for (oc = x->o_connections; oc; oc = oc->oc_next)
388 pd_list(oc->oc_to, s, argc, argv);
389}
390
391void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv)
392{
393 t_outconnect *oc;
394 char c;
395 if (&c < stacklimit)
396 outlet_stackerror(x);
397 else for (oc = x->o_connections; oc; oc = oc->oc_next)
398 typedmess(oc->oc_to, s, argc, argv);
399}
400
401 /* get the outlet's declared symbol */
402t_symbol *outlet_getsymbol(t_outlet *x)
403{
404 return (x->o_sym);
405}
406
407void outlet_free(t_outlet *x)
408{
409 t_object *y = x->o_owner;
410 t_outlet *x2;
411 if (y->ob_outlet == x) y->ob_outlet = x->o_next;
412 else for (x2 = y->ob_outlet; x2; x2 = x2->o_next)
413 if (x2->o_next == x)
414 {
415 x2->o_next = x->o_next;
416 break;
417 }
418 t_freebytes(x, sizeof(*x));
419}
420
421t_outconnect *obj_connect(t_object *source, int outno,
422 t_object *sink, int inno)
423{
424 t_inlet *i;
425 t_outlet *o;
426 t_pd *to;
427 t_outconnect *oc, *oc2;
428
429 for (o = source->ob_outlet; o && outno; o = o->o_next, outno--) ;
430 if (!o) return (0);
431
432 if (sink->ob_pd->c_firstin)
433 {
434 if (!inno)
435 {
436 to = &sink->ob_pd;
437 goto doit;
438 }
439 else inno--;
440 }
441 for (i = sink->ob_inlet; i && inno; i = i->i_next, inno--) ;
442 if (!i) return (0);
443 to = &i->i_pd;
444doit:
445 oc = (t_outconnect *)t_getbytes(sizeof(*oc));
446 oc->oc_next = 0;
447 oc->oc_to = to;
448 /* append it to the end of the list */
449 /* LATER we might cache the last "oc" to make this faster. */
450 if ((oc2 = o->o_connections))
451 {
452 while (oc2->oc_next) oc2 = oc2->oc_next;
453 oc2->oc_next = oc;
454 }
455 else o->o_connections = oc;
456 if (o->o_sym == &s_signal) canvas_update_dsp();
457
458 return (oc);
459}
460
461void obj_disconnect(t_object *source, int outno, t_object *sink, int inno)
462{
463 t_inlet *i;
464 t_outlet *o;
465 t_pd *to;
466 t_outconnect *oc, *oc2;
467
468 for (o = source->ob_outlet; o && outno; o = o->o_next, outno--)
469 if (!o) return;
470 if (sink->ob_pd->c_firstin)
471 {
472 if (!inno)
473 {
474 to = &sink->ob_pd;
475 goto doit;
476 }
477 else inno--;
478 }
479 for (i = sink->ob_inlet; i && inno; i = i->i_next, inno--) ;
480 if (!i) return;
481 to = &i->i_pd;
482doit:
483 if (!(oc = o->o_connections)) return;
484 if (oc->oc_to == to)
485 {
486 o->o_connections = oc->oc_next;
487 freebytes(oc, sizeof(*oc));
488 goto done;
489 }
490 while((oc2 = oc->oc_next))
491 {
492 if (oc2->oc_to == to)
493 {
494 oc->oc_next = oc2->oc_next;
495 freebytes(oc2, sizeof(*oc2));
496 goto done;
497 }
498 oc = oc2;
499 }
500done:
501 if (o->o_sym == &s_signal) canvas_update_dsp();
502}
503
504/* ------ traversal routines for code that can't see our structures ------ */
505
506int obj_noutlets(t_object *x)
507{
508 int n;
509 t_outlet *o;
510 for (o = x->ob_outlet, n = 0; o; o = o->o_next) n++;
511 return (n);
512}
513
514int obj_ninlets(t_object *x)
515{
516 int n;
517 t_inlet *i;
518 for (i = x->ob_inlet, n = 0; i; i = i->i_next) n++;
519 if (x->ob_pd->c_firstin) n++;
520 return (n);
521}
522
523t_outconnect *obj_starttraverseoutlet(t_object *x, t_outlet **op, int nout)
524{
525 t_outlet *o = x->ob_outlet;
526 while (nout-- && o) o = o->o_next;
527 *op = o;
528 if (o) return (o->o_connections);
529 else return (0);
530}
531
532t_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect,
533 t_object **destp, t_inlet **inletp, int *whichp)
534{
535 t_pd *y;
536 y = lastconnect->oc_to;
537 if (ISINLET(y))
538 {
539 int n;
540 t_inlet *i = (t_inlet *)y, *i2;
541 t_object *dest = i->i_owner;
542 for (n = dest->ob_pd->c_firstin, i2 = dest->ob_inlet;
543 i2 && i2 != i; i2 = i2->i_next) n++;
544 *whichp = n;
545 *destp = dest;
546 *inletp = i;
547 }
548 else
549 {
550 *whichp = 0;
551 *inletp = 0;
552 *destp = ((t_object *)y);
553 }
554 return (lastconnect->oc_next);
555}
556
557 /* this one checks that a pd is indeed a patchable object, and returns
558 it, correctly typed, or zero if the check failed. */
559t_object *pd_checkobject(t_pd *x)
560{
561 if ((*x)->c_patchable) return ((t_object *)x);
562 else return (0);
563}
564
565 /* move an inlet or outlet to the head of the list */
566void obj_moveinletfirst(t_object *x, t_inlet *i)
567{
568 t_inlet *i2;
569 if (x->ob_inlet == i) return;
570 else for (i2 = x->ob_inlet; i2; i2 = i2->i_next)
571 if (i2->i_next == i)
572 {
573 i2->i_next = i->i_next;
574 i->i_next = x->ob_inlet;
575 x->ob_inlet = i;
576 return;
577 }
578}
579
580void obj_moveoutletfirst(t_object *x, t_outlet *o)
581{
582 t_outlet *o2;
583 if (x->ob_outlet == o) return;
584 else for (o2 = x->ob_outlet; o2; o2 = o2->o_next)
585 if (o2->o_next == o)
586 {
587 o2->o_next = o->o_next;
588 o->o_next = x->ob_outlet;
589 x->ob_outlet = o;
590 return;
591 }
592}
593
594 /* routines for DSP sorting, which are used in d_ugen.c and g_canvas.c */
595 /* LATER try to consolidate all the slightly different routines. */
596
597int obj_nsiginlets(t_object *x)
598{
599 int n;
600 t_inlet *i;
601 for (i = x->ob_inlet, n = 0; i; i = i->i_next)
602 if (i->i_symfrom == &s_signal) n++;
603 if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin) n++;
604 return (n);
605}
606
607 /* get the index, among signal inlets, of the mth inlet overall */
608int obj_siginletindex(t_object *x, int m)
609{
610 int n = 0;
611 t_inlet *i;
612 if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin)
613 {
614 if (!m--) return (0);
615 n++;
616 }
617 for (i = x->ob_inlet; i; i = i->i_next, m--)
618 if (i->i_symfrom == &s_signal)
619 {
620 if (m == 0) return (n);
621 n++;
622 }
623 return (-1);
624}
625
626int obj_issignalinlet(t_object *x, int m)
627{
628 t_inlet *i;
629 if (x->ob_pd->c_firstin)
630 {
631 if (!m)
632 return (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin);
633 else m--;
634 }
635 for (i = x->ob_inlet; i && m; i = i->i_next, m--)
636 ;
637 return (i && (i->i_symfrom == &s_signal));
638}
639
640int obj_nsigoutlets(t_object *x)
641{
642 int n;
643 t_outlet *o;
644 for (o = x->ob_outlet, n = 0; o; o = o->o_next)
645 if (o->o_sym == &s_signal) n++;
646 return (n);
647}
648
649int obj_sigoutletindex(t_object *x, int m)
650{
651 int n;
652 t_outlet *o2;
653 for (o2 = x->ob_outlet, n = 0; o2; o2 = o2->o_next, m--)
654 if (o2->o_sym == &s_signal)
655 {
656 if (m == 0) return (n);
657 n++;
658 }
659 return (-1);
660}
661
662int obj_issignaloutlet(t_object *x, int m)
663{
664 /* int n; */
665 t_outlet *o2;
666 for (o2 = x->ob_outlet /* , n = 0 */; o2 && m--; o2 = o2->o_next);
667 return (o2 && (o2->o_sym == &s_signal));
668}
669
670t_sample *obj_findsignalscalar(t_object *x, int m)
671{
672 int n = 0;
673 t_inlet *i;
674 if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin)
675 {
676 if (!m--)
677 return (x->ob_pd->c_floatsignalin > 0 ?
678 (t_sample *)(((char *)x) + x->ob_pd->c_floatsignalin) : 0);
679 n++;
680 }
681 for (i = x->ob_inlet; i; i = i->i_next, m--)
682 if (i->i_symfrom == &s_signal)
683 {
684 if (m == 0)
685 return (&i->i_un.iu_floatsignalvalue);
686 n++;
687 }
688 return (0);
689}
690
691/* and these are only used in g_io.c... */
692
693int inlet_getsignalindex(t_inlet *x)
694{
695 int n = 0;
696 t_inlet *i;
697 for (i = x->i_owner->ob_inlet, n = 0; i && i != x; i = i->i_next)
698 if (i->i_symfrom == &s_signal) n++;
699 return (n);
700}
701
702int outlet_getsignalindex(t_outlet *x)
703{
704 int n = 0;
705 t_outlet *o;
706 for (o = x->o_owner->ob_outlet, n = 0; o && o != x; o = o->o_next)
707 if (o->o_sym == &s_signal) n++;
708 return (n);
709}
710