aboutsummaryrefslogtreecommitdiff
path: root/puredata
diff options
context:
space:
mode:
Diffstat (limited to 'puredata')
-rw-r--r--puredata/CONTENTS10
-rw-r--r--puredata/Makefile12
-rw-r--r--puredata/pdp_base.c415
-rw-r--r--puredata/pdp_comm.c367
-rw-r--r--puredata/pdp_compat.c57
-rw-r--r--puredata/pdp_control.c186
-rw-r--r--puredata/pdp_dpd_base.c270
-rw-r--r--puredata/pdp_imagebase.c79
-rw-r--r--puredata/pdp_queue.c386
-rw-r--r--puredata/pdp_ut.c262
10 files changed, 2044 insertions, 0 deletions
diff --git a/puredata/CONTENTS b/puredata/CONTENTS
new file mode 100644
index 0000000..19eeaaa
--- /dev/null
+++ b/puredata/CONTENTS
@@ -0,0 +1,10 @@
+base pdp base pd object
+image_base image processing base pd object
+dpd_base bucket base pd object
+comm pdp communication protocol in pd
+compat legacy pdp stuff
+control control pdp from within pd
+fp object interface to the forth system
+forthconsole console interface to the forth system
+queue processing queue and synchro stuff
+ut some utility pd objects
diff --git a/puredata/Makefile b/puredata/Makefile
new file mode 100644
index 0000000..11c78ec
--- /dev/null
+++ b/puredata/Makefile
@@ -0,0 +1,12 @@
+
+OBJECTS = pdp_base.o pdp_imagebase.o pdp_dpd_base.o pdp_ut.o pdp_queue.o pdp_comm.o \
+ pdp_control.o pdp_compat.o $(PDP_PDMOD)
+
+
+include ../Makefile.config
+
+all: $(OBJECTS)
+
+clean:
+ rm -f *~
+ rm -f *.o
diff --git a/puredata/pdp_base.c b/puredata/pdp_base.c
new file mode 100644
index 0000000..194134e
--- /dev/null
+++ b/puredata/pdp_base.c
@@ -0,0 +1,415 @@
+/*
+ * Pure Data Packet base class implementation.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+/*
+
+ This file contains the pdp base class object.
+ This is really nothing more than an attempt to stay away from c++
+ as far as possible, while having some kind of base class functionality
+ for pdp (tucking away the communication & thread protocol).
+
+*/
+
+#include "pdp_base.h"
+#include <stdarg.h>
+
+
+static void pdp_base_debug(t_pdp_base *b, t_floatarg f)
+{
+ int i;
+ post("debug");
+ post("inlets: %d", b->b_inlets);
+ post("\tpacket\tnext_packet");
+ for (i=0; i<b->b_inlets; i++)
+ post("\t%d\t%d", b->b_packet[i], b->b_packet_next[i]);
+ //post("outlets: %d", b->b_inlets);
+}
+
+static void pdp_base_thread(t_pdp_base *b, t_floatarg f)
+{
+ int i = (int)f;
+ if ((i == 0) || (i == 1)) b->b_thread_enabled = i;
+}
+
+static void pdp_base_process(t_pdp_base *b)
+{
+
+ if (b->b_process_method)
+ (*b->b_process_method)(b);
+}
+
+/* this method is called after the thread has finished processing */
+static void pdp_base_postprocess(t_pdp_base *b)
+{
+ /* call the derived class postproc callback if there is any */
+ if (b->b_postproc_method)
+ (*b->b_postproc_method)(b);
+
+ /* unregister (mark unused) packet and propagate if packet is valid */
+ if (b->b_outlet[0])
+ pdp_pass_if_valid(b->b_outlet[0], &b->b_packet[0]);
+}
+
+
+/* move the passive packets in place */
+void pdp_base_movepassive(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ int i;
+
+ /* if a cold packet was received in the meantime
+ swap it in, else keep the old one */
+ for (i=1; i<b->b_inlets; i++){
+ pdp_replace_if_valid(&b->b_packet[i], &b->b_packet_next[i]);
+ }
+
+
+}
+
+/* the standard bang method */
+void pdp_base_bang(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ int i;
+
+ /* if pdp thread is still processing, do nothing */
+ if (-1 != b->b_queue_id) return;
+
+ /* move packets in place */
+ pdp_base_movepassive(x);
+
+
+ /* if there is a preproc method defined, call it inside
+ the pd thread. (mainly for allocations) */
+ if (b->b_preproc_method)
+ (*b->b_preproc_method)(b);
+
+ /* check if we need to use pdp queue */
+ if (b->b_thread_enabled){
+
+ /* add the process method and callback to the process queue */
+ pdp_procqueue_add(b->b_q, b, pdp_base_process, pdp_base_postprocess, &b->b_queue_id);
+ }
+ else{
+ /* call both methods directly */
+ pdp_base_process(b);
+ pdp_base_postprocess(b);
+ }
+}
+
+/* hot packet input handler */
+void pdp_base_input_hot(t_pdp_base *b, t_symbol *s, t_floatarg f)
+{
+
+ int p = (int)f;
+
+ /* dont register if active inlet is disabled */
+ if (!b->b_active_inlet_enabled) return;
+
+ /* register the packet (readonly or read/write)
+ or drop it if we have an active packet
+ if type template is not null, packet will be converted */
+
+
+ if (b->b_active_inlet_readonly){
+ if (s == S_REGISTER_RO){
+ if (b->b_type_template[0]){
+ pdp_packet_convert_ro_or_drop(&b->b_packet[0], p, b->b_type_template[0]);
+ }
+ else{
+ pdp_packet_copy_ro_or_drop(&b->b_packet[0], p);
+ }
+ }
+ }
+ else{
+ if (s == S_REGISTER_RW) {
+ if (b->b_type_template[0]){
+ pdp_packet_convert_rw_or_drop(&b->b_packet[0], p, b->b_type_template[0]);
+ }
+ else{
+ pdp_packet_copy_rw_or_drop(&b->b_packet[0], p);
+ }
+ }
+ }
+
+ /* start processing if there is an active packet to process
+ and the processing method is not active */
+
+ if ((s == S_PROCESS) && (-1 != b->b_packet[0]) && (-1 == b->b_queue_id)){
+ pdp_base_bang(b);
+ }
+ //if ((pdp_sym_prc() == s) && (-1 != b->b_packet[0]) && (!b->b_dropped)) pdp_base_bang(b);
+
+}
+
+/* cold packet input handlers */
+void pdp_base_input_cold(t_pdp_base *b, t_symbol *s, int ac, t_atom *av)
+{
+
+ int p;
+ int i;
+ char msg[] = "pdp1";
+ char *c;
+
+ int inlet;
+
+ //post("pdp_base_input_cold: got packet");
+
+ /* do cheap tests first */
+ if (ac != 2) return;
+ if (av[0].a_type != A_SYMBOL) return;
+ if (av[0].a_w.w_symbol != S_REGISTER_RO) return;
+ if (av[1].a_type != A_FLOAT) return;
+ p = (int)av[1].a_w.w_float;
+
+
+ /* check if it's a pdp message
+ and determine inlet */
+ for (i=1; i<MAX_NB_PDP_BASE_INLETS; i++){
+ if (s == gensym(msg)){
+ inlet = i;
+ goto found;
+ }
+ else{
+ msg[3]++;
+ }
+ }
+ return;
+
+
+ found:
+
+ /* store the packet and trow away
+ the old one, if there is any */
+
+ pdp_packet_copy_ro_or_drop(&b->b_packet_next[inlet], p);
+}
+
+
+void pdp_base_set_process_method(void *x, t_pdp_method m)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ b->b_process_method = m;
+}
+
+void pdp_base_set_preproc_method(void *x, t_pdp_method m)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ b->b_preproc_method = m;
+}
+
+
+void pdp_base_set_postproc_method(void *x, t_pdp_method m)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ b->b_postproc_method = m;
+}
+
+
+void pdp_base_queue_wait(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ pdp_procqueue_wait(b->b_q);
+}
+
+void pdp_base_set_queue(void *x, t_pdp_procqueue *q)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ pdp_base_queue_wait(x);
+ b->b_q = q;
+}
+
+t_pdp_procqueue *pdp_base_get_queue(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ return b->b_q;
+}
+
+void pdp_base_setup(t_class *c)
+{
+
+ /* add pdp base class methods */
+ class_addmethod(c, (t_method)pdp_base_thread, gensym("thread"), A_FLOAT, A_NULL);
+ class_addmethod(c, (t_method)pdp_base_debug, gensym("debug"), A_NULL);
+
+ /* hot packet handler */
+ class_addmethod(c, (t_method)pdp_base_input_hot, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+
+ /* cold packet handler */
+ class_addanything(c, (t_method)pdp_base_input_cold);
+}
+
+/* pdp base instance constructor */
+void pdp_base_init(void *x)
+{
+ int i;
+ t_pdp_base *b = (t_pdp_base *)x;
+
+ b->b_channel_mask = -1;
+
+ for(i=0; i<MAX_NB_PDP_BASE_INLETS; i++){
+ b->b_packet[i] = -1;
+ b->b_packet_next[i] = -1;
+ b->b_type_template[i] = 0;
+ }
+
+ b->b_queue_id = -1;
+ //b->b_dropped = 0;
+ b->b_process_method = 0;
+ b->b_preproc_method = 0;
+ b->b_inlets = 1;
+ b->b_outlets = 0;
+ b->b_active_inlet_enabled = 1;
+ b->b_active_inlet_readonly = 0;
+ b->b_thread_enabled = 1;
+
+ // default queue is pdp queue
+ b->b_q = pdp_queue_get_queue();
+
+}
+
+/* base instance destructor */
+void pdp_base_free(void *x)
+{
+ int i;
+ t_pdp_base *b = (t_pdp_base *)x;
+ /* remove process method from queue before deleting data */
+ pdp_procqueue_finish(b->b_q, b->b_queue_id);
+
+ /* delete stuff */
+ for(i=0; i<MAX_NB_PDP_BASE_INLETS; i++){
+ pdp_packet_mark_unused(b->b_packet[i]);
+ pdp_packet_mark_unused(b->b_packet_next[i]);
+ }
+
+}
+
+void pdp_base_readonly_active_inlet(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ b->b_active_inlet_readonly = 1;
+}
+
+void pdp_base_disable_active_inlet(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ b->b_active_inlet_enabled = 0;
+}
+
+
+/* add an inlet */
+void pdp_base_add_pdp_inlet(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ char s[] = "pdp0";
+ s[3] += b->b_inlets;
+
+ if (b->b_inlets < MAX_NB_PDP_BASE_INLETS){
+ inlet_new(&b->x_obj, &b->x_obj.ob_pd, gensym("pdp"), gensym(s));
+ b->b_inlets++;
+ }
+ else {
+ post("pdp_base_add_pdp_inlet: only %d pdp inlets allowed. ignoring.", MAX_NB_PDP_BASE_INLETS);
+ }
+}
+
+
+/* add an outlet: only one allowed */
+t_outlet *pdp_base_add_pdp_outlet(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ t_outlet *outlet = outlet_new(&b->x_obj, &s_anything);
+
+
+ if (b->b_outlets < MAX_NB_PDP_BASE_OUTLETS){
+ b->b_outlet[b->b_outlets] = outlet;
+ b->b_outlets++;
+ }
+
+ return outlet;
+
+}
+
+void pdp_base_set_packet(void *x, int inlet, int packet)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+
+ if (inlet < b->b_inlets){
+ //post("%d %d", b->b_packet[inlet], b->b_packet_next[inlet]);
+ pdp_packet_mark_unused(b->b_packet[inlet]);
+ b->b_packet[inlet] = packet;
+ }
+}
+
+
+int pdp_base_get_packet(void *x, int inlet)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+
+ if (inlet < b->b_inlets){
+ //post("%d %d", b->b_packet[inlet], b->b_packet_next[inlet]);
+ return (b->b_packet[inlet]);
+ }
+
+ return -1;
+}
+
+int pdp_base_move_packet(void *x, int inlet)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ int p;
+
+ if (inlet < b->b_inlets){
+ p = b->b_packet[inlet];
+ b->b_packet[inlet] = -1;
+ return (p);
+ }
+
+ return -1;
+}
+
+
+
+t_object *pdp_base_get_object(void *x)
+{
+ return (t_object *)x;
+}
+
+void pdp_base_add_gen_inlet(void *x, t_symbol *from, t_symbol *to)
+{
+ t_object *o = (t_object *)x;
+ inlet_new(o, &o->ob_pd, from, to);
+}
+
+void pdp_base_disable_thread(void *x)
+{
+
+ t_pdp_base *b = (t_pdp_base *)x;
+ b->b_thread_enabled = 0;
+}
+
+void pdp_base_set_type_template(void *x, int inlet, t_pdp_symbol *type_template)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ if (inlet < b->b_inlets){
+ b->b_type_template[inlet] = type_template;
+ }
+}
diff --git a/puredata/pdp_comm.c b/puredata/pdp_comm.c
new file mode 100644
index 0000000..4c67659
--- /dev/null
+++ b/puredata/pdp_comm.c
@@ -0,0 +1,367 @@
+/*
+ * Pure Data Packet system implementation.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* this file contains misc communication (packet) methods for pd */
+
+
+#include <stdio.h>
+#include "pdp_pd.h"
+#include "pdp_internals.h"
+#include "pdp_packet.h"
+#include "pdp_comm.h"
+#include "pdp_type.h"
+#include "pdp_control.h"
+#include "pdp_mem.h"
+#include "pdp_queue.h" // for notify drop: fix this (should be from pdp_control.h)
+#include "pdp_debug.h"
+
+/* all symbols are C style */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+/* interface to pd system:
+ pdp/dpd communication protocol in pd
+ pd <-> pdp atom and list conversion */
+
+ /* NOTE: when using the outlet_pdp methods, the packet
+ is no longer read/write, but can be readonly */
+
+
+ /* NOTE: since 0.13 the passing packet is no more.
+ in order to limit copying. processors should always register ro,
+ and replace with writable when packet needs to be written in the process method */
+
+
+/* send a packet to an outlet */
+void outlet_pdp_register(t_outlet *out, int packetid)
+{
+ t_atom atom[2];
+
+ SETFLOAT(atom+1, (float)packetid);
+
+ /* during the following phase,
+ objects can register a ro copy */
+
+ SETSYMBOL(atom+0, S_REGISTER_RO);
+ outlet_anything(out, S_PDP, 2, atom);
+
+ /* DEPRECIATED: objects can register a rw copy
+ but this will always copy the packet. it is better
+ to perform a pdp_packet_replace_with_writable operation during the process step */
+
+ SETSYMBOL(atom+0, S_REGISTER_RW);
+ outlet_anything(out, S_PDP, 2, atom);
+
+}
+/* send a packet to an outlet */
+void outlet_pdp_process(t_outlet *out)
+{
+ t_atom atom[2];
+
+ /* set a dummy invalid packet.
+ this is for uniform pdp messages in pd, for ease of routing. */
+ SETFLOAT(atom+1, (float)-1);
+
+ /* during the process phase, objects can perform pdp_packet_replace_with_writable
+ and process the packet data */
+ SETSYMBOL(atom+0, S_PROCESS);
+ outlet_anything(out, S_PDP, 2, atom);
+
+}
+
+/* for compat */
+void outlet_pdp(t_outlet *out, int packetid)
+{
+ outlet_pdp_register(out, packetid);
+ outlet_pdp_process(out);
+}
+
+/* send an accumulation packet to an outlet */
+void outlet_dpd(t_outlet *out, int packetid)
+{
+ t_atom atom[2];
+
+ SETFLOAT(atom+1, (float)packetid);
+
+ SETSYMBOL(atom+0, S_INSPECT);
+ outlet_anything(out, S_DPD, 2, atom);
+
+ SETSYMBOL(atom+0, S_ACCUMULATE);
+ outlet_anything(out, S_DPD, 2, atom);
+
+}
+
+/* unregister a packet and send it to an outlet */
+void
+
+pdp_packet_pass_if_valid(t_outlet *outlet, int *packet_ptr)
+{
+ t_pdp *header = pdp_packet_header(*packet_ptr);
+ if (header){
+
+ /* send register phase */
+ outlet_pdp_register(outlet, *packet_ptr);
+
+ /* unregister */
+ pdp_packet_mark_unused(*packet_ptr);
+ *packet_ptr = -1;
+
+ /* send process phase */
+ outlet_pdp_process(outlet);
+
+ }
+}
+
+void
+pdp_packet_replace_if_valid(int *dpacket, int *spacket)
+{
+ if (-1 != *spacket){
+ pdp_packet_mark_unused(*dpacket);
+ *dpacket = *spacket;
+ *spacket = -1;
+ }
+
+}
+
+
+int
+pdp_packet_copy_ro_or_drop(int *dpacket, int spacket)
+{
+ int drop = 0;
+ if (*dpacket == -1) *dpacket = pdp_packet_copy_ro(spacket);
+ else {
+ /* send a notification there is a dropped packet */
+ pdp_control_notify_drop(spacket);
+ drop = 1;
+ }
+ return drop;
+}
+
+
+int
+pdp_packet_copy_rw_or_drop(int *dpacket, int spacket)
+{
+ int drop = 0;
+ if (*dpacket == -1) *dpacket = pdp_packet_copy_rw(spacket);
+ else {
+ /* send a notification there is a dropped packet */
+ pdp_control_notify_drop(spacket);
+ drop = 1;
+ }
+ return drop;
+}
+
+int
+pdp_packet_convert_ro_or_drop(int *dpacket, int spacket, t_pdp_symbol *template)
+{
+ int drop = 0;
+
+ if (!template) return pdp_packet_copy_ro_or_drop(dpacket, spacket);
+
+ if (*dpacket == -1) *dpacket = pdp_packet_convert_ro(spacket, template);
+ else {
+ /* send a notification there is a dropped packet */
+ pdp_control_notify_drop(spacket);
+ drop = 1;
+ }
+ return drop;
+}
+
+
+int
+pdp_packet_convert_rw_or_drop(int *dpacket, int spacket, t_pdp_symbol *template)
+{
+ int drop = 0;
+
+ if (!template) return pdp_packet_copy_rw_or_drop(dpacket, spacket);
+
+ if (*dpacket == -1) *dpacket = pdp_packet_convert_rw(spacket, template);
+ else {
+ /* send a notification there is a dropped packet */
+ pdp_control_notify_drop(spacket);
+ drop = 1;
+ }
+ return drop;
+}
+
+
+/* send a pdp list to a pd outlet. packets are not copied but passed! */
+void outlet_pdp_atom(t_outlet *out, t_pdp_atom *a)
+{
+ int packet = -1;
+ if (!a) return;
+ switch(a->t){
+ case a_float:
+ outlet_float(out, a->w.w_float);
+ return;
+ case a_int:
+ outlet_float(out, (float)a->w.w_int);
+ return;
+ case a_symbol:
+ outlet_symbol(out, gensym(a->w.w_symbol->s_name));
+ return;
+ case a_list:
+ outlet_pdp_list(out, a->w.w_list);
+ return;
+ case a_packet:
+ pdp_packet_pass_if_valid(out, &a->w.w_packet);
+ return;
+ default:
+ return;
+ }
+}
+
+void outlet_pdp_list(t_outlet *out, struct _pdp_list *l)
+{
+ int elements;
+ t_atom *atomlist;
+ t_pdp_atom *pdp_a;
+ t_atom *pd_a;
+ t_symbol *pd_selector;
+
+ if (!l) return;
+ switch(l->elements){
+ case 0: /* bang */
+ outlet_bang(out);
+ return;
+ case 1: /* atom */
+ outlet_pdp_atom(out, l->first);
+ return;
+ default: /* proper list*/
+ elements = l->elements;
+
+ /* allocate list */
+ atomlist = pdp_alloc(sizeof (t_atom) * l->elements);
+ pd_a = atomlist;
+ pdp_a = l->first;
+
+ /* setup selector */
+ if (pdp_a->t != a_symbol){
+ pd_selector = gensym("list");
+ }
+ else {
+ pd_selector = gensym(pdp_a->w.w_symbol->s_name);
+ elements--;
+ pdp_a = pdp_a->next;
+ }
+
+ /* setup atoms */
+ while (pdp_a){
+ switch(pdp_a->t){
+ case a_float:
+ SETFLOAT(pd_a, pdp_a->w.w_float);
+ break;
+ case a_int:
+ SETFLOAT(pd_a, (float)pdp_a->w.w_int);
+ break;
+ case a_symbol:
+ SETSYMBOL(pd_a, gensym(pdp_a->w.w_symbol->s_name));
+ break;
+ default:
+ SETSYMBOL(pd_a, gensym("invalid"));
+ break;
+ }
+
+ pdp_a = pdp_a->next;
+ pd_a++;
+ }
+
+ /* send out */
+ outlet_anything(out, pd_selector, elements, atomlist);
+
+
+
+ /* clean up */
+ pdp_dealloc(atomlist);
+
+ }
+
+
+}
+
+
+void pd_atom_to_pdp_atom(t_atom *pdatom, t_pdp_atom *pdpatom)
+{
+ switch (pdatom->a_type){
+ case A_FLOAT:
+ pdpatom->t = a_float;
+ pdpatom->w.w_float = pdatom->a_w.w_float;
+ break;
+ case A_SYMBOL:
+ pdpatom->t = a_symbol;
+ pdpatom->w.w_symbol = pdp_gensym(pdatom->a_w.w_symbol->s_name);
+ break;
+ default:
+ pdpatom->t = a_undef;
+ break;
+ }
+}
+
+
+
+/* some "accelerated" pd symbols */
+t_symbol s_pdp = {"pdp", 0, 0};
+t_symbol s_register_ro = {"register_ro", 0, 0};
+t_symbol s_register_rw = {"register_rw", 0, 0};
+t_symbol s_process = {"process", 0, 0};
+t_symbol s_dpd = {"dpd", 0, 0};
+t_symbol s_inspect = {"inspect", 0, 0};
+t_symbol s_accumulate = {"accumulate", 0, 0};
+t_symbol s_chanmask = {"chanmask", 0, 0};
+
+// internal pd method
+t_symbol *dogensym(char *s, t_symbol *oldsym);
+static void _addsym(t_symbol *s)
+{
+
+ /* don't kill me for this one..
+ if the symbol is already defined and used, .. well, that's a problem
+ but right now it seems a reasonable hack */
+
+ t_symbol *sret = dogensym(s->s_name, s);
+ if (s != sret){
+ post("PDP INIT ERROR: pd symbol clash adding symbol %s: new=%08x old=%08x", s->s_name, s, sret);
+ post("try loading pdp before other libraries");
+ }
+}
+
+void
+pdp_pdsym_setup(void)
+{
+
+ _addsym(&s_pdp);
+ _addsym(&s_register_ro);
+ _addsym(&s_register_rw);
+ _addsym(&s_process);
+ _addsym(&s_dpd);
+ _addsym(&s_inspect);
+ _addsym(&s_accumulate);
+ _addsym(&s_chanmask);
+
+}
+
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/puredata/pdp_compat.c b/puredata/pdp_compat.c
new file mode 100644
index 0000000..e7bc0c2
--- /dev/null
+++ b/puredata/pdp_compat.c
@@ -0,0 +1,57 @@
+/*
+ * Pure Data Packet system implementation. Compatibility routines.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+/* this file contains misc communication methods */
+
+#include <stdio.h>
+
+#include "pdp_pd.h"
+#include "pdp_comm.h"
+#include "pdp_internals.h"
+
+/* all symbols are C style */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+
+void
+pdp_pass_if_valid(t_outlet *outlet, int *packet)
+{
+ pdp_packet_pass_if_valid(outlet, packet);
+}
+
+void
+pdp_replace_if_valid(int *dpacket, int *spacket)
+{
+ pdp_packet_replace_if_valid(dpacket, spacket);
+
+}
+
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/puredata/pdp_control.c b/puredata/pdp_control.c
new file mode 100644
index 0000000..0b49fd9
--- /dev/null
+++ b/puredata/pdp_control.c
@@ -0,0 +1,186 @@
+/*
+ * Pure Data Packet system implementation: control object
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+/* this is an actual pd class that is used for communication with the
+ pdp framework */
+
+#include "pdp_internals.h"
+#include "pdp_control.h"
+#include "pdp_packet.h"
+#include <stdio.h>
+
+/* all symbols are C style */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+
+static long dropped_packets;
+
+static t_class* pdp_control_class;
+
+
+/* pdp control instance data */
+
+struct _pdp_control;
+typedef struct _pdp_control
+{
+ t_object x_obj;
+ t_outlet *x_outlet0;
+ struct _pdp_control *x_next;
+
+} t_pdp_control;
+
+
+
+static t_pdp_control *pdp_control_list;
+
+static void pdp_control_info(t_pdp_control *x)
+{
+}
+
+static void pdp_control_collectgarbage(t_pdp_control *x)
+{
+ int nb_packets_freed = pdp_pool_collect_garbage();
+ post("pdp_control: freed %d packets", nb_packets_freed);
+
+}
+
+static void pdp_control_set_mem_limit(t_pdp_control *x, t_floatarg f)
+{
+ int limit = (int)f;
+ if (limit < 0) limit = 0;
+ pdp_pool_set_max_mem_usage(limit);
+ if (limit) post("pdp_control: set memory limit to %d bytes", limit);
+ else post("pdp_control: disabled memory limit");
+
+}
+
+static void pdp_control_thread(t_pdp_control *x, t_floatarg f)
+{
+ int t = (int)f;
+
+ if (t){
+ post("pdp_control: pdp is now using its own processing thread");
+ pdp_queue_use_thread(1);
+ }
+ else {
+ post("pdp_control: pdp is now using the main pd thread");
+ pdp_queue_use_thread(0);
+ }
+}
+
+
+static void pdp_control_send_drop_message(t_pdp_control *x)
+{
+ t_atom atom[1];
+ t_symbol *s = gensym("pdp_drop");
+
+ SETFLOAT(atom+0, (float)dropped_packets);
+ outlet_anything(x->x_outlet0, s, 1, atom);
+}
+
+
+static void pdp_control_free(t_pdp_control *x)
+{
+ /* remove from linked list */
+ t_pdp_control *curr = pdp_control_list;
+ if (pdp_control_list == x) pdp_control_list = x->x_next;
+ else while (curr){
+ if (curr->x_next == x) {
+ curr->x_next = x->x_next;
+ break;
+ }
+ else {
+ curr = curr->x_next;
+ }
+
+ }
+}
+
+
+static void *pdp_control_new(void)
+{
+ t_pdp_control *x = (t_pdp_control *)pd_new(pdp_control_class);
+ x->x_outlet0 = outlet_new(&x->x_obj, &s_anything);
+
+ /* add to list */
+ x->x_next = pdp_control_list;
+ pdp_control_list = x;
+ return x;
+}
+
+/************************* class methods ***************************************/
+
+
+void pdp_control_addmethod(t_method m, t_symbol *s)
+{
+ class_addmethod(pdp_control_class, m, s, A_GIMME, A_NULL);
+}
+
+void pdp_control_setup(void)
+{
+
+ pdp_control_list = 0;
+ dropped_packets = 0;
+
+ /* setup pd class data */
+ pdp_control_class = class_new(gensym("pdp_control"), (t_newmethod)pdp_control_new,
+ (t_method)pdp_control_free, sizeof(t_pdp_control), 0, A_NULL);
+
+
+ class_addmethod(pdp_control_class, (t_method)pdp_control_info, gensym("info"), A_NULL);
+ class_addmethod(pdp_control_class, (t_method)pdp_control_thread, gensym("thread"), A_DEFFLOAT, A_NULL);
+ class_addmethod(pdp_control_class, (t_method)pdp_control_collectgarbage, gensym("collectgarbage"), A_NULL);
+ class_addmethod(pdp_control_class, (t_method)pdp_control_set_mem_limit, gensym("memlimit"), A_FLOAT, A_NULL);
+}
+
+
+
+void pdp_control_notify_broadcast(t_pdp_control_method_notify *notify)
+{
+ t_pdp_control *curr = pdp_control_list;
+ while (curr){
+ (*notify)(curr);
+ curr = curr->x_next;
+ }
+}
+
+
+
+/************************* notify class methods *************************/
+
+void pdp_control_notify_drop(int packet)
+{
+ dropped_packets++;
+
+ /* send drop notify to controller class instances */
+ pdp_control_notify_broadcast(pdp_control_send_drop_message);
+ //post("dropped packet");
+}
+
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/puredata/pdp_dpd_base.c b/puredata/pdp_dpd_base.c
new file mode 100644
index 0000000..371b99e
--- /dev/null
+++ b/puredata/pdp_dpd_base.c
@@ -0,0 +1,270 @@
+/*
+ * Pure Data Packet module. DPD base class implementation.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+#include "pdp_dpd_base.h"
+#include "pdp_internals.h"
+
+
+#define THIS(b) t_pdp_dpd_base *b = (t_pdp_dpd_base *)x
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+
+/* PRIVATE METHODS */
+
+
+
+
+/* dpd packet context input handler */
+static void _pdp_dpd_base_context_input(t_pdp_dpd_base *b, t_symbol *s, t_floatarg f)
+{
+
+ int p = (int)f;
+ int i;
+
+ //post ("pdp_dpd_base_context_input: got %s %d", s->s_name, p);
+
+ /* sources/sinks have active inlet disabled */
+ if (b->b_dpd_active_inlet_disabled) return;
+
+ /* handle inspect message */
+ if (s == S_INSPECT){
+
+ /* store packet for inspector */
+ b->b_context_packet = p;
+
+ /* add inspector to pdp queue
+ this is special: it doesn't use a command object */
+ pdp_dpd_base_queue_command(b, b, b->b_inspector_method, b->b_inspector_callback, 0);
+ }
+
+ /* handle accumulate message */
+ if (s == S_ACCUMULATE){
+
+ /* store context for accumulator methods */
+ b->b_context_packet = p;
+
+ /* call bang */
+ pdp_dpd_base_bang(b);
+
+
+ }
+
+}
+
+/* default command object (returns self) */
+void *_pdp_dpd_base_get_command_object(void *x){return x;}
+
+/* PUBLIC METHODS */
+
+
+void pdp_dpd_base_queue_command(void *x, void *c, t_pdp_method process,
+ t_pdp_method callback, int *id)
+{
+ THIS(b);
+ t_pdp_procqueue *q = pdp_base_get_queue(x);
+ pdp_procqueue_add(q, c, process, callback, id);
+
+}
+
+/* bang method (propagate context to outlet) : it is not registered as a pd message by default ! */
+void pdp_dpd_base_bang(void *x)
+{
+ THIS(b);
+ int i, id;
+ void *cobj;
+
+ /* move passive pdp packets in place */
+ pdp_base_movepassive(x);
+
+ /* get command object (or use self) */
+ cobj = b->b_command_factory_method ? (b->b_command_factory_method)(b) : b;
+ //post(" command object is %x. object is %x", cobj, b);
+
+
+ /* queue acc method & propagate for all outlets */
+ for (i=b->b_nb_context_outlets; i--;){
+
+
+ /* propagate the context packet to the outlet */
+ if (b->b_outlet_enable[i]){
+ pdp_dpd_base_queue_command(x, cobj, b->b_accum_method[i], b->b_accum_callback[i], 0);
+ outlet_dpd(b->b_context_outlet[i], b->b_context_packet);
+ }
+ else{
+ //post("outlet %d disabled", i);
+ }
+ }
+
+ /* queue cleanup method */
+ if (b->b_cleanup_method)
+ //pdp_procqueue_add(b->b_q, b, b->b_cleanup_method, 0, &b->b_cleanup_queue_id);
+ pdp_dpd_base_queue_command(x, cobj, b->b_cleanup_method, b->b_cleanup_callback, 0);
+
+ /* send communication complete notify */
+ if (b->b_complete_notify)
+ (b->b_complete_notify)(x);
+
+}
+
+/* get/set context packet */
+int pdp_dpd_base_get_context_packet(void *x){
+ THIS(b);
+ return b->b_context_packet;
+}
+int pdp_dpd_base_move_context_packet(void *x){
+ THIS(b);
+ int p = b->b_context_packet;
+ b->b_context_packet = -1;
+ return p;
+}
+
+void pdp_dpd_base_set_context_packet(void *x, int p){
+ THIS(b);
+ pdp_packet_mark_unused(b->b_context_packet);
+ b->b_context_packet = p;
+}
+
+/* add a cleanup callback (called after all propagation is finished) for sources/sinks */
+void pdp_dpd_base_add_cleanup(void *x, t_pdp_method cleanup_method, t_pdp_method cleanup_callback)
+{
+ THIS(b);
+ b->b_cleanup_method = cleanup_method;
+ b->b_cleanup_callback = cleanup_callback;
+ //b->b_cleanup_queue_id = -1;
+}
+
+/* add a inspector callback */
+void pdp_dpd_base_add_inspector(void *x, t_pdp_method inspector_method)
+{
+ THIS(b);
+ b->b_inspector_method = inspector_method;
+ //b->b_inspector_queue_id = -1;
+}
+
+/* add a context outlet */
+t_outlet *pdp_dpd_base_add_outlet(void *x, t_pdp_method accum_method, t_pdp_method accum_callback)
+{
+ THIS(b);
+ int i = b->b_nb_context_outlets;
+ if (i < PDP_DPD_MAX_CONTEXT_OUTLETS){
+ b->b_context_outlet[i] = outlet_new((t_object *)b, &s_anything);
+ b->b_outlet_enable[i] = 1;
+ b->b_accum_method[i] = accum_method;
+ b->b_accum_callback[i] = accum_callback;
+ //b->b_accum_queue_id[i] = -1;
+ b->b_nb_context_outlets++;
+ return b->b_context_outlet[i];
+ }
+ else{
+ post("pdp_dpd_base_add_outlet: no more free outlet slots");
+ return 0;
+ }
+
+}
+
+
+/* destructor */
+void pdp_dpd_base_free(void *x)
+{
+ THIS(b);
+
+ /* free base */
+ pdp_base_free(b);
+}
+
+
+void pdp_dpd_base_disable_active_inlet(void *x)
+{
+ THIS(b);
+ b->b_dpd_active_inlet_disabled = 1;
+}
+
+
+
+void pdp_dpd_base_enable_outlet(void *x, int outlet, int toggle)
+{
+ THIS(b);
+ if (outlet >=0 && outlet < PDP_DPD_MAX_CONTEXT_OUTLETS){
+ b->b_outlet_enable[outlet] = toggle;
+ }
+
+}
+
+
+void pdp_dpd_base_register_complete_notify(void *x, t_pdp_method method)
+{
+ THIS(b);
+ b->b_complete_notify = method;
+}
+
+void pdp_dpd_base_register_command_factory_method(void *x, t_pdp_newmethod command_factory_method)
+{
+ THIS(b);
+ b->b_command_factory_method = command_factory_method;
+}
+
+
+/* init method */
+void pdp_dpd_base_init(void *x)
+{
+ THIS(b);
+
+ /* super init */
+ pdp_base_init(b);
+
+ /* disable pdp messages on active inlet (dpd messages are used as sync) */
+ pdp_base_disable_active_inlet(b);
+
+ /* init data */
+ b->b_nb_context_outlets = 0;
+ b->b_context_packet = -1;
+ b->b_cleanup_method = 0;
+ //b->b_cleanup_queue_id = -1;
+ b->b_inspector_method = 0;
+ //b->b_inspector_queue_id = -1;
+ b->b_dpd_active_inlet_disabled = 0;
+
+ // default notify == none
+ b->b_complete_notify = 0;
+
+ // default command object getter
+ b->b_command_factory_method = 0;
+
+}
+
+
+void pdp_dpd_base_setup(t_class *class)
+{
+
+ pdp_base_setup(class);
+ class_addmethod(class, (t_method)_pdp_dpd_base_context_input, gensym("dpd"), A_SYMBOL, A_FLOAT, A_NULL);
+
+}
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/puredata/pdp_imagebase.c b/puredata/pdp_imagebase.c
new file mode 100644
index 0000000..f9634e1
--- /dev/null
+++ b/puredata/pdp_imagebase.c
@@ -0,0 +1,79 @@
+/*
+ * Pure Data Packet image processor base class implementation.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+/*
+
+ This file contains the pdp image base class object.
+*/
+
+#include "pdp_imagebase.h"
+#include <stdarg.h>
+
+
+static void pdp_imagebase_chanmask(t_pdp_base *b, t_floatarg f)
+{
+ int i = (int)f;
+ if (i < 0) i = -1;
+ b->b_channel_mask = i;
+}
+
+void pdp_imagebase_setup(t_class *c)
+{
+ /* parent class setup */
+ pdp_base_setup(c);
+
+ /* add pdp base class methods */
+ class_addmethod(c, (t_method)pdp_imagebase_chanmask, gensym("chanmask"), A_FLOAT, A_NULL);
+
+}
+
+/* pdp base instance constructor */
+void pdp_imagebase_init(void *x)
+{
+ int i;
+ t_pdp_imagebase *b = (t_pdp_imagebase *)x;
+
+ /* init super */
+ pdp_base_init(x);
+
+ /* convert all active incoming packet types to image */
+ pdp_base_set_type_template(x, 0, pdp_gensym("image/*/*"));
+
+ /* default chanmask == all */
+ b->b_channel_mask = -1;
+
+}
+
+/* base instance destructor */
+void pdp_imagebase_free(void *x)
+{
+ /* free super */
+ pdp_base_free(x);
+
+}
+
+/* chanmask getter */
+u32 pdp_imagebase_get_chanmask(void *x)
+{
+ t_pdp_base *b = (t_pdp_base *)x;
+ return b->b_channel_mask;
+}
+
diff --git a/puredata/pdp_queue.c b/puredata/pdp_queue.c
new file mode 100644
index 0000000..b66289a
--- /dev/null
+++ b/puredata/pdp_queue.c
@@ -0,0 +1,386 @@
+/*
+ * Pure Data Packet - processor queue module.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+
+/*
+ this is a the processor queue pdp system module
+ it receives tasks from objects that are schedules to
+ be computed in another thread. the object is signalled back
+ when the task is completed, using a polling mechanism
+ based on a pd clock.
+
+ the queue object can be reused. the pdp system however only
+ has one instance (one pdp queue. pdp remains a serial program, though
+ it can run in a separate thread)
+
+ */
+
+
+#include <string.h>
+
+#include "pdp_queue.h"
+#include "pdp_mem.h"
+
+
+#define D if (0)
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define PDP_QUEUE_LOGSIZE 10
+#define PDP_QUEUE_DELTIME 10.0f
+
+
+/* there are 3 synchro methods, which can be used i.e. to ensure
+ all processing is done before shared resources are freed.
+
+ all 3 wait for the processing thread to finish, and
+
+ _wait: leaves callback queue untouched
+ _finish: clears the queue_id item in the callback queue
+ _flush: waits for thread and calls callbacks
+ and loops until callback list is empty
+
+*/
+
+
+
+/********************* general purpose pd process queue class *********************/
+
+void pdp_procqueue_wait(t_pdp_procqueue *q)
+{
+ D post("pdp_procqueue_wait(%x): waiting for pdp_queue_thread to finish processing", q);
+ pthread_mutex_lock(&q->mut);
+ while(((q->curr - q->head) & q->mask) != 0){
+
+ pthread_cond_wait(&q->cond_processingdone, &q->mut);
+ }
+ pthread_mutex_unlock(&q->mut);
+ D post("pdp_procqueue_wait(%x): pdp_procqueue_thread has finished processing", q);
+
+}
+void pdp_procqueue_finish(t_pdp_procqueue *q, int index)
+{
+
+ if (-1 == index) {
+ //post("pdp_pq_remove: index == -1");
+ return;
+ }
+ /* wait for processing thread to finish*/
+ pdp_procqueue_wait(q);
+
+ /* invalidate callback at index */
+ q->q[index & q->mask].x_callback = 0;
+ q->q[index & q->mask].x_queue_id = 0;
+
+}
+
+static void pdp_procqueue_callback (t_pdp_procqueue *q);
+
+void pdp_procqueue_flush(t_pdp_procqueue *q)
+{
+ /* wait once */
+ pdp_procqueue_wait(q);
+
+ do {
+
+ /* process callbacks and wait again
+ in case the callbacks introduced new tasks */
+ pdp_procqueue_callback(q);
+ pdp_procqueue_wait(q);
+
+ }
+ /* repeat if callback list is not empty */
+ while ((q->curr - q->head) & q->mask);
+
+ D post("pdp_procqueue_flush: done");
+}
+
+static void pdp_procqueue_signal_processor(t_pdp_procqueue *q)
+{
+
+ //NOTE: uncommenting these post statements causes a libc crash
+ //in mutex lock in putc
+ //D post("pdp_procqueue_signal_processor(%x): signalling process thread", q);
+ pthread_mutex_lock(&q->mut);
+ pthread_cond_signal(&q->cond_dataready);
+ pthread_mutex_unlock(&q->mut);
+ //D post("pdp_procqueue_signal_processor(%x): signalling done", q);
+
+
+}
+
+static void pdp_procqueue_wait_for_feeder(t_pdp_procqueue *q)
+{
+
+
+ /* only use locking when there is no data */
+ if(((q->curr - q->head) & q->mask) == 0){
+
+ /* signal processing done */
+ D post("pdp_procqueue_wait_for_feeder(%x): signalling processing is done", q);
+ pthread_mutex_lock(&q->mut);
+ pthread_cond_signal(&q->cond_processingdone);
+
+ /* wait until there is an item in the queue */
+ while(((q->curr - q->head) & q->mask) == 0){
+ pthread_cond_wait(&q->cond_dataready, &q->mut);
+ }
+
+ pthread_mutex_unlock(&q->mut);
+ D post("pdp_procqueue_wait_for_feeder(%x): waiting done", q);
+
+ }
+}
+
+
+int pdp_procqueue_full(t_pdp_procqueue *q)
+{
+ return (1 == ((q->tail - q->head) & q->mask));
+}
+
+
+void pdp_procqueue_add(t_pdp_procqueue *q, void *owner, void *process, void *callback, int *queue_id)
+{
+ int i;
+
+ /* if processing is in not in thread, just call the funcs */
+ if (!q->use_thread){
+ D post("pdp_procqueue_add(%q): calling processing routine directly", q);
+ if (queue_id) *queue_id = -1;
+ if (process) ((t_pdpmethod) process)(owner);
+ if (callback) ((t_pdpmethod) callback)(owner);
+ return;
+ }
+
+
+ /* if queue is full, print an error message and return */
+ if (pdp_procqueue_full(q)) {
+ post("pdp_procqueue_add: WARNING: processing queue (%x) is full.\n", q);
+ post("pdp_procqueue_add: WARNING: tail %08x, head %08x (%08x), mask %08x.\n", q->tail, q->head, q->head & q->mask, q->mask);
+ post("pdp_procqueue_add: WARNING: skipping process method, calling callback directly.\n");
+ if (queue_id) *queue_id = -1;
+ if (callback) ((t_pdpmethod) callback)(owner);
+ return;
+ //exit(1);
+ }
+
+ /* schedule method in thread queue */
+ i = q->head & q->mask;
+ q->q[i].x_owner = owner;
+ q->q[i].x_process = process;
+ q->q[i].x_callback = callback;
+ q->q[i].x_queue_id = queue_id;
+ if (queue_id) *queue_id = i;
+ //post("pdp_queue_add: added method to queue, index %d", i);
+
+
+ // increase the packet count
+ q->packets++;
+
+ // move head forward
+ q->head++;
+
+ pdp_procqueue_signal_processor(q);
+
+}
+
+
+/* processing thread */
+static void *pdp_procqueue_thread(void *vq)
+{
+ t_pdp_procqueue *q = (t_pdp_procqueue *)vq;
+
+ D post("pdp_procqueue_thread(%x): thread started", q);
+
+ while(1){
+ t_process_queue_item *p;
+
+
+ D post("pdp_procqueue_thread(%x): waiting for feeder", q);
+
+ /* wait until there is data available */
+ pdp_procqueue_wait_for_feeder(q);
+
+
+ D post("pdp_procqueue_thread(%x): processing %d", q, q->curr & q->mask);
+
+
+ /* call the process routine */
+ p = &q->q[q->curr & q->mask];
+ if (p->x_process)
+ (p->x_process)(p->x_owner);
+
+ /* advance */
+ q->curr++;
+
+
+ }
+ return 0;
+}
+
+
+/* call back all the callbacks */
+static void pdp_procqueue_callback (t_pdp_procqueue *q)
+{
+
+ /* call callbacks for finished packets */
+ while(0 != ((q->curr - q->tail) & q->mask))
+ {
+ int i = q->tail & q->mask;
+ /* invalidate queue id */
+ if(q->q[i].x_queue_id) *q->q[i].x_queue_id = -1;
+ /* call callback */
+ if(q->q[i].x_callback) (q->q[i].x_callback)(q->q[i].x_owner);
+ //else post("pdp_pq_tick: callback %d is disabled",i );
+ q->tail++;
+ }
+
+}
+
+/* the clock method */
+static void pdp_procqueue_tick (t_pdp_procqueue *q)
+{
+ /* do work */
+ //if (!(ticks % 1000)) post("pdp tick %d", ticks);
+
+ if (!q->use_thread) return;
+
+ /* call callbacks */
+ pdp_procqueue_callback(q);
+
+ /* increase counter */
+ q->ticks++;
+
+ /* set clock for next update */
+ clock_delay(q->pdp_clock, q->deltime);
+}
+
+
+
+void pdp_procqueue_use_thread(t_pdp_procqueue* q, int t)
+{
+ /* if thread usage is being disabled,
+ wait for thread to finish processing first */
+ if (t == 0) {
+ pdp_procqueue_wait(q);
+ q->use_thread = 0;
+ pdp_procqueue_callback(q);
+ clock_unset(q->pdp_clock);
+ }
+ else {
+ clock_unset(q->pdp_clock);
+ clock_delay(q->pdp_clock, q->deltime);
+ q->use_thread = 1;
+ }
+
+}
+
+void pdp_procqueue_init(t_pdp_procqueue *q, double milliseconds, int logsize)
+{
+ pthread_attr_t attr;
+ int size = 1 << logsize;
+
+ /* setup pdp queue processor object */
+ q->ticks = 0;
+ q->deltime = milliseconds;
+
+ /* setup queue data */
+ q->mask = size - 1;
+ q->head = 0;
+ q->tail = 0;
+ q->curr = 0;
+ q->q = pdp_alloc(size * sizeof(t_process_queue_item));
+ memset(q->q, 0, size * sizeof(t_process_queue_item));
+
+ /* enable threads */
+ q->use_thread = 1;
+
+ /* setup synchro stuff */
+ pthread_mutex_init(&q->mut, NULL);
+ pthread_cond_init(&q->cond_dataready, NULL);
+ pthread_cond_init(&q->cond_processingdone, NULL);
+
+
+ /* allocate the clock */
+ q->pdp_clock = clock_new(q, (t_method)pdp_procqueue_tick);
+
+ /* set the clock */
+ clock_delay(q->pdp_clock, 0);
+
+ /* start processing thread */
+
+ /* glibc doc says SCHED_OTHER is default,
+ but it seems not to be when initiated from a RT thread
+ so we explicitly set it here */
+ pthread_attr_init (&attr);
+ //pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
+ pthread_attr_setschedpolicy(&attr, SCHED_OTHER);
+
+ D post("pdp_procqueue_init(%x): starting thread", q);
+ pthread_create(&q->thread_id, &attr, pdp_procqueue_thread, (void *)q);
+ D post("pdp_procqueue_init(%x): back in pd thread", q);
+
+ /* wait for processing thread to finish */
+ //pdp_procqueue_wait(q);
+
+ /* set default disable/enable thread here */
+ //post("pdp_queue: THREAD PROCESSING ON BY DEFAULT!!");
+ pdp_procqueue_use_thread(q,0);
+
+}
+
+
+
+
+/* the (static) pdp queue object */
+static t_pdp_procqueue pdp_queue;
+
+
+/* get the default queue */
+t_pdp_procqueue *pdp_queue_get_queue(void){return &pdp_queue;}
+
+
+#if 1
+/* default pdp queue shortcut methods */
+void pdp_queue_wait() {pdp_procqueue_wait(&pdp_queue);}
+void pdp_queue_finish(int index) { pdp_procqueue_finish(&pdp_queue, index);}
+void pdp_queue_add(void *owner, void *process, void *callback, int *queue_id) {
+ pdp_procqueue_add(&pdp_queue, owner, process, callback, queue_id);
+}
+void pdp_queue_use_thread(int t) {pdp_procqueue_use_thread(&pdp_queue, t);}
+void pdp_queue_setup(void){
+ pdp_procqueue_init(&pdp_queue, PDP_QUEUE_DELTIME, PDP_QUEUE_LOGSIZE);
+ pdp_procqueue_use_thread(&pdp_queue,0);
+}
+#endif
+
+
+
+
+
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/puredata/pdp_ut.c b/puredata/pdp_ut.c
new file mode 100644
index 0000000..a1369e3
--- /dev/null
+++ b/puredata/pdp_ut.c
@@ -0,0 +1,262 @@
+/*
+ * Pure Data Packet - Utility toolkit objects.
+ * Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+
+/* This file contains some small utility pd objects that make working with
+ pdp objects a lot easier. Mainly as glue to be used in the abstractions
+ in the distro. */
+
+
+#include "pdp_pd.h"
+#include <math.h>
+
+/* this object does an add, scale, clip operation */
+
+t_class *pdp_ut_addscaleclip_class;
+
+typedef struct pdp_ut_addscaleclip_struct
+{
+ t_object x_obj;
+ t_outlet *x_outlet0;
+ t_float x_min;
+ t_float x_max;
+ t_float x_offset;
+ t_float x_scale;
+} t_pdp_ut_addscaleclip;
+
+
+static void pdp_ut_addscaleclip_float(t_pdp_ut_addscaleclip *x, t_floatarg f)
+{
+ f += x->x_offset;
+ f *= x->x_scale;
+ f = (f < x->x_min) ? x->x_min : f;
+ f = (f > x->x_max) ? x->x_max : f;
+ outlet_float(x->x_outlet0, f);
+}
+
+static void pdp_ut_addscaleclip_free(t_pdp_ut_addscaleclip *x){}
+
+void *pdp_ut_addscaleclip_new(t_floatarg offset, t_floatarg scale, t_floatarg min, t_floatarg max)
+{
+ t_pdp_ut_addscaleclip *x = (t_pdp_ut_addscaleclip *)pd_new(pdp_ut_addscaleclip_class);
+ x->x_outlet0 = outlet_new(&x->x_obj, &s_float);
+ x->x_offset = offset;
+ x->x_scale = scale;
+ x->x_min = min;
+ x->x_max = max;
+ return (void *)x;
+}
+
+void pdp_ut_addscaleclip_setup(void)
+{
+ pdp_ut_addscaleclip_class = class_new(gensym("pdp_ut_addscaleclip"), (t_newmethod)pdp_ut_addscaleclip_new,
+ (t_method)pdp_ut_addscaleclip_free, sizeof(t_pdp_ut_addscaleclip), 0,
+ A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
+ class_addfloat(pdp_ut_addscaleclip_class, pdp_ut_addscaleclip_float);
+}
+
+
+/* pdp_ut_logmap does a logarithmic parameter mapping [0->1] x -> min(max/min)^x max an add, scale, clip operation */
+/* pdp_ut_logmap_comp does x -> min(max/min)^(1-x) */
+/* pdp_ut_linmap dos x -> min + (max - min * x */
+
+t_class *pdp_ut_linmap_class;
+t_class *pdp_ut_logmap_class;
+t_class *pdp_ut_logmap_comp_class;
+
+typedef struct pdp_ut_map_struct
+{
+ t_object x_obj;
+ t_outlet *x_outlet0;
+ t_float x_min;
+ t_float x_max;
+} t_pdp_ut_map;
+
+
+static void pdp_ut_logmap_float(t_pdp_ut_map *x, t_floatarg f)
+{
+ f = (f < 0.0f) ? 0.0f : f;
+ f = (f > 1.0f) ? 1.0f : f;
+
+ f = x->x_min * pow((x->x_max / x->x_min), f);
+
+ outlet_float(x->x_outlet0, f);
+}
+
+static void pdp_ut_linmap_float(t_pdp_ut_map *x, t_floatarg f)
+{
+ f = (f < 0.0f) ? 0.0f : f;
+ f = (f > 1.0f) ? 1.0f : f;
+
+ f = x->x_min + ((x->x_max - x->x_min) * f);
+
+ outlet_float(x->x_outlet0, f);
+}
+
+static void pdp_ut_logmap_comp_float(t_pdp_ut_map *x, t_floatarg f)
+{
+ f = (f < 0.0f) ? 0.0f : f;
+ f = (f > 1.0f) ? 1.0f : f;
+
+ f = x->x_min * pow((x->x_max / x->x_min), (1.0f - f));
+
+ outlet_float(x->x_outlet0, f);
+}
+
+static void pdp_ut_map_free(t_pdp_ut_map *x){}
+
+
+void pdp_ut_map_init(t_pdp_ut_map *x, t_floatarg min, t_floatarg max)
+{
+ x->x_outlet0 = outlet_new(&x->x_obj, &s_float);
+ x->x_min = min;
+ x->x_max = max;
+}
+
+void *pdp_ut_logmap_new(t_floatarg min, t_floatarg max)
+{
+ t_pdp_ut_map *x = (t_pdp_ut_map *)pd_new(pdp_ut_logmap_class);
+ pdp_ut_map_init(x, min, max);
+ return (void *)x;
+}
+
+void *pdp_ut_linmap_new(t_floatarg min, t_floatarg max)
+{
+ t_pdp_ut_map *x = (t_pdp_ut_map *)pd_new(pdp_ut_linmap_class);
+ pdp_ut_map_init(x, min, max);
+ return (void *)x;
+}
+
+void *pdp_ut_logmap_comp_new(t_floatarg min, t_floatarg max)
+{
+ t_pdp_ut_map *x = (t_pdp_ut_map *)pd_new(pdp_ut_logmap_comp_class);
+ pdp_ut_map_init(x, min, max);
+ return (void *)x;
+}
+
+void pdp_ut_logmap_setup(void)
+{
+ pdp_ut_logmap_class = class_new(gensym("pdp_ut_logmap"), (t_newmethod)pdp_ut_logmap_new,
+ (t_method)pdp_ut_map_free, sizeof(t_pdp_ut_map), 0,
+ A_FLOAT, A_FLOAT, A_NULL);
+ class_addfloat(pdp_ut_logmap_class, pdp_ut_logmap_float);
+}
+
+void pdp_ut_logmap_comp_setup(void)
+{
+ pdp_ut_logmap_comp_class = class_new(gensym("pdp_ut_logmap_comp"), (t_newmethod)pdp_ut_logmap_comp_new,
+ (t_method)pdp_ut_map_free, sizeof(t_pdp_ut_map), 0,
+ A_FLOAT, A_FLOAT, A_NULL);
+ class_addfloat(pdp_ut_logmap_comp_class, pdp_ut_logmap_comp_float);
+}
+
+void pdp_ut_linmap_setup(void)
+{
+ pdp_ut_linmap_class = class_new(gensym("pdp_ut_linmap"), (t_newmethod)pdp_ut_linmap_new,
+ (t_method)pdp_ut_map_free, sizeof(t_pdp_ut_map), 0,
+ A_FLOAT, A_FLOAT, A_NULL);
+ class_addfloat(pdp_ut_linmap_class, pdp_ut_linmap_float);
+}
+
+
+
+t_class *pdp_ut_rgb2ycrcb_class;
+
+typedef struct pdp_ut_rgb2ycrcb
+{
+ t_object x_obj;
+ t_outlet *x_outlet_luma;
+ t_outlet *x_outlet_chroma_red;
+ t_outlet *x_outlet_chroma_blue;
+
+ t_float x_red, x_green, x_blue;
+
+} t_pdp_ut_rgb2ycrcb;
+
+
+static void pdp_ut_rgb2ycrcb_bang (t_pdp_ut_rgb2ycrcb* x)
+{
+
+ float luma = 0.299f * x->x_red + 0.587f * x->x_green + 0.114f * x->x_blue;
+ float chroma_red = (x->x_red - luma) * 0.713f;
+ float chroma_blue = (x->x_blue - luma) * 0.565f;
+
+ outlet_float(x->x_outlet_chroma_blue, chroma_blue);
+ outlet_float(x->x_outlet_chroma_red, chroma_red);
+ outlet_float(x->x_outlet_luma, luma);
+
+}
+
+
+static void pdp_ut_rgb2ycrcb_red (t_pdp_ut_rgb2ycrcb* x, t_floatarg f) {x->x_red = f; pdp_ut_rgb2ycrcb_bang(x);}
+static void pdp_ut_rgb2ycrcb_green (t_pdp_ut_rgb2ycrcb* x, t_floatarg f) {x->x_green = f; pdp_ut_rgb2ycrcb_bang(x);}
+static void pdp_ut_rgb2ycrcb_blue (t_pdp_ut_rgb2ycrcb* x, t_floatarg f) {x->x_blue = f; pdp_ut_rgb2ycrcb_bang(x);}
+
+
+
+static void pdp_ut_rgb2ycrcb_free (t_pdp_ut_rgb2ycrcb* x) {}
+static void* pdp_ut_rgb2ycrcb_new(void)
+{
+ t_pdp_ut_rgb2ycrcb *x = (t_pdp_ut_rgb2ycrcb *)pd_new(pdp_ut_rgb2ycrcb_class);
+
+
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("green"));
+ inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("blue"));
+
+ x->x_outlet_luma = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet_chroma_red = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet_chroma_blue = outlet_new(&x->x_obj, &s_float);
+
+ x->x_red = 0.0f;
+ x->x_green = 0.0f;
+ x->x_blue = 0.0f;
+
+
+ return (void *)x;
+}
+
+void pdp_ut_rgb2ycrcb_setup(void)
+{
+ pdp_ut_rgb2ycrcb_class = class_new(gensym("pdp_ut_rgb2ycrcb"), (t_newmethod)pdp_ut_rgb2ycrcb_new,
+ (t_method)pdp_ut_rgb2ycrcb_free, sizeof(t_pdp_ut_rgb2ycrcb), 0, A_NULL);
+ class_addfloat(pdp_ut_rgb2ycrcb_class, pdp_ut_rgb2ycrcb_red);
+ class_addmethod(pdp_ut_rgb2ycrcb_class, (t_method)pdp_ut_rgb2ycrcb_green, gensym("green"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_ut_rgb2ycrcb_class, (t_method)pdp_ut_rgb2ycrcb_blue, gensym("blue"), A_FLOAT, A_NULL);
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+void pdp_ut_setup(void)
+{
+ pdp_ut_addscaleclip_setup();
+ pdp_ut_logmap_setup();
+ pdp_ut_logmap_comp_setup();
+ pdp_ut_linmap_setup();
+ pdp_ut_rgb2ycrcb_setup();
+}
+
+
+#ifdef __cplusplus
+}
+#endif