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. */
5 /* this file handles Max-style patchable objects, i.e., objects which
6 can interconnect via inlets and outlets; also, the (terse) generic
7 behavior for "gobjs" appears at the end of this file. */
15 t_gpointer
*iu_pointerslot
;
16 t_float
*iu_floatslot
;
17 t_symbol
**iu_symslot
;
18 t_sample iu_floatsignalvalue
;
24 struct _inlet
*i_next
;
28 union inletunion i_un
;
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
36 static t_class
*inlet_class
, *pointerinlet_class
, *floatinlet_class
,
39 #define ISINLET(pd) ((*(pd) == inlet_class) || \
40 (*(pd) == pointerinlet_class) || \
41 (*(pd) == floatinlet_class) || \
42 (*(pd) == symbolinlet_class))
44 /* --------------------- generic inlets ala max ------------------ */
46 t_inlet
*inlet_new(t_object
*owner
, t_pd
*dest
, t_symbol
*s1
, t_symbol
*s2
)
48 t_inlet
*x
= (t_inlet
*)pd_new(inlet_class
), *y
, *y2
;
52 x
->i_un
.iu_floatsignalvalue
= 0;
56 if((y
= owner
->ob_inlet
))
58 while((y2
= y
->i_next
)) y
= y2
;
61 else owner
->ob_inlet
= x
;
65 static void inlet_wrong(t_inlet
*x
, t_symbol
*s
)
67 pd_error(x
->i_owner
, "inlet: expected '%s' but got '%s'",
68 x
->i_symfrom
->s_name
, s
->s_name
);
71 /* LATER figure out how to make these efficient: */
72 static void inlet_bang(t_inlet
*x
)
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
);
80 static void inlet_pointer(t_inlet
*x
, t_gpointer
*gp
)
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
);
88 static void inlet_float(t_inlet
*x
, t_float f
)
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
);
99 static void inlet_symbol(t_inlet
*x
, t_symbol
*s
)
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
);
107 static void inlet_list(t_inlet
*x
, t_symbol
*s
, int argc
, t_atom
*argv
)
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
);
119 static void inlet_anything(t_inlet
*x
, t_symbol
*s
, int argc
, t_atom
*argv
)
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
);
128 void inlet_free(t_inlet
*x
)
130 t_object
*y
= x
->i_owner
;
132 if (y
->ob_inlet
== x
) y
->ob_inlet
= x
->i_next
;
133 else for (x2
= y
->ob_inlet
; x2
; x2
= x2
->i_next
)
136 x2
->i_next
= x
->i_next
;
139 t_freebytes(x
, sizeof(*x
));
142 /* ----- pointerinlets, floatinlets, syminlets: optimized inlets ------- */
144 static void pointerinlet_pointer(t_inlet
*x
, t_gpointer
*gp
)
146 gpointer_unset(x
->i_pointerslot
);
147 *(x
->i_pointerslot
) = *gp
;
148 if (gp
->gp_stub
) gp
->gp_stub
->gs_refcount
++;
151 t_inlet
*pointerinlet_new(t_object
*owner
, t_gpointer
*gp
)
153 t_inlet
*x
= (t_inlet
*)pd_new(pointerinlet_class
), *y
, *y2
;
156 x
->i_symfrom
= &s_pointer
;
157 x
->i_pointerslot
= gp
;
159 if((y
= owner
->ob_inlet
))
161 while((y2
= y
->i_next
)) y
= y2
;
164 else owner
->ob_inlet
= x
;
168 static void floatinlet_float(t_inlet
*x
, t_float f
)
170 *(x
->i_floatslot
) = f
;
173 t_inlet
*floatinlet_new(t_object
*owner
, t_float
*fp
)
175 t_inlet
*x
= (t_inlet
*)pd_new(floatinlet_class
), *y
, *y2
;
178 x
->i_symfrom
= &s_float
;
181 if((y
= owner
->ob_inlet
))
183 while((y2
= y
->i_next
)) y
= y2
;
186 else owner
->ob_inlet
= x
;
190 static void symbolinlet_symbol(t_inlet
*x
, t_symbol
*s
)
195 t_inlet
*symbolinlet_new(t_object
*owner
, t_symbol
**sp
)
197 t_inlet
*x
= (t_inlet
*)pd_new(symbolinlet_class
), *y
, *y2
;
200 x
->i_symfrom
= &s_symbol
;
203 if((y
= owner
->ob_inlet
))
205 while((y2
= y
->i_next
)) y
= y2
;
208 else owner
->ob_inlet
= x
;
212 /* ---------------------- routine to handle lists ---------------------- */
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. */
217 void obj_list(t_object
*x
, t_symbol
*s
, int argc
, t_atom
*argv
)
221 t_inlet
*ip
= ((t_object
*)x
)->ob_inlet
;
228 for (count
= argc
-1, ap
= argv
+1; ip
&& count
--; ap
++, ip
= ip
->i_next
)
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
);
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
);
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
);
250 pointerinlet_class
= class_new(gensym("inlet"), 0, 0,
251 sizeof(t_inlet
), CLASS_PD
, 0);
252 class_addpointer(pointerinlet_class
, pointerinlet_pointer
);
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
);
258 symbolinlet_class
= class_new(gensym("inlet"), 0, 0,
259 sizeof(t_inlet
), CLASS_PD
, 0);
260 class_addsymbol(symbolinlet_class
, symbolinlet_symbol
);
264 /* --------------------------- outlets ------------------------------ */
266 static char *stacklimit
, *topstack
;
267 #define STACKSIZE 1000000
268 static int outlet_eventno
;
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
273 void outlet_setstacklim(void)
277 stacklimit
= (&c
) - STACKSIZE
;
281 /* get a number unique to the (clock, MIDI, GUI, etc.) event we're on */
282 int sched_geteventno( void)
284 return (outlet_eventno
);
289 struct _outconnect
*oc_next
;
296 struct _outlet
*o_next
;
297 t_outconnect
*o_connections
;
301 t_outlet
*outlet_new(t_object
*owner
, t_symbol
*s
)
303 t_outlet
*x
= (t_outlet
*)getbytes(sizeof(*x
)), *y
, *y2
;
306 if((y
= owner
->ob_outlet
))
308 while((y2
= y
->o_next
)) y
= y2
;
311 else owner
->ob_outlet
= x
;
312 x
->o_connections
= 0;
317 static void outlet_stackerror(t_outlet
*x
)
319 pd_error(x
->o_owner
, "stack overflow");
320 stacklimit
= topstack
;
323 void outlet_bang(t_outlet
*x
)
328 outlet_stackerror(x
);
329 else for (oc
= x
->o_connections
; oc
; oc
= oc
->oc_next
)
333 void outlet_pointer(t_outlet
*x
, t_gpointer
*gp
)
339 outlet_stackerror(x
);
343 gpointer_copy(gp
, &gpointer
);
344 for (oc
= x
->o_connections
; oc
; oc
= oc
->oc_next
)
345 pd_pointer(oc
->oc_to
, &gpointer
);
346 gpointer_unset(&gpointer
);
349 for (oc
= x
->o_connections
; oc
; oc
= oc
->oc_next
)
350 pd_pointer(oc
->oc_to
, &gpointer
);
355 void outlet_float(t_outlet
*x
, t_float f
)
360 outlet_stackerror(x
);
361 else for (oc
= x
->o_connections
; oc
; oc
= oc
->oc_next
)
362 pd_float(oc
->oc_to
, f
);
365 void outlet_symbol(t_outlet
*x
, t_symbol
*s
)
370 outlet_stackerror(x
);
371 else for (oc
= x
->o_connections
; oc
; oc
= oc
->oc_next
)
372 pd_symbol(oc
->oc_to
, s
);
375 void outlet_list(t_outlet
*x
, t_symbol
*s
, int argc
, t_atom
*argv
)
380 outlet_stackerror(x
);
381 else for (oc
= x
->o_connections
; oc
; oc
= oc
->oc_next
)
382 pd_list(oc
->oc_to
, s
, argc
, argv
);
385 void outlet_anything(t_outlet
*x
, t_symbol
*s
, int argc
, t_atom
*argv
)
390 outlet_stackerror(x
);
391 else for (oc
= x
->o_connections
; oc
; oc
= oc
->oc_next
)
392 typedmess(oc
->oc_to
, s
, argc
, argv
);
395 /* get the outlet's declared symbol */
396 t_symbol
*outlet_getsymbol(t_outlet
*x
)
401 void outlet_free(t_outlet
*x
)
403 t_object
*y
= x
->o_owner
;
405 if (y
->ob_outlet
== x
) y
->ob_outlet
= x
->o_next
;
406 else for (x2
= y
->ob_outlet
; x2
; x2
= x2
->o_next
)
409 x2
->o_next
= x
->o_next
;
412 t_freebytes(x
, sizeof(*x
));
415 t_outconnect
*obj_connect(t_object
*source
, int outno
,
416 t_object
*sink
, int inno
)
421 t_outconnect
*oc
, *oc2
;
423 for (o
= source
->ob_outlet
; o
&& outno
; o
= o
->o_next
, outno
--) ;
426 if (sink
->ob_pd
->c_firstin
)
435 for (i
= sink
->ob_inlet
; i
&& inno
; i
= i
->i_next
, inno
--) ;
439 oc
= (t_outconnect
*)t_getbytes(sizeof(*oc
));
442 /* append it to the end of the list */
443 /* LATER we might cache the last "oc" to make this faster. */
444 if ((oc2
= o
->o_connections
))
446 while (oc2
->oc_next
) oc2
= oc2
->oc_next
;
449 else o
->o_connections
= oc
;
450 if (o
->o_sym
== &s_signal
) canvas_update_dsp();
455 void obj_disconnect(t_object
*source
, int outno
, t_object
*sink
, int inno
)
460 t_outconnect
*oc
, *oc2
;
462 for (o
= source
->ob_outlet
; o
&& outno
; o
= o
->o_next
, outno
--)
464 if (sink
->ob_pd
->c_firstin
)
473 for (i
= sink
->ob_inlet
; i
&& inno
; i
= i
->i_next
, inno
--) ;
477 if (!(oc
= o
->o_connections
)) return;
480 o
->o_connections
= oc
->oc_next
;
481 freebytes(oc
, sizeof(*oc
));
484 while((oc2
= oc
->oc_next
))
486 if (oc2
->oc_to
== to
)
488 oc
->oc_next
= oc2
->oc_next
;
489 freebytes(oc2
, sizeof(*oc2
));
495 if (o
->o_sym
== &s_signal
) canvas_update_dsp();
498 /* ------ traversal routines for code that can't see our structures ------ */
500 int obj_noutlets(t_object
*x
)
504 for (o
= x
->ob_outlet
, n
= 0; o
; o
= o
->o_next
) n
++;
508 int obj_ninlets(t_object
*x
)
512 for (i
= x
->ob_inlet
, n
= 0; i
; i
= i
->i_next
) n
++;
513 if (x
->ob_pd
->c_firstin
) n
++;
517 t_outconnect
*obj_starttraverseoutlet(t_object
*x
, t_outlet
**op
, int nout
)
519 t_outlet
*o
= x
->ob_outlet
;
520 while (nout
-- && o
) o
= o
->o_next
;
522 if (o
) return (o
->o_connections
);
526 t_outconnect
*obj_nexttraverseoutlet(t_outconnect
*lastconnect
,
527 t_object
**destp
, t_inlet
**inletp
, int *whichp
)
530 y
= lastconnect
->oc_to
;
534 t_inlet
*i
= (t_inlet
*)y
, *i2
;
535 t_object
*dest
= i
->i_owner
;
536 for (n
= dest
->ob_pd
->c_firstin
, i2
= dest
->ob_inlet
;
537 i2
&& i2
!= i
; i2
= i2
->i_next
) n
++;
546 *destp
= ((t_object
*)y
);
548 return (lastconnect
->oc_next
);
551 /* this one checks that a pd is indeed a patchable object, and returns
552 it, correctly typed, or zero if the check failed. */
553 t_object
*pd_checkobject(t_pd
*x
)
555 if ((*x
)->c_patchable
) return ((t_object
*)x
);
559 /* move an inlet or outlet to the head of the list */
560 void obj_moveinletfirst(t_object
*x
, t_inlet
*i
)
563 if (x
->ob_inlet
== i
) return;
564 else for (i2
= x
->ob_inlet
; i2
; i2
= i2
->i_next
)
567 i2
->i_next
= i
->i_next
;
568 i
->i_next
= x
->ob_inlet
;
574 void obj_moveoutletfirst(t_object
*x
, t_outlet
*o
)
577 if (x
->ob_outlet
== o
) return;
578 else for (o2
= x
->ob_outlet
; o2
; o2
= o2
->o_next
)
581 o2
->o_next
= o
->o_next
;
582 o
->o_next
= x
->ob_outlet
;
588 /* routines for DSP sorting, which are used in d_ugen.c and g_canvas.c */
589 /* LATER try to consolidate all the slightly different routines. */
591 int obj_nsiginlets(t_object
*x
)
595 for (i
= x
->ob_inlet
, n
= 0; i
; i
= i
->i_next
)
596 if (i
->i_symfrom
== &s_signal
) n
++;
597 if (x
->ob_pd
->c_firstin
&& x
->ob_pd
->c_floatsignalin
) n
++;
601 /* get the index, among signal inlets, of the mth inlet overall */
602 int obj_siginletindex(t_object
*x
, int m
)
606 if (x
->ob_pd
->c_firstin
&& x
->ob_pd
->c_floatsignalin
)
608 if (!m
--) return (0);
611 for (i
= x
->ob_inlet
; i
; i
= i
->i_next
, m
--)
612 if (i
->i_symfrom
== &s_signal
)
614 if (m
== 0) return (n
);
620 int obj_issignalinlet(t_object
*x
, int m
)
623 if (x
->ob_pd
->c_firstin
)
626 return (x
->ob_pd
->c_firstin
&& x
->ob_pd
->c_floatsignalin
);
629 for (i
= x
->ob_inlet
; i
&& m
; i
= i
->i_next
, m
--)
631 return (i
&& (i
->i_symfrom
== &s_signal
));
634 int obj_nsigoutlets(t_object
*x
)
638 for (o
= x
->ob_outlet
, n
= 0; o
; o
= o
->o_next
)
639 if (o
->o_sym
== &s_signal
) n
++;
643 int obj_sigoutletindex(t_object
*x
, int m
)
647 for (o2
= x
->ob_outlet
, n
= 0; o2
; o2
= o2
->o_next
, m
--)
648 if (o2
->o_sym
== &s_signal
)
650 if (m
== 0) return (n
);
656 int obj_issignaloutlet(t_object
*x
, int m
)
660 for (o2
= x
->ob_outlet
, n
= 0; o2
&& m
--; o2
= o2
->o_next
);
661 return (o2
&& (o2
->o_sym
== &s_signal
));
664 t_sample
*obj_findsignalscalar(t_object
*x
, int m
)
668 if (x
->ob_pd
->c_firstin
&& x
->ob_pd
->c_floatsignalin
)
671 return (x
->ob_pd
->c_floatsignalin
> 0 ?
672 (t_sample
*)(((char *)x
) + x
->ob_pd
->c_floatsignalin
) : 0);
675 for (i
= x
->ob_inlet
; i
; i
= i
->i_next
, m
--)
676 if (i
->i_symfrom
== &s_signal
)
679 return (&i
->i_un
.iu_floatsignalvalue
);
685 /* and these are only used in g_io.c... */
687 int inlet_getsignalindex(t_inlet
*x
)
691 for (i
= x
->i_owner
->ob_inlet
, n
= 0; i
&& i
!= x
; i
= i
->i_next
)
692 if (i
->i_symfrom
== &s_signal
) n
++;
696 int outlet_getsignalindex(t_outlet
*x
)
700 for (o
= x
->o_owner
->ob_outlet
, n
= 0; o
&& o
!= x
; o
= o
->o_next
)
701 if (o
->o_sym
== &s_signal
) n
++;