diff options
author | Hans-Christoph Steiner <eighthave@users.sourceforge.net> | 2005-11-28 01:07:25 +0000 |
---|---|---|
committer | Hans-Christoph Steiner <eighthave@users.sourceforge.net> | 2005-11-28 01:07:25 +0000 |
commit | c50ce0e0217ea07e2d450add2ab29cecea66fa96 (patch) | |
tree | 0700e1e5fd14ff99e5a4c6d4b4937268a1d9804f /puredata | |
parent | d7f71f87b112317d8303f25b4a536b943ea59d97 (diff) |
This commit was generated by cvs2svn to compensate for changes in r4059,
which included commits to RCS files with non-trunk default branches.
svn path=/trunk/externals/pdp/; revision=4060
Diffstat (limited to 'puredata')
-rw-r--r-- | puredata/CONTENTS | 10 | ||||
-rw-r--r-- | puredata/Makefile | 12 | ||||
-rw-r--r-- | puredata/pdp_base.c | 415 | ||||
-rw-r--r-- | puredata/pdp_comm.c | 367 | ||||
-rw-r--r-- | puredata/pdp_compat.c | 57 | ||||
-rw-r--r-- | puredata/pdp_control.c | 186 | ||||
-rw-r--r-- | puredata/pdp_dpd_base.c | 270 | ||||
-rw-r--r-- | puredata/pdp_forthproc.c_bak | 807 | ||||
-rw-r--r-- | puredata/pdp_imagebase.c | 79 | ||||
-rw-r--r-- | puredata/pdp_queue.c | 386 | ||||
-rw-r--r-- | puredata/pdp_ut.c | 262 |
11 files changed, 2851 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_forthproc.c_bak b/puredata/pdp_forthproc.c_bak new file mode 100644 index 0000000..e6517d5 --- /dev/null +++ b/puredata/pdp_forthproc.c_bak @@ -0,0 +1,807 @@ +/* + * Pure Data Packet 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. + * + */ + +/* TODO: ADD THREAD SUPPORT */ + +/* + +this is the pd interface to forth processes / processors. +in time, it should become the only interface between the processing +functionality in pdp, and pd objects representing this functionality + +example object definitions: + +a forth process is a virtual machine operating on a private +rotation stack, as found in hp rpn calcs (this is the machine state vector) +the tick operation leaves the stack size and relative position of +the elements invariant + +this is a very crude and simple framework. it is an experiment to +see how far this forth thing can be pushed to solve the problem +of dataflow processing in a simple way, by cutting out the fat. +yes, chuck moore's ideas are viral.. + +a forth process has a setup code that will construct an initial stack template + +an object definition is a list of +- list of symbolic input to stack location mappings (first = active) +- list of symbolic output to stack location mappings +- stack template init code (bootblock) +- process code + +the stack is the machine memory + +the mapping between pdp's forth processors and pd object +is handled by pdmaps. these can probably be reused to build other +object oriented interfaces to the forth processors + +*/ + + +/* pdp forthprocs support thread processing. + + there is only one stack, which + serves as the machine state and input/output storage. + + when processing in thread, the input/output event + queue is used, when processing directly pd events + are inserted into the stack directly and the queues + are bypassed + +*/ + + +#include <pthread.h> +#include "pdp_pd.h" +#include "pdp_comm.h" +#include "pdp_packet.h" +#include "pdp_mem.h" +#include "pdp_forth.h" +#include "pdp_pdmap.h" +#include "pdp_queue.h" +#include "pdp_debug.h" + + + +/* this object instantiates a forth processor */ + +typedef struct forthproc_struct +{ + t_object x_obj; + t_pdp_list *x_processor; // the processor definition + t_pdp_list *x_pdmap; // the pd port mappings + t_pdp_list *x_program; // the forth program + + + t_pdp_list *x_stack; // the state stack + t_pdp_list *x_stack_template; // a "fake" stack serving as a type template + + t_pdp_list *x_input_events; // current input event list + + t_pdp_list *x_queue_input; // queue of input event lists, event = (atom index, word) + t_pdp_list *x_queue_output; // queue of output event lists, event = (outlet ptr, word) + + t_pdp_procqueue *x_q; // process queue + + + pthread_mutex_t x_mut; // queue mutex + + int x_nb_outlets; // number of pd outlets + t_outlet **x_outlet; + + int x_thread; // use thread processing or not + + t_symbol *x_protocol; // protocol used (pdp or dpd) + + /* pdp image legacy */ + int x_chanmask; + + /* dpd */ + int x_dpd_packet; + t_outlet *x_dpd_outlet; + +} t_forthproc; + + +static inline void lock(t_forthproc *x){pthread_mutex_lock(&x->x_mut);} +static inline void unlock(t_forthproc *x){pthread_mutex_unlock(&x->x_mut);} + + +/* send an atom to an outlet */ +static void send_pdp_atom_to_outlet(t_outlet *out, t_pdp_atom *a) +{ + outlet_pdp_atom(out, a); +} + +/* output stack contents to outlet or output event list */ +static void output_from_stack(t_forthproc *x) +{ + t_pdp_list *outsym = pdp_forth_pdmap_outlist(x->x_pdmap); + + /* create an event list if we're in thread mode */ + t_pdp_list *eventlist = x->x_thread ? pdp_list_new(0) : 0; + + static void _do_outlet(int index, t_pdp_atom *pname, t_pdp_list *eventlist){ + t_outlet *out = x->x_outlet[index]; + t_pdp_atom *a = pdp_forth_processor_stackatom_from_outputname( + x->x_processor, x->x_stack, pname->w.w_symbol); + + PDP_ASSERT(a); + + /* bang in reverse order by using head recursion */ + if (pname->next) _do_outlet(index+1, pname->next, eventlist); + + /* send the atom to the outlet if no event list */ + if (!eventlist){ + send_pdp_atom_to_outlet(out, a); + } + /* or add it to the event list */ + else { + t_pdp_list *ev = pdp_list_new(2); + pdp_list_set_0(ev, a_pointer, + (t_pdp_word)(void*)out); // store outlet ptr + pdp_list_set_1(ev, a->t, a->w); // store atom + pdp_list_add_back(eventlist, a_list, + (t_pdp_word)ev); // store event in list + + /* if it was a packet, clear the stacks reference */ + if (a->t == a_packet) a->w.w_packet = -1; + } + + } + + _do_outlet(0, outsym->first, eventlist); + + /* add eventlist to output event queue */ + if (eventlist){ + + lock(x); + pdp_list_add_back(x->x_queue_output, a_list, + (t_pdp_word)eventlist); + unlock(x); + } +} + +/* legacy hack: setup channel mask for image processing */ +static void setup_chanmask(t_forthproc *x) +{ + if (x->x_chanmask != -1){ + t_pdp_symbol *pname = pdp_forth_pdmap_get_pname(x->x_pdmap, pdp_gensym("pdp")); + t_pdp_atom *a = pdp_forth_processor_stackatom_from_inputname(x->x_processor, x->x_stack, pname); + int *packet; + if (a && a->t == a_packet){ + packet = &a->w.w_packet; + pdp_packet_replace_with_writable(packet); // make sure it's a private copy + pdp_packet_image_set_chanmask(*packet, x->x_chanmask); + //post("chanmask set to %d", x->x_chanmask); + } + } +} + +static void exec_program(t_forthproc *x) +{ + int error; + setup_chanmask(x); + if (e_ok != (error = pdp_forth_execute_def(x->x_stack, x->x_program))){ + post("error %d (%s) executing forth processor", + error, pdp_forth_word_error(error)); + post("PROGRAM:"); + pdp_list_print(x->x_program); + post("STACK:"); + pdp_list_print(x->x_stack); + post(""); + + /* delete stack and create a new one */ + pdp_tree_strip_packets(x->x_stack); + pdp_tree_free(x->x_stack); + x->x_stack = pdp_forth_processor_setup_stack(x->x_processor); + + + } + + +} + + +static void thread_exec(t_forthproc *x) +{ + t_pdp_list *event_list; + t_pdp_atom *a; + + /* get input event list from input event queue */ + PDP_ASSERT(x->x_queue_input->elements); + lock(x); + event_list = pdp_list_pop(x->x_queue_input).w_list; + unlock(x); + + /* add input events to state stack */ + for (a=event_list->first; a; a=a->next){ + int index = a->w.w_list->first->w.w_int; + t_pdp_atom *src = a->w.w_list->first->next; + + t_pdp_atom *dest = x->x_stack->first; + while (index--) dest = dest->next; + + PDP_ASSERT(dest->t == src->t); + + switch(src->t){ + + /* copy pure atoms */ + case a_float: + case a_int: + case a_symbol: + dest->w = src->w; + break; + + /* move reference atoms */ + case a_packet: + pdp_packet_mark_unused(dest->w.w_packet); + dest->w = src->w; + src->w.w_packet = -1; + break; + + /* ignored */ + case a_pointer: + case a_list: + default: + break; + } + } + + + + /* free event list */ + pdp_tree_free(event_list); + + /* run the process */ + exec_program(x); + + /* send output events to output event queue */ + output_from_stack(x); + +} + +static void thread_output(t_forthproc *x) +{ + t_pdp_list *event_list; + t_pdp_atom *a; + + /* get output event list from output event queue */ + PDP_ASSERT(x->x_queue_output->elements); + lock(x); + event_list = pdp_list_pop(x->x_queue_output).w_list; + unlock(x); + + /* send */ + for (a=event_list->first; a; a=a->next){ + t_outlet *outlet = a->w.w_list->first->w.w_pointer; + t_pdp_atom *outatom = a->w.w_list->first->next; + PDP_ASSERT(outlet); + PDP_ASSERT(outatom); + send_pdp_atom_to_outlet(outlet, outatom); + } + + /* free event list */ + pdp_tree_strip_packets(event_list); + pdp_tree_free(event_list); + +} + +/* handle dpd packet passing */ +static void dpd_output(t_forthproc *x) +{ + //post("checking dpd"); + //post("protocol: %s, outlet: %08x, packet: %d", x->x_protocol->s_name, x->x_dpd_outlet, x->x_dpd_packet); + if ((x->x_protocol != S_DPD) + || (!x->x_dpd_outlet) + || (x->x_dpd_packet == -1)) return; + + /* send the dpd packet to the special (first) outlet */ + //post("sending dpd"); + outlet_dpd(x->x_dpd_outlet, x->x_dpd_packet); + x->x_dpd_packet = -1; + +} + + +/* this method is called after + an active event is received */ + +static void run(t_forthproc *x) +{ + + /* NO THREAD: + no brainer: execute and output in one go */ + + if (!x->x_thread){ + + /* run the word */ + exec_program(x); + + /* output stuff */ + output_from_stack(x); + dpd_output(x); + } + + /* THREAD: + start queueing operations + + this is a bit harder since we need to make a copy of the machine state (stack) + before we send off a command to process it in the queue + + this case is handled separately, because processing without thread is obviously + more efficient regarding memory usage and locality of reference. + + */ + + else { + + t_pdp_list *newstack; + + /* compared to the previous approach, no 'automatic' dropping is applied + for forth processors. the input and output queues are of indefinite length. + + dropping is the responsability of the sender or a special object + that uses the processing queue to synchronize (see 3dp_windowcontext) + + this allows for pipelining. + + a drawback is that feedback is a problem with this approach, + but it already was with the previous one. + + the only exception to the dropping rule is when the procqueue is full + + */ + + + /* make sure process queue is not full */ + if (pdp_procqueue_full(x->x_q)){ + + post("forthproc: WARNING: procqueue is full. dropping input events."); + + /* clear the input event list */ + pdp_tree_strip_packets(x->x_input_events); + pdp_tree_free(x->x_input_events); + x->x_input_events = pdp_list_new(0); + + /* exit */ + return; + } + + /* add collected input events to input event queue, and create + a new empty input event list */ + lock(x); + pdp_list_add_back(x->x_queue_input, a_list, (t_pdp_word)x->x_input_events); + x->x_input_events = pdp_list_new(0); + unlock(x); + + /* queue the process method & callback */ + pdp_procqueue_add(x->x_q, x, thread_exec, thread_output, 0); + + /* how to handle dpd packets? + they both have direct and indirect output + let's try this: the dpd input is always passed on directly. + the other outputs are just like pdp outputs */ + + dpd_output(x); + + } +} + + +static int handle_special_message(t_forthproc *x, t_symbol *s, int argc, t_atom *argv) +{ + /* handle the chanmask message. this is a legacy thingy */ + if (s == S_CHANMASK){ + if ((argc == 1) && (argv->a_type == A_FLOAT)){ + x->x_chanmask = (int)argv->a_w.w_float; + return 1; + } + } + + return 0; +} + + +/* pd message handler: + receives a pd message and stores an event in the input queue + or directly on the stack */ + +static void handle_pd_message(t_forthproc *x, t_symbol *s, int argc, t_atom *argv) +{ + int active = 0; + int index; + int i; + t_pdp_atom ta; + t_pdp_symbol *message_id; + t_pdp_symbol *pname; + + + /* get the param name from the received symbol */ + message_id = pdp_gensym(s->s_name); + pname = pdp_forth_pdmap_get_pname(x->x_pdmap, message_id); + + /* if the parameter name is null, it should be interpreted + as a active */ + if (pname == PDP_SYM_NULL){ + run(x); + return; + } + + + /* get the stack atom index */ + index = pdp_forth_processor_map_inputname_to_stackindex(x->x_processor, pname); + t_pdp_atom *stack_atom; + + if (index < 0){ + /* message is not in the pdmap: check any special messages */ + if (!handle_special_message(x, s, argc, argv)) + post("got invalid msg %s", s->s_name); + return; + } + + /* get stack atom + if thread processing is on, get an atom from the template stack + because the real stack is in an indeterminate state */ + i=index; + stack_atom = x->x_thread ? x->x_stack_template->first : x->x_stack->first; + while (i--) stack_atom = stack_atom->next; + + + /* store the type */ + ta.t = stack_atom->t; + + /* check if it is an active inlet + only floats, symbols, bangs, pdp and dpd messages can be active, + the others will be translated to different selectors */ + + if ((&s_float == s) + ||(&s_bang == s) + ||(&s_symbol == s) + ||(S_PDP == s) + ||(S_DPD == s)) active = 1; + + /* interprete the anything message according to expected type (ta.t) + and put the result in the input event queue w */ + + switch(ta.t){ + + case a_float: + if ((argc != 1) || argv[0].a_type != A_FLOAT) post("bad float msg"); + else ta.w.w_float = argv[0].a_w.w_float; + break; + + case a_int: + if ((argc != 1) || argv[0].a_type != A_FLOAT) post("bad float msg"); + else ta.w.w_int = (int)argv[0].a_w.w_float; + break; + + case a_symbol: + if ((argc != 1) || argv[0].a_type != A_SYMBOL) post("bad symbol msg"); + else ta.w.w_symbol = pdp_gensym(argv[0].a_w.w_symbol->s_name); + + case a_list: + post("a_list: not supported yet"); + break; + + case a_packet: + if ((argc != 2) + || argv[0].a_type != A_SYMBOL + || argv[1].a_type != A_FLOAT) post ("bad pdp msg"); + else{ + t_symbol *command = argv[0].a_w.w_symbol; + int packet = (int)argv[1].a_w.w_float; + + + /* PDP */ + /* register the pd packet readonly by default: stack owns a ro copy. + the forth words should convert a packet to rw if they need to + (we can't tell here) */ + if (command == S_REGISTER_RO){ + ta.w.w_packet = pdp_packet_copy_ro(packet); + } + + /* DPD: does not work when multiple context outlets are involved */ + + /* register readonly just like pdp packets. but for dpd context + processors it's understood that the packet can be modified. + and store the reference to pass it along, if it's an active dpd packet */ + else if (command == S_ACCUMULATE){ + + ta.w.w_packet = pdp_packet_copy_ro(packet); + + if (s == S_DPD){ // only store main (left) inlet's dpd messages + x->x_dpd_packet = ta.w.w_packet; + } + } + + else { + /* if it's not a register_ro (pdp) phase, or an accumulate (dpd) phase, + we're not going to do anything with the paket */ + ta.w.w_packet = -1; + } + + /* only the pdp process message or dpd accumulate message can be active */ + if ((command != S_PROCESS) + && (command != S_ACCUMULATE)) active = 0; + + } + break; + default: + post("unknown"); + return; + + } + + /* check if we need to store the atom into the processor stack + directly or should put it in the input event queue */ + + if (!x->x_thread){ + /* handle packets */ + if (ta.t == a_packet){ + + /* only copy if valid (if it was valid and it's a register_ro phase */ + if (ta.w.w_packet != -1){ + pdp_packet_mark_unused(stack_atom->w.w_packet); + stack_atom->w = ta.w; + } + } + /* handle other atoms: store directly */ + else{ + stack_atom->w = ta.w; + } + } + else { + + /* don't store invalid packets */ + if (!(ta.t == a_packet && ta.w.w_packet == -1)){ + + /* store atom + location in event list */ + t_pdp_list *ev = pdp_list_new(2); + pdp_list_set_0(ev, a_int, (t_pdp_word)index); // store atom location + pdp_list_set_1(ev, ta.t, ta.w); // store atom + pdp_list_add_back(x->x_input_events, a_list, + (t_pdp_word)ev); // store event in list + + } + } + + + /* run the processor if it was an active input */ + if (active) run(x); + + +} + + + +/* OTHER METHODS */ +/* these serve as a replacement for the pdp_base class + the most prominent messages are: + - debug + - chanmask + - thread on/off/default on a per object basis +*/ + + + +/* MEM & INIT */ + +static void forthproc_free(t_forthproc *x) +{ + /* wait for thread processing to finish */ + pdp_procqueue_flush(x->x_q); + + /* free state stack + template stack */ + pdp_tree_strip_packets(x->x_stack); + pdp_tree_free(x->x_stack); + pdp_tree_free(x->x_stack_template); + + + /* free input event list */ + pdp_tree_strip_packets(x->x_input_events); + pdp_tree_free(x->x_input_events); + + /* free input/output event queues */ + pdp_tree_strip_packets(x->x_queue_input); + pdp_tree_strip_packets(x->x_queue_output); + pdp_tree_free(x->x_queue_input); + pdp_tree_free(x->x_queue_output); + + /* delete outlet pointer array */ + if (x->x_outlet) pdp_dealloc(x->x_outlet); +} + +t_class *forthproc_class; + + + +static void *forthproc_new(t_symbol *protocol, int argc, t_atom *argv) +{ + t_forthproc *x; + t_pdp_atom *a; + t_symbol *procname; + int i; + + /* get processor name */ + if ((argc < 1) || (argv[0].a_type != A_SYMBOL)) return 0; + procname = argv[0].a_w.w_symbol; + argc--; + argv++; + + + /* allocate */ + x = (t_forthproc *)pd_new(forthproc_class); + + /* init */ + x->x_stack = 0; + x->x_stack_template = 0; + x->x_outlet = 0; + x->x_pdmap = 0; + x->x_processor = 0; + x->x_thread = 1; + x->x_program = 0; + x->x_chanmask = -1; + x->x_dpd_outlet = 0; + x->x_dpd_packet = -1; + + /* get the protocol */ + x->x_protocol = protocol; + //post("forthproc using protocol: %s", protocol->s_name); + + /* input event list */ + x->x_input_events = pdp_list_new(0); + + /* input/output event queues */ + x->x_queue_input = pdp_list_new(0); + x->x_queue_output = pdp_list_new(0); + + /* default queue is pdp queue */ + x->x_q = pdp_queue_get_queue(); + + + + /* get the pd mapper */ + x->x_pdmap = pdp_forth_pdmap_getbyname(pdp_gensym(procname->s_name)); + if (!x->x_pdmap) goto error; + + /* get the processor */ + x->x_processor = pdp_forth_pdmap_get_processor(x->x_pdmap); + if (!x->x_processor) goto error; + + /* get the program */ + x->x_program = x->x_processor->first->next->next->next->w.w_list; + + /* create state stack template and remove packets + so they don't consume resources */ + x->x_stack_template = pdp_forth_processor_setup_stack(x->x_processor); + pdp_tree_strip_packets(x->x_stack_template); + + /* create the state stack */ + x->x_stack = pdp_forth_processor_setup_stack(x->x_processor); + + + /* create additional inlets from description */ + for (a = pdp_forth_pdmap_inlist(x->x_pdmap)->first; a; a=a->next){ + t_pdp_symbol *message_id = a->w.w_symbol; + t_symbol *dsym = gensym(message_id->s_name); + t_symbol *ssym = 0; + + t_pdp_symbol *pname = pdp_forth_pdmap_get_pname(x->x_pdmap, message_id); + t_pdp_atom *sa = pdp_forth_processor_stackatom_from_inputname(x->x_processor, x->x_stack, pname); + if (sa) { + switch(sa->t){ + case a_float: + case a_int: + ssym = &s_float; break; + case a_packet: + ssym = S_PDP; break; + default: + post("unsupported type on stack"); break; + } + } + + /* add inlet */ + if (ssym){ + //post("adding %s inlet %s (forth processor param %s)", + // ssym->s_name, dsym->s_name, pname->s_name); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, ssym, dsym); + } + else { + post("error adding inlet"); + } + } + + /* create outlets */ + if (x->x_protocol == S_DPD){ + x->x_dpd_outlet = outlet_new(&x->x_obj, &s_anything); + } + + x->x_nb_outlets = pdp_forth_pdmap_outlist(x->x_pdmap)->elements; + if (x->x_nb_outlets){ + x->x_outlet = pdp_alloc(x->x_nb_outlets * sizeof(*x->x_outlet)); + for (i=0; i<x->x_nb_outlets; i++){ + x->x_outlet[i] = outlet_new(&x->x_obj, &s_anything); + } + } + + /* interpret arguments */ + //pdp_list_print(pdp_forth_pdmap_arglist(x->x_pdmap)); + for(a = pdp_forth_pdmap_arglist(x->x_pdmap)->first; + a && argc; + a=a->next, argv++, argc--){ + + t_pdp_atom *sa = pdp_forth_processor_stackatom_from_inputname + (x->x_processor, x->x_stack, a->w.w_symbol); + if (!sa) { + post("parameter %s not found", a->w.w_symbol->s_name); + continue; + } + //post("loading parameter %s", a->w.w_symbol->s_name); + /* handle symbols */ + if((sa->t == a_symbol) && (argv->a_type == A_SYMBOL)) + sa->w.w_symbol = pdp_gensym(argv->a_w.w_symbol->s_name); + /* handle floats */ + else if (argv->a_type == A_FLOAT){ + switch(sa->t){ + case a_float: sa->w.w_float = argv->a_w.w_float; break; + case a_int: sa->w.w_int = (int)argv->a_w.w_float; break; + default: break; + } + } + + } + + + /* finished */ + return (void *)x; + + error: + + post ("error creating forth processor %s", procname->s_name); + forthproc_free(x); + return 0; + +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_forthproc_setup(void) +{ + int i; + + /* create a standard pd class */ + forthproc_class = class_new(gensym("pdp"), (t_newmethod)forthproc_new, + (t_method)forthproc_free, sizeof(t_forthproc), 0, A_GIMME, A_NULL); + class_addcreator((t_newmethod)forthproc_new, gensym("dpd"), A_GIMME, A_NULL); + + /* add global message handler */ + class_addanything(forthproc_class, (t_method)handle_pd_message); + +} + +#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 |