A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 710 lines 17 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/* 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