diff options
61 files changed, 11984 insertions, 0 deletions
diff --git a/modules/generic/Makefile b/modules/generic/Makefile new file mode 100644 index 0000000..d075b3e --- /dev/null +++ b/modules/generic/Makefile @@ -0,0 +1,15 @@ +current: all_modules + +include ../../Makefile.config + +PDP_MOD = pdp_reg.o pdp_del.o pdp_snap.o pdp_trigger.o \ + pdp_route.o pdp_inspect.o pdp_loop.o pdp_description.o pdp_convert.o \ + pdp_forthproc.o + +# build generic modules +all_modules: $(PDP_MOD) + +clean: + rm -f *~ + rm -f *.o + diff --git a/modules/generic/README b/modules/generic/README new file mode 100644 index 0000000..2c8bff0 --- /dev/null +++ b/modules/generic/README @@ -0,0 +1,2 @@ +This directory contains generic packet processors (i.e. containers). +Should work with any packet type. diff --git a/modules/generic/pdp_convert.c b/modules/generic/pdp_convert.c new file mode 100644 index 0000000..cc7dd8c --- /dev/null +++ b/modules/generic/pdp_convert.c @@ -0,0 +1,104 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" + + +typedef struct pdp_convert_struct +{ + t_object x_obj; + t_symbol *x_type_mask; + t_outlet *x_outlet0; + int x_packet0; + +} t_pdp_convert; + + + +static void pdp_convert_type_mask(t_pdp_convert *x, t_symbol *s) +{ + x->x_type_mask = s; +} + +static void pdp_convert_input_0(t_pdp_convert *x, t_symbol *s, t_floatarg f) +{ + int p = (int)f; + int passes, i; + + if (s== gensym("register_ro")){ + pdp_packet_mark_unused(x->x_packet0); + if (x->x_type_mask->s_name[0]) + x->x_packet0 = pdp_packet_convert_ro(p, pdp_gensym(x->x_type_mask->s_name)); + else + x->x_packet0 = pdp_packet_copy_ro(p); + } + + + if ((s == gensym("process")) && (-1 != x->x_packet0)){ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet0); + } +} + + +t_class *pdp_convert_class; + + + +void pdp_convert_free(t_pdp_convert *x) +{ + pdp_packet_mark_unused(x->x_packet0); +} + +void *pdp_convert_new(t_symbol *s) +{ + t_pdp_convert *x = (t_pdp_convert *)pd_new(pdp_convert_class); + + x->x_type_mask = s; + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_packet0 = -1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_convert_setup(void) +{ + + + pdp_convert_class = class_new(gensym("pdp_convert"), (t_newmethod)pdp_convert_new, + (t_method)pdp_convert_free, sizeof(t_pdp_convert), 0, A_DEFSYMBOL, A_NULL); + + + class_addmethod(pdp_convert_class, (t_method)pdp_convert_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addsymbol(pdp_convert_class, (t_method)pdp_convert_type_mask); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/generic/pdp_del.c b/modules/generic/pdp_del.c new file mode 100644 index 0000000..4b51023 --- /dev/null +++ b/modules/generic/pdp_del.c @@ -0,0 +1,193 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" + + +typedef struct pdp_del_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + + t_outlet **x_outlet; + + int *x_packet; + int x_order; + int x_head; + int x_delay; +} t_pdp_del; + + + + + +static void pdp_del_input_0(t_pdp_del *x, t_symbol *s, t_floatarg f) +{ + int in; + int out; + int packet; + + /* if this is a register_ro message or register_rw message, register with packet factory */ + /* if this is a process message, start the processing + propagate stuff to outputs */ + + if (s == gensym("register_ro")){ + in = (x->x_head % x->x_order); + //post("pdp_del: marking unused packed id=%d on loc %d", x->x_packet[0], in); + pdp_packet_mark_unused(x->x_packet[in]); + packet = pdp_packet_copy_ro((int)f); + + + // TODO TODO TODO !!!! + + //pdp_packet_print_debug((int)f); + + + + + x->x_packet[in] = packet; + //post("pdp_del: writing packed id=%d on loc %d", packet, in); + } + else if (s == gensym("process")){ + out = (((x->x_head + x->x_delay)) % x->x_order); + packet = x->x_packet[out]; + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet[out]); + +/* + if (-1 != packet){ + //post("pdp_del: packet %d has id %d", out, packet); + pdp_packet_mark_unused(packet); + outlet_pdp(x->x_outlet0, packet); + x->x_packet[out] = -1; + } + + + else { + //post("pdp_del: packet %d is empty", out); + } +*/ + + x->x_head = (x->x_head + x->x_order - 1) % x->x_order; + } + + +} + + + + + +static void pdp_del_delay(t_pdp_del *x, t_floatarg fdel) +{ + int del = (int)fdel; + if (del < 0) del = 0; + if (del >= x->x_order) del = x->x_order - 1; + + x->x_delay = del; + +} + +static void pdp_del_reset(t_pdp_del *x) +{ + int i; + for (i=0; i<x->x_order; i++) { + pdp_packet_mark_unused(x->x_packet[i]); + x->x_packet[i] = -1; + } + x->x_head = 0; + +} + +static void pdp_del_debug(t_pdp_del *x) +{ + int i; + post ("order %d", x->x_order); + post ("delay %d", x->x_delay); + post ("head %d", x->x_head); + for (i=0; i<x->x_order; i++) { + post("%d ", x->x_packet[i]); + } +} + +static void pdp_del_free(t_pdp_del *x) +{ + pdp_del_reset(x); + pdp_dealloc (x->x_packet); +} + +t_class *pdp_del_class; + + + +void *pdp_del_new(t_floatarg forder, t_floatarg fdel) +{ + int order = (int)forder; + int del; + int logorder; + int i; + t_pdp_del *x = (t_pdp_del *)pd_new(pdp_del_class); + + del = order; + order++; + + if (del < 0) del = 0; + if (order <= 2) order = 2; + + //post("pdp_del: order = %d", order); + + x->x_order = order; + x->x_packet = (int *)pdp_alloc(sizeof(int)*order); + for(i=0; i<order; i++) x->x_packet[i] = -1; + x->x_delay = del; + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("delay")); + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_del_setup(void) +{ + + + pdp_del_class = class_new(gensym("pdp_del"), (t_newmethod)pdp_del_new, + (t_method)pdp_del_free, sizeof(t_pdp_del), 0, A_DEFFLOAT, A_NULL); + + class_addmethod(pdp_del_class, (t_method)pdp_del_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_del_class, (t_method)pdp_del_delay, gensym("delay"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_del_class, (t_method)pdp_del_reset, gensym("reset"), A_NULL); + + class_addmethod(pdp_del_class, (t_method)pdp_del_debug, gensym("_debug"), A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/generic/pdp_description.c b/modules/generic/pdp_description.c new file mode 100644 index 0000000..7da684b --- /dev/null +++ b/modules/generic/pdp_description.c @@ -0,0 +1,98 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" + + + +typedef struct pdp_description_struct +{ + t_object x_obj; + t_outlet *x_outlet; + +} t_pdp_description; + + + + +static void pdp_description_input_pdp(t_pdp_description *x, t_symbol *s, t_floatarg f) +{ + int p = (int)f; + t_symbol *rro = pdp_sym_rro(); + + if (rro == s){ + outlet_symbol(x->x_outlet, gensym(pdp_packet_get_description(p)->s_name)); + } +} + +static void pdp_description_input_dpd(t_pdp_description *x, t_symbol *s, t_floatarg f) +{ + int p = (int)f; + t_symbol *ins = pdp_sym_ins(); + + if (ins == s){ + outlet_symbol(x->x_outlet, gensym(pdp_packet_get_description(p)->s_name)); + } +} + + +static void pdp_description_free(t_pdp_description *x) +{ + +} + +t_class *pdp_description_class; + + + +static void *pdp_description_new(t_symbol *s, int argc, t_atom *argv) +{ + t_pdp_description *x = (t_pdp_description *)pd_new(pdp_description_class); + + x->x_outlet = outlet_new(&x->x_obj, &s_symbol); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_description_setup(void) +{ + + + pdp_description_class = class_new(gensym("pdp_description"), (t_newmethod)pdp_description_new, + (t_method)pdp_description_free, sizeof(t_pdp_description), 0, A_NULL); + + class_addmethod(pdp_description_class, (t_method)pdp_description_input_pdp, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_description_class, (t_method)pdp_description_input_dpd, gensym("dpd"), A_SYMBOL, A_DEFFLOAT, A_NULL); + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/generic/pdp_forthproc.c b/modules/generic/pdp_forthproc.c new file mode 100644 index 0000000..011396f --- /dev/null +++ b/modules/generic/pdp_forthproc.c @@ -0,0 +1,244 @@ +/* + * 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. + * + */ + + +/* + +example object definitions: + +; stack: (mix_float in_packet state_packet) + +(motion-blur + ((in 1) (feedback 0)) + ((out 0)) + (ip ip 0.0) + (dup down mix dup down down)) + + +; stack: (ouput_packet packet_type) + +(noise-gen + ((bang -1) (type 1)) + ((out 0)) + (image/YCrCb/320x240 ip) + (dup noise)) + +; constraints: +; if a processor accepts more than one atom message (float, list, pdp) +; of the same type they MUST be remapped to other inlets + +(pd-mappings + (motion-blur ((in 0) (feedback 1) (out 0))) + (noise ((out 0)))) + + +*/ + + +#include "pdp.h" +#include "pdp_forth.h" + +#define MAXINLETS 4 + +/* this object instantiates a forth processor */ + +typedef struct pdp_forthproc_struct +{ + t_object x_obj; + t_pdp_list *x_processor; // the processor definition + + + t_pdp_list *x_passive_stack; // we're still into thread processing + t_pdp_list *x_stack; + t_pdp_list *x_program; + +} t_pdp_forthproc; + + + +static int _check_portlist(t_pdp_list *l) +{ + t_pdp_atom *a; + post ("number of ports = %d", l->elements); + for (a = l->first; a; a=a->next){ + t_pdp_atom *b; + if (a->t != a_list) goto error; + if (a->w.w_list->elements != 2) goto error; + b = a->w.w_list->first; + if ((b->t != a_symbol) && (b->t != a_int)) goto error; + b = b->next; + if (b->t != a_int) goto error; + } + post ("port mappings:"); + for (a = l->first; a; a=a->next) pdp_list_print(a->w.w_list); + return 1; + error: + return 0; +} + +static int _check_processor(t_pdp_list *p) +{ + t_pdp_atom *a; + t_pdp_list *l; + + post ("list elements = %d", p->elements); + if (p->elements != 4) goto error; + for (a=p->first; a; a=a->next) if (a->t != a_list) goto error; + post ("all elements are lists: OK"); + + a = p->first; + post ("checking inlets"); + if (!_check_portlist(a->w.w_list)) goto error; + + a = a->next; + post ("checking outlets"); + if (!_check_portlist(a->w.w_list)) goto error; + + a = a->next; post ("init program:"); pdp_list_print(a->w.w_list); + a = a->next; post ("loop program:"); pdp_list_print(a->w.w_list); + + + return 1; + + error: + post("not a valid processor"); + return 0; +} + +/* this function maps an input symbol to a type (how to interpret the + anything message) and a stack index */ +static inline void pdp_forthproc_map_symbol(t_pdp_forthproc *x, t_symbol *s, + t_pdp_word_type *t, int *i) +{ +} + +/* this function stores a new item in an index on the stack + and executes the forth process if this is an active location */ +static inline void pdp_forthproc_write_stack(t_pdp_forthproc *x, int i, t_pdp_word w) +{ +} + +static void pdp_forthproc_anything(t_pdp_forthproc *x, t_symbol *s, int argc, t_atom *argv) +{ + /* if the symbol's length is one character, it's a remapped input. + this instance contains a mapping from symbol->(type, stack entry) */ + + t_pdp_word_type type = a_undef; + t_pdp_word word = (t_pdp_word)0; + int index = -1; + + /* determine what we got and where we need to put it */ + pdp_forthproc_map_symbol(x, s, &type, &index); + + /* interprete the anything message according to expected type. + and put the result in w */ + + /* TODO: put pd list <-> pdp list code in core (also in pdp_guile) */ + switch(type){ + case a_float: + case a_int: + case a_symbol: + case a_list: + case a_packet: + default: + post("got %s, and dunno what to do with it", s->s_name); + return; + + } + + /* write the word to the stack + and run the word if this was an active location */ + pdp_forthproc_write_stack(x, index, word); + +} + +static void pdp_forthproc_free(t_pdp_forthproc *x) +{ + pdp_tree_strip_packets(x->x_stack); + pdp_tree_free(x->x_stack); +} + +t_class *pdp_forthproc_class; + + +void *pdp_forthproc_new(t_symbol *s) +{ + t_pdp_forthproc *x; + + /* get processor */ + t_pdp_symbol *procname = pdp_gensym(s->s_name); + if (!procname->s_processor) return 0; + + /* allocate */ + x = (t_pdp_forthproc *)pd_new(pdp_forthproc_class); + x->x_processor = procname->s_processor; + post("pdp_forthproc %s", s->s_name); + pdp_list_print(x->x_processor); + + + if (_check_processor(x->x_processor)) post ("processor OK"); + + /* create state stack */ + x->x_stack = pdp_stack_new(); + x->x_passive_stack = pdp_stack_new(); + pdp_forth_execute_def(x->x_stack, x->x_processor->first->next->next->w.w_list); + pdp_forth_execute_def(x->x_passive_stack, x->x_processor->first->next->next->w.w_list); + post("initial stack:"); + pdp_list_print(x->x_stack); + + + /* create inlets from description */ + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_bang, gensym("A")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_bang, gensym("B")); + + + + return (void *)x; +} + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_forthproc_setup(void) +{ + int i; + char iname[] = "i1"; + char def[] = "(((0 1) (1 0)) ((0 0)) (ip ip 0.0) (dup down mix dup down down))"; + + /* create a test processor */ + pdp_gensym("testproc")->s_processor = pdp_forth_compile_def(def); + + /* create a standard pd class */ + pdp_forthproc_class = class_new(gensym("pdp_forthproc"), (t_newmethod)pdp_forthproc_new, + (t_method)pdp_forthproc_free, sizeof(t_pdp_forthproc), 0, A_SYMBOL, A_NULL); + + /* add global message handler */ + class_addanything(pdp_forthproc_class, (t_method)pdp_forthproc_anything); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/generic/pdp_inspect.c b/modules/generic/pdp_inspect.c new file mode 100644 index 0000000..b669475 --- /dev/null +++ b/modules/generic/pdp_inspect.c @@ -0,0 +1,124 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" + +/* adapted from the pd trigger object */ + +#define TR_BANG 0 +#define TR_FLOAT 1 +#define TR_SYMBOL 2 +#define TR_POINTER 3 +#define TR_LIST 4 +#define TR_ANYTHING 5 +#define TR_PDP 6 + +/* + +$$$TODO: emplement so that it behaves like the standard trigger object + +i.e. [trigger bang pdp pdp bang pdp] + +register_ro and register_rw messages pass right trough, +since they're not action events, only configure events. +a bang is made equivalent to a process event. + +*/ + +typedef struct pdp_inspect_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet; + +} t_pdp_inspect; + + + + +static void pdp_inspect_input_0(t_pdp_inspect *x, t_symbol *s, t_floatarg f) +{ + t_atom atom[2]; + t_symbol *pdp = gensym("pdp"); + t_symbol *prc = gensym("process"); + t_symbol *rro = gensym("register_ro"); + int i; + + + /* if there is a reg_ro, shortcut the right outlet */ + if (s == rro){ + SETSYMBOL(atom+0, s); + SETFLOAT(atom+1, f); + outlet_anything(x->x_outlet, pdp, 2, atom); + SETSYMBOL(atom+0, prc); + outlet_anything(x->x_outlet, pdp, 1, atom); + } + + +} + + + +static void pdp_inspect_free(t_pdp_inspect *x) +{ + +} + +t_class *pdp_inspect_class; + + + +static void *pdp_inspect_new(void) +{ + t_pdp_inspect *x = (t_pdp_inspect *)pd_new(pdp_inspect_class); + + + x->x_outlet = outlet_new(&x->x_obj, &s_anything); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_inspect_setup(void) +{ + + + pdp_inspect_class = class_new(gensym("pdp_inspect_ro"), (t_newmethod)pdp_inspect_new, + (t_method)pdp_inspect_free, sizeof(t_pdp_inspect), 0, A_GIMME, A_NULL); + + class_addcreator((t_newmethod)pdp_inspect_new, gensym("pdp_t"), A_GIMME, 0); + + class_addmethod(pdp_inspect_class, (t_method)pdp_inspect_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/generic/pdp_loop.c b/modules/generic/pdp_loop.c new file mode 100644 index 0000000..259eb8f --- /dev/null +++ b/modules/generic/pdp_loop.c @@ -0,0 +1,296 @@ +/* + * 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. + * + */ + + +/* + + pdp_loop: a looping packet delay line + messages: + record x: start recording at position x (default = 0) + stop: stop recording + float: output packet at position + bang: output next packet + rewind: rewind + loop: set looping mode + +*/ + + +#include "pdp.h" +#include "pdp_internals.h" + + +typedef struct pdp_loop_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + t_outlet *x_outlet1; + + int *x_packet; + int x_order; /* size of the packet loop */ + int x_play_head; /* next position to play back */ + int x_record_head; /* next position to record to */ + + int x_loop; + int x_recording_frames; /* nb frames left to record */ + //int x_recording_shot; /* single frame recording is on */ +} t_pdp_loop; + + + + + +static void pdp_loop_input_0(t_pdp_loop *x, t_symbol *s, t_floatarg f) +{ + int in; + int out; + int packet; + + + /* if recording is off, ignore packet */ + if ((!x->x_recording_frames)) return; + + /* store a packet on register ro */ + if (s == gensym("register_ro")){ + + /* delete old & store new */ + in = x->x_record_head; //% x->x_order; + pdp_packet_mark_unused(x->x_packet[in]); + packet = pdp_packet_copy_ro((int)f); + x->x_packet[in] = packet; + + /* advance head & decrease record counter */ + x->x_recording_frames--; + x->x_record_head++; + + /* turn off recording if we are at the end */ + if (x->x_record_head == x->x_order) x->x_recording_frames = 0; + } +} + + +static void pdp_loop_bang(t_pdp_loop *x){ + int out; + int packet; + + out = x->x_play_head; + + /* don't play if we're at the end of the sequence and looping is disabled */ + if ((!x->x_loop) && (out >= x->x_order)) return; + + /* wrap index */ + out %= x->x_order; + + /* output the current packet */ + packet = x->x_packet[out]; + outlet_float(x->x_outlet1, (float)out); // output location + if (-1 != packet) outlet_pdp(x->x_outlet0, packet); // output packet + + /* advance playback head */ + x->x_play_head++; + +} + + + + + + +static void pdp_loop_reset(t_pdp_loop *x) +{ + int i; + for (i=0; i<x->x_order; i++) { + pdp_packet_mark_unused(x->x_packet[i]); + x->x_packet[i] = -1; + } + x->x_play_head = 0; + x->x_record_head = 0; + +} + +static void pdp_loop_record(t_pdp_loop *x, t_floatarg fstart, t_floatarg fdur) +{ + int istart = (int)fstart; + int idur = (int)fdur; + istart %= x->x_order; + if (istart<0) istart+= x->x_order; + if (idur <= 0) idur = x->x_order - istart; + + x->x_record_head = istart; + x->x_recording_frames = idur; +} + +static void pdp_loop_store(t_pdp_loop *x, t_floatarg f) +{ + int i = (int)f; + i %= x->x_order; + if (i<0) i+= x->x_order; + + x->x_record_head = i; + x->x_recording_frames = 1; +} + +static void pdp_loop_seek(t_pdp_loop *x, t_floatarg f) +{ + int i = (int)f; + i %= x->x_order; + if (i<0) i+= x->x_order; + + x->x_play_head = i; +} + +static void pdp_loop_seek_hot(t_pdp_loop *x, t_floatarg f) +{ + pdp_loop_seek(x, f); + pdp_loop_bang(x); +} + + +static void pdp_loop_stop(t_pdp_loop *x) +{ + x->x_recording_frames = 0; +} + +static void pdp_loop_loop(t_pdp_loop *x, t_floatarg f) +{ + if (f == 0.0f) x->x_loop = 0; + if (f == 1.0f) x->x_loop = 1; + +} +static void pdp_loop_free(t_pdp_loop *x) +{ + pdp_loop_reset(x); + pdp_dealloc (x->x_packet); +} + +static int pdp_loop_realsize(float f) +{ + int order = (int)f; + if (order <= 2) order = 2; + return order; +} + + +static void pdp_loop_resize(t_pdp_loop *x, t_floatarg f) +{ + int i; + int order = pdp_loop_realsize(f); + int *newloop; + + /* if size didn't change, do nothing */ + if (x->x_order == order) return; + + /* create new array */ + newloop = (int *)pdp_alloc(sizeof(int) * order); + + + /* extend it */ + if (x->x_order < order){ + + /* copy old packets */ + for (i=0; i<x->x_order; i++) newloop[i] = x->x_packet[i]; + + /* loop extend the rest */ + for (i=x->x_order; i<order; i++) newloop[i] = pdp_packet_copy_ro(x->x_packet[i % x->x_order]); + + } + + /* or shrink it */ + else { + /* copy part of old packets */ + for (i=0; i<order; i++) newloop[i] = x->x_packet[i]; + + /* delete the other part of old packets */ + for (i=order; i<x->x_order; i++) pdp_packet_mark_unused(x->x_packet[i]); + + /* adjust heads */ + x->x_play_head %= order; + x->x_record_head %= order; + + } + + /* delete old line & store new */ + pdp_dealloc (x->x_packet); + x->x_packet = newloop; + x->x_order = order; + + +} + + +t_class *pdp_loop_class; + + + +void *pdp_loop_new(t_floatarg f) +{ + int i; + int order = pdp_loop_realsize(f); + t_pdp_loop *x = (t_pdp_loop *)pd_new(pdp_loop_class); + + x->x_order = order; + x->x_packet = (int *)pdp_alloc(sizeof(int)*order); + for(i=0; i<order; i++) x->x_packet[i] = -1; + + x->x_play_head = 0; + x->x_record_head = 0; + x->x_recording_frames = 0; + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("seek")); + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_outlet1 = outlet_new(&x->x_obj, &s_anything); + + x->x_loop = 1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_loop_setup(void) +{ + + + pdp_loop_class = class_new(gensym("pdp_loop"), (t_newmethod)pdp_loop_new, + (t_method)pdp_loop_free, sizeof(t_pdp_loop), 0, A_DEFFLOAT, A_NULL); + + class_addmethod(pdp_loop_class, (t_method)pdp_loop_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_loop_class, (t_method)pdp_loop_record, gensym("record"), A_DEFFLOAT, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_loop_class, (t_method)pdp_loop_store, gensym("store"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_loop_class, (t_method)pdp_loop_reset, gensym("reset"), A_NULL); + class_addmethod(pdp_loop_class, (t_method)pdp_loop_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_loop_class, (t_method)pdp_loop_stop, gensym("stop"), A_NULL); + class_addmethod(pdp_loop_class, (t_method)pdp_loop_seek, gensym("seek"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_loop_class, (t_method)pdp_loop_resize, gensym("size"), A_FLOAT, A_NULL); + class_addmethod(pdp_loop_class, (t_method)pdp_loop_loop, gensym("loop"), A_FLOAT, A_NULL); + class_addfloat(pdp_loop_class, (t_method)pdp_loop_seek_hot); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/generic/pdp_reg.c b/modules/generic/pdp_reg.c new file mode 100644 index 0000000..8b07c14 --- /dev/null +++ b/modules/generic/pdp_reg.c @@ -0,0 +1,168 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_png.h" +#include "pdp_internals.h" + + +typedef struct pdp_reg_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + + int x_packet0; + +} t_pdp_reg; + + +static void pdp_reg_load_png(t_pdp_reg *x, t_pdp_symbol *s) +{ + int packet = pdp_packet_bitmap_from_png_file(s->s_name); + if (-1 == packet){ + post("pdp_reg: error loading png file %s", s->s_name); + } + else{ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = packet; + } + +} + +static void pdp_reg_save_png(t_pdp_reg *x, t_pdp_symbol *s) +{ + int newpacket = pdp_packet_convert_ro(x->x_packet0, pdp_gensym("bitmap/*/*")); + + if (-1 == newpacket){ + post("pdp_reg: nothing to save"); + return; + } + + if (!(pdp_packet_bitmap_save_png_file(newpacket, s->s_name))){ + post("pdp_reg: error saving png file %s", s->s_name); + } + + pdp_packet_mark_unused(newpacket); + +} + + +static void pdp_reg_bang(t_pdp_reg *x) +{ + + if (-1 != x->x_packet0) outlet_pdp(x->x_outlet0, x->x_packet0); + +} + + + +static void pdp_reg_input_0(t_pdp_reg *x, t_symbol *s, t_floatarg f) +{ + + /* if this is a register_ro message or register_rw message, register with packet factory */ + + /* if this is a process message, start the processing + propagate stuff to outputs */ + + if (s == gensym("register_ro")){ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = pdp_packet_copy_ro((int)f); + //post("in0ro: requested %d, got %d", (int)f, x->x_packet0); + } + else if (s == gensym("process")){ + pdp_reg_bang(x); + + } + + +} + + +static void pdp_reg_input_1(t_pdp_reg *x, t_symbol *s, t_floatarg f) +{ + + /* if this is a register_ro message or register_rw message, register with packet factory */ + + /* if this is a process message, start the processing + propagate stuff to outputs */ + + if (s == gensym("register_ro")){ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = pdp_packet_copy_ro((int)f); + //post("in0ro: requested %d, got %d", (int)f, x->x_packet0); + } + +} + + + +static void pdp_reg_free(t_pdp_reg *x) +{ + pdp_packet_mark_unused(x->x_packet0); + +} + +t_class *pdp_reg_class; + + + +void *pdp_reg_new(void) +{ + t_pdp_reg *x = (t_pdp_reg *)pd_new(pdp_reg_class); + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("pdp"), gensym("pdp1")); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_reg_setup(void) +{ + + + pdp_reg_class = class_new(gensym("pdp_reg"), (t_newmethod)pdp_reg_new, + (t_method)pdp_reg_free, sizeof(t_pdp_reg), 0, A_NULL); + + + class_addmethod(pdp_reg_class, (t_method)pdp_reg_bang, gensym("bang"), A_NULL); + + class_addmethod(pdp_reg_class, (t_method)pdp_reg_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_reg_class, (t_method)pdp_reg_input_1, gensym("pdp1"), A_SYMBOL, A_DEFFLOAT, A_NULL); + + class_addmethod(pdp_reg_class, (t_method)pdp_reg_save_png, gensym("save_png"), A_SYMBOL, A_NULL); + class_addmethod(pdp_reg_class, (t_method)pdp_reg_load_png, gensym("load_png"), A_SYMBOL, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/generic/pdp_route.c b/modules/generic/pdp_route.c new file mode 100644 index 0000000..c410d1f --- /dev/null +++ b/modules/generic/pdp_route.c @@ -0,0 +1,146 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_internals.h" + +// dynamic ???? +#define PDP_ROUTE_MAX_NB_OUTLETS 100 + +typedef struct pdp_route_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet[PDP_ROUTE_MAX_NB_OUTLETS]; + + int x_nb_outlets; + int x_route; + int x_route_next; + + +} t_pdp_route; + + +static void pdp_route_input_0(t_pdp_route *x, t_symbol *s, t_floatarg f) +{ + t_atom atom[2]; + t_symbol *pdp = gensym("pdp"); + + + /* trigger on register_ro */ + if (s == gensym("register_ro")){ + x->x_route = x->x_route_next; + } + + /* propagate the pdp message */ + SETSYMBOL(atom+0, s); + SETFLOAT(atom+1, f); + outlet_anything(x->x_outlet[x->x_route], pdp, 2, atom); + +} + +static void pdp_route_input_0_dpd(t_pdp_route *x, t_symbol *s, t_floatarg f) +{ + + /* trigger on accumulate */ + if (s == gensym("accumulate")){ + x->x_route = x->x_route_next; + } + + /* propagate the dpd message */ + outlet_dpd(x->x_outlet[x->x_route], (int)f); + +} + + + +static void pdp_route_route(t_pdp_route *x, t_floatarg f) +{ + int route = (int)f; + + if (route < 0) route = 0; + if (route >= x->x_nb_outlets) route = x->x_nb_outlets - 1; + + x->x_route_next = route; + +} + + + +static void pdp_route_free(t_pdp_route *x) +{ + +} + +t_class *pdp_route_class; + + + +void *pdp_route_new(t_floatarg f) +{ + int nboutlets = (int)f; + int i; + + t_pdp_route *x = (t_pdp_route *)pd_new(pdp_route_class); + + + if (nboutlets < 2) nboutlets = 2; + if (nboutlets >= PDP_ROUTE_MAX_NB_OUTLETS) nboutlets = PDP_ROUTE_MAX_NB_OUTLETS - 1; + + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("route")); + + x->x_nb_outlets = nboutlets; + x->x_route = 0; + x->x_route_next = 0; + + for (i=0; i<nboutlets; i++) + x->x_outlet[i] = outlet_new(&x->x_obj, &s_anything); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_route_setup(void) +{ + + + pdp_route_class = class_new(gensym("pdp_route"), (t_newmethod)pdp_route_new, + (t_method)pdp_route_free, sizeof(t_pdp_route), 0, A_DEFFLOAT, A_NULL); + + + class_addmethod(pdp_route_class, (t_method)pdp_route_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_route_class, (t_method)pdp_route_input_0_dpd, gensym("dpd"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_route_class, (t_method)pdp_route_route, gensym("route"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/generic/pdp_snap.c b/modules/generic/pdp_snap.c new file mode 100644 index 0000000..73ec26c --- /dev/null +++ b/modules/generic/pdp_snap.c @@ -0,0 +1,120 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_internals.h" + +typedef struct pdp_snap_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + + int x_packet0; + bool x_snapnext; + +} t_pdp_snap; + + +static void pdp_snap_bang(t_pdp_snap *x) +{ + + if (-1 != x->x_packet0) + outlet_pdp(x->x_outlet0, x->x_packet0); + +} + + + + +static void pdp_snap_input_1(t_pdp_snap *x, t_symbol *s, t_floatarg f) +{ + + /* if this is a register_ro message or register_rw message, register with packet factory */ + + /* if this is a process message, start the processing + propagate stuff to outputs */ + + if (s == gensym("register_ro")){ + if(x->x_snapnext) { + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = pdp_packet_copy_ro((int)f); + x->x_snapnext = false; + } + } + +} + +static void pdp_snap_snap(t_pdp_snap *x) +{ + x->x_snapnext = true; +} + +static void pdp_snap_free(t_pdp_snap *x) +{ + pdp_packet_mark_unused(x->x_packet0); + +} + +t_class *pdp_snap_class; + + + +void *pdp_snap_new(void) +{ + t_pdp_snap *x = (t_pdp_snap *)pd_new(pdp_snap_class); + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("pdp"), gensym("pdp1")); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_snapnext = false; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_snap_setup(void) +{ + + + pdp_snap_class = class_new(gensym("pdp_snap"), (t_newmethod)pdp_snap_new, + (t_method)pdp_snap_free, sizeof(t_pdp_snap), 0, A_NULL); + + + class_addmethod(pdp_snap_class, (t_method)pdp_snap_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_snap_class, (t_method)pdp_snap_snap, gensym("snap"), A_NULL); + + class_addmethod(pdp_snap_class, (t_method)pdp_snap_input_1, gensym("pdp1"), A_SYMBOL, A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/generic/pdp_trigger.c b/modules/generic/pdp_trigger.c new file mode 100644 index 0000000..4bac9d1 --- /dev/null +++ b/modules/generic/pdp_trigger.c @@ -0,0 +1,192 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_internals.h" + +/* adapted from the pd trigger object */ + +#define TR_BANG 0 +#define TR_FLOAT 1 +#define TR_SYMBOL 2 +#define TR_POINTER 3 +#define TR_LIST 4 +#define TR_ANYTHING 5 +#define TR_PDP 6 + +/* + +$$$TODO: emplement so that it behaves like the standard trigger object + +i.e. [trigger bang pdp pdp bang pdp] + +register_ro and register_rw messages pass right trough, +since they're not action events, only configure events. +a bang is made equivalent to a process event. + +*/ + +typedef struct triggerout +{ + int u_type; /* outlet type from above */ + t_outlet *u_outlet; +} t_triggerout; + + +typedef struct pdp_trigger_struct +{ + t_object x_obj; + t_float x_f; + + int x_n; + t_triggerout *x_vec; + +} t_pdp_trigger; + + + + +static void pdp_trigger_input_pdp(t_pdp_trigger *x, t_symbol *s, t_floatarg f) +{ + t_atom atom[2]; + t_symbol *pdp = pdp_sym_pdp(); + t_symbol *prc = pdp_sym_prc(); + t_triggerout *u; + int i; + + for (i = x->x_n, u = x->x_vec + i; u--, i--;){ + /* trigger bang outlet only when a process event is recieved */ + if ((u->u_type == TR_BANG) && (s == prc)){ + outlet_bang(u->u_outlet); + } + /* just pass the message if it is a pdp outlet */ + if ((u->u_type) == TR_PDP){ + SETSYMBOL(atom+0, s); + SETFLOAT(atom+1, f); + if (s == prc) outlet_anything(u->u_outlet, pdp, 1, atom); + else outlet_anything(u->u_outlet, pdp, 2, atom); + + } + } + +} + +static void pdp_trigger_input_dpd(t_pdp_trigger *x, t_symbol *s, t_floatarg f) +{ + t_atom atom[2]; + t_symbol *dpd = pdp_sym_dpd(); + t_symbol *acc = pdp_sym_acc(); + t_triggerout *u; + int i; + int p = (int)f; + + for (i = x->x_n, u = x->x_vec + i; u--, i--;){ + /* trigger outlet only when an accumulate event is recieved */ + if (s == acc){ + + /* output bang */ + if (u->u_type == TR_BANG) outlet_bang(u->u_outlet); + + /* output a complete dpd message if it is a pdp outlet */ + if ((u->u_type) == TR_PDP){ + outlet_dpd(u->u_outlet, p); + } + } + } + +} + + +static void pdp_trigger_free(t_pdp_trigger *x) +{ + pdp_dealloc(x->x_vec); +} + +t_class *pdp_trigger_class; + + + +static void *pdp_trigger_new(t_symbol *s, int argc, t_atom *argv) +{ + t_pdp_trigger *x = (t_pdp_trigger *)pd_new(pdp_trigger_class); + t_atom defarg[2], *ap; + t_triggerout *u; + int i; + + + if (!argc) + { + argv = defarg; + argc = 2; + SETSYMBOL(&defarg[0], gensym("pdp")); + SETSYMBOL(&defarg[1], gensym("bang")); + } + + x->x_n = argc; + x->x_vec = pdp_alloc(argc * sizeof(*x->x_vec)); + + for (i = 0, ap = argv, u = x->x_vec; i < argc; u++, ap++, i++) + { + t_atomtype thistype = ap->a_type; + char c; + if (thistype == TR_SYMBOL) c = ap->a_w.w_symbol->s_name[0]; + else c = 0; + if (c == 'p') + u->u_type = TR_PDP, + u->u_outlet = outlet_new(&x->x_obj, &s_anything); + else if (c == 'b') + u->u_type = TR_BANG, u->u_outlet = outlet_new(&x->x_obj, &s_bang); + else + { + pd_error(x, "pdp_trigger: %s: bad type", ap->a_w.w_symbol->s_name); + u->u_type = TR_BANG, u->u_outlet = outlet_new(&x->x_obj, &s_bang); + } + } + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_trigger_setup(void) +{ + + + pdp_trigger_class = class_new(gensym("pdp_trigger"), (t_newmethod)pdp_trigger_new, + (t_method)pdp_trigger_free, sizeof(t_pdp_trigger), 0, A_GIMME, A_NULL); + + class_addcreator((t_newmethod)pdp_trigger_new, gensym("pdp_t"), A_GIMME, 0); + + class_addmethod(pdp_trigger_class, (t_method)pdp_trigger_input_pdp, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_trigger_class, (t_method)pdp_trigger_input_dpd, gensym("dpd"), A_SYMBOL, A_DEFFLOAT, A_NULL); + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_basic/Makefile b/modules/image_basic/Makefile new file mode 100644 index 0000000..d82b2cb --- /dev/null +++ b/modules/image_basic/Makefile @@ -0,0 +1,20 @@ +current: all_modules + +include ../../Makefile.config + +PDP_MOD = pdp_add.o pdp_conv.o \ + pdp_mix.o pdp_mul.o pdp_randmix.o \ + pdp_bq.o pdp_noise.o \ + pdp_gain.o \ + pdp_zoom.o pdp_cheby.o \ + pdp_constant.o \ + pdp_logic.o pdp_stateless.o pdp_plasma.o + + +# build basic image processing modules (derived from base class) +all_modules: $(PDP_MOD) + +clean: + rm -f *~ + rm -f *.o + diff --git a/modules/image_basic/README b/modules/image_basic/README new file mode 100644 index 0000000..965d277 --- /dev/null +++ b/modules/image_basic/README @@ -0,0 +1,5 @@ +This directory contains "normal" planar 16 bit unsigned image packet processors, +derived from the t_pdp_imagebase class defined in pdp_imagebase.h + +Most modules are wrappers around monochrome bitmap processors (pdp_imageproc_*) + diff --git a/modules/image_basic/pdp_add.c b/modules/image_basic/pdp_add.c new file mode 100644 index 0000000..0366c16 --- /dev/null +++ b/modules/image_basic/pdp_add.c @@ -0,0 +1,109 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_imagebase.h" + +typedef struct pdp_add_struct +{ + /* a pdp derived class has the t_pdp_imagebase data member as first entry */ + /* it contains the pd object and the data members for the pdp_base class */ + t_pdp_imagebase x_base; + +} t_pdp_add; + + +/* the process method */ +static void pdp_add_process(t_pdp_add *x) +{ + /* get received packets */ + int p0, p1; + + /* get channel mask */ + int mask = pdp_imagebase_get_chanmask(x); + + /* this processes the packets using a pdp image processor */ + /* replace this with your own processing code */ + /* for raw packet acces: use pdp_pacjet_header() and pdp_packet_data() */ + p0 = pdp_base_get_packet(x,0); + p1 = pdp_base_get_packet(x,1); + + pdp_imageproc_dispatch_2buf(&pdp_imageproc_add_process, 0, mask, p0, p1); + + +} + + +static void pdp_add_free(t_pdp_add *x) +{ + /* free super: this is mandatory + (it stops the thread if there is one running and frees all packets) */ + pdp_imagebase_free(x); + + /* if you have allocated more packets + this is the place to free them with pdp_mark_unused */ +} + +t_class *pdp_add_class; + + +void *pdp_add_new(void) +{ + /* allocate */ + t_pdp_add *x = (t_pdp_add *)pd_new(pdp_add_class); + + /* init super: this is mandatory */ + pdp_imagebase_init(x); + + /* set the pdp processing method */ + pdp_base_set_process_method(x, (t_pdp_method)pdp_add_process); + + /* create additional cold (readonly) pdp inlets (there is already one pdp inlet) */ + pdp_base_add_pdp_inlet(x); + + /* create a pdp_outlet */ + pdp_base_add_pdp_outlet(x); + + return (void *)x; +} + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_add_setup(void) +{ + /* create a standard pd class */ + pdp_add_class = class_new(gensym("pdp_add"), (t_newmethod)pdp_add_new, + (t_method)pdp_add_free, sizeof(t_pdp_add), 0, A_NULL); + + /* inherit pdp base class methods */ + pdp_imagebase_setup(pdp_add_class); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_basic/pdp_bq.c b/modules/image_basic/pdp_bq.c new file mode 100644 index 0000000..088e50b --- /dev/null +++ b/modules/image_basic/pdp_bq.c @@ -0,0 +1,462 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_imagebase.h" +#include <math.h> + +/* computes a transfer function: + * + * b0 + b1 z^(-1) + b2 z^(-2) + * T(z) = -------------------------- + * 1 + a1 z^(-1) + a2 z^(-2) + * + */ + + +typedef struct pdp_bq_struct +{ + + t_pdp_imagebase x_base; //pdp_bq derives from pdp_base + + + /* state packets for bqt */ + int x_packet1; + int x_packet2; + + + unsigned int x_nbpasses; + + /* single direction */ + unsigned int x_direction; + + bool x_reset_on_formatchange; + + void *x_biquad; + + float x_coefs_a[3]; // a0, -a1, -a2 + float x_coefs_b[3]; // b0, b1, b2 + float x_state_u[2]; // u0, u1 + float x_state_u_save[2]; // u0, u1 (for reset) + +} t_pdp_bq; + + +/************************* COEFFICIENT METHODS ****************************/ + +static void pdp_bq_a0(t_pdp_bq *x, t_floatarg f){x->x_coefs_a[0] = f;} +static void pdp_bq_a1(t_pdp_bq *x, t_floatarg f){x->x_coefs_a[1] = -f;} +static void pdp_bq_a2(t_pdp_bq *x, t_floatarg f){x->x_coefs_a[2] = -f;} + +static void pdp_bq_b0(t_pdp_bq *x, t_floatarg f){x->x_coefs_b[0] = f;} +static void pdp_bq_b1(t_pdp_bq *x, t_floatarg f){x->x_coefs_b[1] = f;} +static void pdp_bq_b2(t_pdp_bq *x, t_floatarg f){x->x_coefs_b[2] = f;} + +static void pdp_bq_u0(t_pdp_bq *x, t_floatarg f){x->x_state_u_save[0] = f;} +static void pdp_bq_u1(t_pdp_bq *x, t_floatarg f){x->x_state_u_save[1] = f;} + + +static void pdp_bq_setcoefs(t_pdp_bq *x, + float a0, float a1, float a2, + float b0, float b1, float b2) +{ + pdp_bq_a0(x,a0); + pdp_bq_a1(x,a1); + pdp_bq_a2(x,a2); + pdp_bq_b0(x,b0); + pdp_bq_b1(x,b1); + pdp_bq_b2(x,b2); + pdp_imageproc_bq_setcoef(x->x_biquad, x->x_coefs_a); +} + +static void pdp_bq_setstate(t_pdp_bq *x, float u0, float u1) +{ + pdp_bq_u0(x,u0); + pdp_bq_u1(x,u1); + pdp_imageproc_bq_setcoef(x->x_biquad, x->x_coefs_a); +} + + + +/* reso lowpass */ +static void pdp_bq_lpf(t_pdp_bq *x, t_floatarg f, t_floatarg Q) +{ + float a0, a1, a2, b0, b1, b2, cs, sn, w, alpha; + w = 2.0 * M_PI * f; + cs = cos(w); + sn = sin(w); + + alpha = sn*sinh(1.0f/(2.0f*Q)); + b0 = (1.0 - cs)/2.0; + b1 = 1.0 - cs; + b2 = (1.0 - cs)/2.0; + a0 = (1.0 + alpha); + a1 = -2.0*cs; + a2 = 1.0 - alpha; + + pdp_bq_setcoefs(x, a0, a1, a2, b0, b1, b2); + +} + +/* reso highpass */ +static void pdp_bq_hpf(t_pdp_bq *x, t_floatarg f, t_floatarg Q) +{ + float a0, a1, a2, b0, b1, b2, cs, sn, w, alpha; + w = 2.0 * M_PI * f; + cs = cos(w); + sn = sin(w); + + alpha = sn*sinh(1.0f/(2.0f*Q)); + + b0 = (1.0 + cs)/2.0; + b1 = -1.0 - cs; + b2 = (1.0 + cs)/2.0; + a0 = (1.0 + alpha); + a1 = -2.0*cs; + a2 = 1.0 - alpha; + + pdp_bq_setcoefs(x, a0, a1, a2, b0, b1, b2); + +} + + +/* reso allpass */ +static void pdp_bq_apf(t_pdp_bq *x, t_floatarg f, t_floatarg Q) +{ + float a0, a1, a2, b0, b1, b2, cs, sn, w, alpha; + w = 2.0 * M_PI * f; + cs = cos(w); + sn = sin(w); + + alpha = sn*sinh(1.0f/(2.0f*Q)); + + b0 = (1.0 - alpha); + b1 = -2.0 * cs; + b2 = (1.0 + alpha); + a0 = (1.0 + alpha); + a1 = -2.0*cs; + a2 = 1.0 - alpha; + + pdp_bq_setcoefs(x, a0, a1, a2, b0, b1, b2); +} + +/* reso band stop (notch) */ +static void pdp_bq_bsf(t_pdp_bq *x, t_floatarg f, t_floatarg Q) +{ + float a0, a1, a2, b0, b1, b2, cs, sn, w, alpha; + w = 2.0 * M_PI * f; + cs = cos(w); + sn = sin(w); + + alpha = sn*sinh(1.0f/(2.0f*Q)); + + b0 = 1.0; + b1 = -2.0 * cs; + b2 = 1.0; + a0 = (1.0 + alpha); + a1 = -2.0*cs; + a2 = 1.0 - alpha; + + pdp_bq_setcoefs(x, a0, a1, a2, b0, b1, b2); + +} + +static void pdp_bq_onep(t_pdp_bq *x, t_floatarg f) +{ + float a0,a1,a2,b0,b1,b2; + + if (f>1.0f) f = 1.0f; + if (f<0.0f) f = 0.0f; + + a0 = 1.0f; + a1 = -(1.0f - f); + a2 = 0.0f; + b0 = f; + b1 = 0.0f; + b2 = 0.0f; + pdp_bq_setcoefs(x, a0, a1, a2, b0, b1, b2); +} + +static void pdp_bq_twop(t_pdp_bq *x, t_floatarg f) +{ + float f1; + float a0,a1,a2,b0,b1,b2; + + if (f>1.0) f = 1.0; + if (f<0.0) f = 0.0; + + f1 = 1.0 - f; + + a0 = 1.0f; + a1 = -2.0f*f1; + a2 = f1*f1; + b0 = f*f; + b1 = 0.0f; + b2 = 0.0f; + + pdp_bq_setcoefs(x, a0, a1, a2, b0, b1, b2); +} + + + + + +/************************* PROCESS METHODS ****************************/ + +static void pdp_bqt_process(t_pdp_bq *x) +{ + /* get received packets */ + int p0 = pdp_base_get_packet(x, 0); + + /* get channel mask */ + u32 mask = pdp_imagebase_get_chanmask(x); + + pdp_imageproc_dispatch_3buf(&pdp_imageproc_bqt_process, x->x_biquad, + mask, p0, x->x_packet1, x->x_packet2); +} + +static void pdp_bq_process(t_pdp_bq *x) +{ + /* get received packets */ + int p0 = pdp_base_get_packet(x, 0); + + /* get channel mask */ + u32 mask = pdp_imagebase_get_chanmask(x); + + pdp_imageproc_bq_setnbpasses(x->x_biquad, x->x_nbpasses); + pdp_imageproc_bq_setdirection(x->x_biquad, x->x_direction); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_bq_process, x->x_biquad, mask, p0); +} + +static void pdp_bqt_reset(t_pdp_bq *x) +{ + pdp_imageproc_dispatch_1buf(&pdp_imageproc_zero_process, 0, -1, x->x_packet1); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_zero_process, 0, -1, x->x_packet2); +} + + + +static void pdp_bqt_preproc(t_pdp_bq *x) +{ + /* get received packets */ + int p0 = pdp_base_get_packet(x, 0); + + /* check if state packets are compatible */ + if (!(pdp_packet_image_compat(p0, x->x_packet1) + && pdp_packet_image_compat(p0, x->x_packet1))){ + + /* if not, create new state packets by copying the input packets */ + post("pdp_bqt: created new state packets"); + pdp_packet_mark_unused(x->x_packet1); + pdp_packet_mark_unused(x->x_packet2); + x->x_packet1 = pdp_packet_clone_rw(p0); + x->x_packet2 = pdp_packet_clone_rw(p0); + + /* reset */ + if (x->x_reset_on_formatchange) pdp_bqt_reset(x); + + } +} + +/************************* CONFIG METHODS ****************************/ + + +static void pdp_bq_passes(t_pdp_bq *x, t_floatarg f) +{ + int passes = (int)f; + passes = passes < 0 ? 0 : passes; + x->x_nbpasses = passes; + +} + +static void pdp_bq_lr(t_pdp_bq *x, t_floatarg f) +{ + if (f == 1.0f) x->x_direction |= PDP_IMAGEPROC_BIQUAD_LEFT2RIGHT; + if (f == 0.0f) x->x_direction &= ~PDP_IMAGEPROC_BIQUAD_LEFT2RIGHT; +} + +static void pdp_bq_rl(t_pdp_bq *x, t_floatarg f) +{ + if (f == 1.0f) x->x_direction |= PDP_IMAGEPROC_BIQUAD_RIGHT2LEFT; + if (f == 0.0f) x->x_direction &= ~PDP_IMAGEPROC_BIQUAD_RIGHT2LEFT; +} +static void pdp_bq_tb(t_pdp_bq *x, t_floatarg f) +{ + if (f == 1.0f) x->x_direction |= PDP_IMAGEPROC_BIQUAD_TOP2BOTTOM; + if (f == 0.0f) x->x_direction &= ~PDP_IMAGEPROC_BIQUAD_TOP2BOTTOM; +} + +static void pdp_bq_bt(t_pdp_bq *x, t_floatarg f) +{ + if (f == 1.0f) x->x_direction |= PDP_IMAGEPROC_BIQUAD_BOTTOM2TOP; + if (f == 0.0f) x->x_direction &= ~PDP_IMAGEPROC_BIQUAD_BOTTOM2TOP; +} + + +static void pdp_bq_hor(t_pdp_bq *x, t_floatarg f) +{ + pdp_bq_lr(x, f); + pdp_bq_rl(x, f); +} +static void pdp_bq_ver(t_pdp_bq *x, t_floatarg f) +{ + pdp_bq_tb(x, f); + pdp_bq_bt(x, f); +} + + +/************************* DES/CONSTRUCTORS ****************************/ + +static void pdp_bq_free(t_pdp_bq *x) +{ + pdp_imagebase_free(x); + pdp_imageproc_bq_delete(x->x_biquad); + pdp_packet_mark_unused(x->x_packet1); + pdp_packet_mark_unused(x->x_packet2); + +} + + +void pdp_bq_init(t_pdp_bq *x) +{ + x->x_packet1 = -1; + x->x_packet2 = -1; + + x->x_nbpasses = 1; + x->x_reset_on_formatchange = true; + + x->x_biquad = pdp_imageproc_bq_new(); + + pdp_bq_setstate(x, 0.0f, 0.0f); + pdp_bq_onep(x, 0.1f); + +} + + + +/* class pointers */ + +t_class *pdp_bq_class; /* biquad spacial processing */ +t_class *pdp_bqt_class; /* biquad time processing */ + +void *pdp_bq_new(void) +{ + t_pdp_bq *x = (t_pdp_bq *)pd_new(pdp_bq_class); + + pdp_imagebase_init(x); + pdp_base_add_pdp_outlet(x); + + pdp_base_set_process_method(x, (t_pdp_method)pdp_bq_process); + pdp_base_add_gen_inlet(x, gensym("float"), gensym("passes")); + + pdp_bq_init(x); + return (void *)x; +} + +void *pdp_bqt_new(void) +{ + t_pdp_bq *x = (t_pdp_bq *)pd_new(pdp_bqt_class); + + pdp_imagebase_init(x); + pdp_base_add_pdp_outlet(x); + + pdp_base_set_preproc_method(x, (t_pdp_method)pdp_bqt_preproc); + pdp_base_set_process_method(x, (t_pdp_method)pdp_bqt_process); + + pdp_bq_init(x); + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + + + + + +/************************* CLASS CONSTRUCTORS ****************************/ + + +void pdp_bq_coefmethods_setup(t_class *c) +{ + + /* raw coefficient methods */ + class_addmethod(c, (t_method)pdp_bq_a1, gensym("a1"), A_FLOAT, A_NULL); + class_addmethod(c, (t_method)pdp_bq_a2, gensym("a2"), A_FLOAT, A_NULL); + class_addmethod(c, (t_method)pdp_bq_b0, gensym("b0"), A_FLOAT, A_NULL); + class_addmethod(c, (t_method)pdp_bq_b1, gensym("b1"), A_FLOAT, A_NULL); + class_addmethod(c, (t_method)pdp_bq_b2, gensym("b2"), A_FLOAT, A_NULL); + //class_addmethod(c, (t_method)pdp_bq_u1, gensym("u1"), A_FLOAT, A_NULL); + //class_addmethod(c, (t_method)pdp_bq_u2, gensym("u2"), A_FLOAT, A_NULL); + + /* real pole filters */ + class_addmethod(c, (t_method)pdp_bq_onep, gensym("onep"), A_FLOAT, A_NULL); + class_addmethod(c, (t_method)pdp_bq_twop, gensym("twop"), A_FLOAT, A_NULL); + + /* resonnant pole filters */ + class_addmethod(c, (t_method)pdp_bq_lpf, gensym("lpf"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(c, (t_method)pdp_bq_hpf, gensym("hpf"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(c, (t_method)pdp_bq_apf, gensym("apf"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(c, (t_method)pdp_bq_bsf, gensym("bsf"), A_FLOAT, A_FLOAT, A_NULL); +} + +void pdp_bq_setup(void) +{ + + /* setup spatial processing class */ + + pdp_bq_class = class_new(gensym("pdp_bq"), (t_newmethod)pdp_bq_new, + (t_method)pdp_bq_free, sizeof(t_pdp_bq), 0, A_NULL); + + pdp_imagebase_setup(pdp_bq_class); + pdp_bq_coefmethods_setup(pdp_bq_class); + + class_addmethod(pdp_bq_class, (t_method)pdp_bq_passes, gensym("passes"), A_FLOAT, A_NULL); + class_addmethod(pdp_bq_class, (t_method)pdp_bq_hor, gensym("hor"), A_FLOAT, A_NULL); + class_addmethod(pdp_bq_class, (t_method)pdp_bq_ver, gensym("ver"), A_FLOAT, A_NULL); + class_addmethod(pdp_bq_class, (t_method)pdp_bq_tb, gensym("tb"), A_FLOAT, A_NULL); + class_addmethod(pdp_bq_class, (t_method)pdp_bq_bt, gensym("bt"), A_FLOAT, A_NULL); + class_addmethod(pdp_bq_class, (t_method)pdp_bq_lr, gensym("lr"), A_FLOAT, A_NULL); + class_addmethod(pdp_bq_class, (t_method)pdp_bq_rl, gensym("rl"), A_FLOAT, A_NULL); + + + + /* setup time processing class */ + pdp_bqt_class = class_new(gensym("pdp_bqt"), (t_newmethod)pdp_bqt_new, + (t_method)pdp_bq_free, sizeof(t_pdp_bq), 0, A_NULL); + + pdp_imagebase_setup(pdp_bqt_class); + pdp_bq_coefmethods_setup(pdp_bqt_class); + + + /* control */ + class_addmethod(pdp_bqt_class, (t_method)pdp_bqt_reset, gensym("reset"), A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_basic/pdp_cheby.c b/modules/image_basic/pdp_cheby.c new file mode 100644 index 0000000..1bd1a16 --- /dev/null +++ b/modules/image_basic/pdp_cheby.c @@ -0,0 +1,189 @@ +/* + * 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. + * + */ + + +#include <stdio.h> +#include <gsl/gsl_math.h> +#include <gsl/gsl_chebyshev.h> +#include "pdp.h" +#include "pdp_imagebase.h" + + +typedef struct pdp_cheby_struct +{ + t_pdp_imagebase x_base; + void *x_cheby; + int x_iterations; + int x_order; + gsl_cheb_series *x_cs; + gsl_function x_F; + float *x_vec; + int x_nbpoints; + +} t_pdp_cheby; + + + +static double pdp_cheby_mappingfunction(double f, void *params) +{ + t_pdp_cheby *x = (t_pdp_cheby *)params; + int index; + + /* if there's no array, return the identity function */ + if (!x->x_vec) return f; + + /* else interpolate the array */ + index = ((f + 1) * 0.5) * (x->x_nbpoints - 1); + return x->x_vec[index]; + +} + +static void pdp_cheby_coef(t_pdp_cheby *x, t_floatarg c, t_floatarg f) +{ + pdp_imageproc_cheby_setcoef(x->x_cheby, (int)c, f); +} + +static void pdp_cheby_approx(t_pdp_cheby *x, t_symbol *s) +{ + int i; + t_garray *a; + + /* check if array is valid */ + if (!(a = (t_garray *)pd_findbyclass(s, garray_class))){ + post("pdp_cheby: %s: no such array", s->s_name); + } + /* get data */ + else if (!garray_getfloatarray(a, &x->x_nbpoints, &x->x_vec)){ + post("pdp_cheby: %s: bad template", s->s_name); + + } + + else{ + + /* calculate approximation */ + gsl_cheb_init (x->x_cs, &x->x_F, -1.0, 1.0); + + /* propagate coefficients */ + for (i=0; i<=x->x_order; i++){ + pdp_cheby_coef(x, i, x->x_cs->c[i]); + } + } + + x->x_vec = 0; + return; + + +} + + +static void pdp_cheby_process(t_pdp_cheby *x) +{ + int p0 = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_cheby_setnbpasses(x->x_cheby, x->x_iterations); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_cheby_process, x->x_cheby, mask, p0); +} + + + +static void pdp_cheby_reset(t_pdp_cheby *x) +{ + int i; + for (i = 0; i <= x->x_order; i++) + pdp_imageproc_cheby_setcoef(x->x_cheby, i, 0); +} + + +static void pdp_cheby_iterations(t_pdp_cheby *x, t_floatarg f) +{ + int i = (int)f; + if (i<0) i = 0; + x->x_iterations = i; + +} +static void pdp_cheby_free(t_pdp_cheby *x) +{ + pdp_imagebase_free(x); + pdp_imageproc_cheby_delete(x->x_cheby); + gsl_cheb_free(x->x_cs); + +} + +t_class *pdp_cheby_class; + + + +void *pdp_cheby_new(t_floatarg f) +{ + t_pdp_cheby *x = (t_pdp_cheby *)pd_new(pdp_cheby_class); + int order = (int)(f); + + /* super init */ + pdp_imagebase_init(x); + + /* create i/o */ + pdp_base_add_gen_inlet(x, gensym("float"), gensym("iterations")); + pdp_base_add_pdp_outlet(x); + + /* setup callback */ + pdp_base_set_process_method(x, (t_pdp_method)pdp_cheby_process); + + /* data init */ + x->x_cheby = pdp_imageproc_cheby_new(order); + x->x_iterations = 1; + + if (order < 2) order = 2; + x->x_order = order; + + /* init gls chebychev series object */ + x->x_cs = gsl_cheb_alloc(order); + x->x_F.function = pdp_cheby_mappingfunction; + x->x_F.params = x; + x->x_vec = 0; + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_cheby_setup(void) +{ + + + pdp_cheby_class = class_new(gensym("pdp_cheby"), (t_newmethod)pdp_cheby_new, + (t_method)pdp_cheby_free, sizeof(t_pdp_cheby), 0, A_DEFFLOAT, A_NULL); + + pdp_imagebase_setup(pdp_cheby_class); + + class_addmethod(pdp_cheby_class, (t_method)pdp_cheby_coef, gensym("coef"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_cheby_class, (t_method)pdp_cheby_iterations, gensym("iterations"), A_FLOAT, A_NULL); + class_addmethod(pdp_cheby_class, (t_method)pdp_cheby_reset, gensym("reset"), A_NULL); + class_addmethod(pdp_cheby_class, (t_method)pdp_cheby_approx, gensym("approx"), A_SYMBOL, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_basic/pdp_constant.c b/modules/image_basic/pdp_constant.c new file mode 100644 index 0000000..e5b088e --- /dev/null +++ b/modules/image_basic/pdp_constant.c @@ -0,0 +1,162 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_imagebase.h" + + +typedef struct pdp_constant_struct +{ + t_pdp_imagebase x_base; + + t_outlet *x_outlet0; + + int x_packet0; + + t_symbol *x_type; + + unsigned int x_width; + unsigned int x_height; + + void *x_constant; + +} t_pdp_constant; + + + +void pdp_constant_type(t_pdp_constant *x, t_symbol *s) +{ + x->x_type = s; +} + + +void pdp_constant_value(t_pdp_constant *x, t_floatarg f) +{ + if (f>1.0f) f = 1.0f; + if (f<-1.0f) f = -1.0f; + + x->x_constant = (void *)((s32)(0x7fff * f)); +} + + + +static void pdp_constant_process(t_pdp_constant *x) +{ + /* get channel mask */ + u32 mask = pdp_imagebase_get_chanmask(x); + + /* create new packet */ + if (x->x_type == gensym("yv12")){x->x_packet0 = pdp_packet_new_image_YCrCb(x->x_width, x->x_height);} + else if (x->x_type == gensym("grey")){x->x_packet0 = pdp_packet_new_image_grey(x->x_width, x->x_height);} + else return; + + /* this processes the packets using a pdp image processor */ + pdp_imageproc_dispatch_1buf(&pdp_imageproc_constant_process, x->x_constant, mask, x->x_packet0); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_constant_process, 0, ~mask, x->x_packet0); + + return; +} + +static void pdp_constant_postproc(t_pdp_constant *x) +{ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet0); +} + +static void pdp_constant_bang(t_pdp_constant *x) +{ + pdp_base_bang(x); +} + +static void pdp_constant_dim(t_pdp_constant *x, t_floatarg w, t_floatarg h) +{ + x->x_width = pdp_imageproc_legalwidth((int)w); + x->x_height = pdp_imageproc_legalheight((int)h); + //post("dims %d %d", x->x_width, x->x_height); +} + + +static void pdp_constant_free(t_pdp_constant *x) +{ + pdp_imagebase_free(x); + pdp_packet_mark_unused(x->x_packet0); + +} + +t_class *pdp_constant_class; + + + +void *pdp_constant_new(void) +{ + int i; + t_pdp_constant *x = (t_pdp_constant *)pd_new(pdp_constant_class); + + /* super init */ + pdp_imagebase_init(x); + + /* in/out*/ + pdp_base_add_gen_inlet(x, gensym("float"), gensym("value")); + x->x_outlet0 = pdp_base_add_pdp_outlet(x); + + /* base callbacks */ + pdp_base_disable_active_inlet(x); + pdp_base_set_process_method(x, (t_pdp_method)pdp_constant_process); + pdp_base_set_postproc_method(x, (t_pdp_method)pdp_constant_postproc); + + /* data init */ + x->x_packet0 = -1; + pdp_constant_dim(x, 320, 240); + pdp_constant_value(x, 0.0f); + pdp_constant_type(x, gensym("yv12")); + + + return (void *)x; +} + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +void pdp_constant_setup(void) +{ + + + pdp_constant_class = class_new(gensym("pdp_constant"), (t_newmethod)pdp_constant_new, + (t_method)pdp_constant_free, sizeof(t_pdp_constant), 0, A_NULL); + + pdp_imagebase_setup(pdp_constant_class); + + class_addmethod(pdp_constant_class, (t_method)pdp_constant_value, gensym("value"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_constant_class, (t_method)pdp_constant_type, gensym("type"), A_SYMBOL, A_NULL); + class_addmethod(pdp_constant_class, (t_method)pdp_constant_dim, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_constant_class, (t_method)pdp_constant_bang, gensym("bang"), A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_basic/pdp_conv.c b/modules/image_basic/pdp_conv.c new file mode 100644 index 0000000..dfce381 --- /dev/null +++ b/modules/image_basic/pdp_conv.c @@ -0,0 +1,212 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_imagebase.h" + + +typedef struct pdp_conv_struct +{ + t_pdp_imagebase x_base; + + + unsigned int x_nbpasses; + bool x_horizontal; + bool x_vertical; + + void *x_convolver_hor; + void *x_convolver_ver; + +} t_pdp_conv; + + + +static void pdp_conv_process(t_pdp_conv *x) +{ + int p = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + + if (x->x_vertical){ + pdp_imageproc_conv_setnbpasses(x->x_convolver_ver, x->x_nbpasses); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_conv_process, x->x_convolver_ver, mask, p); + } + + if (x->x_horizontal){ + pdp_imageproc_conv_setnbpasses(x->x_convolver_hor, x->x_nbpasses); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_conv_process, x->x_convolver_hor, mask, p); + } + +} + + + +static void pdp_conv_passes(t_pdp_conv *x, t_floatarg f) +{ + int passes = (int)f; + passes = passes < 0 ? 0 : passes; + x->x_nbpasses = passes; + +} +static void pdp_conv_hor(t_pdp_conv *x, t_floatarg f) +{ + int hor = (int)f; + x->x_horizontal = (hor != 0); + +} +static void pdp_conv_ver(t_pdp_conv *x, t_floatarg f) +{ + int ver = (int)f; + x->x_vertical = (ver != 0); +} +static void pdp_conv_free(t_pdp_conv *x) +{ + pdp_imagebase_free(x); + pdp_imageproc_conv_delete(x->x_convolver_hor); + pdp_imageproc_conv_delete(x->x_convolver_ver); +} + +/* setup hmask */ + +static void pdp_conv_hleft(t_pdp_conv *x, t_floatarg f) +{ + pdp_imageproc_conv_setmin1(x->x_convolver_hor, f); + +} +static void pdp_conv_hmiddle(t_pdp_conv *x, t_floatarg f) +{ + pdp_imageproc_conv_setzero(x->x_convolver_hor, f); +} +static void pdp_conv_hright(t_pdp_conv *x, t_floatarg f) +{ + pdp_imageproc_conv_setplus1(x->x_convolver_hor, f); +} + +static void pdp_conv_hmask(t_pdp_conv *x, t_floatarg l, t_floatarg m, t_floatarg r) +{ + pdp_conv_hleft(x, l); + pdp_conv_hmiddle(x, m); + pdp_conv_hright(x, r); +} + +static void pdp_conv_vtop(t_pdp_conv *x, t_floatarg f) +{ + pdp_imageproc_conv_setmin1(x->x_convolver_ver, f); +} +static void pdp_conv_vmiddle(t_pdp_conv *x, t_floatarg f) +{ + pdp_imageproc_conv_setzero(x->x_convolver_ver, f); + +} +static void pdp_conv_vbottom(t_pdp_conv *x, t_floatarg f) +{ + pdp_imageproc_conv_setplus1(x->x_convolver_ver, f); +} + +static void pdp_conv_vmask(t_pdp_conv *x, t_floatarg l, t_floatarg m, t_floatarg r) +{ + pdp_conv_vtop(x, l); + pdp_conv_vmiddle(x, m); + pdp_conv_vbottom(x, r); +} + + +static void pdp_conv_mask(t_pdp_conv *x, t_floatarg l, t_floatarg m, t_floatarg r) +{ + pdp_conv_hmask(x, l, m, r); + pdp_conv_vmask(x, l, m, r); +} + +t_class *pdp_conv_class; + + + +void *pdp_conv_new(void) +{ + t_pdp_conv *x = (t_pdp_conv *)pd_new(pdp_conv_class); + + /* super init */ + pdp_imagebase_init(x); + + /* inlets & outlets */ + pdp_base_add_gen_inlet(x, gensym("float"), gensym("passes")); + pdp_base_add_pdp_outlet(x); + + /* register */ + pdp_base_set_process_method(x, (t_pdp_method)pdp_conv_process); + + x->x_nbpasses = 1; + x->x_horizontal = true; + x->x_vertical = true; + + x->x_convolver_hor = pdp_imageproc_conv_new(); + x->x_convolver_ver = pdp_imageproc_conv_new(); + + pdp_imageproc_conv_setbordercolor(x->x_convolver_hor, 0); + pdp_imageproc_conv_setbordercolor(x->x_convolver_ver, 0); + + pdp_imageproc_conv_setorientation(x->x_convolver_hor, PDP_IMAGEPROC_CONV_HORIZONTAL); + pdp_imageproc_conv_setorientation(x->x_convolver_ver, PDP_IMAGEPROC_CONV_VERTICAL); + + pdp_conv_mask(x, .25,.5,.25); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_conv_setup(void) +{ + + + pdp_conv_class = class_new(gensym("pdp_conv"), (t_newmethod)pdp_conv_new, + (t_method)pdp_conv_free, sizeof(t_pdp_conv), 0, A_NULL); + + pdp_imagebase_setup(pdp_conv_class); + + class_addmethod(pdp_conv_class, (t_method)pdp_conv_passes, gensym("passes"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_conv_class, (t_method)pdp_conv_hor, gensym("hor"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_conv_class, (t_method)pdp_conv_ver, gensym("ver"), A_DEFFLOAT, A_NULL); + + class_addmethod(pdp_conv_class, (t_method)pdp_conv_hleft, gensym("hleft"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_conv_class, (t_method)pdp_conv_hmiddle, gensym("hmiddle"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_conv_class, (t_method)pdp_conv_hright, gensym("hright"), A_DEFFLOAT, A_NULL); + + class_addmethod(pdp_conv_class, (t_method)pdp_conv_vtop, gensym("vtop"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_conv_class, (t_method)pdp_conv_vmiddle, gensym("vmiddle"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_conv_class, (t_method)pdp_conv_vbottom, gensym("vbottom"), A_DEFFLOAT, A_NULL); + + class_addmethod(pdp_conv_class, (t_method)pdp_conv_vmask, gensym("vmask"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_conv_class, (t_method)pdp_conv_hmask, gensym("hmask"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_conv_class, (t_method)pdp_conv_mask, gensym("mask"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_basic/pdp_gain.c b/modules/image_basic/pdp_gain.c new file mode 100644 index 0000000..c36f758 --- /dev/null +++ b/modules/image_basic/pdp_gain.c @@ -0,0 +1,111 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_imagebase.h" + + +typedef struct pdp_gain_struct +{ + t_pdp_imagebase x_base; + void *x_gain; + +} t_pdp_gain; + + + + +static void pdp_gain_process(t_pdp_gain *x) +{ + int p = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + + pdp_packet_image_set_chanmask(p, mask); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_gain_process, x->x_gain, 0, p); + +} + + +static void pdp_gain_gain(t_pdp_gain *x, t_floatarg f) +{ + pdp_imageproc_gain_setgain(x->x_gain, f); +} + + + +t_class *pdp_gain_class; + + + +void pdp_gain_free(t_pdp_gain *x) +{ + pdp_imagebase_free(x); + pdp_imageproc_gain_delete(x->x_gain); +} + +void *pdp_gain_new(t_floatarg f) +{ + t_pdp_gain *x = (t_pdp_gain *)pd_new(pdp_gain_class); + + /* super init */ + pdp_imagebase_init(x); + + /* no arg, or zero -> gain = 1 */ + if (f==0.0f) f = 1.0f; + + + /* io */ + pdp_base_add_gen_inlet(x, gensym("float"), gensym("gain")); + pdp_base_add_pdp_outlet(x); + + /* callbacks */ + pdp_base_set_process_method(x, (t_pdp_method)pdp_gain_process); + + x->x_gain = pdp_imageproc_gain_new(); + pdp_gain_gain(x, f); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_gain_setup(void) +{ + + + pdp_gain_class = class_new(gensym("pdp_gain"), (t_newmethod)pdp_gain_new, + (t_method)pdp_gain_free, sizeof(t_pdp_gain), 0, A_DEFFLOAT, A_NULL); + + pdp_imagebase_setup(pdp_gain_class); + + class_addmethod(pdp_gain_class, (t_method)pdp_gain_gain, gensym("gain"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_basic/pdp_logic.c b/modules/image_basic/pdp_logic.c new file mode 100644 index 0000000..002557c --- /dev/null +++ b/modules/image_basic/pdp_logic.c @@ -0,0 +1,252 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_imagebase.h" + +typedef struct pdp_logic_struct +{ + t_pdp_imagebase x_base; + + void *x_mask; + +} t_pdp_logic; + + +static void pdp_logic_process_and(t_pdp_logic *x) +{ + int p0 = pdp_base_get_packet(x, 0); + int p1 = pdp_base_get_packet(x, 1); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_2buf(&pdp_imageproc_and_process, 0, mask, p0, p1); +} + +static void pdp_logic_process_or(t_pdp_logic *x) +{ + int p0 = pdp_base_get_packet(x, 0); + int p1 = pdp_base_get_packet(x, 1); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_2buf(&pdp_imageproc_or_process, 0, mask, p0, p1); +} + +static void pdp_logic_process_xor(t_pdp_logic *x) +{ + int p0 = pdp_base_get_packet(x, 0); + int p1 = pdp_base_get_packet(x, 1); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_2buf(&pdp_imageproc_xor_process, 0, mask, p0, p1); +} + +static void pdp_logic_process_not(t_pdp_logic *x) +{ + int p0 = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_not_process, 0, mask, p0); +} + +static void pdp_logic_process_mask(t_pdp_logic *x) +{ + int p0 = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_mask_process, x->x_mask, mask, p0); +} + +static void pdp_logic_process_softthresh(t_pdp_logic *x) +{ + int p0 = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_softthresh_process, x->x_mask, mask, p0); +} + + +static void pdp_logic_process_hardthresh(t_pdp_logic *x) +{ + int p0 = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_hardthresh_process, x->x_mask, mask, p0); +} + + +static void pdp_logic_set_mask(t_pdp_logic *x, t_floatarg f) +{ + /* using a pointer as a variable hmm? */ + u32 mask = ((u32)f) & 0xffff; + x->x_mask = ((void * )mask); +} + +static void pdp_logic_set_threshold(t_pdp_logic *x, t_floatarg f) +{ + /* using a pointer as a variable hmm? */ + if (f<0.0f) f = 0.0f; + if (f>1.0f) f = 1.0f; + x->x_mask = (void *)((u32)(((float)0x7fff) * f)); +} + +static void pdp_logic_set_depth(t_pdp_logic *x, t_floatarg f) +{ + u32 mask; + int shift = (16 - ((int)f)); + if (shift < 0) shift = 0; + if (shift > 16) shift = 16; + mask = ((0xffff)<<shift) & 0xffff; + x->x_mask = (void *)mask; + +} + + +static void pdp_logic_free(t_pdp_logic *x) +{ + /* remove process method from queue before deleting data */ + pdp_imagebase_free(x); +} + +t_class *pdp_logic_class; + + +/* common new method */ +void *pdp_logic_new(void) +{ + t_pdp_logic *x = (t_pdp_logic *)pd_new(pdp_logic_class); + + /* super init */ + pdp_imagebase_init(x); + + /* outlet */ + pdp_base_add_pdp_outlet(x); + x->x_mask = 0; + + return (void *)x; +} + +void *pdp_logic_new_and(void) +{ + t_pdp_logic *x = pdp_logic_new(); + /* init in/out */ + pdp_base_add_pdp_inlet(x); + pdp_base_set_process_method(x, (t_pdp_method)pdp_logic_process_and); + + return (void *)x; +} + +void *pdp_logic_new_or(void) +{ + t_pdp_logic *x = pdp_logic_new(); + /* init in/out */ + pdp_base_add_pdp_inlet(x); + pdp_base_set_process_method(x, (t_pdp_method)pdp_logic_process_or); + return (void *)x; +} + +void *pdp_logic_new_xor(void) +{ + t_pdp_logic *x = pdp_logic_new(); + /* init in/out */ + pdp_base_add_pdp_inlet(x); + pdp_base_set_process_method(x, (t_pdp_method)pdp_logic_process_xor); + return (void *)x; +} + +void *pdp_logic_new_not(void) +{ + t_pdp_logic *x = pdp_logic_new(); + /* init in/out */ + pdp_base_set_process_method(x, (t_pdp_method)pdp_logic_process_not); + return (void *)x; +} + +void *pdp_logic_new_mask(void) +{ + t_pdp_logic *x = pdp_logic_new(); + /* init in/out */ + pdp_base_add_gen_inlet(x, gensym("float"), gensym("mask")); + pdp_base_set_process_method(x, (t_pdp_method)pdp_logic_process_mask); + + x->x_mask = (void *)0xffff; + return (void *)x; +} + +void *pdp_logic_new_depth(void) +{ + t_pdp_logic *x = pdp_logic_new(); + /* init in/out */ + pdp_base_add_gen_inlet(x, gensym("float"), gensym("depth")); + pdp_base_set_process_method(x, (t_pdp_method)pdp_logic_process_mask); + + x->x_mask = (void *)0xffff; + return (void *)x; +} + +void *pdp_logic_new_softthresh(t_floatarg f) +{ + t_pdp_logic *x = pdp_logic_new(); + /* init in/out */ + pdp_base_add_gen_inlet(x, gensym("float"), gensym("threshold")); + pdp_base_set_process_method(x, (t_pdp_method)pdp_logic_process_softthresh); + pdp_logic_set_threshold(x,f); + + return (void *)x; +} + + +void *pdp_logic_new_hardthresh(t_floatarg f) +{ + t_pdp_logic *x = pdp_logic_new(); + /* init in/out */ + pdp_base_add_gen_inlet(x, gensym("float"), gensym("threshold")); + pdp_base_set_process_method(x, (t_pdp_method)pdp_logic_process_hardthresh); + pdp_logic_set_threshold(x,f); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_logic_setup(void) +{ + + + pdp_logic_class = class_new(gensym("pdp_and"), (t_newmethod)pdp_logic_new_and, + (t_method)pdp_logic_free, sizeof(t_pdp_logic), 0, A_DEFFLOAT, A_NULL); + + pdp_imagebase_setup(pdp_logic_class); + + class_addcreator((t_newmethod)pdp_logic_new_or, gensym("pdp_or"), A_NULL); + class_addcreator((t_newmethod)pdp_logic_new_xor, gensym("pdp_xor"), A_NULL); + class_addcreator((t_newmethod)pdp_logic_new_not, gensym("pdp_not"), A_NULL); + class_addcreator((t_newmethod)pdp_logic_new_mask, gensym("pdp_bitmask"), A_NULL); + class_addcreator((t_newmethod)pdp_logic_new_depth, gensym("pdp_bitdepth"), A_NULL); + class_addcreator((t_newmethod)pdp_logic_new_softthresh, gensym("pdp_sthresh"), A_NULL); + class_addcreator((t_newmethod)pdp_logic_new_hardthresh, gensym("pdp_hthresh"), A_NULL); + + class_addmethod(pdp_logic_class, (t_method)pdp_logic_set_mask, gensym("mask"), A_FLOAT, A_NULL); + class_addmethod(pdp_logic_class, (t_method)pdp_logic_set_depth, gensym("depth"), A_FLOAT, A_NULL); + class_addmethod(pdp_logic_class, (t_method)pdp_logic_set_threshold, gensym("threshold"), A_FLOAT, A_NULL); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_basic/pdp_mix.c b/modules/image_basic/pdp_mix.c new file mode 100644 index 0000000..2d01d38 --- /dev/null +++ b/modules/image_basic/pdp_mix.c @@ -0,0 +1,165 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_imagebase.h" + +typedef struct pdp_mix_struct +{ + t_pdp_imagebase x_base; + + t_outlet *x_outlet0; + t_outlet *x_outlet1; + + void *x_mixer; + + int x_extrapolate; + +} t_pdp_mix; + + +static void pdp_mix_process(t_pdp_mix *x) +{ + int p0 = pdp_base_get_packet(x, 0); + int p1 = pdp_base_get_packet(x, 1); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_2buf(&pdp_imageproc_mix_process, x->x_mixer, mask, p0, p1); +} + + +static void pdp_mix_mix(t_pdp_mix *x, t_floatarg f) +{ + float f2; + if (!x->x_extrapolate){ + if (f < 0.0f) f = 0.0f; + if (f > 1.0f) f = 1.0f; + } + + f2 = (1.0f - f); + pdp_imageproc_mix_setleftgain(x->x_mixer, f2); + pdp_imageproc_mix_setrightgain(x->x_mixer, f); + +} + +static void pdp_mix_mix1(t_pdp_mix *x, t_floatarg f) +{ + pdp_imageproc_mix_setleftgain(x->x_mixer, f); + +} +static void pdp_mix_mix2(t_pdp_mix *x, t_floatarg f2) +{ + pdp_imageproc_mix_setrightgain(x->x_mixer, f2); +} + +static void pdp_mix_extrapolate(t_pdp_mix *x, t_floatarg f) +{ + if (f == 0.0f) x->x_extrapolate = 0; + if (f == 1.0f) x->x_extrapolate = 1; +} + + +static void pdp_mix_free(t_pdp_mix *x) +{ + pdp_imagebase_free(x); + pdp_imageproc_mix_delete(x->x_mixer); +} + +t_class *pdp_mix_class; +t_class *pdp_mix2_class; + + +void *pdp_mix_common_init(t_pdp_mix *x) +{ + int i; + + pdp_imagebase_init(x); + pdp_base_add_pdp_inlet(x); + pdp_base_add_pdp_outlet(x); + pdp_base_set_process_method(x, (t_pdp_method)pdp_mix_process); + + x->x_extrapolate = 0; + x->x_mixer = pdp_imageproc_mix_new(); + pdp_mix_mix(x, 0.0f); + + return (void *)x; +} + + +void *pdp_mix_new(t_floatarg mix) +{ + t_pdp_mix *x = (t_pdp_mix *)pd_new(pdp_mix_class); + pdp_mix_common_init(x); + pdp_base_add_gen_inlet(x, gensym("float"), gensym("mix")); + + if (mix == 0.0f) mix = 0.5f; + pdp_mix_mix(x, mix); + return (void *)x; +} + +void *pdp_mix2_new(t_floatarg mix1, t_floatarg mix2) +{ + t_pdp_mix *x = (t_pdp_mix *)pd_new(pdp_mix2_class); + pdp_mix_common_init(x); + + pdp_base_add_gen_inlet(x, gensym("float"), gensym("mix1")); + pdp_base_add_gen_inlet(x, gensym("float"), gensym("mix2")); + + if ((mix1 == 0.0f) && (mix2 == 0.0f)) mix1 = mix2 = 0.5f; + pdp_mix_mix1(x, mix1); + pdp_mix_mix2(x, mix2); + return (void *)x; +} + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +void pdp_mix_setup(void) +{ + + + pdp_mix_class = class_new(gensym("pdp_mix"), (t_newmethod)pdp_mix_new, + (t_method)pdp_mix_free, sizeof(t_pdp_mix), 0, A_DEFFLOAT, A_NULL); + + pdp_imagebase_setup(pdp_mix_class); + class_addmethod(pdp_mix_class, (t_method)pdp_mix_mix, gensym("mix"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_mix_class, (t_method)pdp_mix_extrapolate, gensym("extrapolate"), A_DEFFLOAT, A_NULL); + + + + + pdp_mix2_class = class_new(gensym("pdp_mix2"), (t_newmethod)pdp_mix2_new, + (t_method)pdp_mix_free, sizeof(t_pdp_mix), 0, A_DEFFLOAT, A_DEFFLOAT, A_NULL); + + pdp_imagebase_setup(pdp_mix2_class); + class_addmethod(pdp_mix2_class, (t_method)pdp_mix_mix1, gensym("mix1"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_mix2_class, (t_method)pdp_mix_mix2, gensym("mix2"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_mix2_class, (t_method)pdp_mix_extrapolate, gensym("extrapolate"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_basic/pdp_mul.c b/modules/image_basic/pdp_mul.c new file mode 100644 index 0000000..c7321d7 --- /dev/null +++ b/modules/image_basic/pdp_mul.c @@ -0,0 +1,85 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_imagebase.h" + +typedef struct pdp_mul_struct +{ + t_pdp_imagebase x_base; + +} t_pdp_mul; + + + +static void pdp_mul_process(t_pdp_mul *x) +{ + int p0 = pdp_base_get_packet(x, 0); + int p1 = pdp_base_get_packet(x, 1); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_2buf(&pdp_imageproc_mul_process, 0, mask, p0, p1); +} + + + +static void pdp_mul_free(t_pdp_mul *x) +{ + pdp_imagebase_free(x); +} + +t_class *pdp_mul_class; + + + +void *pdp_mul_new(void) +{ + t_pdp_mul *x = (t_pdp_mul *)pd_new(pdp_mul_class); + + /* super init */ + pdp_imagebase_init(x); + pdp_base_add_pdp_inlet(x); + pdp_base_add_pdp_outlet(x); + pdp_base_set_process_method(x, (t_pdp_method)pdp_mul_process); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_mul_setup(void) +{ + + + pdp_mul_class = class_new(gensym("pdp_mul"), (t_newmethod)pdp_mul_new, + (t_method)pdp_mul_free, sizeof(t_pdp_mul), 0, A_NULL); + + pdp_imagebase_setup(pdp_mul_class); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_basic/pdp_noise.c b/modules/image_basic/pdp_noise.c new file mode 100644 index 0000000..cb8f772 --- /dev/null +++ b/modules/image_basic/pdp_noise.c @@ -0,0 +1,160 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_imagebase.h" + + +typedef struct pdp_noise_struct +{ + t_pdp_imagebase x_base; + + int x_packet0; + t_outlet *x_outlet0; + void *x_noisegen; + + t_symbol *x_type; + + unsigned int x_width; + unsigned int x_height; + +} t_pdp_noise; + + + +void pdp_noise_type(t_pdp_noise *x, t_symbol *s) +{ + x->x_type = s; +} + + +void pdp_noise_random(t_pdp_noise *x, t_floatarg seed) +{ + if (seed == 0.0f) seed = (float)random(); + pdp_imageproc_random_setseed(x->x_noisegen, seed); + +} + +/* called inside pdp thread */ +static void pdp_noise_process(t_pdp_noise *x) +{ + /* seed the 16 bit rng with a new random number from the clib */ + pdp_noise_random(x, 0.0f); + + /* create new packet */ + if (x->x_type == gensym("grey")) { + x->x_packet0 = pdp_packet_new_image_grey(x->x_width, x->x_height); + } + else if (x->x_type == gensym("yv12")) { + x->x_packet0 = pdp_packet_new_image_YCrCb(x->x_width, x->x_height); + } + else return; + + /* call the image processor */ + pdp_imageproc_dispatch_1buf(&pdp_imageproc_random_process, x->x_noisegen, + -1, x->x_packet0); +} + +/* called inside pd thread: involves an outlet */ +static void pdp_noise_postproc(t_pdp_noise *x) +{ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet0); +} + +static void pdp_noise_bang(t_pdp_noise *x) +{ + pdp_base_bang(x); +} + +static void pdp_noise_dim(t_pdp_noise *x, t_floatarg w, t_floatarg h) +{ + x->x_width = pdp_imageproc_legalwidth((int)w); + x->x_height = pdp_imageproc_legalheight((int)h); + //post("dims %d %d", x->x_width, x->x_height); +} + + +static void pdp_noise_free(t_pdp_noise *x) +{ + pdp_imagebase_free(x); + + /* tidy up */ + pdp_packet_mark_unused(x->x_packet0); + pdp_imageproc_random_delete(x->x_noisegen); +} + +t_class *pdp_noise_class; + + +void *pdp_noise_new(void) +{ + int i; + + t_pdp_noise *x = (t_pdp_noise *)pd_new(pdp_noise_class); + + pdp_imagebase_init(x); + pdp_base_disable_active_inlet(x); + pdp_base_set_process_method(x, (t_pdp_method)pdp_noise_process); + pdp_base_set_postproc_method(x, (t_pdp_method)pdp_noise_postproc); + + x->x_outlet0 = pdp_base_add_pdp_outlet(x); + x->x_packet0 = -1; + + x->x_width = 320; + x->x_height = 240; + + x->x_noisegen = pdp_imageproc_random_new(); + + pdp_noise_random(x, 0.0f); + pdp_noise_type(x, gensym("yv12")); + + return (void *)x; +} + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +void pdp_noise_setup(void) +{ + + + pdp_noise_class = class_new(gensym("pdp_noise"), (t_newmethod)pdp_noise_new, + (t_method)pdp_noise_free, sizeof(t_pdp_noise), 0, A_NULL); + + pdp_imagebase_setup(pdp_noise_class); + + class_addmethod(pdp_noise_class, (t_method)pdp_noise_random, gensym("seed"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_noise_class, (t_method)pdp_noise_type, gensym("type"), A_SYMBOL, A_NULL); + class_addmethod(pdp_noise_class, (t_method)pdp_noise_dim, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_noise_class, (t_method)pdp_noise_bang, gensym("bang"), A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_basic/pdp_plasma.c b/modules/image_basic/pdp_plasma.c new file mode 100644 index 0000000..78d886c --- /dev/null +++ b/modules/image_basic/pdp_plasma.c @@ -0,0 +1,166 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_imagebase.h" + + +typedef struct pdp_plasma_struct +{ + t_pdp_imagebase x_base; + + int x_packet0; + t_outlet *x_outlet0; + void *x_plasmagen; + + t_symbol *x_type; + + unsigned int x_width; + unsigned int x_height; + +} t_pdp_plasma; + + + +void pdp_plasma_type(t_pdp_plasma *x, t_symbol *s) +{ + x->x_type = s; +} + + +void pdp_plasma_random(t_pdp_plasma *x, t_floatarg seed) +{ + if (seed == 0.0f) seed = (float)random(); + pdp_imageproc_plasma_setseed(x->x_plasmagen, seed); + +} + +void pdp_plasma_turbulence(t_pdp_plasma *x, t_floatarg f) +{ + pdp_imageproc_plasma_setturbulence(x->x_plasmagen, f); + +} + +/* called inside pdp thread */ +static void pdp_plasma_process(t_pdp_plasma *x) +{ + /* seed the 16 bit rng with a new random number from the clib */ + pdp_plasma_random(x, 0.0f); + + /* create new packet */ + if (x->x_type == gensym("grey")) {x->x_packet0 = pdp_packet_new_image_grey(x->x_width, x->x_height);} + else if (x->x_type == gensym("yv12")) {x->x_packet0 = pdp_packet_new_image_YCrCb(x->x_width, x->x_height);} + else return; + + /* call the image processor */ + pdp_imageproc_dispatch_1buf(&pdp_imageproc_plasma_process, x->x_plasmagen, + -1, x->x_packet0); +} + +/* called inside pd thread: involves an outlet */ +static void pdp_plasma_postproc(t_pdp_plasma *x) +{ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet0); +} + +static void pdp_plasma_bang(t_pdp_plasma *x) +{ + pdp_base_bang(x); +} + +static void pdp_plasma_dim(t_pdp_plasma *x, t_floatarg w, t_floatarg h) +{ + x->x_width = pdp_imageproc_legalwidth((int)w); + x->x_height = pdp_imageproc_legalheight((int)h); + //post("dims %d %d", x->x_width, x->x_height); +} + + +static void pdp_plasma_free(t_pdp_plasma *x) +{ + pdp_imagebase_free(x); + + /* tidy up */ + pdp_packet_mark_unused(x->x_packet0); + pdp_imageproc_plasma_delete(x->x_plasmagen); +} + +t_class *pdp_plasma_class; + + +void *pdp_plasma_new(void) +{ + int i; + + t_pdp_plasma *x = (t_pdp_plasma *)pd_new(pdp_plasma_class); + + pdp_imagebase_init(x); + pdp_base_disable_active_inlet(x); + pdp_base_set_process_method(x, (t_pdp_method)pdp_plasma_process); + pdp_base_set_postproc_method(x, (t_pdp_method)pdp_plasma_postproc); + + pdp_base_add_gen_inlet(x, gensym("float"), gensym("turbulence")); + x->x_outlet0 = pdp_base_add_pdp_outlet(x); + x->x_packet0 = -1; + + x->x_width = 320; + x->x_height = 240; + + x->x_plasmagen = pdp_imageproc_plasma_new(); + + pdp_plasma_random(x, 0.0f); + pdp_plasma_type(x, gensym("yv12")); + pdp_plasma_turbulence(x, 0.1); + + + return (void *)x; +} + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +void pdp_plasma_setup(void) +{ + + + pdp_plasma_class = class_new(gensym("pdp_plasma"), (t_newmethod)pdp_plasma_new, + (t_method)pdp_plasma_free, sizeof(t_pdp_plasma), 0, A_NULL); + + pdp_imagebase_setup(pdp_plasma_class); + + class_addmethod(pdp_plasma_class, (t_method)pdp_plasma_random, gensym("seed"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_plasma_class, (t_method)pdp_plasma_turbulence, gensym("turbulence"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_plasma_class, (t_method)pdp_plasma_type, gensym("type"), A_SYMBOL, A_NULL); + class_addmethod(pdp_plasma_class, (t_method)pdp_plasma_dim, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_plasma_class, (t_method)pdp_plasma_bang, gensym("bang"), A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_basic/pdp_randmix.c b/modules/image_basic/pdp_randmix.c new file mode 100644 index 0000000..2fd6adf --- /dev/null +++ b/modules/image_basic/pdp_randmix.c @@ -0,0 +1,113 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_imagebase.h" + +typedef struct pdp_randmix_struct +{ + t_pdp_imagebase x_base; + void *x_randmixer; + +} t_pdp_randmix; + + +void pdp_randmix_random(t_pdp_randmix *x, t_floatarg seed) +{ + pdp_imageproc_randmix_setseed(x->x_randmixer, seed); +} + + +static void pdp_randmix_process(t_pdp_randmix *x) +{ + int p0 = pdp_base_get_packet(x, 0); + int p1 = pdp_base_get_packet(x, 1); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_2buf(&pdp_imageproc_randmix_process, x->x_randmixer, mask, p0, p1); + +} + + +static void pdp_randmix_threshold(t_pdp_randmix *x, t_floatarg f) +{ + pdp_imageproc_randmix_setthreshold(x->x_randmixer, f); + +} + + + +static void pdp_randmix_free(t_pdp_randmix *x) +{ + pdp_imagebase_free(x); + pdp_imageproc_randmix_delete(x->x_randmixer); +} + +t_class *pdp_randmix_class; + + +void *pdp_randmix_new(void) +{ + int i; + + t_pdp_randmix *x = (t_pdp_randmix *)pd_new(pdp_randmix_class); + + pdp_imagebase_init(x); + pdp_base_add_pdp_inlet(x); + pdp_base_add_gen_inlet(x, gensym("float"), gensym("threshold")); + + pdp_base_set_process_method(x, (t_pdp_method)pdp_randmix_process); + + pdp_base_add_pdp_outlet(x); + x->x_randmixer = pdp_imageproc_randmix_new(); + + pdp_randmix_threshold(x, 0.5f); + pdp_randmix_random(x, 0.0f); + + return (void *)x; +} + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +void pdp_randmix_setup(void) +{ + + + pdp_randmix_class = class_new(gensym("pdp_randmix"), (t_newmethod)pdp_randmix_new, + (t_method)pdp_randmix_free, sizeof(t_pdp_randmix), 0, A_NULL); + + pdp_imagebase_setup(pdp_randmix_class); + + class_addmethod(pdp_randmix_class, (t_method)pdp_randmix_threshold, gensym("threshold"), A_DEFFLOAT, A_NULL); + class_addmethod(pdp_randmix_class, (t_method)pdp_randmix_random, gensym("seed"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_basic/pdp_stateless.c b/modules/image_basic/pdp_stateless.c new file mode 100644 index 0000000..cdcf313 --- /dev/null +++ b/modules/image_basic/pdp_stateless.c @@ -0,0 +1,185 @@ +/* + * Pure Data Packet module. Some stateless image operations. + * 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.h" +#include "pdp_imagebase.h" + +typedef struct pdp_stateless_struct +{ + t_pdp_imagebase x_base; + +} t_pdp_stateless; + + + +static void pdp_stateless_process_abs(t_pdp_stateless *x) +{ + int p0 = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_abs_process, 0, mask, p0); +} + +static void pdp_stateless_process_hardthresh(t_pdp_stateless *x) +{ + int p0 = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_hardthresh_process, 0, mask, p0); +} + +static void pdp_stateless_process_zthresh(t_pdp_stateless *x) +{ + int p0 = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_zthresh_process, 0, mask, p0); +} + +static void pdp_stateless_process_positive(t_pdp_stateless *x) +{ + int p0 = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_ispositive_process, 0, mask, p0); +} + +static void pdp_stateless_process_sign(t_pdp_stateless *x) +{ + int p0 = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_sign_process, 0, mask, p0); +} + +static void pdp_stateless_process_flip_tb(t_pdp_stateless *x) +{ + int p0 = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_flip_tb_process, 0, mask, p0); +} + +static void pdp_stateless_process_flip_lr(t_pdp_stateless *x) +{ + int p0 = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_1buf(&pdp_imageproc_flip_lr_process, 0, mask, p0); +} + +static void pdp_stateless_free(t_pdp_stateless *x) +{ + /* remove process method from queue before deleting data */ + pdp_imagebase_free(x); +} + +t_class *pdp_stateless_class; + + +/* common new method */ +void *pdp_stateless_new(void) +{ + t_pdp_stateless *x = (t_pdp_stateless *)pd_new(pdp_stateless_class); + + /* super init */ + pdp_imagebase_init(x); + + /* outlet */ + pdp_base_add_pdp_outlet(x); + + return (void *)x; +} + +void *pdp_stateless_new_abs(void) +{ + t_pdp_stateless *x = pdp_stateless_new(); + /* init in/out */ + pdp_base_set_process_method(x, (t_pdp_method)pdp_stateless_process_abs); + return (void *)x; +} + +void *pdp_stateless_new_zthresh(void) +{ + t_pdp_stateless *x = pdp_stateless_new(); + /* init in/out */ + pdp_base_set_process_method(x, (t_pdp_method)pdp_stateless_process_zthresh); + return (void *)x; +} + +void *pdp_stateless_new_positive(void) +{ + t_pdp_stateless *x = pdp_stateless_new(); + /* init in/out */ + pdp_base_set_process_method(x, (t_pdp_method)pdp_stateless_process_positive); + return (void *)x; +} + +void *pdp_stateless_new_sign(void) +{ + t_pdp_stateless *x = pdp_stateless_new(); + /* init in/out */ + pdp_base_set_process_method(x, (t_pdp_method)pdp_stateless_process_sign); + return (void *)x; +} + +void *pdp_stateless_new_flip_tb(void) +{ + t_pdp_stateless *x = pdp_stateless_new(); + /* init in/out */ + pdp_base_set_process_method(x, (t_pdp_method)pdp_stateless_process_flip_tb); + return (void *)x; +} + + +void *pdp_stateless_new_flip_lr(void) +{ + t_pdp_stateless *x = pdp_stateless_new(); + /* init in/out */ + pdp_base_set_process_method(x, (t_pdp_method)pdp_stateless_process_flip_lr); + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_stateless_setup(void) +{ + + + pdp_stateless_class = class_new(gensym("pdp_abs"), (t_newmethod)pdp_stateless_new_abs, + (t_method)pdp_stateless_free, sizeof(t_pdp_stateless), 0, A_NULL); + + pdp_imagebase_setup(pdp_stateless_class); + + class_addcreator((t_newmethod)pdp_stateless_new_zthresh, gensym("pdp_zthresh"), A_NULL); + class_addcreator((t_newmethod)pdp_stateless_new_positive, gensym("pdp_positive"), A_NULL); + class_addcreator((t_newmethod)pdp_stateless_new_sign, gensym("pdp_sign"), A_NULL); + class_addcreator((t_newmethod)pdp_stateless_new_flip_tb, gensym("pdp_flip_tb"), A_NULL); + class_addcreator((t_newmethod)pdp_stateless_new_flip_lr, gensym("pdp_flip_lr"), A_NULL); + + /* future extensions */ + //class_addcreator((t_newmethod)pdp_stateless_new_garble, gensym("pdp_garble"), A_NULL); + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_basic/pdp_zoom.c b/modules/image_basic/pdp_zoom.c new file mode 100644 index 0000000..48ba167 --- /dev/null +++ b/modules/image_basic/pdp_zoom.c @@ -0,0 +1,221 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_imagebase.h" + + + +typedef struct pdp_zoom_struct +{ + t_pdp_imagebase x_base; + + int x_packet1; + t_outlet *x_outlet0; + void *x_zoom; + + int x_quality; //not used + + +} t_pdp_zoom; + + +static void pdp_zoom_process(t_pdp_zoom *x) +{ + int p0 = pdp_base_get_packet(x, 0); + u32 mask = pdp_imagebase_get_chanmask(x); + pdp_imageproc_dispatch_2buf(&pdp_imageproc_resample_affinemap_process, x->x_zoom, mask, p0, x->x_packet1); +} + +static void pdp_zoom_postproc(t_pdp_zoom *x) +{ + /* delete source packet */ + pdp_packet_mark_unused(pdp_base_move_packet(x, 0)); + + /* unregister and propagate if valid dest packet */ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet1); +} + +static void pdp_zoom_preproc(t_pdp_zoom *x) +{ + int p = pdp_base_get_packet(x, 0); + t_pdp *header0 = pdp_packet_header(p); + if ((header0) && (PDP_IMAGE == header0->type)){ + x->x_packet1 = pdp_packet_clone_rw(p); + } + +} + + + +static void pdp_zoom_zoom_x(t_pdp_zoom *x, t_floatarg f) +{ + pdp_imageproc_resample_affinemap_setzoomx(x->x_zoom, f); +} + +static void pdp_zoom_angle(t_pdp_zoom *x, t_floatarg f) +{ + pdp_imageproc_resample_affinemap_setangle(x->x_zoom, f); +} + +static void pdp_zoom_zoom_y(t_pdp_zoom *x, t_floatarg f) +{ + pdp_imageproc_resample_affinemap_setzoomy(x->x_zoom, f); +} + +static void pdp_zoom_zoom(t_pdp_zoom *x, t_floatarg f) +{ + pdp_zoom_zoom_x(x, f); + pdp_zoom_zoom_y(x, f); +} + +static void pdp_zoom_center_x(t_pdp_zoom *x, t_floatarg f) +{ + pdp_imageproc_resample_affinemap_setcenterx(x->x_zoom, f); +} + +static void pdp_zoom_center_y(t_pdp_zoom *x, t_floatarg f) +{ + pdp_imageproc_resample_affinemap_setcentery(x->x_zoom, f); +} +static void pdp_zoom_center(t_pdp_zoom *x, t_floatarg fx, t_floatarg fy) +{ + pdp_zoom_center_x(x, fx); + pdp_zoom_center_y(x, fy); +} + +// not used +static void pdp_zoom_quality(t_pdp_zoom *x, t_floatarg f) +{ + if (f==0) x->x_quality = 0; + if (f==1) x->x_quality = 1; +} + + +t_class *pdp_zoom_class; + + + +void pdp_zoom_free(t_pdp_zoom *x) +{ + pdp_imagebase_free(x); + pdp_imageproc_resample_affinemap_delete(x->x_zoom); + pdp_packet_mark_unused(x->x_packet1); +} + + +void pdp_zoom_init_common(t_pdp_zoom *x) +{ + pdp_imagebase_init(x); + pdp_base_set_process_method(x, (t_pdp_method)pdp_zoom_process); + pdp_base_set_postproc_method(x, (t_pdp_method)pdp_zoom_postproc); + pdp_base_set_preproc_method(x, (t_pdp_method)pdp_zoom_preproc); + + x->x_outlet0 = pdp_base_add_pdp_outlet(x); + x->x_packet1 = -1; + x->x_zoom = pdp_imageproc_resample_affinemap_new(); + + //quality is not used: all routines are "high quality" bilinear + //pdp_zoom_quality(x, 1); + pdp_zoom_center_x(x, 0.5f); + pdp_zoom_center_y(x, 0.5f); + +} + + +void *pdp_zoom_new(t_floatarg zoom) +{ + t_pdp_zoom *x = (t_pdp_zoom *)pd_new(pdp_zoom_class); + + pdp_zoom_init_common(x); + pdp_base_add_gen_inlet(x, gensym("float"), gensym("zoom")); + + if (zoom == 0.0f) zoom = 1.0f; + pdp_zoom_zoom(x, zoom); + pdp_zoom_angle(x, 0.0f); + + return (void *)x; +} + +void *pdp_zrot_new(t_floatarg zoom, t_floatarg angle) +{ + t_pdp_zoom *x = (t_pdp_zoom *)pd_new(pdp_zoom_class); + + pdp_zoom_init_common(x); + pdp_base_add_gen_inlet(x, gensym("float"), gensym("zoom")); + pdp_base_add_gen_inlet(x, gensym("float"), gensym("angle")); + + + if (zoom == 0.0f) zoom = 1.0f; + pdp_zoom_zoom(x, zoom); + pdp_zoom_angle(x, angle); + + return (void *)x; +} + +void *pdp_rotate_new(t_floatarg angle) +{ + t_pdp_zoom *x = (t_pdp_zoom *)pd_new(pdp_zoom_class); + + pdp_zoom_init_common(x); + + pdp_base_add_gen_inlet(x, gensym("float"), gensym("angle")); + + pdp_zoom_zoom(x, 1.0f); + pdp_zoom_angle(x, angle); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_zoom_setup(void) +{ + + pdp_zoom_class = class_new(gensym("pdp_zoom"), (t_newmethod)pdp_zoom_new, + (t_method)pdp_zoom_free, sizeof(t_pdp_zoom), 0, A_DEFFLOAT, A_NULL); + + class_addcreator((t_newmethod)pdp_zrot_new, gensym("pdp_zrot"), A_DEFFLOAT, A_DEFFLOAT, A_NULL); + class_addcreator((t_newmethod)pdp_rotate_new, gensym("pdp_rotate"), A_DEFFLOAT, A_NULL); + + pdp_imagebase_setup(pdp_zoom_class); + + class_addmethod(pdp_zoom_class, (t_method)pdp_zoom_quality, gensym("quality"), A_FLOAT, A_NULL); + class_addmethod(pdp_zoom_class, (t_method)pdp_zoom_center_x, gensym("centerx"), A_FLOAT, A_NULL); + class_addmethod(pdp_zoom_class, (t_method)pdp_zoom_center_y, gensym("centery"), A_FLOAT, A_NULL); + class_addmethod(pdp_zoom_class, (t_method)pdp_zoom_center, gensym("center"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_zoom_class, (t_method)pdp_zoom_zoom_x, gensym("zoomx"), A_FLOAT, A_NULL); + class_addmethod(pdp_zoom_class, (t_method)pdp_zoom_zoom_y, gensym("zoomy"), A_FLOAT, A_NULL); + class_addmethod(pdp_zoom_class, (t_method)pdp_zoom_zoom, gensym("zoom"), A_FLOAT, A_NULL); + class_addmethod(pdp_zoom_class, (t_method)pdp_zoom_angle, gensym("angle"), A_FLOAT, A_NULL); + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_io/Makefile b/modules/image_io/Makefile new file mode 100644 index 0000000..b306ad8 --- /dev/null +++ b/modules/image_io/Makefile @@ -0,0 +1,11 @@ +current: all_modules + +include ../../Makefile.config + +# build optional modules +all_modules: $(PDP_OPTMOD) + +clean: + rm -f *~ + rm -f *.o + diff --git a/modules/image_io/README b/modules/image_io/README new file mode 100644 index 0000000..9493047 --- /dev/null +++ b/modules/image_io/README @@ -0,0 +1,2 @@ +This directory contains input/output modules for image packets. +Most of this is platform dependent stuff, and will be conditionally compiled. diff --git a/modules/image_io/pdp_glx.c b/modules/image_io/pdp_glx.c new file mode 100644 index 0000000..810912e --- /dev/null +++ b/modules/image_io/pdp_glx.c @@ -0,0 +1,558 @@ +/* + * 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. + * + */ + +// gl stuff +#include <GL/gl.h> +#include <GL/glx.h> +#include <GL/glu.h> +//#include <GL/glut.h> + +// pdp stuff +#include "pdp.h" +#include "pdp_base.h" + +// some x window glue code +#include "pdp_xwindow.h" + +// pdp stuff +#include "pdp.h" +#include "pdp_llconv.h" +//#include "pdp_opengl.h" + + +/* initial image dimensions */ +#define PDP_OGL_W 320 +#define PDP_OGL_H 240 + +#define PDP_OGL_AUTOCREATE_RETRY 10 + + +typedef struct pdp_glx_struct +{ + t_object x_obj; + + t_pdp_xwindow x_xwin; + + t_outlet *x_outlet; + + int x_packet0; + int x_queue_id; + t_symbol *x_display; + + Display *x_dpy; + + XVisualInfo *x_vis_info; + GLXContext x_glx_context; + + GLuint x_texture; + u32 x_tex_width; + u32 x_tex_height; + + unsigned char *x_data; + unsigned int x_width; + unsigned int x_height; + int x_last_encoding; + + int x_initialized; + int x_autocreate; + +} t_pdp_glx; + + + +static void pdp_glx_cursor(t_pdp_glx *x, t_floatarg f) +{ + pdp_xwindow_cursor(&x->x_xwin, f); +} + +static void pdp_glx_destroy(t_pdp_glx* x) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + XEvent e; + + if (x->x_initialized){ + pdp_procqueue_finish(q, x->x_queue_id); + x->x_queue_id = -1; + glXDestroyContext(x->x_dpy, x->x_glx_context); + pdp_xwindow_close(&x->x_xwin); + XCloseDisplay(x->x_dpy); + x->x_initialized = false; + } + +} + + +static void pdp_glx_fullscreen(t_pdp_glx *x) +{ + pdp_xwindow_fullscreen(&x->x_xwin); +} + +static void pdp_glx_resize(t_pdp_glx* x, t_floatarg width, t_floatarg height) +{ + pdp_xwindow_resize(&x->x_xwin, width, height); +} + +static void pdp_glx_move(t_pdp_glx* x, t_floatarg width, t_floatarg height) +{ + pdp_xwindow_move(&x->x_xwin, width, height); +} + +static void pdp_glx_moveresize(t_pdp_glx* x, t_floatarg xoff, t_floatarg yoff, t_floatarg width, t_floatarg height) +{ + pdp_xwindow_moveresize(&x->x_xwin, xoff, yoff, width, height); +} + +static void pdp_glx_tile(t_pdp_glx* x, t_floatarg xtiles, t_floatarg ytiles, t_floatarg i, t_floatarg j) +{ + pdp_xwindow_tile(&x->x_xwin, xtiles, ytiles, i, j); +} + + + + +void pdp_glx_generate_texture(t_pdp_glx *x) +{ + u32 width = x->x_tex_width; + u32 height = x->x_tex_height; + u32 depth = 4; + u32 i; + + u8 *dummydata = 0; + + while (x->x_width > width) width <<= 1; + while (x->x_height > height) height <<= 1; + + dummydata = (u8 *)pdp_alloc(width*height*depth); + + for (i=0; i<width*height*depth; i++){dummydata[i] = random(); } + + /* set window context current */ + glXMakeCurrent(x->x_dpy, x->x_xwin.win, x->x_glx_context); + + /* generate texture if necessary */ + if (!glIsTexture(x->x_texture)) glGenTextures(1, &(x->x_texture)); + + glBindTexture(GL_TEXTURE_2D, x->x_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, dummydata); + + pdp_dealloc(dummydata); + + x->x_tex_width = width; + x->x_tex_height = height; +} + +void pdp_glx_regenerate_texture(t_pdp_glx *x) +{ + if ((x->x_width > x->x_tex_width) || (x->x_height > x->x_tex_height)) pdp_glx_generate_texture(x); + +} + + +static void pdp_glx_create(t_pdp_glx* x) +{ + unsigned int *uintdata = (unsigned int *)(x->x_data); + XEvent e; + unsigned int i; + static int vis_attr[] = {GLX_RGBA, GLX_RED_SIZE, 4, GLX_GREEN_SIZE, 4, GLX_BLUE_SIZE, 4, + GLX_DEPTH_SIZE, 16, GLX_DOUBLEBUFFER, None}; + + + if (x->x_initialized) return; + + /* manually open a display */ + if (NULL == (x->x_dpy = XOpenDisplay(x->x_display->s_name))){ + post("pdp_glx: cant open display %s\n",x->x_display->s_name); + x->x_initialized = false; + return; + } + + /* create a window on the display */ + if (!(x->x_initialized = pdp_xwindow_create_on_display(&x->x_xwin, x->x_dpy))) + goto exit_close_dpy; + + + /* create a glx visual */ + if (!(x->x_vis_info = glXChooseVisual(x->x_dpy, x->x_xwin.screen, vis_attr))){ + post("pdp_glx: can't create visual"); + goto exit_close_win; + } + //post("visual: %x", x->x_vis_info); + + /* create the rendering context */ + if (!(x->x_glx_context = glXCreateContext(x->x_dpy, x->x_vis_info, 0 /*share list*/, GL_TRUE))){ + post("pdp_glx: can't create render context"); + goto exit_close_win; + } + //post("context: %x", x->x_glx_context); + + + /* create texture */ + pdp_glx_generate_texture(x); + + + /* we're done initializing */ + x->x_initialized = true; + + /* disable/enable cursor */ + //pdp_glx_cursor(x, x->x_cursor); + return; + + + exit_close_win: + pdp_xwindow_close(&x->x_xwin); + exit_close_dpy: + XCloseDisplay(x->x_dpy); + x->x_initialized = false; + return; +} + +static int pdp_glx_try_autocreate(t_pdp_glx *x) +{ + + if (x->x_autocreate){ + post("pdp_glx: autocreate window"); + pdp_glx_create(x); + if (!(x->x_initialized)){ + x->x_autocreate--; + if (!x->x_autocreate){ + post ("pdp_glx: autocreate failed %d times: disabled", PDP_OGL_AUTOCREATE_RETRY); + post ("pdp_glx: send [autocreate 1] message to re-enable"); + return 0; + } + } + else return 1; + + } + return 0; +} + +static void pdp_glx_bang(t_pdp_glx *x); + +static void pdp_glx_fill_texture(t_pdp_glx *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + void *data = pdp_packet_data (x->x_packet0); + + int i = header->info.image.width; + + + /* ensure image buffer is correct dim */ + if ((header->info.image.width != x->x_width) + || (header->info.image.height != x->x_height)) { + if (x->x_data) pdp_dealloc (x->x_data); + x->x_width = header->info.image.width; + x->x_height = header->info.image.height; + x->x_data = pdp_alloc(4*x->x_width*x->x_height); + } + + /* ensure texture is correct dim */ + pdp_glx_regenerate_texture(x); + + + /* set window context current */ + glXMakeCurrent(x->x_dpy, x->x_xwin.win, x->x_glx_context); + glBindTexture(GL_TEXTURE_2D, x->x_texture); + + switch (header->info.image.encoding){ + case PDP_IMAGE_GREY: + /* convert image to greyscale 8 bit */ + pdp_llconv(data,RIF_GREY______S16, x->x_data, RIF_GREY______U8, x->x_width, x->x_height); + + /* upload grey subtexture */ + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, x->x_width, x->x_height, GL_LUMINANCE, GL_UNSIGNED_BYTE, x->x_data); + + break; + case PDP_IMAGE_YV12: + + /* convert image to rgb 8 bit */ + pdp_llconv(data,RIF_YVU__P411_S16, x->x_data, RIF_RGB__P____U8, x->x_width, x->x_height); + + /* upload subtexture */ + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, x->x_width, x->x_height, GL_RGB, GL_UNSIGNED_BYTE, x->x_data); + + break; + default: + break; + } + + +} + +static void pdp_glx_process(t_pdp_glx *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + void *data = pdp_packet_data (x->x_packet0); + + + if (-1 != x->x_queue_id) return; + + /* check if window is initialized */ + if (!(x->x_initialized)){ + if (!pdp_glx_try_autocreate(x)) return; + } + + /* check data packet */ + if (!(header)) { + post("pdp_glx: invalid packet header"); + return; + } + if (PDP_IMAGE != header->type) { + post("pdp_glx: packet is not a PDP_IMAGE"); + return; + } + if ((PDP_IMAGE_YV12 != header->info.image.encoding) + && (PDP_IMAGE_GREY != header->info.image.encoding)) { + post("pdp_glx: packet is not a PDP_IMAGE_YV12/GREY"); + return; + } + + + /* fill the texture with the data in the packet */ + pdp_glx_fill_texture(x); + + /* display the new image */ + pdp_glx_bang(x); + + +} + + + +static void pdp_glx_display_texture(t_pdp_glx *x) +{ + float fx = (float)x->x_width / x->x_tex_width; + float fy = (float)x->x_height / x->x_tex_height; + + if (!x->x_initialized) return; + + /* set window context current */ + glXMakeCurrent(x->x_dpy, x->x_xwin.win, x->x_glx_context); + + /* setup viewport, projection and modelview */ + glViewport(0, 0, x->x_xwin.winwidth, x->x_xwin.winheight); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(0.0, x->x_xwin.winwidth, 0.0, x->x_xwin.winheight); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + + /* enable default texture */ + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, x->x_texture); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + + /* display texture */ + glBegin(GL_QUADS); + glTexCoord2f(fx, fy); + glVertex2i(x->x_xwin.winwidth,0); + glTexCoord2f(fx, 0); + glVertex2i(x->x_xwin.winwidth, x->x_xwin.winheight); + glTexCoord2f(0.0, 0.0); + glVertex2i(0, x->x_xwin.winheight); + glTexCoord2f(0, fy); + glVertex2i(0,0); + glEnd(); + + + glFlush(); + glXSwapBuffers(x->x_dpy,x->x_xwin.win); + +} + + + +/* redisplays image */ +static void pdp_glx_bang_thread(t_pdp_glx *x) +{ + + + pdp_glx_display_texture(x); + XFlush(x->x_dpy); + +} + +static void pdp_glx_bang_callback(t_pdp_glx *x) +{ + /* receive events & output them */ + pdp_xwindow_send_events(&x->x_xwin, x->x_outlet); + + /* release the packet if there is one */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1;} + +static void pdp_glx_bang(t_pdp_glx *x) +{ + + /* check if window is initialized */ + if (!(x->x_initialized)){ + if (!pdp_glx_try_autocreate(x)) return; + } + + + /* if previous queued method returned + schedule a new one, else ignore */ + +/* + if (-1 == x->x_queue_id) { + pdp_queue_add(x, pdp_glx_bang_thread, pdp_glx_bang_callback, &x->x_queue_id); + } +*/ + /* don't process in thread */ + pdp_glx_bang_thread(x); + pdp_glx_bang_callback(x); + +} + + + +static void pdp_glx_input_0(t_pdp_glx *x, t_symbol *s, t_floatarg f) +{ + + if (s == gensym("register_ro")) pdp_packet_copy_ro_or_drop(&x->x_packet0, (int)f); + if (s == gensym("process")) pdp_glx_process(x); +} + + + +static void pdp_glx_vga(t_pdp_glx *x) +{ + pdp_glx_resize(x, 640, 480); +} + +static void pdp_glx_autocreate(t_pdp_glx *x, t_floatarg f) +{ + if (f != 0.0f) x->x_autocreate = PDP_OGL_AUTOCREATE_RETRY; + else x->x_autocreate = 0; +} + +static void pdp_glx_display(t_pdp_glx *x, t_symbol *s) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + pdp_procqueue_finish(q, x->x_queue_id); + x->x_queue_id = -1; + x->x_display = s; + if (x->x_initialized){ + pdp_glx_destroy(x); + pdp_glx_create(x); + } +} + + + +static void pdp_glx_free(t_pdp_glx *x) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + pdp_procqueue_finish(q, x->x_queue_id); + pdp_glx_destroy(x); + pdp_xwindow_free(&x->x_xwin); + if (x->x_data) pdp_dealloc (x->x_data); + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_glx_class; + + + +void *pdp_glx_new(void) +{ + t_pdp_glx *x = (t_pdp_glx *)pd_new(pdp_glx_class); + + pdp_xwindow_init(&x->x_xwin); + + x->x_outlet = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_queue_id = -1; + x->x_display = gensym(":0"); + + x->x_dpy = 0; + + x->x_width = PDP_OGL_W; + x->x_height = PDP_OGL_H; + + x->x_data = pdp_alloc(4*PDP_OGL_W*PDP_OGL_H); + + x->x_initialized = 0; + pdp_glx_autocreate(x,1); + x->x_last_encoding = -1; + + x->x_tex_width = 64; + x->x_tex_height = 64; + + //pdp_glx_create(x); + + return (void *)x; +} + + + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_glx_setup(void) +{ + + + pdp_glx_class = class_new(gensym("pdp_glx"), (t_newmethod)pdp_glx_new, + (t_method)pdp_glx_free, sizeof(t_pdp_glx), 0, A_NULL); + + /* add creator for pdp_tex_win */ + + class_addmethod(pdp_glx_class, (t_method)pdp_glx_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_create, gensym("open"), A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_create, gensym("create"), A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_autocreate, gensym("autocreate"), A_FLOAT, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_destroy, gensym("destroy"), A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_destroy, gensym("close"), A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_move, gensym("move"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_move, gensym("pos"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_resize, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_resize, gensym("size"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_display, gensym("display"), A_SYMBOL, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_cursor, gensym("cursor"), A_FLOAT, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_fullscreen, gensym("fullscreen"), A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_moveresize, gensym("posdim"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_glx_class, (t_method)pdp_glx_tile, gensym("tile"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + + + /* accept both pdp and pdp_tex packets */ + class_addmethod(pdp_glx_class, (t_method)pdp_glx_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + + + /* some shortcuts for the lazy */ + class_addmethod(pdp_glx_class, (t_method)pdp_glx_vga, gensym("vga"), A_NULL); + +} + +#ifdef __cplusplus +} +#endif + + diff --git a/modules/image_io/pdp_qt.c b/modules/image_io/pdp_qt.c new file mode 100644 index 0000000..5e1111c --- /dev/null +++ b/modules/image_io/pdp_qt.c @@ -0,0 +1,974 @@ +/* + * 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. + * + */ + + +#include <quicktime/lqt.h> +#include <quicktime/colormodels.h> + +#include "pdp.h" +#include "pdp_llconv.h" + + +#define min(x,y) ((x<y)?(x):(y)) + + + +#define FREE(x) {if (x) {pdp_dealloc(x); x=0;} else post("free null pointer");} + + +/* debug macro */ +//#define DEBUG_MSG_ENABLED + +#ifdef DEBUG_MSG_ENABLED + +#define DEBUG_MSG(EXP)\ +fprintf (stderr, "mark start: [" #EXP "], on line %d\n", __LINE__);\ + EXP \ +fprintf (stderr, "mark end: [" #EXP "], on line %d\n", __LINE__); + +#else +#define DEBUG_MSG(EXP) EXP +#endif + +typedef struct pdp_qt_struct +{ + t_object x_obj; + t_float x_f; + + float x_gain; + + + t_symbol *x_name; // this is our name + int x_istilde; // 0==pdp_qt / 1==pdp_qt~ + int x_syncaudio; + + + /* clock object */ + t_clock *x_clock; + int x_counter; + int x_queue_id; + + /* audio outlets */ + t_outlet *x_outleft; + t_outlet *x_outright; + + /* message outlets */ + t_outlet *x_outlet0; + t_outlet *x_outlet1; + t_outlet *x_outlet2; + + /* pdp data */ + int x_packet0; + + /* toggles */ + int x_loop; + int x_autoplay; + + /* qt data */ + unsigned char ** x_qt_rows; // pointer array to rows / colour planes + float ** x_qt_audiochans; // pointer array to audio channel buffers + unsigned char * x_qt_frame; + quicktime_t *x_qt; + int x_qt_cmodel; + + //t_pdp_qt_data *x_state_data; + + /* audio data */ + int x_chunk_current; + float *x_chunk_buf; + float *x_chunk[2][2]; + int x_chunk_used[2]; // marks if chunk is used or not + int x_chunk_size; + int x_chunk_pos; + + /* global state */ + int x_initialized; + int x_frame; + int x_frame_thread; + int x_process_in_thread; + + + /* audio info */ + int x_audio_tracks; // ==0 means audio not available + int x_audio_channels; + long x_audio_samplerate; + long x_audio_length; + + /* video info */ + int x_video_tracks; // ==0 means video not available + float x_video_framerate; + long x_video_length; + unsigned int x_video_width; + unsigned int x_video_height; + + +} t_pdp_qt; + + +static void pdp_qt_bang(t_pdp_qt *x); + +static void pdp_qt_close(t_pdp_qt *x) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + + /* disable clock */ + clock_unset(x->x_clock); + pdp_procqueue_finish(q, x->x_queue_id); + + if (x->x_initialized){ + /* close file */ + quicktime_close(x->x_qt); + x->x_initialized = 0; + + + /* free video data */ + if (x->x_video_tracks){ + FREE(x->x_qt_frame); + FREE(x->x_qt_rows); + x->x_video_tracks = 0; + //x->x_qt_rows = 0; + //x->x_qt_frame = 0; + } + + /* free audio data */ + if (x->x_audio_tracks){ + x->x_chunk_used[0] = 0; + x->x_chunk_used[1] = 0; + FREE(x->x_chunk_buf); + FREE(x->x_qt_audiochans); + x->x_audio_tracks = 0; + //x->x_qt_audiochans = 0; + //x->x_chunk_buf = 0; + x->x_chunk[0][0] = 0; + x->x_chunk[0][1] = 0; + x->x_chunk[1][0] = 0; + x->x_chunk[1][1] = 0; + } + + + } + + +} + +void pdp_qt_create_pdp_packet(t_pdp_qt *x) +{ + t_pdp *header; + t_image *image; + + + /* round to next legal size */ + /* if size is illegal, image distortion will occur */ + u32 w = pdp_imageproc_legalwidth(x->x_video_width); + u32 h = pdp_imageproc_legalheight(x->x_video_height); + + + int nbpixels = w * h; + int packet_size = (nbpixels + (nbpixels >> 1)) << 1; + + + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = pdp_packet_new_image_YCrCb(w, h); + header = pdp_packet_header(x->x_packet0); + image = pdp_packet_image_info(x->x_packet0); + + if (!header){ + post("%s: ERROR: can't create new packet", x->x_name->s_name); + return; + } + + + //header->info.image.encoding = (x->x_qt_cmodel == BC_RGB888) ? PDP_IMAGE_GREY : PDP_IMAGE_YV12; + //image->encoding = PDP_IMAGE_YV12; + //image->width = w; + //image->height = h; +} + + + + +static void pdp_qt_open(t_pdp_qt *x, t_symbol *name) +{ + unsigned int size; + unsigned int i; + unsigned int chunk_bytesize; + + post("%s: opening %s", x->x_name->s_name, name->s_name); + + + /* close previous one */ + pdp_qt_close(x); + + + /* check if qt file */ + if(0 == quicktime_check_sig(name->s_name)){ + post("%s: ERROR: not a quicktime file", x->x_name->s_name); + goto exit; + } + + /* open */ + DEBUG_MSG(x->x_qt = quicktime_open(name->s_name, 1, 0);) + if (!(x->x_qt)){ + post("%s: ERROR: can't open file", x->x_name->s_name); + goto exit; + } + + /* check video */ + x->x_video_tracks = 0; + if (quicktime_has_video(x->x_qt)) { + x->x_video_framerate = quicktime_frame_rate (x->x_qt, 0); + x->x_video_length = quicktime_video_length (x->x_qt, 0); + x->x_video_width = quicktime_video_width (x->x_qt, 0); + x->x_video_height = quicktime_video_height (x->x_qt, 0); + post("%s: video stream found (%dx%d pixels, %0.00f fps, %d frames, %s codec)", + x->x_name->s_name, x->x_video_width, x->x_video_height, x->x_video_framerate, + x->x_video_length, quicktime_video_compressor(x->x_qt, 0)); + x->x_video_tracks = quicktime_video_tracks(x->x_qt); + + } + + + /* check audior */ + x->x_audio_tracks = 0; + if (quicktime_has_audio(x->x_qt)) { + x->x_audio_tracks = quicktime_audio_tracks (x->x_qt); + //x->x_audio_channels = quicktime_track_channels (x->x_qt, 0); + x->x_audio_channels = lqt_total_channels (x->x_qt); + x->x_audio_samplerate = quicktime_sample_rate (x->x_qt, 0); + x->x_audio_length = quicktime_audio_length (x->x_qt, 0); + x->x_chunk_size = (int)((float)x->x_audio_samplerate / x->x_video_framerate); + post("%s: audio stream found (%d channels, %d Hz, %d samples, chunksize %d)", + x->x_name->s_name, x->x_audio_channels, x->x_audio_samplerate, x->x_audio_length, x->x_chunk_size); + } + + /* check if video codec is supported */ + if (x->x_video_tracks){ + if (!quicktime_supported_video(x->x_qt,0)) { + post("%s: WARNING: unsupported video codec",x->x_name->s_name); + x->x_video_tracks = 0; + } + } + + /* check if audio codec is supported */ + if (x->x_audio_tracks){ + if (!quicktime_supported_audio(x->x_qt,0)) { + post("%s: WARNING: unsupported audio codec", x->x_name->s_name); + x->x_audio_tracks = 0; + } + } + + + + /* check which colormodel to use */ + if (x->x_video_tracks){ + + if (quicktime_reads_cmodel(x->x_qt,BC_YUV420P,0)){ + post("%s: using colormodel YUV420P", x->x_name->s_name); + x->x_qt_cmodel = BC_YUV420P; + } + else if (quicktime_reads_cmodel(x->x_qt,BC_YUV422,0)){ + post("%s: using colormodel YUV422", x->x_name->s_name); + x->x_qt_cmodel = BC_YUV422; + } + else if (quicktime_reads_cmodel(x->x_qt,BC_RGB888,0)){ + post("%s: using colormodel RGB888", x->x_name->s_name); + x->x_qt_cmodel = BC_RGB888; + } + else { + post("%s: WARNING: can't find a usable colour model", x->x_name->s_name); + x->x_video_tracks = 0; + } + + } + + + + /* no video == errors */ + if (!x->x_video_tracks) { + post("%s: ERROR: no usable video stream found.", x->x_name->s_name); + goto exit_close; + } + + + /* initialize video data structures */ + if (x->x_video_tracks){ + + /* allocate enough space for all supported colormodels (24bpp)*/ + x->x_frame = 0; + x->x_qt_frame = (unsigned char*)pdp_alloc(x->x_video_width * x->x_video_height * 3); + x->x_qt_rows = (unsigned char **)pdp_alloc(sizeof(unsigned char *) * x->x_video_height); + size = x->x_video_width * x->x_video_height; + + switch(x->x_qt_cmodel){ + case BC_YUV420P: + /* planar with u&v 2x2 subsampled */ + x->x_qt_rows[0] = &x->x_qt_frame[0]; + x->x_qt_rows[2] = &x->x_qt_frame[size]; + x->x_qt_rows[1] = &x->x_qt_frame[size + (size>>2)]; + break; + + case BC_YUV422: + /* packed with u&v 2x subsampled (lines) */ + /* later on we will convert this to planar */ + for(i=0; i< x->x_video_height; i++) x->x_qt_rows[i] = &x->x_qt_frame[i * x->x_video_width * 2]; + break; + + case BC_RGB888: + /* packed rgb */ + /* later on we will convert this to planar */ + for(i=0; i< x->x_video_height; i++) x->x_qt_rows[i] = &x->x_qt_frame[i * x->x_video_width * 3]; + break; + + default: + post("%s: error on init: unkown colour model",x->x_name->s_name); + break; + } + + DEBUG_MSG(quicktime_set_cmodel(x->x_qt, x->x_qt_cmodel);) + outlet_float(x->x_outlet2, (float)quicktime_video_length(x->x_qt,0)); + + } + + /* initialize audio data structures */ + if (x->x_audio_tracks){ + x->x_chunk_pos = 0; + x->x_chunk_current = 0; + + chunk_bytesize = sizeof(float)*x->x_chunk_size; + x->x_chunk_buf = (float *)pdp_alloc(chunk_bytesize * 4); + memset(x->x_chunk_buf, 0, chunk_bytesize * 4); + x->x_chunk[0][0] = x->x_chunk_buf; + x->x_chunk[0][1] = x->x_chunk_buf + x->x_chunk_size ; + x->x_chunk[1][0] = x->x_chunk_buf + x->x_chunk_size * 2; + x->x_chunk[1][1] = x->x_chunk_buf + x->x_chunk_size * 3; + x->x_chunk_used[0] = 0; + x->x_chunk_used[1] = 0; + x->x_syncaudio = x->x_istilde; //sync on audio if this is a tilde object + + DEBUG_MSG(if (x->x_audio_channels == 0) exit(1);) + x->x_qt_audiochans = (float **)pdp_alloc(x->x_audio_channels * sizeof(float **)); + memset(x->x_qt_audiochans, 0, x->x_audio_channels * sizeof(float **)); + } + else { + x->x_syncaudio = 0; + } + + + /* everything went well */ + x->x_initialized = 1; + + /* start playback if outplay is on */ + if(x->x_autoplay) clock_delay(x->x_clock, 1000.0L / (double)x->x_video_framerate); + + /* brag about success */ + post("%s: %s opened", x->x_name->s_name, name->s_name); + + return; + + /* error exits */ + + exit_close: + DEBUG_MSG(quicktime_close(x->x_qt);) + + exit: + x->x_initialized = 0; + x->x_audio_tracks = 0; + x->x_video_tracks = 0; + return; + +} + + +//static void pdp_qt_setposition(t_pdp_qt *x, int pos) +//{ +// x->x_frame = pos; +// DEBUG_MSG(if(x->x_video_tracks) quicktime_set_video_position(x->x_qt, pos, 0);) +// DEBUG_MSG(if(x->x_audio_tracks) quicktime_set_audio_position(x->x_qt, pos * x->x_chunk_size, 0);) +// +//} + + +static void pdp_qt_bangaudio(t_pdp_qt *x) +{ + int lefterr=0; + int righterr=0; + int err=0; + int sample = 0; + int remaining = 0; + int readamount = 0; + + + + if (!x->x_initialized){ + //post("pdp_qt: no qt file opened"); + return; + } + + + if (!x->x_audio_tracks){ + //post("pdp_qt: no audio stream present"); + return; + } + + + + //DEBUG_MSG(sample = quicktime_audio_position(x->x_qt,0);) + + + // if the active chunk is unused, clear it and mark it used + if (!x->x_chunk_used[x->x_chunk_current]){ + //post("%s: clearing unused active chunk",x->x_name->s_name); + + + + //probably this is the !@#%&*(*)&!$() bug + //memset(x->x_chunk[0][x->x_chunk_current], 0, sizeof(float)*2*x->x_chunk_size); + //memset(x->x_chunk[1][x->x_chunk_current], 0, sizeof(float)*2*x->x_chunk_size); + + memset(x->x_chunk[0][x->x_chunk_current], 0, sizeof(float) * x->x_chunk_size); + memset(x->x_chunk[1][x->x_chunk_current], 0, sizeof(float) * x->x_chunk_size); + + + + + x->x_chunk_used[x->x_chunk_current] = 1; + } + + // compute the remaining time + DEBUG_MSG(remaining = (int ) ( quicktime_audio_length(x->x_qt, 0) - quicktime_audio_position(x->x_qt, 0) );) + readamount = min(remaining, x->x_chunk_size); + if (!readamount) return; + + + // if the inactive chunk is unused, fill it with the current frame's audio data and mark it used + if (!x->x_chunk_used[!x->x_chunk_current]){ + switch(x->x_audio_channels){ + case 1: + x->x_qt_audiochans[0] = x->x_chunk[0][!x->x_chunk_current]; + x->x_qt_audiochans[1] = 0; + DEBUG_MSG(err = lqt_decode_audio(x->x_qt, NULL, x->x_qt_audiochans, readamount);) + break; + default: + x->x_qt_audiochans[0] = x->x_chunk[0][!x->x_chunk_current]; + x->x_qt_audiochans[1] = x->x_chunk[1][!x->x_chunk_current]; + DEBUG_MSG(err = lqt_decode_audio(x->x_qt, NULL, x->x_qt_audiochans, readamount);) + break; + } + x->x_chunk_used[!x->x_chunk_current] = 1; + } + // if it is used, something went wrong with sync + else{ + //post("%s: dropping audio chunk %d.",x->x_name->s_name, x->x_frame_thread); + } + + + if (err) post("%s: error decoding audio",x->x_name->s_name, x->x_frame_thread); + + // ensure audio pointer points to next frame's data + //DEBUG_MSG(quicktime_set_audio_position(x->x_qt, sample + readamount, 0);) + +} + + + + +static void pdp_qt_bangvideo(t_pdp_qt *x) +{ + unsigned int w, h, nbpixels, packet_size, i,j; + unsigned int *source, *dest; + unsigned int uoffset, voffset; + short int* data; + t_pdp* header; + + // check if we want greyscale output or not + //int grey = (x->x_qt_cmodel == BC_RGB888); + + static short int gain[4] = {0x7fff, 0x7fff, 0x7fff, 0x7fff}; + + if ((!x->x_initialized) || (!x->x_video_tracks)){ + //post("pdp_qt: no qt file opened"); + return; + } + + w = x->x_video_width; + h = x->x_video_height; + nbpixels = x->x_video_width * x->x_video_height; + + // create a new packet + pdp_qt_create_pdp_packet(x); + + header = pdp_packet_header(x->x_packet0); + + if (!header) { + post("%s: ERROR: no packet available", x->x_name->s_name); + return; + } + + data = (short int *) pdp_packet_data(x->x_packet0); + + + DEBUG_MSG(lqt_decode_video(x->x_qt, x->x_qt_rows, 0);) + + + switch(x->x_qt_cmodel){ + case BC_YUV420P: + pdp_llconv(x->x_qt_frame, RIF_YVU__P411_U8, data, RIF_YVU__P411_S16, x->x_video_width, x->x_video_height); + break; + + case BC_YUV422: + pdp_llconv(x->x_qt_frame, RIF_YUYV_P____U8, data, RIF_YVU__P411_S16, x->x_video_width, x->x_video_height); + break; + + case BC_RGB888: + pdp_llconv(x->x_qt_frame, RIF_RGB__P____U8, data, RIF_YVU__P411_S16, x->x_video_width, x->x_video_height); + break; + + default: + post("%s: error on decode: unkown colour model",x->x_name->s_name); + break; + } + + + +} + +static void pdp_qt_sendpacket(t_pdp_qt *x) +{ + + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet0); + + //if (x->x_packet0 != -1){ + //pdp_packet_mark_unused(x->x_packet0); + //outlet_pdp(x->x_outlet0, x->x_packet0); + //x->x_packet0 = -1; + //} +} + + +static void pdp_qt_thread_bang(t_pdp_qt *x) +{ + // set audio position + if(x->x_video_tracks) quicktime_set_video_position(x->x_qt, x->x_frame_thread, 0); + + // bang video + pdp_qt_bangvideo(x); + + // if it's a tilde object, bang audio + if (x->x_istilde && x->x_audio_tracks){ + quicktime_set_audio_position(x->x_qt, x->x_frame_thread * x->x_chunk_size, 0); + pdp_qt_bangaudio(x); + } + +} + + +static void pdp_qt_bang(t_pdp_qt *x) +{ + int length, pos; + + t_pdp_procqueue *q = pdp_queue_get_queue(); + + /* return if not initialized */ + if (!x->x_initialized) return; + + //length = quicktime_video_length(x->x_qt,0); + //pos = quicktime_video_position(x->x_qt,0); + length = x->x_video_length; + pos = x->x_frame; + + + /* check bounds */ + if (x->x_loop){ + pos = x->x_frame % length; + if (pos < 0) pos += length; + } + else{ + if (pos < 0) pos = 0; + if (pos >= length) pos = length - 1; + } + + /* store next frame for access in thread */ + x->x_frame_thread = pos; + + + // if autoplay is on and we do not have audio synchro + // set clock to play next frame + if (x->x_autoplay && !x->x_syncaudio) clock_delay(x->x_clock, 1000.0L / (double)x->x_video_framerate); + + + // make sure prev decode is finished don't drop frames in this one + pdp_procqueue_finish(q, x->x_queue_id); + x->x_queue_id = -1; + + /* only decode new stuff if previous is done */ + if (-1 == x->x_queue_id){ + // send the current frame number to outlet + outlet_float(x->x_outlet1, (float)pos); + + //pdp_qt_setposition(x, pos); + + // start process method + if (x->x_process_in_thread) pdp_procqueue_add(q, x, pdp_qt_thread_bang, pdp_qt_sendpacket, &x->x_queue_id); + else { + pdp_qt_thread_bang(x); + pdp_qt_sendpacket(x); + } + } + // advance frame + x->x_frame = pos + 1; + + + // send the packet + //pdp_qt_sendpacket(x); +} + + + +//static void pdp_qt_getaudiochunk(t_pdp_qt *x, int channel) +//{ +// if (!x->x_audio_tracks) return; +// quicktime_decode_audio(x->x_qt, NULL, x->x_chunk[channel][x->x_chunk_current], x->x_chunk_size<<1, channel); +// +//} + +static void pdp_qt_loop(t_pdp_qt *x, t_floatarg loop) +{ + int loopi = (int)loop; + x->x_loop = !(loopi == 0); +} + +static void pdp_qt_autoplay(t_pdp_qt *x, t_floatarg play) +{ + int playi = (int)play; + x->x_autoplay = !(playi == 0); + + + // reset clock if autoplay is off + if (!x->x_autoplay) clock_unset(x->x_clock); + + +} + + + +static void pdp_qt_frame_cold(t_pdp_qt *x, t_floatarg frameindex) +{ + int frame = (int)frameindex; + //int length; + + + x->x_frame = frame; + + //if (!(x->x_initialized)) return; + + //length = quicktime_video_length(x->x_qt,0); + + //frame = (frame >= length) ? length-1 : frame; + //frame = (frame < 0) ? 0 : frame; + + //pdp_qt_setposition(x, frame); +} + +static void pdp_qt_frame(t_pdp_qt *x, t_floatarg frameindex) +{ + pdp_qt_frame_cold(x, frameindex); + pdp_qt_bang(x); +} + +static void pdp_qt_stop(t_pdp_qt *x) +{ + pdp_qt_autoplay(x, 0); +} + +static void pdp_qt_continue(t_pdp_qt *x) +{ + pdp_qt_autoplay(x, 1); + pdp_qt_bang(x); +} + + +static void pdp_qt_play(t_pdp_qt *x){ + pdp_qt_frame_cold(x, 0); + pdp_qt_continue(x); +} + + + + +static void pdp_qt_importaudio(t_pdp_qt *x, t_symbol *array, t_floatarg channel) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + int c = (int)channel; + t_garray *g; + int vecsize; + int sample; + float *f; + int i; + + /* if there's no audio, there's nothing to export */ + if (!x->x_audio_tracks) return; + + /* check audio channel */ + if ((c < 0) || (c >= x->x_audio_channels)) return; + + /* check if array exists */ + if (!(g = (t_garray *)pd_findbyclass(array, garray_class))){ + pd_error(x, "%s: no such table", array->s_name); + return; + } + + post("%s: importing audio channel %d into array %s", x->x_name->s_name, c, array->s_name); + + + // make sure decode is finished + pdp_procqueue_finish(q, x->x_queue_id); + x->x_queue_id = -1; + + + /* resize array */ + garray_resize(g, x->x_audio_length); + + /* for sanity's sake let's clear the save-in-patch flag here */ + garray_setsaveit(g, 0); + garray_getfloatarray(g, &vecsize, &f); + + /* if the resize failed, garray_resize reported the error */ + if (vecsize != x->x_audio_length){ + pd_error(x, "array resize failed"); + return; + } + + /* save pointer in file */ + DEBUG_MSG(sample = quicktime_audio_position(x->x_qt, 0);) + DEBUG_MSG(quicktime_set_audio_position(x->x_qt, 0, 0);) + + /* transfer the audio file to the end of the array */ + DEBUG_MSG(quicktime_decode_audio(x->x_qt, NULL, f, vecsize, c);) + + /* restore pointer in file */ + DEBUG_MSG(quicktime_set_audio_position(x->x_qt, sample, 0);) + + +} + + + +static t_int *pdp_qt_perform(t_int *w) +{ + t_pdp_qt *x = (t_pdp_qt *)w[1]; + t_float *out0 = (t_float *)w[2]; + t_float *out1 = (t_float *)w[3]; + t_int n = (t_int)w[4]; + + t_int xfer_samples; + if (!x->x_initialized || !x->x_audio_tracks) goto zero; + + while(1){ + // check current chunk + if (!x->x_chunk_used[x->x_chunk_current]) goto zero; + + + // transfer from chunk to output + xfer_samples = min(n, x->x_chunk_size - x->x_chunk_pos); + + //x->x_audio_channels = 1; + + if (x->x_audio_channels == 1){ + memcpy(out0, x->x_chunk[0][x->x_chunk_current] + x->x_chunk_pos, sizeof(float)*xfer_samples); + memcpy(out1, x->x_chunk[0][x->x_chunk_current] + x->x_chunk_pos, sizeof(float)*xfer_samples); + } + else { + memcpy(out0, x->x_chunk[0][x->x_chunk_current] + x->x_chunk_pos, sizeof(float)*xfer_samples); + memcpy(out1, x->x_chunk[1][x->x_chunk_current] + x->x_chunk_pos, sizeof(float)*xfer_samples); + } + out0 += xfer_samples; + out1 += xfer_samples; + n -= xfer_samples; + x->x_chunk_pos += xfer_samples; + + + // check if chunk is finished, if so mark unused, swap buffers and set clock + if (x->x_chunk_size == x->x_chunk_pos){ + x->x_chunk_used[x->x_chunk_current] = 0; + x->x_chunk_pos = 0; + x->x_chunk_current ^= 1; + if (x->x_autoplay) clock_delay(x->x_clock, 0L); + } + + // if chunk is not finished, the output buffer is full + else{ + goto exit; + } + + } + + + zero: + // fill the rest of the output with zeros + memset(out0, 0, sizeof(float)*n); + memset(out1, 0, sizeof(float)*n); + + exit: + return(w+5); +} + +static void pdp_qt_dsp(t_pdp_qt *x, t_signal **sp) +{ + dsp_add(pdp_qt_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); + +} + +static void pdp_qt_process_in_thread(t_pdp_qt *x, t_float f) +{ + + int t = (f != 0.0f); + + x->x_process_in_thread = t; + + post("pdp_qt: thread processing switched %d", t ? "on" : "off"); + +} + +static void pdp_qt_tick(t_pdp_qt *x) +{ + + // bang audio/video + pdp_qt_bang(x); +} + +static void pdp_qt_free(t_pdp_qt *x) +{ + clock_unset(x->x_clock); + pdp_qt_close(x); + clock_free(x->x_clock); + //free (x->x_state_data); + +} + + + +t_class *pdp_qt_class; +t_class *pdp_qt_tilde_class; + + +void pdp_qt_init_common(t_pdp_qt *x) +{ + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("frame_cold")); + + /* add common outlets */ + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_outlet1 = outlet_new(&x->x_obj, &s_float); + x->x_outlet2 = outlet_new(&x->x_obj, &s_float); + + /* init */ + x->x_gain = 1.0f; + x->x_process_in_thread = 0; + x->x_packet0 = -1; + x->x_queue_id = -1; + x->x_initialized = 0; + x->x_audio_tracks = 0; + x->x_video_tracks = 0; + x->x_loop = 0; + x->x_autoplay = 0; + x->x_chunk[0][0] = 0; + x->x_chunk[0][1] = 0; + x->x_chunk[1][0] = 0; + x->x_chunk[1][1] = 0; + + /* initialize clock object */ + x->x_clock = clock_new(x, (t_method)pdp_qt_tick); + + + + +} + +void *pdp_qt_new(void) +{ + t_pdp_qt *x = (t_pdp_qt *)pd_new(pdp_qt_class); + x->x_name = gensym("pdp_qt"); + x->x_istilde = 0; + pdp_qt_init_common(x); + return (void *)x; +} + +void *pdp_qt_tilde_new(void) +{ + t_pdp_qt *x = (t_pdp_qt *)pd_new(pdp_qt_tilde_class); + x->x_name = gensym("pdp_qt"); + x->x_istilde = 1; + + pdp_qt_init_common(x); + + /* add outlets to the right so pdp_qt~ can replace pdp_qt without breaking a patch */ + x->x_outleft = outlet_new(&x->x_obj, &s_signal); + x->x_outright = outlet_new(&x->x_obj, &s_signal); + + return (void *)x; +} + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_qt_setup_common(t_class *class) +{ + class_addmethod(class, (t_method)pdp_qt_bang, gensym("bang"), A_NULL); + class_addmethod(class, (t_method)pdp_qt_close, gensym("close"), A_NULL); + class_addmethod(class, (t_method)pdp_qt_open, gensym("open"), A_SYMBOL, A_NULL); + class_addmethod(class, (t_method)pdp_qt_autoplay, gensym("autoplay"), A_DEFFLOAT, A_NULL); + class_addmethod(class, (t_method)pdp_qt_stop, gensym("stop"), A_NULL); + class_addmethod(class, (t_method)pdp_qt_play, gensym("play"), A_NULL); + class_addmethod(class, (t_method)pdp_qt_continue, gensym("cont"), A_NULL); + class_addmethod(class, (t_method)pdp_qt_loop, gensym("loop"), A_DEFFLOAT, A_NULL); + class_addfloat (class, (t_method)pdp_qt_frame); + class_addmethod(class, (t_method)pdp_qt_frame_cold, gensym("frame_cold"), A_FLOAT, A_NULL); + class_addmethod(class, (t_method)pdp_qt_process_in_thread, gensym("thread"), A_FLOAT, A_NULL); + class_addmethod(class, (t_method)pdp_qt_importaudio, gensym("importaudio"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(class, (t_method)pdp_qt_importaudio, gensym("dump"), A_SYMBOL, A_DEFFLOAT, A_NULL); +} + +void pdp_qt_setup(void) +{ + + /* plain class */ + pdp_qt_class = class_new(gensym("pdp_qt"), (t_newmethod)pdp_qt_new, + (t_method)pdp_qt_free, sizeof(t_pdp_qt), 0, A_NULL); + pdp_qt_setup_common(pdp_qt_class); + + + /* tilde class */ + pdp_qt_tilde_class = class_new(gensym("pdp_qt~"), (t_newmethod)pdp_qt_tilde_new, + (t_method)pdp_qt_free, sizeof(t_pdp_qt), 0, A_NULL); + pdp_qt_setup_common(pdp_qt_tilde_class); + + class_addmethod(pdp_qt_tilde_class, (t_method)pdp_qt_dsp, gensym("dsp"), 0); +} + +#ifdef __cplusplus +} +#endif + + diff --git a/modules/image_io/pdp_sdl.c b/modules/image_io/pdp_sdl.c new file mode 100644 index 0000000..d30ef84 --- /dev/null +++ b/modules/image_io/pdp_sdl.c @@ -0,0 +1,510 @@ +/* + * Pure Data Packet module. + * Copyright (c) 2003 by martin pi <pi@attacksyour.net> + * 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. + * + */ + +/* + +pdp sdl output + +DONE: + +TODO: + * close window (event) + * fullscreen chose resolution + * event handling in different object (and look at mplayer for that!) + +*/ + + +#include <stdio.h> +//#include <unistd.h> +//#include <sys/ipc.h> +//#include <sys/shm.h> + +#include <SDL/SDL.h> + +//#include <quicktime/lqt.h> +//#include <quicktime/colormodels.h> + +#include "pdp.h" +#include "pdp_llconv.h" + + +/* initial image dimensions */ +#define PDP_SDL_W 320 +#define PDP_SDL_H 240 + + +typedef struct pdp_sdl_struct { + t_object x_obj; + t_float x_f; + + int x_packet0; + int x_queue_id; + + SDL_Surface *x_sdl_surface; + SDL_Overlay *x_sdl_overlay; + SDL_Rect x_sdl_rect; + + int x_xid; + + Uint32 x_sdl_format; + + int x_winwidth; + int x_winheight; + + unsigned int x_width; + unsigned int x_height; + int x_last_encoding; + int x_cursor; + + int x_initialized; + int x_backfromthread; + + int x_fullscreen; + +} t_pdp_sdl; + +static SDL_Surface *pdp_sdl_getSurface(int xid, char* title, int width, int height, int bits, int fullscreenflag, int cursorflag) { + Uint32 flags; + int size,i; + SDL_Surface *screen; + char SDL_hack[32]; + + /* next lines from gstreamer plugin sdlvideosink */ + if (xid < 0) unsetenv("SDL_WINDOWID"); + else { + sprintf(SDL_hack, "%d", xid); + setenv("SDL_WINDOWID", SDL_hack, 1); + } + + /* Initialize SDL */ + if (!SDL_WasInit(SDL_INIT_VIDEO)) { + if (SDL_Init (SDL_INIT_VIDEO|SDL_INIT_NOPARACHUTE)) { + post("SDL: Initializing of SDL failed: %s.\n", SDL_GetError()); + return NULL; + } + /* ignore events :: only keys and wm_quit */ + for ( i=SDL_NOEVENT; i<SDL_NUMEVENTS; ++i ) + if( !(i & (SDL_KEYDOWN|SDL_VIDEORESIZE)) ) + SDL_EventState(i, SDL_IGNORE); + + + } + +/* +gem : SDL_OPENGL|SDL_DOUBLEBUF|SDL_HWSURFACE|SDL_ANYFORMAT|SDL_OPENGLBLIT; +working: SDL_ANYFORMAT|SDL_RESIZABLE|SDL_RLEACCEL; +*/ + flags = SDL_SWSURFACE | SDL_RESIZABLE; +// flags = SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_ANYFORMAT|SDL_RLEACCEL; + if ( fullscreenflag>0 ) { + flags |= SDL_FULLSCREEN|SDL_DOUBLEBUF; + } + + screen = SDL_SetVideoMode(width, height, bits, flags); + if ( screen == NULL ) { + post("Couldn't set video mode: %s\n", SDL_GetError()); + return NULL; + } + + SDL_WM_SetCaption (title, title); + + SDL_ShowCursor(cursorflag); + + return screen; +} + +static SDL_Surface *pdp_sdl_recreateSurface(SDL_Surface *old, int xid, char* title, int width, int height, int bits, int fullscreenflag, int cursorflag) { + SDL_Surface *new = pdp_sdl_getSurface(xid, title, width, height, bits, fullscreenflag, cursorflag); + if (new != NULL) SDL_FreeSurface(old); + return new; +} + +static inline void pdp_sdl_getOverlay(t_pdp_sdl* x) { + x->x_sdl_overlay = SDL_CreateYUVOverlay(x->x_width, x->x_height, x->x_sdl_format, x->x_sdl_surface); +} + +static inline void pdp_sdl_freeOverlay(t_pdp_sdl* x) { + SDL_FreeYUVOverlay(x->x_sdl_overlay); +} + +static int pdp_sdl_drawImage(t_pdp_sdl* x, t_image *image, short int *pixels) { + + unsigned int width = image->width; + unsigned int height = image->height; + int encoding = image->encoding; + unsigned int* uintdata; + int i; + + + /* 8bit y fulscale and 8bit u,v 2x2 subsampled */ + //static short int gain[4] = {0x0100, 0x0100, 0x0100, 0x0100}; + int nbpixels = width * height; + long size = (width * height + (((width>>1)*(height>>1))<<1)); + + /* check if xvimage needs to be recreated */ + if ((width != x->x_width) || (height != x->x_height)){ + post("pdp_xv: replace image"); + x->x_width = width; + x->x_height = height; + pdp_sdl_freeOverlay(x); + pdp_sdl_getOverlay(x); + } + + SDL_LockYUVOverlay(x->x_sdl_overlay); + if (pixels) { + pdp_llconv(pixels,RIF_YVU__P411_S16, (Uint8 *)(* x->x_sdl_overlay->pixels), RIF_YVU__P411_U8, x->x_width, x->x_height); + } else bzero((Uint8 *)(* x->x_sdl_overlay->pixels), size); + SDL_UnlockYUVOverlay(x->x_sdl_overlay); + + return 1; +} + +static void pdp_sdl_fullscreen(t_pdp_sdl *x, t_floatarg f); + +static inline void pdp_sdl_recreate(t_pdp_sdl *x) { + x->x_sdl_surface = pdp_sdl_recreateSurface(x->x_sdl_surface, x->x_xid,"pdp-sdl", x->x_winwidth, x->x_winheight, 16, x->x_fullscreen,x->x_cursor); +} + +static int pdp_sdl_create(t_pdp_sdl *x) { + if (x->x_initialized){ + return 0; + } + x->x_initialized = 0; + + x->x_sdl_surface = pdp_sdl_getSurface(x->x_xid, "pdp-sdl", x->x_winwidth, x->x_winheight, 16, x->x_fullscreen,x->x_cursor); + if (x->x_sdl_surface != NULL) { + pdp_sdl_getOverlay(x); + if (x->x_sdl_overlay != NULL) { + x->x_sdl_rect.x = 0; + x->x_sdl_rect.y = 0; + x->x_sdl_rect.w = x->x_winwidth; + x->x_sdl_rect.h = x->x_winheight; + x->x_initialized = 1; + post("created successfully"); + } + } + x->x_backfromthread = 1; + + return x->x_initialized; +} + +static void pdp_sdl_destroy(t_pdp_sdl *x); +static void pdp_sdl_resize(t_pdp_sdl *x,t_floatarg,t_floatarg); + +static void pdp_sdl_checkEvents(t_pdp_sdl *x) { + Uint8 *keys; + SDL_Event event; + + if (SDL_PollEvent(&event)!=1) return; + + switch( event.type ){ + case SDL_KEYDOWN: + case SDL_KEYUP: + keys = SDL_GetKeyState(NULL); + + if(keys[SDLK_UP]) { post("up"); } + if(keys[SDLK_DOWN]) { post("down"); } + + if(keys[SDLK_ESCAPE]) pdp_sdl_fullscreen(x,0); + break; + + case SDL_QUIT: + pdp_sdl_destroy(x); + break; + case SDL_VIDEORESIZE: + pdp_sdl_resize(x,(t_floatarg)event.resize.w,(t_floatarg)event.resize.h); + break; + default: + break; + } + + +} + +static void pdp_sdl_bang(t_pdp_sdl *x); + +static void pdp_sdl_process(t_pdp_sdl *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + void *data = pdp_packet_data (x->x_packet0); + + + if (!x->x_backfromthread) return; + + /* check if window is initialized */ + if (!(x->x_initialized)){ + if (!pdp_sdl_create(x)) return; + } + + /* check for pending sdl events */ + pdp_sdl_checkEvents(x); + + /* check data packet */ + if (!(header)) { + post("pdp_sdl: invalid packet header"); + return; + } + if (PDP_IMAGE != header->type) { + post("pdp_sdl: packet is not a PDP_IMAGE"); + return; + } + if (header->info.image.encoding != PDP_IMAGE_YV12) { + post("pdp_sdl: packet is not a PDP_IMAGE_YV12"); + return; + } + + /* copy the packet to the sdlimage */ + pdp_sdl_drawImage(x, &header->info.image, (short int *)data); + + /* display the new image */ + pdp_sdl_bang(x); +} + +static void pdp_sdl_destroy(t_pdp_sdl *x) { + if (x->x_initialized){ + pdp_sdl_freeOverlay(x); + SDL_FreeSurface(x->x_sdl_surface); + x->x_initialized = 0; + SDL_Quit(); + } +} + +static void pdp_sdl_random(t_pdp_sdl *x) { + unsigned int i; + long *intdata = (long *)(* x->x_sdl_overlay->pixels); + SDL_LockYUVOverlay(x->x_sdl_overlay); + for(i=0; i<x->x_width*x->x_height/4; i++) intdata[i]=random(); + SDL_UnlockYUVOverlay(x->x_sdl_overlay); +} + +/* redisplays image */ +static void pdp_sdl_bang_thread(t_pdp_sdl *x) { + if (SDL_DisplayYUVOverlay(x->x_sdl_overlay, &(* x).x_sdl_rect) <0) + post("pdp_sdl: __LINE__ cannot display"); +} + +static void pdp_sdl_bang_callback(t_pdp_sdl *x) +{ + x->x_backfromthread = 1; + + /* release the packet if there is one */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1;} + +static void pdp_sdl_bang(t_pdp_sdl *x) { + + /* if previous queued method returned + schedule a new one, else ignore */ + if (x->x_backfromthread) { + x->x_backfromthread = 0; + pdp_queue_add(x, pdp_sdl_bang_thread, pdp_sdl_bang_callback, &x->x_queue_id); + } +} + +static void pdp_sdl_input_0(t_pdp_sdl *x, t_symbol *s, t_floatarg f) { + + if (s == gensym("register_ro")) pdp_packet_copy_ro_or_drop(&x->x_packet0, (int)f); + if (s == gensym("process")) pdp_sdl_process(x); + +} + +static void pdp_sdl_resize(t_pdp_sdl* x, t_floatarg width, t_floatarg height) { + + if (x->x_initialized && (!x->x_fullscreen) && (width>0) && (height>0)) { + post("should get %d/%d",(int)width,(int) height); + x->x_winwidth=(int)width; + x->x_winheight=(int)height; + pdp_sdl_recreate(x); + } +} + + +static void pdp_sdl_fullscreen(t_pdp_sdl *x, t_floatarg f) { + if (f == x->x_fullscreen) return; + + x->x_fullscreen = (f != 0.0f); + x->x_cursor=0; + + pdp_sdl_recreate(x); +} + +static void pdp_sdl_cursor(t_pdp_sdl *x, t_floatarg f) { + if (f == x->x_cursor) return; + + x->x_cursor = (f != 0.0f); + SDL_ShowCursor(x->x_cursor); +} + + +/* sets new target window */ + +static void pdp_sdl_win(t_pdp_sdl *x, t_floatarg *f) { + pdp_queue_finish(x->x_queue_id); + x->x_queue_id = -1; + x->x_xid = (int)f; + pdp_sdl_recreate(x); +} + +/* be very carefule not to set DGA fro here! */ +/* use export SDL_VIDEODRIVER=dga (or equivalent for your shell) instead */ + +static void pdp_sdl_renderer(t_pdp_sdl *x, t_symbol *s) { + char SDL_hack[32]; + + pdp_sdl_destroy(x); + + /* next lines from gstreamer plugin sdlvideosink */ + unsetenv("SDL_VIDEODRIVER"); + + sprintf(SDL_hack, "%s", s->s_name); + setenv("SDL_VIDEODRIVER", SDL_hack, 1); + + pdp_sdl_create(x); +} + +static void pdp_sdl_free(t_pdp_sdl *x) +{ + pdp_queue_finish(x->x_queue_id); + pdp_sdl_destroy(x); + pdp_packet_mark_unused(x->x_packet0); +// SDL_Quit(); +} + +static void pdp_sdl_listmodes(const char* title, Uint32 flags) { + SDL_Rect ** modes; + int i; + + /* Get available modes */ + modes = SDL_ListModes(NULL, flags); + + /* Check is there are any modes available */ + if(modes == (SDL_Rect **)0){ + printf("%s : No modes available!", title); + return; + } + + /* Check if our resolution is restricted */ + if(modes == (SDL_Rect **)-1){ + post("%s : All resolutions available.", title); + } else { + /* Print valid modes */ + for(i=0;modes[i];++i) + post("%s : %d x %d", title, modes[i]->w, modes[i]->h); + } + +} + +static void pdp_sdl_modes(t_pdp_sdl *x) { + pdp_sdl_listmodes("FULL|HWSURF|||||||||||||||||||||||||", SDL_FULLSCREEN|SDL_HWSURFACE); + pdp_sdl_listmodes("HWSURF|RESIZ|ASYNC|HWACCEL||||||||||", SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL); + pdp_sdl_listmodes("HWSURF|RESIZ|ASYNC|HWACCEL|FULL|DBUF", SDL_HWSURFACE|SDL_RESIZABLE|SDL_ASYNCBLIT|SDL_HWACCEL|SDL_FULLSCREEN|SDL_DOUBLEBUF); + pdp_sdl_listmodes("OPENGL|DBUF|HWSURF|ANYF|GLBLIT||||||", SDL_OPENGL|SDL_DOUBLEBUF|SDL_HWSURFACE|SDL_ANYFORMAT|SDL_OPENGLBLIT); + pdp_sdl_listmodes("ANYF|RESIZ|RLEA|||||||||||||||||||||", SDL_ANYFORMAT|SDL_RESIZABLE|SDL_RLEACCEL); +} + +static void pdp_sdl_info(t_pdp_sdl *x) { + const SDL_VideoInfo *narf; + post("\nSDL video info: note that this only works under dga mode\n"); + narf = SDL_GetVideoInfo(); + post("Is it possible to create hardware surfaces?\t\thw_available=%d",narf->hw_available); + post("Is there a window manager available?\t\t\twm_available=%d",narf->wm_available); + post("Are hardware to hardware blits accelerated?\t\tblit_hw=%d",narf->blit_hw); + post("Are hardware to hardware colorkey blits accelerated?\tblit_hw_CC=%d",narf->blit_hw_CC); + post("Are hardware to hardware alpha bits accelerated?\tblit_hw_A=%d",narf->blit_hw_A); + post("Are software to hardware blits accelerated?\t\tblit_sw=%d",narf->blit_sw); + post("Are software to hardware colorkey blits accelerated?\tblit_sw_CC=%d",narf->blit_sw_CC); + post("Are software to hardware alpha blits accelerated?\tblit_sw_A=%d",narf->blit_sw_A); + post("Are color fills accelerated?\t\t\t\tblit_fill=%d",narf->blit_fill); + post("Total amount of video_mem: %d",narf->video_mem); + +} + +t_class *pdp_sdl_class; + +void *pdp_sdl_new(void) { + + + t_pdp_sdl *x = (t_pdp_sdl *)pd_new(pdp_sdl_class); + + x->x_packet0 = -1; + x->x_queue_id = -1; + + x->x_sdl_surface = NULL; + x->x_sdl_overlay = NULL; + x->x_sdl_format = SDL_YV12_OVERLAY; + + x->x_winwidth = PDP_SDL_W; + x->x_winheight = PDP_SDL_H; + + x->x_width = PDP_SDL_W; + x->x_height = PDP_SDL_H; + + x->x_backfromthread = 1; + x->x_initialized = 0; + + x->x_fullscreen = 0; + x->x_cursor=1; + + x->x_xid = -1; + + pdp_sdl_create(x); + + return (void *)x; +} + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_sdl_setup(void) +{ + + + pdp_sdl_class = class_new(gensym("pdp_sdl"), (t_newmethod)pdp_sdl_new, + (t_method)pdp_sdl_free, sizeof(t_pdp_sdl), 0, A_NULL); + + + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_random, gensym("random"), A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_create, gensym("create"), A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_info, gensym("info"), A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_modes, gensym("modes"), A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_destroy, gensym("destroy"), A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_destroy, gensym("close"), A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_resize, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_resize, gensym("size"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_fullscreen, gensym("fullscreen"), A_FLOAT, A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_cursor, gensym("cursor"), A_FLOAT, A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_win, gensym("window"), A_FLOAT, A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_renderer, gensym("renderer"), A_SYMBOL, A_NULL); + class_addmethod(pdp_sdl_class, (t_method)pdp_sdl_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif + diff --git a/modules/image_io/pdp_v4l.c b/modules/image_io/pdp_v4l.c new file mode 100644 index 0000000..2fdf12d --- /dev/null +++ b/modules/image_io/pdp_v4l.c @@ -0,0 +1,794 @@ +/* + * 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. + * + */ + + +#include "pdp_config.h" +#include "pdp.h" +#include "pdp_llconv.h" +#include "pdp_imageproc.h" +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <linux/types.h> +#include <linux/videodev.h> +#include <sys/mman.h> +#include <sched.h> +#include <pthread.h> + +// dont open any more after a set number +// of failed attempts +// this is to prevent locks on auto-open +// is reset when manually opened or closed +#define PDP_XV_RETRIES 10 + +//include it anyway +//#ifdef HAVE_PWCV4L +#include "pwc-ioctl.h" +//#endif + + +#define DEVICENO 0 +#define NBUF 2 +#define COMPOSITEIN 1 + + + + +typedef struct pdp_v4l_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + + bool x_initialized; + bool x_auto_open; + + unsigned int x_width; + unsigned int x_height; + int x_channel; + unsigned int x_norm; + int x_freq; + + unsigned int x_framerate; + + struct video_tuner x_vtuner; + struct video_picture x_vpicture; + struct video_buffer x_vbuffer; + struct video_capability x_vcap; + struct video_channel x_vchannel; + struct video_audio x_vaudio; + struct video_mbuf x_vmbuf; + struct video_mmap x_vmmap[NBUF]; + struct video_window x_vwin; + int x_tvfd; + int x_frame; + unsigned char *x_videobuf; + int x_skipnext; + int x_mytopmargin, x_mybottommargin; + int x_myleftmargin, x_myrightmargin; + + t_symbol *x_device; + t_symbol *x_image_type; + //int x_pdp_image_type; + int x_v4l_palette; + + pthread_t x_thread_id; + int x_continue_thread; + int x_frame_ready; + int x_only_new_frames; + int x_last_frame; + + + int x_open_retry; + + u32 x_minwidth; + u32 x_maxwidth; + u32 x_minheight; + u32 x_maxheight; + + +} t_pdp_v4l; + + + + + +static void pdp_v4l_audio(t_pdp_v4l *x, t_floatarg f) +{ + int i = 0; + if (x->x_initialized){ + fprintf(stderr," audios : %d\n",x->x_vcap.audios); + x->x_vaudio.audio = 0; + ioctl(x->x_tvfd,VIDIOCGAUDIO, &x->x_vaudio); + + fprintf(stderr," %d (%s): ",i,x->x_vaudio.name); + if (x->x_vaudio.flags & VIDEO_AUDIO_MUTABLE) + fprintf(stderr,"muted=%s ", + (x->x_vaudio.flags & VIDEO_AUDIO_MUTE) ? "yes":"no"); + if (x->x_vaudio.flags & VIDEO_AUDIO_VOLUME) + fprintf(stderr,"volume=%d ",x->x_vaudio.volume); + if (x->x_vaudio.flags & VIDEO_AUDIO_BASS) + fprintf(stderr,"bass=%d ",x->x_vaudio.bass); + if (x->x_vaudio.flags & VIDEO_AUDIO_TREBLE) + fprintf(stderr,"treble=%d ",x->x_vaudio.treble); + fprintf(stderr,"\n"); + + } +} + + +static void pdp_v4l_close(t_pdp_v4l *x) +{ + /* close the v4l device and dealloc buffer */ + + void *dummy; + + /* terminate thread if there is one */ + if(x->x_continue_thread){ + x->x_continue_thread = 0; + pthread_join (x->x_thread_id, &dummy); + } + + + if (x->x_tvfd >= 0) + { + close(x->x_tvfd); + x->x_tvfd = -1; + } + + if (x->x_initialized){ + munmap(x->x_videobuf, x->x_vmbuf.size); + x->x_initialized = false; + } + +} + +static void pdp_v4l_close_manual(t_pdp_v4l *x) +{ + x->x_open_retry = PDP_XV_RETRIES; + pdp_v4l_close(x); + +} + +static void pdp_v4l_close_error(t_pdp_v4l *x) +{ + pdp_v4l_close(x); + if(x->x_open_retry) x->x_open_retry--; +} + + +static void pdp_v4l_pwc_init(t_pdp_v4l *x) +{ + struct pwc_probe probe; + int isPhilips = 0; + +#ifdef HAVE_PWCV4L + /* skip test */ + isPhilips = 1; +#else + /* test for pwc */ + if (ioctl(x->x_tvfd, VIDIOCPWCPROBE, &probe) == 0) + if (!strcmp(x->x_vcap.name, probe.name)) + isPhilips = 1; + +#endif + + /* don't do pwc specific stuff */ + if (!isPhilips) return; + + post("pdp_v4l: detected pwc"); + + if(ioctl(x->x_tvfd, VIDIOCPWCRUSER)){ + perror("pdp_v4l: pwc: VIDIOCPWCRUSER"); + goto closit; + } + + if (ioctl(x->x_tvfd, VIDIOCGWIN, &x->x_vwin)){ + perror("pdp_v4l: pwc: VIDIOCGWIN"); + goto closit; + } + + + + if (x->x_vwin.flags & PWC_FPS_MASK){ + //post("pdp_v4l: pwc: camera framerate: %d", (x->x_vwin.flags & PWC_FPS_MASK) >> PWC_FPS_SHIFT); + //post("pdp_v4l: pwc: setting camera framerate to %d", x->x_framerate); + x->x_vwin.flags &= PWC_FPS_MASK; + x->x_vwin.flags |= (x->x_framerate << PWC_FPS_SHIFT); + if (ioctl(x->x_tvfd, VIDIOCSWIN, &x->x_vwin)){ + perror("pdp_v4l: pwc: VIDIOCSWIN"); + goto closit; + } + if (ioctl(x->x_tvfd, VIDIOCGWIN, &x->x_vwin)){ + perror("pdp_v4l: pwc: VIDIOCGWIN"); + goto closit; + } + post("pdp_v4l: camera framerate set to %d fps", (x->x_vwin.flags & PWC_FPS_MASK) >> PWC_FPS_SHIFT); + + } + + + return; + + + + closit: + pdp_v4l_close_error(x); + return; + +} + +static void pdp_v4l_sync_frame(t_pdp_v4l* x){ + /* grab frame */ + if (ioctl(x->x_tvfd, VIDIOCSYNC, &x->x_vmmap[x->x_frame].frame) < 0){ + perror("pdp_v4l: VIDIOCSYNC"); + pdp_v4l_close(x); + return; + } +} + +static void pdp_v4l_capture_frame(t_pdp_v4l* x){ + if (ioctl(x->x_tvfd, VIDIOCMCAPTURE, &x->x_vmmap[x->x_frame]) < 0){ + if (errno == EAGAIN) + post("pdp_v4l: can't sync (no video source?)\n"); + else + perror("pdp_v4l: VIDIOCMCAPTURE"); + if (ioctl(x->x_tvfd, VIDIOCMCAPTURE, &x->x_vmmap[x->x_frame]) < 0) + perror("pdp_v4l: VIDIOCMCAPTURE2"); + + post("pdp_v4l: frame %d %d, format %d, width %d, height %d", + x->x_frame, x->x_vmmap[x->x_frame].frame, x->x_vmmap[x->x_frame].format, + x->x_vmmap[x->x_frame].width, x->x_vmmap[x->x_frame].height); + + pdp_v4l_close(x); + return; + } +} + + +static void *pdp_v4l_thread(void *voidx) +{ + t_pdp_v4l *x = ((t_pdp_v4l *)voidx); + + + /* flip buffers */ + x->x_frame ^= 0x1; + + /* capture with a double buffering scheme */ + while (x->x_continue_thread){ + + /* schedule capture command for next frame */ + pdp_v4l_capture_frame(x); + + /* wait until previous capture is ready */ + x->x_frame ^= 0x1; + pdp_v4l_sync_frame(x); + + /* setup pointers for main thread */ + x->x_frame_ready = 1; + x->x_last_frame = x->x_frame; + + } + + return 0; +} + +static void pdp_v4l_setlegaldim(t_pdp_v4l *x, int xx, int yy); + +static void pdp_v4l_open(t_pdp_v4l *x, t_symbol *name) +{ + /* open a v4l device and allocate a buffer */ + + unsigned int size; + int i; + + unsigned int width, height; + + + /* if already opened -> close */ + if (x->x_initialized) pdp_v4l_close(x); + + + /* exit if retried too much */ + if (!x->x_open_retry){ + post("pdp_v4l: retry count reached zero for %s", name->s_name); + post("pdp_v4l: try to open manually"); + return; + } + + post("pdp_v4l: opening %s", name->s_name); + + x->x_device = name; + + if ((x->x_tvfd = open(name->s_name, O_RDWR)) < 0) + { + post("pdp_v4l: error:"); + perror(name->s_name); + goto closit; + } + + + if (ioctl(x->x_tvfd, VIDIOCGCAP, &x->x_vcap) < 0) + { + perror("get capabilities"); + goto closit; + } + + post("pdp_v4l: cap: name %s type %d channels %d maxw %d maxh %d minw %d minh %d", + x->x_vcap.name, x->x_vcap.type, x->x_vcap.channels, x->x_vcap.maxwidth, x->x_vcap.maxheight, + x->x_vcap.minwidth, x->x_vcap.minheight); + + x->x_minwidth = pdp_imageproc_legalwidth(x->x_vcap.minwidth); + x->x_maxwidth = pdp_imageproc_legalwidth_round_down(x->x_vcap.maxwidth); + x->x_minheight = pdp_imageproc_legalheight(x->x_vcap.minheight); + x->x_maxheight = pdp_imageproc_legalheight_round_down(x->x_vcap.maxheight); + + + if (ioctl(x->x_tvfd, VIDIOCGPICT, &x->x_vpicture) < 0) + { + perror("VIDIOCGCAP"); + goto closit; + } + + post("pdp_v4l: picture: brightness %d depth %d palette %d", + x->x_vpicture.brightness, x->x_vpicture.depth, x->x_vpicture.palette); + + /* get channel info */ + for (i = 0; i < x->x_vcap.channels; i++) + { + x->x_vchannel.channel = i; + if (ioctl(x->x_tvfd, VIDIOCGCHAN, &x->x_vchannel) < 0) + { + perror("VDIOCGCHAN"); + goto closit; + } + post("pdp_v4l: channel %d name %s type %d flags %d", + x->x_vchannel.channel, x->x_vchannel.name, + x->x_vchannel.type, x->x_vchannel.flags); + } + + /* switch to the desired channel */ + if (x->x_channel < 0) x->x_channel = 0; + if (x->x_channel >= x->x_vcap.channels) x->x_channel = x->x_vcap.channels - 1; + + x->x_vchannel.channel = x->x_channel; + if (ioctl(x->x_tvfd, VIDIOCGCHAN, &x->x_vchannel) < 0) + { + perror("pdp_v4l: warning: VDIOCGCHAN"); + post("pdp_v4l: cant change to channel %d",x->x_channel); + + // ignore error + // goto closit; + } + else{ + post("pdp_v4l: switched to channel %d", x->x_channel); + } + + x->x_vchannel.norm = x->x_norm; + if (ioctl(x->x_tvfd, VIDIOCSCHAN, &x->x_vchannel) < 0) + { + perror("pdp_v4l: warning: VDIOCSCHAN"); + post("pdp_v4l: cant change to norm %d",x->x_norm); + + // ignore error + // goto closit; + } + + if (x->x_freq > 0){ + if (ioctl(x->x_tvfd, VIDIOCSFREQ, &x->x_freq) < 0) + perror ("couldn't set frequency :"); + } + + + + + /* get mmap numbers */ + if (ioctl(x->x_tvfd, VIDIOCGMBUF, &x->x_vmbuf) < 0) + { + perror("pdp_v4l: VIDIOCGMBUF"); + goto closit; + } + post("pdp_v4l: buffer size %d, frames %d, offset %d %d", x->x_vmbuf.size, + x->x_vmbuf.frames, x->x_vmbuf.offsets[0], x->x_vmbuf.offsets[1]); + if (!(x->x_videobuf = (unsigned char *) + mmap(0, x->x_vmbuf.size, PROT_READ|PROT_WRITE, MAP_SHARED, x->x_tvfd, 0))) + { + perror("pdp_v4l: mmap"); + goto closit; + } + + pdp_v4l_setlegaldim(x, x->x_width, x->x_height); + width = x->x_width; + height = x->x_height; + + for (i = 0; i < NBUF; i++) + { + //x->x_vmmap[i].format = VIDEO_PALETTE_YUV420P; + //x->x_vmmap[i].format = VIDEO_PALETTE_UYVY; + x->x_vmmap[i].width = width; + x->x_vmmap[i].height = height; + x->x_vmmap[i].frame = i; + } + + + //goto test; + + //try yuv planar format + x->x_v4l_palette = VIDEO_PALETTE_YUV420P; + for (i = 0; i < NBUF; i++) x->x_vmmap[i].format = x->x_v4l_palette; + if (ioctl(x->x_tvfd, VIDIOCMCAPTURE, &x->x_vmmap[x->x_frame]) < 0) + { + if (errno == EAGAIN) + post("pdp_v4l: can't sync (no video source?)"); + } + else{ + post("pdp_v4l: using VIDEO_PALETTE_YUV420P"); + goto capture_ok; + } + + + //try VIDEO_PALETTE_YUV422 format + x->x_v4l_palette = VIDEO_PALETTE_YUV422; + for (i = 0; i < NBUF; i++) x->x_vmmap[i].format = x->x_v4l_palette; + if (ioctl(x->x_tvfd, VIDIOCMCAPTURE, &x->x_vmmap[x->x_frame]) < 0) + { + if (errno == EAGAIN) + post("pdp_v4l: can't sync (no video source?)"); + } + else{ + post("pdp_v4l: using VIDEO_PALETTE_YUV422"); + goto capture_ok; + } + + + test: + + //try rgb packed format + x->x_v4l_palette = VIDEO_PALETTE_RGB24; + for (i = 0; i < NBUF; i++) x->x_vmmap[i].format = x->x_v4l_palette; + if (ioctl(x->x_tvfd, VIDIOCMCAPTURE, &x->x_vmmap[x->x_frame]) < 0) + { + if (errno == EAGAIN) + post("pdp_v4l: can't sync (no video source?)"); + } + else{ + post("pdp_v4l: using VIDEO_PALETTE_RGB24"); + goto capture_ok; + } + + + // none of the formats are supported + perror("pdp_v4l: VIDIOCMCAPTURE: format not supported"); + goto closit; + + + capture_ok: + + post("pdp_v4l: frame %d %d, format %d, width %d, height %d", + x->x_frame, x->x_vmmap[x->x_frame].frame, x->x_vmmap[x->x_frame].format, + x->x_vmmap[x->x_frame].width, x->x_vmmap[x->x_frame].height); + + x->x_width = width; + x->x_height = height; + + post("pdp_v4l: Opened video connection (%dx%d)",x->x_width,x->x_height); + + + /* do some pwc specific inits */ + pdp_v4l_pwc_init(x); + + + x->x_initialized = true; + + /* create thread */ + x->x_continue_thread = 1; + x->x_frame_ready = 0; + pthread_create(&x->x_thread_id, 0, pdp_v4l_thread, x); + + return; + closit: + pdp_v4l_close_error(x); + +} + +static void pdp_v4l_open_manual(t_pdp_v4l *x, t_symbol *name) +{ + x->x_open_retry = PDP_XV_RETRIES; + pdp_v4l_open(x, name); +} + + +static void pdp_v4l_channel(t_pdp_v4l *x, t_float f) +{ + int channel = (float)f; + + if (x->x_initialized){ + pdp_v4l_close(x); + x->x_channel = channel; + pdp_v4l_open(x, x->x_device); + } + else + x->x_channel = channel; +} + + +static void pdp_v4l_freq(t_pdp_v4l *x, t_float f) +{ + int freq = (int)f; + + x->x_freq = freq; + if (x->x_freq > 0){ + if (ioctl(x->x_tvfd, VIDIOCSFREQ, &x->x_freq) < 0) + perror ("couldn't set frequency :"); + //else {post("pdp_v4l: tuner frequency: %f MHz", f / 16.0f);} + } + +} + +static void pdp_v4l_freqMHz(t_pdp_v4l *x, t_float f) +{ + pdp_v4l_freq(x, f*16.0f); +} + + +static void pdp_v4l_bang(t_pdp_v4l *x) +{ + + /* if initialized, grab a frame and output it */ + + unsigned int w,h,nbpixels,packet_size,plane1,plane2; + unsigned char *newimage; + int object,length,pos,i,encoding; + t_pdp* header; + t_image* image; + short int * data; + + + static short int gain[4] = {0x7fff, 0x7fff, 0x7fff, 0x7fff}; + + if (!(x->x_initialized)){ + post("pdp_v4l: no device opened"); + + if (x->x_auto_open){ + post("pdp_v4l: attempting auto open"); + pdp_v4l_open(x, x->x_device); + if (!(x->x_initialized)){ + post("pdp_v4l: auto open failed"); + return; + } + } + + else return; + } + + + /* do nothing if there is no frame ready */ + if((!x->x_frame_ready) && (x->x_only_new_frames)) return; + x->x_frame_ready = 0; + + /* get the address of the "other" frame */ + newimage = x->x_videobuf + x->x_vmbuf.offsets[x->x_last_frame]; + + /* create new packet */ + w = x->x_width; + h = x->x_height; + + //nbpixels = w * h; + +/* + switch(x->x_pdp_image_type){ + case PDP_IMAGE_GREY: + packet_size = nbpixels << 1; + break; + case PDP_IMAGE_YV12: + packet_size = (nbpixels + (nbpixels >> 1)) << 1; + break; + default: + packet_size = 0; + post("pdp_v4l: internal error"); + } +*/ + + //packet_size = (nbpixels + (nbpixels >> 1)) << 1; + + + //plane1 = nbpixels; + //plane2 = nbpixels + (nbpixels>>2); + + object = pdp_packet_new_image(PDP_IMAGE_YV12, w, h); + header = pdp_packet_header(object); + image = pdp_packet_image_info(object); + + if (!header){ + post("pdp_v4l: ERROR: can't allocate packet"); + return; + } + + data = (short int *) pdp_packet_data(object); + newimage = x->x_videobuf + x->x_vmbuf.offsets[x->x_frame ^ 0x1]; + + + /* convert data to pdp packet */ + + switch(x->x_v4l_palette){ + case VIDEO_PALETTE_YUV420P: + pdp_llconv(newimage, RIF_YUV__P411_U8, data, RIF_YVU__P411_S16, w, h); + break; + + /* long live standards. v4l's rgb is in fact ogl's bgr */ + case VIDEO_PALETTE_RGB24: + pdp_llconv(newimage, RIF_BGR__P____U8, data, RIF_YVU__P411_S16, w, h); + break; + + case VIDEO_PALETTE_YUV422: + pdp_llconv(newimage, RIF_YUYV_P____U8, data, RIF_YVU__P411_S16, w, h); + break; + + + default: + post("pdp_v4l: unsupported palette"); + break; + } + +/* + if (PDP_IMAGE_YV12 == x->x_pdp_image_type){ + pixel_unpack_u8s16_y(&newimage[0], data, nbpixels>>3, x->x_state_data->gain); + pixel_unpack_u8s16_uv(&newimage[plane1], &data[plane2], nbpixels>>5, x->x_state_data->gain); + pixel_unpack_u8s16_uv(&newimage[plane2], &data[plane1], nbpixels>>5, x->x_state_data->gain); + } +*/ + //x->x_v4l_palette = VIDEO_PALETTE_YUV420P; + //x->x_v4l_palette = VIDEO_PALETTE_RGB24; + +/* + + else if(PDP_IMAGE_GREY == x->x_pdp_image_type){ + pixel_unpack_u8s16_y(&newimage[0], data, nbpixels>>3, x->x_state_data->gain); + } +*/ + //post("pdp_v4l: mark unused %d", object); + + pdp_packet_pass_if_valid(x->x_outlet0, &object); + +} + + +static void pdp_v4l_setlegaldim(t_pdp_v4l *x, int xx, int yy) +{ + + unsigned int w,h; + + w = pdp_imageproc_legalwidth((int)xx); + h = pdp_imageproc_legalheight((int)yy); + + w = (w < x->x_maxwidth) ? w : x->x_maxwidth; + w = (w > x->x_minwidth) ? w : x->x_minwidth; + + h = (h < x->x_maxheight) ? h : x->x_maxheight; + h = (h > x->x_minheight) ? h : x->x_minheight; + + x->x_width = w; + x->x_height = h; +} + +static void pdp_v4l_dim(t_pdp_v4l *x, t_floatarg xx, t_floatarg yy) +{ + if (x->x_initialized){ + pdp_v4l_close(x); + pdp_v4l_setlegaldim(x, (int)xx, (int)yy); + pdp_v4l_open(x, x->x_device); + + } + else{ + pdp_v4l_setlegaldim(x, (int)xx, (int)yy); + } +} + + +static void pdp_v4l_free(t_pdp_v4l *x) +{ + pdp_v4l_close(x); +} + +t_class *pdp_v4l_class; + + + +void *pdp_v4l_new(void) +{ + t_pdp_v4l *x = (t_pdp_v4l *)pd_new(pdp_v4l_class); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + x->x_initialized = false; + + + x->x_tvfd = -1; + x->x_frame = 0; + x->x_last_frame = 0; + + x->x_framerate = 27; + + x->x_auto_open = true; + x->x_device = gensym("/dev/video0"); + + x->x_continue_thread = 0; + x->x_only_new_frames = 1; + + x->x_width = 320; + x->x_height = 240; + +// pdp_v4l_type(x, gensym("yv12")); + + + x->x_open_retry = PDP_XV_RETRIES; + + x->x_channel = 0; + x->x_freq = -1; //don't set freq by default + + x->x_minwidth = pdp_imageproc_legalwidth(0); + x->x_maxwidth = pdp_imageproc_legalwidth_round_down(0x7fffffff); + x->x_minheight = pdp_imageproc_legalheight(0); + x->x_maxheight = pdp_imageproc_legalheight_round_down(0x7fffffff); + + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_v4l_setup(void) +{ + + + pdp_v4l_class = class_new(gensym("pdp_v4l"), (t_newmethod)pdp_v4l_new, + (t_method)pdp_v4l_free, sizeof(t_pdp_v4l), 0, A_NULL); + + + class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_audio, gensym("audio"), A_NULL); + class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_close_manual, gensym("close"), A_NULL); + class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_open_manual, gensym("open"), A_SYMBOL, A_NULL); + class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_channel, gensym("channel"), A_FLOAT, A_NULL); + class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_dim, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_freq, gensym("freq"), A_FLOAT, A_NULL); + class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_freqMHz, gensym("freqMHz"), A_FLOAT, A_NULL); + + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_io/pdp_xv.c b/modules/image_io/pdp_xv.c new file mode 100644 index 0000000..e2d864b --- /dev/null +++ b/modules/image_io/pdp_xv.c @@ -0,0 +1,300 @@ +/* + * Pure Data Packet module. Xvideo image packet output + * 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. + * + */ + +// pdp stuff +#include "pdp.h" +#include "pdp_base.h" + +// some x window glue code +#include "pdp_xwindow.h" + + +#define PDP_XV_AUTOCREATE_RETRY 10 + + +typedef struct pdp_xv_struct +{ + t_object x_obj; + + t_pdp_xwindow x_xwin; + t_pdp_xvideo x_xvid; + + t_outlet *x_outlet; + + int x_packet0; + int x_queue_id; + t_symbol *x_display; + + Display *x_dpy; + + int x_initialized; + int x_autocreate; + + +} t_pdp_xv; + + +static void pdp_xv_cursor(t_pdp_xv *x, t_floatarg f) +{ + pdp_xwindow_cursor(&x->x_xwin, f); +} + + +static void pdp_xv_destroy(t_pdp_xv* x) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + if (x->x_initialized){ + + pdp_procqueue_finish(q, x->x_queue_id); // wait for thread to finish + x->x_queue_id = -1; + pdp_xvideo_close(&x->x_xvid); // close xvideo connection + pdp_xwindow_close(&x->x_xwin); // close the window + XCloseDisplay(x->x_dpy); // close the display connection + x->x_dpy = 0; + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + x->x_initialized = 0; + + } +} + + + +static void pdp_xv_create(t_pdp_xv* x) +{ + int i; + if(x->x_initialized) return; + + /* manually open a display */ + if (NULL == (x->x_dpy = XOpenDisplay(x->x_display->s_name))){ + post("pdp_xv: cant open display %s\n",x->x_display->s_name); + x->x_initialized = false; + return; + } + + /* open an xv port on the display */ + if (!(x->x_initialized = pdp_xvideo_open_on_display(&x->x_xvid, x->x_dpy))) goto exit_close_dpy; + + /* create a window on the display */ + if (!(x->x_initialized = pdp_xwindow_create_on_display(&x->x_xwin, x->x_dpy))) goto exit_close_win; + + /* done */ + return; + + /* cleanup exits */ + exit_close_win: + pdp_xwindow_close(&x->x_xwin); // cose window + exit_close_dpy: + XCloseDisplay(x->x_dpy); // close display + x->x_dpy = 0; +} + +static int pdp_xv_try_autocreate(t_pdp_xv *x) +{ + + if (x->x_autocreate){ + post("pdp_xv: autocreate window"); + pdp_xv_create(x); + if (!(x->x_initialized)){ + x->x_autocreate--; + if (!x->x_autocreate){ + post ("pdp_xv: autocreate failed %d times: disabled", PDP_XV_AUTOCREATE_RETRY); + post ("pdp_xv: send [autocreate 1] message to re-enable"); + return 0; + } + } + else return 1; + + } + return 0; +} + +static void pdp_xv_bang(t_pdp_xv *x); + +static void pdp_xv_bang_thread(t_pdp_xv *x) +{ + pdp_xvideo_display_packet(&x->x_xvid, &x->x_xwin, x->x_packet0); +} + + +static void pdp_xv_bang_callback(t_pdp_xv *x) +{ + /* receive events & output them */ + pdp_xwindow_send_events(&x->x_xwin, x->x_outlet); + + + /* release the packet if there is one */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + +} + +static void pdp_xv_bang(t_pdp_xv *x) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + + /* check if window is initialized */ + if (!(x->x_initialized)){ + if (!pdp_xv_try_autocreate(x)) return; + } + + /* check if we can proceed */ + if (-1 != x->x_queue_id) return; + if (-1 == x->x_packet0) return; + + /* if previous queued method returned + schedule a new one, else ignore */ + if (-1 == x->x_queue_id) { + pdp_procqueue_add(q, x, pdp_xv_bang_thread, pdp_xv_bang_callback, &x->x_queue_id); + } + +} + +static void pdp_xv_input_0(t_pdp_xv *x, t_symbol *s, t_floatarg f) +{ + + if (s == gensym("register_ro")) pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("bitmap/yv12/*")); + if (s == gensym("process")) pdp_xv_bang(x); + +} + + + +static void pdp_xv_autocreate(t_pdp_xv *x, t_floatarg f) +{ + if (f != 0.0f) x->x_autocreate = PDP_XV_AUTOCREATE_RETRY; + else x->x_autocreate = 0; +} + +static void pdp_xv_display(t_pdp_xv *x, t_symbol *s) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + pdp_procqueue_finish(q, x->x_queue_id); + x->x_queue_id = -1; + x->x_display = s; + if (x->x_initialized){ + pdp_xv_destroy(x); + pdp_xv_create(x); + } +} + +static void pdp_xv_fullscreen(t_pdp_xv *x) +{ + pdp_xwindow_fullscreen(&x->x_xwin); +} + +static void pdp_xv_resize(t_pdp_xv* x, t_floatarg width, t_floatarg height) +{ + pdp_xwindow_resize(&x->x_xwin, width, height); +} + +static void pdp_xv_move(t_pdp_xv* x, t_floatarg width, t_floatarg height) +{ + pdp_xwindow_move(&x->x_xwin, width, height); +} + +static void pdp_xv_moveresize(t_pdp_xv* x, t_floatarg xoff, t_floatarg yoff, t_floatarg width, t_floatarg height) +{ + pdp_xwindow_moveresize(&x->x_xwin, xoff, yoff, width, height); +} + +static void pdp_xv_tile(t_pdp_xv* x, t_floatarg xtiles, t_floatarg ytiles, t_floatarg i, t_floatarg j) +{ + pdp_xwindow_tile(&x->x_xwin, xtiles, ytiles, i, j); +} + +static void pdp_xv_vga(t_pdp_xv *x) +{ + pdp_xv_resize(x, 640, 480); +} + +static void pdp_xv_free(t_pdp_xv *x) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + pdp_procqueue_finish(q, x->x_queue_id); + pdp_xv_destroy(x); + pdp_xvideo_free(&x->x_xvid); + pdp_xwindow_free(&x->x_xwin); +} + +t_class *pdp_xv_class; + + + +void *pdp_xv_new(void) +{ + t_pdp_xv *x = (t_pdp_xv *)pd_new(pdp_xv_class); + x->x_outlet = outlet_new(&x->x_obj, &s_anything); + + pdp_xwindow_init(&x->x_xwin); + pdp_xvideo_init(&x->x_xvid); + + x->x_packet0 = -1; + x->x_queue_id = -1; + x->x_display = gensym(":0"); + x->x_dpy = 0; + pdp_xv_autocreate(x,1); + + return (void *)x; +} + + + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_xv_setup(void) +{ + pdp_xv_class = class_new(gensym("pdp_xv"), (t_newmethod)pdp_xv_new, + (t_method)pdp_xv_free, sizeof(t_pdp_xv), 0, A_NULL); + + + class_addmethod(pdp_xv_class, (t_method)pdp_xv_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_create, gensym("open"), A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_create, gensym("create"), A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_autocreate, gensym("autocreate"), A_FLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_destroy, gensym("destroy"), A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_destroy, gensym("close"), A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_resize, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_move, gensym("move"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_move, gensym("pos"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_resize, gensym("size"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_display, gensym("display"), A_SYMBOL, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_cursor, gensym("cursor"), A_FLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_fullscreen, gensym("fullscreen"), A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_moveresize, gensym("posdim"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_xv_class, (t_method)pdp_xv_tile, gensym("tile"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + + /* some shortcuts for the lazy */ + class_addmethod(pdp_xv_class, (t_method)pdp_xv_vga, gensym("vga"), A_NULL); + +} + +#ifdef __cplusplus +} +#endif + + diff --git a/modules/image_special/Makefile b/modules/image_special/Makefile new file mode 100644 index 0000000..7c0f59d --- /dev/null +++ b/modules/image_special/Makefile @@ -0,0 +1,15 @@ +current: all_modules + +include ../../Makefile.config + +PDP_MOD = pdp_chrot.o pdp_grey2mask.o pdp_scale.o pdp_scan.o \ + pdp_scanxy.o pdp_scope.o pdp_slice_cut.o pdp_slice_glue.o \ + pdp_cog.o pdp_histo.o pdp_array.o + +# build special case image (and sound) processing modules +all_modules: $(PDP_MOD) + +clean: + rm -f *~ + rm -f *.o + diff --git a/modules/image_special/README b/modules/image_special/README new file mode 100644 index 0000000..208710f --- /dev/null +++ b/modules/image_special/README @@ -0,0 +1,2 @@ +This directory contains image processors that don't fit into the basic and io categories. + diff --git a/modules/image_special/pdp_array.c b/modules/image_special/pdp_array.c new file mode 100644 index 0000000..41ea0d5 --- /dev/null +++ b/modules/image_special/pdp_array.c @@ -0,0 +1,194 @@ +/*
+ * Pure Data Packet module.
+ * Copyright (c) 2003 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 <math.h>
+
+#include "pdp.h"
+#include "pdp_base.h"
+
+
+typedef struct _pdp_array
+{
+ t_object x_obj;
+ t_symbol *x_array_sym;
+ t_outlet *x_outlet; // for array->pdp
+ t_int x_rows;
+
+ /* the packet */
+ int x_packet0;
+
+} t_pdp_array;
+
+
+static void pdp_array_bang(t_pdp_array *x)
+{
+ post("not implemented");
+}
+
+
+static void pdp_array_input_0(t_pdp_array *x, t_symbol *s, t_floatarg f)
+{
+ int packet = (int)f;
+
+
+ /* register */
+ if (s == gensym("register_ro")){
+ /* replace if not compatible or we are not interpolating */
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = pdp_packet_convert_ro(packet, pdp_gensym("image/grey/*"));
+ }
+
+ /* process */
+ if (s == gensym("process")){
+ float *vec;
+ int nbpoints;
+ t_garray *a;
+ t_pdp *header = pdp_packet_header(x->x_packet0);
+ short int *data = pdp_packet_data(x->x_packet0);
+ if (!header || !data) return;
+
+ /* dump to array if possible */
+ if (!x->x_array_sym){
+ }
+
+ /* check if array is valid */
+ else if (!(a = (t_garray *)pd_findbyclass(x->x_array_sym, garray_class))){
+ post("pdp_array: %s: no such array", x->x_array_sym->s_name);
+ }
+ /* get data */
+ else if (!garray_getfloatarray(a, &nbpoints, &vec)){
+ post("pdp_array: %s: bad template", x->x_array_sym->s_name);
+ }
+ /* scale and dump in array */
+ else{
+ int i;
+ int w = header->info.image.width;
+ int h = header->info.image.height;
+ int N = w*h;
+ N = (nbpoints < N) ? nbpoints : N;
+
+ /* scan rows */
+ if (x->x_rows){
+ for (i=0; i<N; i++)
+ vec[i] = (float)data[i] * (1.0f / (float)0x8000);
+ }
+ /* scan columns */
+ else{
+ for (i=0; i<N; i++) {
+ int x = i / h;
+ int y = i % h;
+ vec[i] = (float)data[x+(h-y-1)*w] * (1.0f / (float)0x8000);
+ }
+ }
+ //garray_redraw(a);
+ }
+
+ }
+}
+
+static void pdp_array_array(t_pdp_array *x, t_symbol *s)
+{
+ //post("setting symbol %x", s);
+ x->x_array_sym = s;
+ x->x_packet0 = -1;
+}
+
+
+static void pdp_array_free(t_pdp_array *x)
+{
+ pdp_packet_mark_unused(x->x_packet0);
+}
+
+
+t_class *pdp_array2grey_class;
+t_class *pdp_grey2array_class;
+
+
+
+void *pdp_array2grey_new(t_symbol *s, t_symbol *r)
+{
+ t_pdp_array *x = (t_pdp_array *)pd_new(pdp_array2grey_class);
+ pdp_array_array(x, s);
+ return (void *)x;
+}
+
+void *pdp_grey2array_new(t_symbol *s, t_symbol *r)
+{
+ t_pdp_array *x = (t_pdp_array *)pd_new(pdp_grey2array_class);
+ pdp_array_array(x, s);
+ if (r == gensym("rows")){
+ x->x_rows = 1;
+ post("pdp_grey2array: scanning rows");
+ }
+ else {
+ x->x_rows = 0;
+ post("pdp_grey2array: scanning columns");
+ }
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_array_setup(void)
+{
+
+ pdp_array2grey_class = class_new(gensym("pdp_array2grey"),
+ (t_newmethod)pdp_array2grey_new,
+ (t_method)pdp_array_free,
+ sizeof(t_pdp_array),
+ 0, A_DEFSYMBOL, A_DEFSYMBOL, A_NULL);
+
+ pdp_grey2array_class = class_new(gensym("pdp_grey2array"),
+ (t_newmethod)pdp_grey2array_new,
+ (t_method)pdp_array_free,
+ sizeof(t_pdp_array),
+ 0, A_DEFSYMBOL, A_DEFSYMBOL, A_NULL);
+
+
+ /* packet input */
+ class_addmethod(pdp_grey2array_class,
+ (t_method)pdp_array_input_0,
+ gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+
+ /* bang method */
+ class_addmethod(pdp_array2grey_class,
+ (t_method)pdp_array_bang, gensym("bang"), A_NULL);
+
+
+ /* bookkeeping */
+ class_addmethod(pdp_array2grey_class, (t_method)pdp_array_array,
+ gensym("array"), A_SYMBOL, A_NULL);
+ class_addmethod(pdp_grey2array_class, (t_method)pdp_array_array,
+ gensym("array"), A_SYMBOL, A_NULL);
+
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
diff --git a/modules/image_special/pdp_chrot.c b/modules/image_special/pdp_chrot.c new file mode 100644 index 0000000..27993c6 --- /dev/null +++ b/modules/image_special/pdp_chrot.c @@ -0,0 +1,144 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_base.h" +#include <math.h> + + +typedef struct pdp_chrot_struct +{ + t_pdp_base x_base; + + float x_matrix[4]; + void *x_crot2d; + +} t_pdp_chrot; + + + +static void pdp_chrot_process(t_pdp_chrot *x) +{ + int packet; + t_pdp *header; + void *data; + unsigned int w,h,size,v_offset; + short int *idata; + + + /* get packet & info */ + packet = pdp_base_get_packet(x, 0); + header = pdp_packet_header(packet); + data = pdp_packet_data (packet); + + /* only process if we have a vlid yv12 image */ + if ((header) && (PDP_IMAGE == header->type) && (PDP_IMAGE_YV12 == header->info.image.encoding)){ + + w = header->info.image.width; + h = header->info.image.height; + + size = w*h; + v_offset = size; + + idata = (short int *)data; + + + /* color rotation for 2 colour planes */ + pdp_imageproc_crot2d_process(x->x_crot2d, idata + v_offset, w>>1, h>>1); + + } + return; +} + + +static void pdp_chrot_setelement(t_pdp_chrot *x, int element, float f) +{ + x->x_matrix[element] = f; + +} + +static void pdp_chrot_angle_radians(t_pdp_chrot *x, t_floatarg angle) +{ + float c = cos(angle); + float s = sin(angle); + + pdp_chrot_setelement(x, 0, c); + pdp_chrot_setelement(x, 1, s); + pdp_chrot_setelement(x, 2, -s); + pdp_chrot_setelement(x, 3, c); + + pdp_imageproc_crot2d_setmatrix(x->x_crot2d, x->x_matrix); +} + +static void pdp_chrot_angle_degrees(t_pdp_chrot *x, t_floatarg angle) +{ + pdp_chrot_angle_radians(x, (angle * (M_PI / 180.f))); + +} + +static void pdp_chrot_free(t_pdp_chrot *x) +{ + pdp_base_free(x); + pdp_imageproc_crot2d_delete(x->x_crot2d); +} + +t_class *pdp_chrot_class; + + + +void *pdp_chrot_new(t_floatarg f) +{ + t_pdp_chrot *x = (t_pdp_chrot *)pd_new(pdp_chrot_class); + + pdp_base_init(x); + pdp_base_add_gen_inlet(x, gensym("float"), gensym("angle")); + pdp_base_add_pdp_outlet(x); + pdp_base_set_process_method(x, (t_pdp_method)pdp_chrot_process); + + x->x_crot2d = pdp_imageproc_crot2d_new(); + pdp_chrot_angle_radians(x, 0.0f); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_chrot_setup(void) +{ + + + pdp_chrot_class = class_new(gensym("pdp_chrot"), (t_newmethod)pdp_chrot_new, + (t_method)pdp_chrot_free, sizeof(t_pdp_chrot), 0, A_DEFFLOAT, A_NULL); + + pdp_base_setup(pdp_chrot_class); + class_addmethod(pdp_chrot_class, (t_method)pdp_chrot_angle_degrees, gensym("angle"), A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_special/pdp_cog.c b/modules/image_special/pdp_cog.c new file mode 100644 index 0000000..c1ea1bf --- /dev/null +++ b/modules/image_special/pdp_cog.c @@ -0,0 +1,243 @@ +/*
+ * Pure Data Packet module.
+ * Copyright (c) 2003 by Johannes Taelman <johannes.taelman@rug.ac.be>
+ * API updates 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.h"
+#include "pdp_base.h"
+#include <math.h>
+
+typedef struct pdp_cog_struct
+{
+ t_object x_obj;
+ t_float x_f;
+
+ t_outlet *x_outlet0;
+ t_outlet *x_outlet1;
+ t_outlet *x_outlet2;
+ t_outlet *x_outlet3;
+ t_outlet *x_outlet4;
+
+ int x_packet0;
+ int x_threshold;
+ int x_do_thresholding;
+} t_pdp_cog;
+
+
+static void _pdp_cog_perform(t_pdp_cog *x, int width, int height, short int *data0)
+{
+ short int *pp;
+ int nbpixels;
+
+ int y;
+ int h,v;
+
+ int rowsums[width];
+ int columnsums[height];
+
+ float vsum,vvar,vcog,vstd;
+ float hsum,hvar,hcog,hstd;
+
+
+ pp=data0;
+
+ nbpixels=width*height;
+
+ for (h=0;h<width;h++)
+ columnsums[h]=0;
+
+ /* create column & row sums from thresholded data */
+ if (x->x_do_thresholding){
+ for (v=0;v<height;v++){
+ int rs=0;
+ for (h=0;h<width;h++){
+ int d=*pp++;
+
+ d=(d>x->x_threshold) ? d : ((d<-x->x_threshold)?(-d):0);
+ columnsums[h]+= d;
+ rs+=d;
+ }
+ rowsums[v]=rs;
+ }
+ }
+
+ /* don't perform thresholding */
+ else{
+ for (v=0;v<height;v++){
+ int rs=0;
+ for (h=0;h<width;h++){
+ int d=*pp++;
+ columnsums[h]+= d;
+ rs+=d;
+ }
+ rowsums[v]=rs;
+ }
+ }
+
+
+ /* compute vertical mean and standard dev */
+ vsum=1;
+ vvar=height*height/4;
+ vcog=height/2;
+ for (v=0;v<height;v++){
+ float d=rowsums[v];
+ vsum+=d;
+ vcog+=d*v;
+ }
+ vcog/=vsum;
+ for (v=0;v<height;v++){
+ float d=rowsums[v];
+ float f=v-vcog;
+ vvar+=d*f*f;
+ }
+ vstd=sqrt(vvar/vsum)/height;
+
+ /* compute horizontal meaan and standard dev */
+ hsum=1;
+ hvar=width*width/4;
+ hcog=width/2;
+ for (h=0;h<width;h++){
+ float d=columnsums[h];
+ hsum+=d;
+ hcog+=d*h;
+ }
+ hcog/=hsum;
+ for (h=0;h<width;h++){
+ float d=columnsums[h];
+ float f=h-hcog;
+ hvar+=d*f*f;
+ }
+ hstd=sqrt(hvar/hsum)/width;
+
+ /* pass it on */
+ outlet_float(x->x_outlet4,vstd);
+ outlet_float(x->x_outlet3,hstd);
+ outlet_float(x->x_outlet2,vcog/height);
+ outlet_float(x->x_outlet1,hcog/width);
+ outlet_float(x->x_outlet0,(1.0f / (float)(0x7fff)) * hsum/(height*width));
+}
+
+
+// packet is an image/*/* packet or invalid */
+static void pdp_cog_perform(t_pdp_cog *x)
+{
+ t_pdp *header0 = pdp_packet_header(x->x_packet0);
+ void *data0 = pdp_packet_data(x->x_packet0);
+ if (!header0 || !data0) return;
+
+ _pdp_cog_perform(x,
+ header0->info.image.width,
+ header0->info.image.height,
+ data0);
+}
+
+
+
+static void pdp_cog_input_0(t_pdp_cog *x, t_symbol *s, t_floatarg f)
+{
+ int packet = (int)f;
+
+ /* register */
+ if (s == gensym("register_ro")){
+ /* replace if not compatible or we are not interpolating */
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = pdp_packet_convert_ro(packet, pdp_gensym("image/*/*"));
+
+ }
+
+ if (s == gensym("process")){
+ pdp_cog_perform(x);
+ }
+
+}
+
+
+static void pdp_cog_threshold(t_pdp_cog *x, t_floatarg f)
+{
+ x->x_threshold=(int) (f * ((float) 0x7fff));
+}
+
+
+
+static void pdp_cog_free(t_pdp_cog *x)
+{
+ pdp_packet_mark_unused(x->x_packet0);
+}
+
+
+t_class *pdp_cog_class;
+
+
+
+void *pdp_cog_new(void)
+{
+
+ t_pdp_cog *x = (t_pdp_cog *)pd_new(pdp_cog_class);
+
+
+ x->x_outlet0 = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet2 = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet3 = outlet_new(&x->x_obj, &s_float);
+ x->x_outlet4 = outlet_new(&x->x_obj, &s_float);
+
+ x->x_packet0 = -1;
+ x->x_do_thresholding = 0;
+
+ return (void *)x;
+}
+
+void *pdp_cog_abs_thresh_new(t_floatarg f)
+{
+ t_pdp_cog *x = (t_pdp_cog *)pdp_cog_new();
+ inlet_new((void *)x, &x->x_obj.ob_pd, gensym("float"),gensym("threshold"));
+ pdp_cog_threshold(x, f);
+ x->x_do_thresholding = 1;
+ return (void *)x;
+}
+
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_cog_setup(void)
+{
+
+ pdp_cog_class = class_new(gensym("pdp_cog"), (t_newmethod)pdp_cog_new,
+ (t_method)pdp_cog_free, sizeof(t_pdp_cog), 0,A_NULL);
+
+ class_addcreator((t_newmethod)pdp_cog_abs_thresh_new, gensym("pdp_cog_abs_thresh"), A_DEFFLOAT, A_NULL);
+
+ class_addmethod(pdp_cog_class, (t_method)pdp_cog_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+
+ class_addmethod(pdp_cog_class, (t_method)pdp_cog_threshold, gensym("threshold"),A_DEFFLOAT, A_NULL);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
diff --git a/modules/image_special/pdp_grey2mask.c b/modules/image_special/pdp_grey2mask.c new file mode 100644 index 0000000..4f10772 --- /dev/null +++ b/modules/image_special/pdp_grey2mask.c @@ -0,0 +1,195 @@ +/* + * 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. + * + */ + + +/* this module converts a greyscale image or the luma channel of a colour image + to a colour image intensity mask, usable for multiplication */ + +#include "pdp.h" +#include "pdp_resample.h" + +typedef struct pdp_grey2mask_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + + int x_packet0; + int x_dropped; + int x_queue_id; + + +} t_pdp_grey2mask; + + + +static void pdp_grey2mask_process_grey(t_pdp_grey2mask *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data (x->x_packet0); + t_pdp *newheader = 0; + short int *newdata = 0; + int newpacket = -1; + + unsigned int w = header->info.image.width; + unsigned int h = header->info.image.height; + + unsigned int size = w*h; + unsigned int totalnbpixels = size; + unsigned int u_offset = size; + unsigned int v_offset = size + (size>>2); + + unsigned int row, col; + + newpacket = pdp_packet_new_image_YCrCb(w, h); + newheader = pdp_packet_header(newpacket); + newdata = (short int *)pdp_packet_data(newpacket); + + /* copy luma channel */ + memcpy(newdata, data, size * sizeof(s16)); + + /* subsample luma -> chroma channel */ + pdp_resample_halve(data, newdata+u_offset, w, h); + + /* copy this to the other chroma channel */ + memcpy(newdata+v_offset, newdata+u_offset, (size>>2)*sizeof(s16)); + + /* delete source packet and replace with new packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = newpacket; + return; +} + +static void pdp_grey2mask_process_yv12(t_pdp_grey2mask *x) +{ + /* process only the luminance channel */ + pdp_grey2mask_process_grey(x); +} + + + +static void pdp_grey2mask_process(t_pdp_grey2mask *x) +{ + int encoding; + t_pdp *header = 0; + + /* check if image data packets are compatible */ + if ( (header = pdp_packet_header(x->x_packet0)) + && (PDP_IMAGE == header->type)){ + + /* pdp_grey2mask_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding){ + + case PDP_IMAGE_YV12: + pdp_grey2mask_process_yv12(x); + break; + + case PDP_IMAGE_GREY: + pdp_grey2mask_process_grey(x); + break; + + default: + /* don't know the type, so dont pdp_grey2mask_process */ + + break; + } + } +} + +static void pdp_grey2mask_sendpacket(t_pdp_grey2mask *x) +{ + /* unregister and propagate if valid packet */ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet0); +} + +static void pdp_grey2mask_input_0(t_pdp_grey2mask *x, t_symbol *s, t_floatarg f) +{ + + int p = (int)f; + + if (s== gensym("register_ro")) x->x_dropped = pdp_packet_copy_ro_or_drop(&x->x_packet0, p); + + + if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped)){ + + + /* add the process method and callback to the process queue */ + + //pdp_queue_add(x, pdp_grey2mask_process, pdp_grey2mask_sendpacket, &x->x_queue_id); + // since the process method creates a packet, this is not processed in the thread + // $$$TODO: fix this + pdp_grey2mask_process(x); + pdp_grey2mask_sendpacket(x); + } + +} + + + +static void pdp_grey2mask_free(t_pdp_grey2mask *x) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + pdp_procqueue_finish(q, x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + +} + +t_class *pdp_grey2mask_class; + + + +void *pdp_grey2mask_new(void) +{ + int i; + + t_pdp_grey2mask *x = (t_pdp_grey2mask *)pd_new(pdp_grey2mask_class); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_packet0 = -1; + x->x_queue_id = -1; + + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_grey2mask_setup(void) +{ + + + pdp_grey2mask_class = class_new(gensym("pdp_grey2mask"), (t_newmethod)pdp_grey2mask_new, + (t_method)pdp_grey2mask_free, sizeof(t_pdp_grey2mask), 0, A_NULL); + + + class_addmethod(pdp_grey2mask_class, (t_method)pdp_grey2mask_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_special/pdp_histo.c b/modules/image_special/pdp_histo.c new file mode 100644 index 0000000..43cdb8c --- /dev/null +++ b/modules/image_special/pdp_histo.c @@ -0,0 +1,415 @@ +/*
+ * Pure Data Packet module.
+ * Copyright (c) 2003 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.h"
+#include "pdp_base.h"
+#include <math.h>
+
+struct _pdp_histo;
+typedef void (*t_histo_proc)(struct _pdp_histo *);
+
+
+typedef struct _pdp_histo
+{
+ t_object x_obj;
+ t_int x_logN;
+ t_symbol *x_array_sym;
+ t_float x_scale;
+ t_int x_debug;
+ t_int x_sample_size; /* pointcloud size */
+ t_histo_proc x_process_method; /* what to do with the histogram */
+ t_outlet *x_outlet0;
+ int x_matrix_output;
+
+ /* the packet */
+ int x_packet0;
+
+ /* packet data */
+ short int *x_data;
+ int x_width;
+ int x_height;
+ int x_nb_pixels;
+
+ /* histo data for processor: these are stored on the stack */
+ int *x_histo;
+
+} t_pdp_histo;
+
+
+static int round_up_2log(int i)
+{
+ int l = 0;
+ i--;
+ while (i) {
+ i >>= 1;
+ l++;
+ }
+ //post("log is %d, 2^n is %d", l, 1 << l);
+
+ l = (l < 16) ? l : 15;
+ return l;
+}
+
+
+static void dump_to_array(t_pdp_histo *x)
+{
+ float *vec;
+ int nbpoints;
+ t_garray *a;
+ int i;
+ int *histo = x->x_histo;
+ int N = 1 << (x->x_logN);
+ float scale = 1.0f / (float)(x->x_nb_pixels);
+
+
+ /* dump to array if possible */
+ if (!x->x_array_sym){
+ }
+
+ /* check if array is valid */
+ else if (!(a = (t_garray *)pd_findbyclass(x->x_array_sym, garray_class))){
+ post("pdp_histo: %s: no such array", x->x_array_sym->s_name);
+ }
+ /* get data */
+ else if (!garray_getfloatarray(a, &nbpoints, &vec)){
+ post("pdp_histo: %s: bad template", x->x_array_sym->s_name);
+ }
+ /* scale and dump in array */
+ else{
+
+ N = (nbpoints < N) ? nbpoints : N;
+ for (i=0; i<N; i++) vec[i] = (float)(histo[i]) * scale * x->x_scale;
+ //garray_redraw(a);
+ }
+
+}
+
+static void get_sampleset(t_pdp_histo *x, int log_tmp_size, int threshold)
+{
+ int N = 1 << log_tmp_size;
+ int mask = N-1;
+ int index, nbpoints, i;
+ t_atom a[2];
+ double scalex = 1.0f / (double)(x->x_width);
+ double scaley = 1.0f / (double)(x->x_height);
+ t_symbol *s = gensym("list");
+ int matrix_packet;
+ double *mat_data;
+
+ /* store the offsets of the points in a in an oversized array
+ the oversizing is to eliminate a division and to limit the
+ searching for a free location after a random index is generated */
+
+ int offset[N];
+
+ /* reset the array */
+ memset(offset, -1, N * sizeof(int));
+
+ /* get the coordinates of the tempsize brightest points
+ and store them in a random location in the hash */
+ for (i=0; i<x->x_nb_pixels; i++){
+ if (x->x_data[i] >= threshold){
+ /* get a random index */
+ int ri = random();
+ //int ri = 0;
+ /* find an empty spot to store it */
+ while (-1 != offset[ri & mask]) ri++;
+ offset[ri & mask] = i;
+ }
+ }
+
+
+ /* repack the array to get the requested
+ sample size at the start */
+ index = 0;
+ nbpoints = 0;
+ while (nbpoints < x->x_sample_size){
+ while (-1 == offset[index]) index++; // ffwd to next nonepty slot
+ offset[nbpoints++] = offset[index++]; // move slot
+ }
+
+
+ /* MATRIX OUTPUT */
+ if (x->x_matrix_output){
+
+ matrix_packet = pdp_packet_new_matrix(x->x_sample_size, 2, PDP_MATRIX_TYPE_RDOUBLE);
+ mat_data = pdp_packet_data(matrix_packet);
+ if (mat_data){
+
+ /* build the cluster data struct */
+ for (i=0; i<x->x_sample_size; i++){
+ mat_data[2*i] = ((double)(offset[i] % x->x_width)) * scalex;
+ mat_data[2*i+1] = ((double)(offset[i] / x->x_width)) * scaley;
+ }
+
+ pdp_pass_if_valid(x->x_outlet0, &matrix_packet);
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = -1;
+ }
+
+ }
+
+ /* IMAGE OUTPUT */
+ else {
+
+ /* get rw copy */
+ pdp_packet_replace_with_writable(&x->x_packet0);
+ x->x_data = pdp_packet_data(x->x_packet0);
+
+ /* mark output packet samples */
+ if (x->x_data){
+ memset(x->x_data, 0, 2*x->x_nb_pixels);
+ for (i=0; i<x->x_sample_size; i++){
+ x->x_data[offset[i]] = 0x7fff;
+ }
+ }
+
+ /* send packet to left outlet */
+ pdp_pass_if_valid(x->x_outlet0, &x->x_packet0);
+ }
+
+
+}
+
+static void get_brightest(t_pdp_histo *x)
+{
+ int i;
+ int *histo = x->x_histo;
+ int N = 1 << (x->x_logN);
+
+ int index, nsamps;
+
+ /* check requested size */
+ if (x->x_sample_size > x->x_nb_pixels){
+ post("WARNING: more samples requested than pixels in image");
+ x->x_sample_size = x->x_nb_pixels;
+ }
+
+
+ /* find limiting index */
+ index = N;
+ nsamps = 0;
+ while (nsamps < x->x_sample_size){
+ index--;
+ nsamps += histo[index];
+ }
+
+ /* status report */
+ if (x->x_debug){
+ post("found %d samples between h[%d] and h[%d]", nsamps, index, N-1);
+ }
+
+ /* get a representative set from the candidates
+ the tempbuf is the rounded log of the nb of samples + 1
+ so it is at least 50% sparse */
+ get_sampleset(x, round_up_2log(nsamps) + 1, index << (15-x->x_logN));
+
+}
+
+
+static void _pdp_histo_perform(t_pdp_histo *x)
+{
+ short int *pp;
+ int N = 1 << x->x_logN;
+ int nbpixels = x->x_width * x->x_height, i;
+
+ int histo[N];
+
+ /* init */
+ for (i=0; i<N; i++) histo[i] = 0;
+
+ /* build histo */
+ for (i=0; i<nbpixels; i++){
+ int index = x->x_data[i] >> (15 - x->x_logN);
+ if (index < 0) index = 0; /* negative -> zero */
+ histo[index]++;
+ }
+
+ /* save the histo stack location */
+ x->x_histo = histo;
+
+ /* print it */
+ if (x->x_debug){
+ post("histogram:");
+ for (i=0; i<N; i++){
+ fprintf(stderr, "%d\t", histo[i]);
+ if (!(i % 10)) post("");
+ }
+ post("");
+ }
+
+ /* call the processor */
+ x->x_process_method(x);
+
+
+}
+
+
+// packet is an image/*/* packet or invalid */
+static void pdp_histo_perform(t_pdp_histo *x)
+{
+ t_pdp *header0 = pdp_packet_header(x->x_packet0);
+ void *data0 = pdp_packet_data(x->x_packet0);
+ if (!header0 || !data0) return;
+
+ x->x_width = header0->info.image.width;
+ x->x_height = header0->info.image.height;
+ x->x_nb_pixels = x->x_width * x->x_height;
+ x->x_data = data0;
+
+ _pdp_histo_perform(x);
+}
+
+
+
+static void pdp_histo_input_0(t_pdp_histo *x, t_symbol *s, t_floatarg f)
+{
+ int packet = (int)f;
+
+ /* register */
+ if (s == gensym("register_rw")){
+ /* replace if not compatible or we are not interpolating */
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = pdp_packet_convert_ro(packet, pdp_gensym("image/grey/*"));
+
+ }
+
+ if (s == gensym("process")){
+ pdp_histo_perform(x);
+ }
+
+}
+
+
+
+static void pdp_histo_samplesize(t_pdp_histo *x, t_floatarg f)
+{
+ int i = (int)f;
+ if (i > 0) x->x_sample_size = i;
+}
+
+
+static void pdp_histo_scale(t_pdp_histo *x, t_floatarg f){x->x_scale = f;}
+
+
+
+static void pdp_histo_size(t_pdp_histo *x, t_floatarg f)
+{
+ int i = (int)f;
+ if (i < 1) return;
+ x->x_logN = round_up_2log(i);
+}
+
+
+static void pdp_histo_array(t_pdp_histo *x, t_symbol *s)
+{
+ //post("setting symbol %x", s);
+ x->x_array_sym = s;
+}
+
+
+static void pdp_histo_free(t_pdp_histo *x)
+{
+ pdp_packet_mark_unused(x->x_packet0);
+}
+
+
+t_class *pdp_histo_class;
+
+
+
+void *pdp_histo_new(t_floatarg f)
+{
+
+ t_pdp_histo *x = (t_pdp_histo *)pd_new(pdp_histo_class);
+ if (f == 0.0f) f = 64;
+ pdp_histo_size(x, f);
+ x->x_packet0 = -1;
+ x->x_debug = 0;
+ x->x_sample_size = 16;
+ return (void *)x;
+}
+
+
+void *pdp_histo_array_new(t_symbol *s, t_float f, t_float f2)
+{
+ t_pdp_histo *x = (t_pdp_histo *)pdp_histo_new(f);
+ if (f2 == 0.0f) f2 = 1.0f;
+ pdp_histo_scale(x, f2);
+ pdp_histo_array(x, s);
+ x->x_process_method = dump_to_array;
+ return (void *)x;
+}
+
+void *pdp_histo_sample_new(t_float nbsamples, t_float histosize)
+{
+ t_pdp_histo *x;
+ if (histosize == 0.0f) histosize = 256.0f;
+ x = (t_pdp_histo *)pdp_histo_new(histosize);
+ if (nbsamples == 0.0f) nbsamples = 16.0f;
+ pdp_histo_samplesize(x, nbsamples);
+ x->x_process_method = get_brightest;
+ x->x_outlet0 = outlet_new(&x->x_obj, gensym("anything"));
+ x->x_matrix_output = 0;
+
+ inlet_new((t_object *)x, (t_pd *)&x->x_obj, gensym("float"), gensym("nbpoints"));
+
+ return (void *)x;
+}
+
+void *pdp_histo_sample_matrix_new(t_float nbsamples, t_float histosize)
+{
+ t_pdp_histo *x = pdp_histo_sample_new(nbsamples, histosize);
+ if (x) x->x_matrix_output = 1;
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_histo_setup(void)
+{
+
+ pdp_histo_class = class_new(gensym("pdp_histo"), (t_newmethod)pdp_histo_array_new,
+ (t_method)pdp_histo_free, sizeof(t_pdp_histo), 0, A_DEFSYMBOL, A_DEFFLOAT, A_DEFFLOAT, A_NULL);
+
+ class_addcreator((t_newmethod)pdp_histo_sample_new, gensym("pdp_pointcloud"), A_DEFFLOAT, A_DEFFLOAT, A_NULL);
+ class_addcreator((t_newmethod)pdp_histo_sample_matrix_new, gensym("pdp_pointcloud_matrix"), A_DEFFLOAT, A_DEFFLOAT, A_NULL);
+
+ class_addmethod(pdp_histo_class, (t_method)pdp_histo_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+
+ class_addmethod(pdp_histo_class, (t_method)pdp_histo_size, gensym("size"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_histo_class, (t_method)pdp_histo_size, gensym("scale"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_histo_class, (t_method)pdp_histo_array, gensym("array"), A_SYMBOL, A_NULL);
+ class_addmethod(pdp_histo_class, (t_method)pdp_histo_samplesize, gensym("nbpoints"), A_FLOAT, A_NULL);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
diff --git a/modules/image_special/pdp_scale.c b/modules/image_special/pdp_scale.c new file mode 100644 index 0000000..3c74bd8 --- /dev/null +++ b/modules/image_special/pdp_scale.c @@ -0,0 +1,277 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_resample.h" + + + +typedef struct pdp_scale_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + + + int x_packet0; + int x_packet1; + int x_dropped; + int x_queue_id; + + unsigned int x_width; + unsigned int x_height; + int x_quality; + + +} t_pdp_scale; + + +static void pdp_scale_process_yv12(t_pdp_scale *x) +{ + t_pdp *header0 = pdp_packet_header(x->x_packet0); + t_pdp *header1 = pdp_packet_header(x->x_packet1); + void *data0 = pdp_packet_data (x->x_packet0); + void *data1 = pdp_packet_data (x->x_packet1); + + unsigned int src_w = header0->info.image.width; + unsigned int src_h = header0->info.image.height; + + unsigned int dst_w = header1->info.image.width; + unsigned int dst_h = header1->info.image.height; + + short int *src_image = (short int *)data0; + short int *dst_image = (short int *)data1; + + unsigned int src_size = src_w*src_h; + unsigned int src_voffset = src_size; + unsigned int src_uoffset = src_size + (src_size>>2); + + unsigned int dst_size = dst_w*dst_h; + unsigned int dst_voffset = dst_size; + unsigned int dst_uoffset = dst_size + (dst_size>>2); + + if (x->x_quality){ + pdp_resample_scale_bilin(src_image, dst_image, src_w, src_h, dst_w, dst_h); + pdp_resample_scale_bilin(src_image+src_voffset, dst_image+dst_voffset, src_w>>1, src_h>>1, dst_w>>1, dst_h>>1); + pdp_resample_scale_bilin(src_image+src_uoffset, dst_image+dst_uoffset, src_w>>1, src_h>>1, dst_w>>1, dst_h>>1); + } + else{ + pdp_resample_scale_nn(src_image, dst_image, src_w, src_h, dst_w, dst_h); + pdp_resample_scale_nn(src_image+src_voffset, dst_image+dst_voffset, src_w>>1, src_h>>1, dst_w>>1, dst_h>>1); + pdp_resample_scale_nn(src_image+src_uoffset, dst_image+dst_uoffset, src_w>>1, src_h>>1, dst_w>>1, dst_h>>1); + } + + return; +} + +static void pdp_scale_process_grey(t_pdp_scale *x) +{ + + t_pdp *header0 = pdp_packet_header(x->x_packet0); + t_pdp *header1 = pdp_packet_header(x->x_packet1); + void *data0 = pdp_packet_data (x->x_packet0); + void *data1 = pdp_packet_data (x->x_packet1); + + unsigned int src_w = header0->info.image.width; + unsigned int src_h = header0->info.image.height; + + unsigned int dst_w = header1->info.image.width; + unsigned int dst_h = header1->info.image.height; + + short int *src_image = (short int *)data0; + short int *dst_image = (short int *)data1; + + if (x->x_quality) pdp_resample_scale_bilin(src_image, dst_image, src_w, src_h, dst_w, dst_h); + else pdp_resample_scale_nn(src_image, dst_image, src_w, src_h, dst_w, dst_h); + + return; + + +} + +static void pdp_scale_sendpacket(t_pdp_scale *x) +{ + /* delete source packet */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + + /* unregister and propagate if valid dest packet */ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet1); +} + +static void pdp_scale_process(t_pdp_scale *x) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + t_pdp *header0 = pdp_packet_header(x->x_packet0); + + /* check data packets */ + + if ((header0) && (PDP_IMAGE == header0->type)){ + + /* if dims are equal, just send the packet */ + if ((header0->info.image.width == x->x_width) + && (header0->info.image.height == x->x_height)){ + x->x_packet1 = x->x_packet0; + x->x_packet0 = -1; + pdp_scale_sendpacket(x); + return; + } + + /* type hub */ + switch(header0->info.image.encoding){ + + case PDP_IMAGE_YV12: + x->x_packet1 = pdp_packet_new_image_YCrCb(x->x_width, x->x_height); + if(x->x_packet1 == -1){ + post("pdp_scale: can't allocate packet"); + return; + } + pdp_procqueue_add(q, x, pdp_scale_process_yv12, pdp_scale_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + x->x_packet1 = pdp_packet_new_image_grey(x->x_width, x->x_height); + if(x->x_packet1 == -1){ + post("pdp_scale: can't allocate packet"); + return; + } + pdp_procqueue_add(q, x, pdp_scale_process_grey, pdp_scale_sendpacket, &x->x_queue_id); + break; + + default: + break; + /* don't know the type, so dont process */ + + } + } + +} + + + + +static void pdp_scale_input_0(t_pdp_scale *x, t_symbol *s, t_floatarg f) +{ + + int p = (int)f; + int passes, i; + + if (s== gensym("register_rw")) x->x_dropped = pdp_packet_copy_ro_or_drop(&x->x_packet0, p); + + + if ((s == gensym("process")) && (-1 != x->x_packet0) && (!x->x_dropped)){ + + /* add the process method and callback to the process queue */ + pdp_scale_process(x); + + } + +} + + + + +static void pdp_scale_width(t_pdp_scale *x, t_floatarg f) +{ + int i = (int)f; + if (i < 32) i = 32; + x->x_width = i; +} + +static void pdp_scale_height(t_pdp_scale *x, t_floatarg f) +{ + int i = (int)f; + if (i < 32) i = 32; + x->x_height = i; +} + + +static void pdp_scale_dim(t_pdp_scale *x, t_floatarg w, t_floatarg h) +{ + pdp_scale_width(x, w); + pdp_scale_height(x, h); +} + +static void pdp_scale_quality(t_pdp_scale *x, t_floatarg f) +{ + if (f==0) x->x_quality = 0; + if (f==1) x->x_quality = 1; +} + + +t_class *pdp_scale_class; + + + +void pdp_scale_free(t_pdp_scale *x) +{ + t_pdp_procqueue *q = pdp_queue_get_queue(); + pdp_procqueue_finish(q, x->x_queue_id); + pdp_packet_mark_unused(x->x_packet0); + pdp_packet_mark_unused(x->x_packet1); +} + +void *pdp_scale_new(t_floatarg fw, t_floatarg fh) +{ + t_pdp_scale *x = (t_pdp_scale *)pd_new(pdp_scale_class); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_packet1 = -1; + x->x_queue_id = -1; + + if ((fw != 0.0f) && (fh != 0.0f)) pdp_scale_dim(x, fw, fh); + else pdp_scale_dim(x, 320, 240); + + pdp_scale_quality(x, 1); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_scale_setup(void) +{ + + + pdp_scale_class = class_new(gensym("pdp_scale"), (t_newmethod)pdp_scale_new, + (t_method)pdp_scale_free, sizeof(t_pdp_scale), 0, A_DEFFLOAT, A_DEFFLOAT, A_NULL); + + + class_addmethod(pdp_scale_class, (t_method)pdp_scale_quality, gensym("quality"), A_FLOAT, A_NULL); + class_addmethod(pdp_scale_class, (t_method)pdp_scale_width, gensym("width"), A_FLOAT, A_NULL); + class_addmethod(pdp_scale_class, (t_method)pdp_scale_height, gensym("height"), A_FLOAT, A_NULL); + class_addmethod(pdp_scale_class, (t_method)pdp_scale_dim, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_scale_class, (t_method)pdp_scale_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_special/pdp_scan.c b/modules/image_special/pdp_scan.c new file mode 100644 index 0000000..9b80fea --- /dev/null +++ b/modules/image_special/pdp_scan.c @@ -0,0 +1,231 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_mmx.h" +#include <math.h> + +#define PDP_SCAN_COSTABLE_SIZE 1024 +static float pdp_cos[PDP_SCAN_COSTABLE_SIZE]; + +typedef struct pdp_scan_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + t_outlet *x_outlet1; + + float x_centerx; + float x_centery; + float x_sizeh; + float x_sizev; + + int x_packet0; + int x_packet1; + + int x_interpolate; + + +} t_pdp_scan; + + +static t_int *pdp_scan_perform(t_int *w) +{ + + t_pdp_scan *x = (t_pdp_scan *)(w[1]); + t_int n = (t_int)(w[2]); + t_float *in = (float *)(w[3]); + t_float *out = (float *)(w[4]); + + + /* check if valid image */ + if (-1 == x->x_packet0){ + while (n--) *out++ = 0; + return (w+5); + } + else{ + + t_pdp *header0 = pdp_packet_header(x->x_packet0); + short int *data0 = (short int *)pdp_packet_data (x->x_packet0); + short int *data1 = (short int *)pdp_packet_data (x->x_packet1); + int width = (float)header0->info.image.width; + float widthm1 = (float)header0->info.image.width - 1; + float heightm1 = (float)header0->info.image.height - 1; + int i; + + float scale = 1.0f / 32767.0f; + + if (x->x_interpolate && (-1 != x->x_packet1)){ + float a_old = 1.0f; + float a_new = 0.0f; + float a_inc = 1.0f / (float)n; + float old, new; + + while(n--){ + float phase = *in++; + int iphase = (int)(phase * PDP_SCAN_COSTABLE_SIZE); + float c = pdp_cos[iphase & (PDP_SCAN_COSTABLE_SIZE - 1)]; + float s = pdp_cos[(iphase - (PDP_SCAN_COSTABLE_SIZE>>1)) & (PDP_SCAN_COSTABLE_SIZE - 1)]; + int xxx = (int)((x->x_centerx + x->x_sizeh * c) * widthm1); + int yyy = (int)((x->x_centery + x->x_sizev * c) * heightm1); + int offset = yyy*width+xxx; + new = ((float)(data0[offset])) * scale; + old = ((float)(data1[offset])) * scale; + *out++ = a_old * old + a_new * new; + a_new += a_inc; + a_old -= a_inc; + } + + pdp_packet_mark_unused(x->x_packet1); + x->x_packet1 = -1; + } + else{ + while(n--){ + float phase = *in++; + int iphase = (int)(phase * PDP_SCAN_COSTABLE_SIZE); + float c = pdp_cos[iphase & (PDP_SCAN_COSTABLE_SIZE - 1)]; + float s = pdp_cos[(iphase - (PDP_SCAN_COSTABLE_SIZE>>1)) & (PDP_SCAN_COSTABLE_SIZE - 1)]; + int xxx = (int)((x->x_centerx + x->x_sizeh * c) * widthm1); + int yyy = (int)((x->x_centery + x->x_sizev * c) * heightm1); + *out++ = ((float)(data0[yyy*width+xxx])) * scale; + } + } + + return (w+5); + + } +} + + + + +static void pdp_scan_input_0(t_pdp_scan *x, t_symbol *s, t_floatarg f) +{ + int packet = (int)f; + + /* register */ + if (s== gensym("register_ro")){ + t_pdp *header = pdp_packet_header(packet); + if (!header) return; + if (PDP_IMAGE != header->type) return; + if ((header->info.image.encoding != PDP_IMAGE_YV12) && (header->info.image.encoding != PDP_IMAGE_GREY)) return; + + /* replace if not compatible or we are not interpolating */ + if (!x->x_interpolate || (!pdp_packet_image_compat(x->x_packet0, packet))){ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = pdp_packet_copy_ro(packet); + } + /* otherwize keep the old one */ + else{ + pdp_packet_mark_unused(x->x_packet1); + x->x_packet1 = x->x_packet0; + x->x_packet0 = pdp_packet_copy_ro(packet); + } + } + + /* pass packet */ + if (s== gensym("process")){ + //if (-1 != x->x_packet0) outlet_pdp (x->x_outlet0, x->x_packet0); + } + + +} + + + + +static void pdp_scan_dsp (t_pdp_scan *x, t_signal **sp) +{ + dsp_add(pdp_scan_perform, 4, x, sp[0]->s_n, sp[0]->s_vec, sp[1]->s_vec); + +} + + +static void pdp_scan_interpolate(t_pdp_scan *x, t_floatarg f) +{ + if (0.0 == f){ + x->x_interpolate = 0; + pdp_packet_mark_unused(x->x_packet1); + } + if (1.0 == f) x->x_interpolate = 1; +} + +static void pdp_scan_free(t_pdp_scan *x) +{ + pdp_packet_mark_unused(x->x_packet0); +} + + +t_class *pdp_scan_class; + + + +void *pdp_scan_new(void) +{ + t_pdp_scan *x = (t_pdp_scan *)pd_new(pdp_scan_class); + + + //x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_outlet1 = outlet_new(&x->x_obj, &s_signal); + x->x_packet0 = -1; + x->x_packet1 = -1; + + x->x_centerx = 0.5f; + x->x_centery = 0.5f; + x->x_sizeh = 0.3; + x->x_sizev = 0.3; + + pdp_scan_interpolate(x, 0); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_scan_setup(void) +{ + int i; + for (i=0; i<PDP_SCAN_COSTABLE_SIZE; i++) + pdp_cos[i] = cos((double)(i) * 2 * M_PI / PDP_SCAN_COSTABLE_SIZE); + + + pdp_scan_class = class_new(gensym("pdp_scan~"), (t_newmethod)pdp_scan_new, + (t_method)pdp_scan_free, sizeof(t_pdp_scan), 0, A_NULL); + + CLASS_MAINSIGNALIN(pdp_scan_class, t_pdp_scan, x_f); + + class_addmethod(pdp_scan_class, (t_method)pdp_scan_interpolate, gensym("interpolate"), A_FLOAT, A_NULL); + class_addmethod(pdp_scan_class, (t_method)pdp_scan_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_scan_class, (t_method)pdp_scan_dsp, gensym("dsp"), A_NULL); + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_special/pdp_scanxy.c b/modules/image_special/pdp_scanxy.c new file mode 100644 index 0000000..6fd7201 --- /dev/null +++ b/modules/image_special/pdp_scanxy.c @@ -0,0 +1,207 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include <math.h> + +typedef struct pdp_scanxy_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + t_outlet *x_outlet1; + + int x_packet0; + int x_packet1; + + int x_interpolate; + + +} t_pdp_scanxy; + + +static t_int *pdp_scanxy_perform(t_int *w) +{ + + t_pdp_scanxy *x = (t_pdp_scanxy *)(w[1]); + t_int n = (t_int)(w[2]); + t_float *inx = (float *)(w[3]); + t_float *iny = (float *)(w[4]); + t_float *out = (float *)(w[5]); + + + /* check if valid image */ + if (-1 == x->x_packet0){ + while (n--) *out++ = 0; + return (w+6); + } + else{ + + t_pdp *header0 = pdp_packet_header(x->x_packet0); + short int *data0 = (short int *)pdp_packet_data (x->x_packet0); + short int *data1 = (short int *)pdp_packet_data (x->x_packet1); + int width = (float)header0->info.image.width; + int height = (float)header0->info.image.height; + int i; + + float scale = 1.0f / 32767.0f; + float scalein = 0x10000; + + if (x->x_interpolate && (-1 != x->x_packet1)){ + float a_old = 1.0f; + float a_new = 0.0f; + float a_inc = 1.0f / (float)n; + float old, new; + + while(n--){ + int xxx = ((((int)(scalein * *inx++)) & 0xffff) * width) >> 16; + int yyy = ((((int)(scalein * *iny++)) & 0xffff) * height) >> 16; + int offset = yyy*width+xxx; + new = ((float)(data0[offset])) * scale; + old = ((float)(data1[offset])) * scale; + *out++ = a_old * old + a_new * new; + a_new += a_inc; + a_old -= a_inc; + } + + pdp_packet_mark_unused(x->x_packet1); + x->x_packet1 = -1; + } + else{ + while(n--){ + int xxx = ((((int)(scalein * *inx++)) & 0xffff) * width) >> 16; + int yyy = ((((int)(scalein * *iny++)) & 0xffff) * height) >> 16; + int offset = yyy*width+xxx; + *out++ = ((float)(data0[offset])) * scale; + } + } + + return (w+6); + + } +} + + + + +static void pdp_scanxy_input_0(t_pdp_scanxy *x, t_symbol *s, t_floatarg f) +{ + int packet = (int)f; + + /* register */ + if (s== gensym("register_ro")){ + t_pdp *header = pdp_packet_header(packet); + if (!header) return; + if (PDP_IMAGE != header->type) return; + if ((header->info.image.encoding != PDP_IMAGE_YV12) && (header->info.image.encoding != PDP_IMAGE_GREY)) return; + + /* replace if not compatible or we are not interpolating */ + if (!x->x_interpolate || (!pdp_packet_image_compat(x->x_packet0, packet))){ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = pdp_packet_copy_ro(packet); + } + /* otherwize keep the old one */ + else{ + pdp_packet_mark_unused(x->x_packet1); + x->x_packet1 = x->x_packet0; + x->x_packet0 = pdp_packet_copy_ro(packet); + } + } + + /* pass packet */ + if (s== gensym("process")){ + //if (-1 != x->x_packet0) outlet_pdp (x->x_outlet0, x->x_packet0); + } + + +} + + + + +static void pdp_scanxy_dsp (t_pdp_scanxy *x, t_signal **sp) +{ + dsp_add(pdp_scanxy_perform, 5, x, sp[0]->s_n, sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec); + +} + + +static void pdp_scanxy_interpolate(t_pdp_scanxy *x, t_floatarg f) +{ + if (0.0 == f){ + x->x_interpolate = 0; + pdp_packet_mark_unused(x->x_packet1); + } + if (1.0 == f) x->x_interpolate = 1; +} + +static void pdp_scanxy_free(t_pdp_scanxy *x) +{ + pdp_packet_mark_unused(x->x_packet0); +} + + +t_class *pdp_scanxy_class; + + + +void *pdp_scanxy_new(void) +{ + t_pdp_scanxy *x = (t_pdp_scanxy *)pd_new(pdp_scanxy_class); + + + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("signal"), gensym("signal")); + x->x_outlet1 = outlet_new(&x->x_obj, &s_signal); + x->x_packet0 = -1; + x->x_packet1 = -1; + + pdp_scanxy_interpolate(x, 0); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_scanxy_setup(void) +{ + + pdp_scanxy_class = class_new(gensym("pdp_scanxy~"), (t_newmethod)pdp_scanxy_new, + (t_method)pdp_scanxy_free, sizeof(t_pdp_scanxy), 0, A_NULL); + + CLASS_MAINSIGNALIN(pdp_scanxy_class, t_pdp_scanxy, x_f); + + class_addmethod(pdp_scanxy_class, (t_method)pdp_scanxy_interpolate, gensym("interpolate"), A_FLOAT, A_NULL); + class_addmethod(pdp_scanxy_class, (t_method)pdp_scanxy_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_scanxy_class, (t_method)pdp_scanxy_dsp, gensym("dsp"), A_NULL); + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_special/pdp_scope.c b/modules/image_special/pdp_scope.c new file mode 100644 index 0000000..4311a0b --- /dev/null +++ b/modules/image_special/pdp_scope.c @@ -0,0 +1,317 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include <string.h> + +#define BUFSIZE 2048 + +typedef struct pdp_scope_data +{ + short int random_seed[4]; + +}t_pdp_scope_data; + +typedef struct pdp_scope_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + + t_pdp_scope_data *x_data; + int x_packet0; + int x_queue_id; + + int x_pdp_image_type; + + unsigned int x_width; + unsigned int x_height; + + float *x_buffer; + int x_needle; + + +} t_pdp_scope; + + + +void pdp_scope_type(t_pdp_scope *x, t_symbol *s) +{ + if (gensym("yv12") == s) {x->x_pdp_image_type = PDP_IMAGE_YV12; return;} + if (gensym("grey") == s) {x->x_pdp_image_type = PDP_IMAGE_GREY; return;} + + x->x_pdp_image_type = -1; + +} + + + + + +static void pdp_scope_createpacket_yv12(t_pdp_scope *x) +{ + t_pdp *header; + + unsigned int w = x->x_width; + unsigned int h = x->x_height; + + unsigned int size = w*h; + unsigned int totalnbpixels = size + (size >> 1); + unsigned int packet_size = totalnbpixels << 1; + + x->x_packet0 = pdp_packet_new_image_YCrCb(w, h); + if(x->x_packet0 == -1){ + post("pdp_scope: can't allocate packet"); + return; + } + header = pdp_packet_header(x->x_packet0); + memset(pdp_packet_data(x->x_packet0), 0, packet_size); + +} + +static void pdp_scope_generate_yv12(t_pdp_scope *x) +{ + unsigned int w = x->x_width; + unsigned int h = x->x_height; + unsigned int size = w*h; + unsigned int totalnbpixels = size + (size >> 1); + short int *data = (short int *) pdp_packet_data(x->x_packet0); + + unsigned int i; + int offset = x->x_needle; + int val; + unsigned int y; + float fh2 = (float)(h/2); + + if (!data) return; + + + for (i=0; i<w; i++){ + y = (h/2) + (int)(fh2 * -x->x_buffer[(offset - w + i) & (BUFSIZE - 1)]); + if (y>=h) y = h-1; + + data[i + y*w] = 0x7fff; + } + + return; + +} + +static void pdp_scope_createpacket_grey(t_pdp_scope *x) +{ + t_pdp *header; + short int *data; + + unsigned int w = x->x_width; + unsigned int h = x->x_height; + + unsigned int size = w*h; + unsigned int totalnbpixels = size; + unsigned int packet_size = totalnbpixels << 1; + + /* create new packet */ + x->x_packet0 = pdp_packet_new_image_grey(w,h); + if(x->x_packet0 == -1){ + post("pdp_scope: can't allocate packet"); + return; + } + + + header = pdp_packet_header(x->x_packet0); + data = (short int *) pdp_packet_data(x->x_packet0); + + memset(pdp_packet_data(x->x_packet0), 0, packet_size); + +} + +static void pdp_scope_generate_grey(t_pdp_scope *x) +{ + unsigned int w = x->x_width; + unsigned int h = x->x_height; + unsigned int totalnbpixels = x->x_width * x->x_height; + short int *data = (short int *) pdp_packet_data(x->x_packet0); + + unsigned int i; + int offset = x->x_needle; + int val; + unsigned int y; + float fh2 = (float)(h/2); + + if (!data) return; + + for (i=0; i<w; i++){ + y = (h/2) + (int)(fh2 * -x->x_buffer[(offset - w + i) & (BUFSIZE - 1)]); + if (y>=h) y = h-1; + + data[i + y*w] = 0x7fff; + } + + return; +} + +static void pdp_scope_sendpacket(t_pdp_scope *x) +{ + /* propagate if valid */ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet0); +} + + +static void pdp_scope_bang(t_pdp_scope *x) +{ + + int encoding; + + /* if we have an active packet, don't do anything */ + if (-1 != x->x_packet0) return; + + switch(x->x_pdp_image_type){ + + case PDP_IMAGE_YV12: + pdp_scope_createpacket_yv12(x); // don't create inside thread!!! + pdp_scope_generate_yv12(x); + pdp_scope_sendpacket(x); + //pdp_queue_add(x, pdp_scope_generate_yv12, pdp_scope_sendpacket, &x->x_queue_id); + break; + + case PDP_IMAGE_GREY: + pdp_scope_createpacket_grey(x); // don't create inside thread!!! + pdp_scope_generate_grey(x); + pdp_scope_sendpacket(x); + //pdp_queue_add(x, pdp_scope_generate_grey, pdp_scope_sendpacket, &x->x_queue_id); + break; + + default: + break; + + } + + + /* release the packet */ + +} + + +static void pdp_scope_dim(t_pdp_scope *x, t_floatarg w, t_floatarg h) +{ + if (w<32.0f) w = 32.0f; + if (h<32.0f) h = 32.0f; + + x->x_width = (unsigned int)w; + x->x_height = (unsigned int)h; +} + + +static void pdp_scope_free(t_pdp_scope *x) +{ + + /* remove callback from process queue */ + t_pdp_procqueue *q = pdp_queue_get_queue(); + pdp_procqueue_finish(q, x->x_queue_id); + + + /* tidy up */ + pdp_packet_mark_unused(x->x_packet0); + pdp_dealloc(x->x_data); + +} +static t_int *pdp_scope_perform(t_int *w) +{ + + + t_float *in = (float *)(w[3]); + t_pdp_scope *x = (t_pdp_scope *)(w[1]); + t_int n = (t_int)(w[2]); + t_int i; + + t_int offset = x->x_needle; + + for (i=0; i<n; i++) + x->x_buffer[(offset+i)&(BUFSIZE-1)] = in[i]; + + x->x_needle = (offset + n ) & (BUFSIZE - 1); + + return (w+4); + +} +static void pdp_scope_dsp(t_pdp_scope *x, t_signal **sp) +{ + dsp_add(pdp_scope_perform, 3, x, sp[0]->s_n, sp[0]->s_vec); + +} + +t_class *pdp_scope_class; + + + + +void *pdp_scope_new(void) +{ + int i; + + t_pdp_scope *x = (t_pdp_scope *)pd_new(pdp_scope_class); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + + x->x_packet0 = -1; + x->x_queue_id = -1; + x->x_width = 320; + x->x_height = 240; + x->x_f = 0.0; + + x->x_data = (t_pdp_scope_data *)pdp_alloc(sizeof(t_pdp_scope_data)); + + pdp_scope_type(x, gensym("yv12")); + + x->x_buffer = (float *)pdp_alloc(sizeof(float) * BUFSIZE); + x->x_needle = 0; + + return (void *)x; +} + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + + +void pdp_scope_setup(void) +{ + + + pdp_scope_class = class_new(gensym("pdp_scope~"), (t_newmethod)pdp_scope_new, + (t_method)pdp_scope_free, sizeof(t_pdp_scope), 0, A_NULL); + + CLASS_MAINSIGNALIN(pdp_scope_class, t_pdp_scope, x_f); + + class_addmethod(pdp_scope_class, (t_method)pdp_scope_type, gensym("type"), A_SYMBOL, A_NULL); + class_addmethod(pdp_scope_class, (t_method)pdp_scope_dim, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(pdp_scope_class, (t_method)pdp_scope_bang, gensym("bang"), A_NULL); + class_addmethod(pdp_scope_class, (t_method)pdp_scope_dsp, gensym("dsp"), 0); +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_special/pdp_slice_cut.c b/modules/image_special/pdp_slice_cut.c new file mode 100644 index 0000000..586f44d --- /dev/null +++ b/modules/image_special/pdp_slice_cut.c @@ -0,0 +1,310 @@ +/* + * 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. + * + */ + + +/* this module converts a greyscale image or the luma channel of a colour image + to a colour image intensity mask, usable for multiplication */ + +#include "pdp.h" +#include "pdp_resample.h" +#include "pdp_imageproc.h" + +typedef struct pdp_slice_cut_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + + int x_packet0; + //int x_dropped; + //int x_queue_id; + + unsigned int x_slice_height; + + +} t_pdp_slice_cut; + + + +static void pdp_slice_cut_process_grey(t_pdp_slice_cut *x) +{ + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data (x->x_packet0); + t_pdp *newheader = 0; + short int *newdata = 0; + int newpacket = -1; + + unsigned int w = header->info.image.width; + unsigned int h = header->info.image.height; + + unsigned int size = w*h; + unsigned int height_left = h; + unsigned int slice_height = 0; + unsigned int slice_yoffset = 0; + unsigned int slice_offset = 0; + unsigned int slice_size_bytes = 0; + + #define min(x,y) (((x)<(y)) ? (x) : (y)) + + while(height_left){ + + /* compute slice size */ + slice_height = min(x->x_slice_height, height_left); + height_left -= slice_height; + slice_size_bytes = (w << 1) * slice_height; + + /* create new slice packet */ + newpacket = pdp_packet_new_image_grey(w, slice_height); + newheader = pdp_packet_header(newpacket); + + if(!newheader){ + post("pdp_slice_cut: can't allocate packet"); + return; + } + + newdata = (s16*)pdp_packet_data(newpacket); + + //newheader->info.image.encoding = PDP_IMAGE_GREY; + //newheader->info.image.width = w; + //newheader->info.image.height = slice_height; + + newheader->info.image.orig_height = h; + newheader->info.image.slice_yoff = slice_yoffset; + + if (slice_height + height_left == h) newheader->info.image.slice_sync = PDP_IMAGE_SLICE_FIRST; + else if (height_left == 0) newheader->info.image.slice_sync = PDP_IMAGE_SLICE_LAST; + else newheader->info.image.slice_sync = PDP_IMAGE_SLICE_BODY; + + /* copy slice data */ + memcpy(newdata, data+slice_offset, slice_size_bytes); + + /* unregister and propagate if valid packet */ + pdp_packet_pass_if_valid(x->x_outlet0, &newpacket); + + /* advance pointer stuff */ + slice_offset += (slice_size_bytes>>1); + slice_yoffset += slice_height; + + } + + + + /* delete source packet when finished */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + return; +} + +static void pdp_slice_cut_process_yv12(t_pdp_slice_cut *x) +{ + + t_pdp *header = pdp_packet_header(x->x_packet0); + short int *data = (short int *)pdp_packet_data (x->x_packet0); + t_pdp *newheader = 0; + short int *newdata = 0; + int newpacket = -1; + + unsigned int w = header->info.image.width; + unsigned int h = header->info.image.height; + + unsigned int size = w*h; + unsigned int height_left = h; + unsigned int slice_height = 0; + unsigned int sequence_number = 0; + unsigned int slice_offset = 0; + unsigned int slice_yoffset = 0; + unsigned int slice_size_bytes = 0; + unsigned int slice_size = 0; + + #define min(x,y) (((x)<(y)) ? (x) : (y)) + + while(height_left){ + + /* compute slice size */ + slice_height = min(x->x_slice_height, height_left); + height_left -= slice_height; + slice_size = w * slice_height; + slice_size_bytes = slice_size << 1; + + /* create new slice packet */ + newpacket = pdp_packet_new_image_YCrCb(w, slice_height); + newheader = pdp_packet_header(newpacket); + + if(!newheader){ + post("pdp_slice_cut: can't allocate packet"); + return; + } + + newdata = (s16*)pdp_packet_data(newpacket); + newheader->info.image.encoding = PDP_IMAGE_YV12; + newheader->info.image.width = w; + newheader->info.image.height = slice_height; + newheader->info.image.orig_height = h; + newheader->info.image.slice_yoff = slice_yoffset; + + if (slice_height + height_left == h) newheader->info.image.slice_sync = PDP_IMAGE_SLICE_FIRST; + else if (height_left == 0) newheader->info.image.slice_sync = PDP_IMAGE_SLICE_LAST; + else newheader->info.image.slice_sync = PDP_IMAGE_SLICE_BODY; + + + /* copy slice data */ + memcpy(newdata, + data + slice_offset, + slice_size_bytes); + + memcpy(newdata + slice_size, + data + size + (slice_offset>>2), + slice_size_bytes>>2); + + memcpy(newdata + slice_size + (slice_size >> 2), + data + size + (size >> 2) + (slice_offset >> 2), + slice_size_bytes>>2); + + /* unregister and propagate if valid packet */ + pdp_packet_pass_if_valid(x->x_outlet0, &newpacket); + + /* advance pointer stuff */ + slice_offset += (slice_size_bytes>>1); + slice_yoffset += slice_height; + + } + + + + /* delete source packet when finished */ + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + return; + +} + + + +static void pdp_slice_cut_process(t_pdp_slice_cut *x) +{ + int encoding; + t_pdp *header = 0; + + + /* check if image data packets are compatible */ + if ( (header = pdp_packet_header(x->x_packet0)) + && (PDP_IMAGE == header->type)){ + + /* pdp_slice_cut_process inputs and write into active inlet */ + switch(pdp_packet_header(x->x_packet0)->info.image.encoding){ + + case PDP_IMAGE_YV12: + pdp_slice_cut_process_yv12(x); + break; + + case PDP_IMAGE_GREY: + pdp_slice_cut_process_grey(x); + break; + + default: + /* don't know the type, so dont pdp_slice_cut_process */ + + break; + } + } +} + +static void pdp_slice_cut_input_0(t_pdp_slice_cut *x, t_symbol *s, t_floatarg f) +{ + + int p = (int)f; + + if (s== gensym("register_ro")) x->x_packet0 = pdp_packet_copy_ro(p); + if ((s == gensym("process")) && (-1 != x->x_packet0)) pdp_slice_cut_process(x); + +} + + + +static void pdp_slice_cut_height(t_pdp_slice_cut *x, t_floatarg f) +{ + int i = (int)f; + x->x_slice_height = pdp_imageproc_legalheight(i); + post("pdp_slice: height set to %d", x->x_slice_height); + +} +static void pdp_slice_cut_forceheight(t_pdp_slice_cut *x, t_floatarg f) +{ + int i = (int)f; + if (i<1) i = 1; + x->x_slice_height = i; + post("pdp_slice: WARNING: forceheight is a debug message. setting this to an abritrary"); + post("pdp_slice: WARNING: value can crash pdp. set it to a multiple of 2 and only use pixel"); + post("pdp_slice: WARNING: operations (no convolution or biquad) in the mmx version."); + post("pdp_slice: height forced to %d", x->x_slice_height); + +} + + +static void pdp_slice_cut_free(t_pdp_slice_cut *x) +{ + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_slice_cut_class; + + + +void *pdp_slice_cut_new(void) +{ + int i; + + t_pdp_slice_cut *x = (t_pdp_slice_cut *)pd_new(pdp_slice_cut_class); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_packet0 = -1; + //x->x_queue_id = -1; + + x->x_slice_height = 8; + post("pdp_slice_cut: WARNING: experimental object"); + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_slice_cut_setup(void) +{ + + + pdp_slice_cut_class = class_new(gensym("pdp_slice_cut"), (t_newmethod)pdp_slice_cut_new, + (t_method)pdp_slice_cut_free, sizeof(t_pdp_slice_cut), 0, A_NULL); + + + class_addmethod(pdp_slice_cut_class, (t_method)pdp_slice_cut_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(pdp_slice_cut_class, (t_method)pdp_slice_cut_height, gensym("height"), A_FLOAT, A_NULL); + class_addmethod(pdp_slice_cut_class, (t_method)pdp_slice_cut_forceheight, gensym("forceheight"), A_FLOAT, A_NULL); + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/image_special/pdp_slice_glue.c b/modules/image_special/pdp_slice_glue.c new file mode 100644 index 0000000..af6d2bd --- /dev/null +++ b/modules/image_special/pdp_slice_glue.c @@ -0,0 +1,215 @@ +/* + * 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. + * + */ + + +/* this module converts a greyscale image or the luma channel of a colour image + to a colour image intensity mask, usable for multiplication */ + +#include "pdp.h" +#include "pdp_resample.h" + +typedef struct pdp_slice_glue_struct +{ + t_object x_obj; + t_float x_f; + + t_outlet *x_outlet0; + + int x_packet0; //input packet + int x_packet1; //output packet + +} t_pdp_slice_glue; + + + +static void pdp_slice_glue_process_grey(t_pdp_slice_glue *x) +{ + t_pdp *header0 = pdp_packet_header(x->x_packet0); + t_pdp *header1 = pdp_packet_header(x->x_packet1); + short int *data0 = (short int *)pdp_packet_data (x->x_packet0); + short int *data1 = (short int *)pdp_packet_data (x->x_packet1); + + + unsigned int offset = header0->info.image.width * header0->info.image.slice_yoff; + unsigned int slice_size = header0->info.image.width * header0->info.image.height; + + memcpy(data1 + offset, data0, slice_size << 1); + + + return; +} + +static void pdp_slice_glue_process_yv12(t_pdp_slice_glue *x) +{ + + t_pdp *header0 = pdp_packet_header(x->x_packet0); + t_pdp *header1 = pdp_packet_header(x->x_packet1); + short int *data0 = (short int *)pdp_packet_data (x->x_packet0); + short int *data1 = (short int *)pdp_packet_data (x->x_packet1); + + unsigned int w = header0->info.image.width; + unsigned int h = header0->info.image.height; + + unsigned int dw = header1->info.image.width; + unsigned int dh = header1->info.image.height; + unsigned int dsize = dh*dw; + + unsigned int offset = w * header0->info.image.slice_yoff; + unsigned int slice_size = w * h; + + memcpy(data1 + offset, data0, slice_size << 1); + memcpy(data1 + dsize + (offset >> 2), data0 + slice_size, slice_size >> 1); + memcpy(data1 + dsize + (dsize >>2) + (offset>>2), data0 + slice_size + (slice_size >> 2), slice_size >> 1); + + + return; + +} + + + +static void pdp_slice_glue_process(t_pdp_slice_glue *x) +{ + int encoding; + int newpacket; + t_pdp *header = 0; + t_pdp *dest_header = 0; + + + /* check if the packet is a valid slice packet */ + /* if not pass it along */ + if (!((header = pdp_packet_header(x->x_packet0)) && + (PDP_IMAGE == header->type) && (header->info.image.orig_height))) goto passalong; + + /* if this is the first slice of a sequence, or we don't have a dest packet yet */ + /* create a new destination packet */ + if ((x->x_packet1 == -1) || (header->info.image.slice_sync & PDP_IMAGE_SLICE_FIRST)){ + pdp_packet_mark_unused(x->x_packet1); + x->x_packet1 = pdp_packet_new_image(header->info.image.encoding, header->info.image.width, header->info.image.orig_height); + dest_header = pdp_packet_header(x->x_packet1); + //dest_header->info.image.encoding = header->info.image.encoding; + //dest_header->info.image.width = header->info.image.width; + //dest_header->info.image.height = header->info.image.orig_height; + } + + /* if this is a body or last slice, it has to be compatible with the current dest packet */ + /* else ignore it */ + else{ + dest_header = pdp_packet_header(x->x_packet1); + if (!((dest_header->info.image.encoding == header->info.image.encoding) && + ((dest_header->info.image.width == header->info.image.width)))) goto dispose; + } + + /* now we have a dest packet and the source packet is compatible with it, so copy the data */ + + + switch(pdp_packet_header(x->x_packet0)->info.image.encoding){ + + case PDP_IMAGE_YV12: + pdp_slice_glue_process_yv12(x); + break; + + case PDP_IMAGE_GREY: + pdp_slice_glue_process_grey(x); + break; + + default: + /* don't know the type, so dont pdp_slice_glue_process */ + + break; + } + + /* if the source packet was a final slice, pass on the constructed packet */ + if (header->info.image.slice_sync & PDP_IMAGE_SLICE_LAST){ + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet1); + } + + + /* processing is done so we delete the source packet */ + dispose: + pdp_packet_mark_unused(x->x_packet0); + x->x_packet0 = -1; + return; + + + passalong: + pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet0); + return; + +} + +static void pdp_slice_glue_input_0(t_pdp_slice_glue *x, t_symbol *s, t_floatarg f) +{ + + int p = (int)f; + + if (s== gensym("register_ro")) x->x_packet0 = pdp_packet_copy_ro(p); + if ((s == gensym("process")) && (-1 != x->x_packet0)) pdp_slice_glue_process(x); + +} + + + + +static void pdp_slice_glue_free(t_pdp_slice_glue *x) +{ + pdp_packet_mark_unused(x->x_packet0); +} + +t_class *pdp_slice_glue_class; + + + +void *pdp_slice_glue_new(void) +{ + int i; + + t_pdp_slice_glue *x = (t_pdp_slice_glue *)pd_new(pdp_slice_glue_class); + + x->x_outlet0 = outlet_new(&x->x_obj, &s_anything); + x->x_packet0 = -1; + + post("pdp_slice_glue: WARNING: experimental object"); + + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_slice_glue_setup(void) +{ + + + pdp_slice_glue_class = class_new(gensym("pdp_slice_glue"), (t_newmethod)pdp_slice_glue_new, + (t_method)pdp_slice_glue_free, sizeof(t_pdp_slice_glue), 0, A_NULL); + + + class_addmethod(pdp_slice_glue_class, (t_method)pdp_slice_glue_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/matrix_basic/Makefile b/modules/matrix_basic/Makefile new file mode 100644 index 0000000..3604041 --- /dev/null +++ b/modules/matrix_basic/Makefile @@ -0,0 +1,12 @@ +current: all_modules + +include ../../Makefile.config + +PDP_MOD = pdp_mat_mul.o pdp_mat_lu.o pdp_mat_vec.o + +all_modules: $(PDP_MOD) + +clean: + rm -f *~ + rm -f *.o + diff --git a/modules/matrix_basic/README b/modules/matrix_basic/README new file mode 100644 index 0000000..d6ece1b --- /dev/null +++ b/modules/matrix_basic/README @@ -0,0 +1,5 @@ +This directory contains "normal" matrix packet processors, +derived from the t_pdp_base class defined in pdp_base.h + +Most modules are wrappers around Gnu Scientific Library (gsl) calls + diff --git a/modules/matrix_basic/clusterstuff.c b/modules/matrix_basic/clusterstuff.c new file mode 100644 index 0000000..432a807 --- /dev/null +++ b/modules/matrix_basic/clusterstuff.c @@ -0,0 +1,540 @@ +/*
+ * Pure Data Packet module.
+ * Copyright (c) 2003 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.h"
+#include "pdp_base.h"
+#include <math.h>
+
+struct _pdp_histo;
+typedef void (*t_histo_proc)(struct _pdp_histo *);
+
+
+/* the cluster struct */
+typedef struct _cluster
+{
+ float N;
+ float cx;
+ float cy;
+} t_cluster;
+
+
+
+typedef struct _pdp_histo
+{
+ t_object x_obj;
+ t_int x_logN;
+ t_symbol *x_array_sym;
+ t_float x_scale;
+ t_int x_debug;
+ t_int x_sample_size; /* pointcloud size */
+ t_int x_nb_clusters; /* nb of clusters */
+ t_cluster *x_cluster; /* cluster data (for tracking) */
+ t_histo_proc x_process_method; /* what to do with the histogram */
+ t_outlet *x_outlet0;
+ t_outlet *x_outlet1;
+
+ /* the packet */
+ int x_packet0;
+
+ /* packet data */
+ short int *x_data;
+ int x_width;
+ int x_height;
+ int x_nb_pixels;
+
+ /* histo data for processor: these are stored on the stack */
+ int *x_histo;
+ int *x_pixel_offset;
+
+} t_pdp_histo;
+
+
+// join 2 clusters. clear the second one
+static void cluster_join(t_cluster *cA, t_cluster *cB)
+{
+ float scale = 1.0f / (cA->N + cB->N);
+ cA->cx = (cA->N * cA->cx + cB->N * cB->cx) * scale;
+ cA->cy = (cA->N * cA->cy + cB->N * cB->cy) * scale;
+ cA->N += cB->N;
+
+ cB->N = 0.0f;
+}
+
+static void cluster_copy(t_cluster *cA, t_cluster *cB)
+{
+ cA->cx = cB->cx;
+ cA->cy = cB->cy;
+ cA->N = cB->N;
+}
+
+static void cluster_clear(t_cluster *c)
+{
+ c->N = 0.0f;
+}
+
+static void cluster_new(t_cluster *c, float x, float y)
+{
+ c->N = 1.0f;
+ c->cx = x;
+ c->cy = y;
+}
+
+static float cluster_dsquared(t_cluster *cA, t_cluster *cB)
+{
+ float dx = cA->cx - cB->cx;
+ float dy = cA->cy - cB->cy;
+ return dx*dx + dy*dy;
+}
+
+
+static int round_up_2log(int i)
+{
+ int l = 0;
+ i--;
+ while (i) {
+ i >>= 1;
+ l++;
+ }
+ //post("log is %d, 2^n is %d", l, 1 << l);
+
+ l = (l < 16) ? l : 15;
+ return l;
+}
+
+
+static void compute_clusters(t_pdp_histo *x)
+{
+ t_cluster c[x->x_sample_size];
+ int i;
+ float scalex = 1.0f / (float)(x->x_width);
+ float scaley = 1.0f / (float)(x->x_height);
+ int nb_clusters = x->x_sample_size;
+
+ /* build the cluster data struct */
+ for (i=0; i<x->x_sample_size; i++)
+ cluster_new(c+i,
+ ((float)(x->x_pixel_offset[i] % x->x_width)) * scalex,
+ ((float)(x->x_pixel_offset[i] / x->x_width)) * scaley);
+
+ /* the clustering loop */
+ while (nb_clusters > x->x_nb_clusters){
+ /* initialize cA, cB, d */
+ int cA=0;
+ int cB=1;
+ float d = cluster_dsquared(c+0, c+1);
+ int i,j;
+
+ /* find the closest 2 clusters:
+ scan the distance matrix above the diagonal */
+ for (i=2; i<nb_clusters; i++){
+ for (j=0; j<i; j++){
+ float dij = cluster_dsquared(c+i, c+j);
+ if (dij < d){
+ cA = j;
+ cB = i;
+ d = dij;
+ }
+ }
+ }
+
+ /* join the two clusters (cA < cB) */
+ cluster_join (c+cA, c+cB);
+
+ /* reduce the distance matrix by moving
+ the last element to the empty spot cB */
+ nb_clusters--;
+ cluster_copy (c+cB, c+nb_clusters);
+ }
+
+ /* copy cluster data */
+ if (!x->x_cluster){
+ int size = sizeof(t_cluster) * x->x_nb_clusters;
+ x->x_cluster = (t_cluster *)pdp_alloc(size);
+ memcpy(x->x_cluster, c, size);
+ }
+ /* or perform tracking */
+ else{
+ int i,j;
+ /* find best matches for the first couple of clusters */
+ for (i=0; i<x->x_nb_clusters - 1; i++){
+ int closest = 0;
+ float d_min = cluster_dsquared(x->x_cluster+i, c);
+
+ /* get closest cluster */
+ for (j=1; j<nb_clusters; j++){
+ float dj = cluster_dsquared(x->x_cluster+i, c+j);
+ if (dj < d_min){
+ closest = j;
+ d_min = dj;
+ }
+ }
+
+ /* replace reference cluster with closest match */
+ cluster_copy(x->x_cluster+i, c+closest);
+
+ /* shrink matrix (like above) */
+ nb_clusters--;
+ cluster_copy(c+closest, c+nb_clusters);
+
+ }
+ /* copy the last cluster */
+ cluster_copy(x->x_cluster + x->x_nb_clusters - 1, c);
+ }
+
+ /* print the clusters */
+ post("clusters:");
+ post("\tN\tcx\tcy");
+ for (i=0; i<x->x_nb_clusters; i++){
+ post("\t%d\t%0.2f\t%0.2f",
+ (int)x->x_cluster[i].N,
+ x->x_cluster[i].cx,
+ x->x_cluster[i].cy);
+ }
+
+
+
+}
+
+static void dump_to_array(t_pdp_histo *x)
+{
+ float *vec;
+ int nbpoints;
+ t_garray *a;
+ int i;
+ int *histo = x->x_histo;
+ int N = 1 << (x->x_logN);
+ float scale = 1.0f / (float)(x->x_nb_pixels);
+
+
+ /* dump to array if possible */
+ if (!x->x_array_sym){
+ }
+
+ /* check if array is valid */
+ else if (!(a = (t_garray *)pd_findbyclass(x->x_array_sym, garray_class))){
+ post("pdp_histo: %s: no such array", x->x_array_sym->s_name);
+ }
+ /* get data */
+ else if (!garray_getfloatarray(a, &nbpoints, &vec)){
+ post("pdp_histo: %s: bad template", x->x_array_sym->s_name);
+ }
+ /* scale and dump in array */
+ else{
+
+ N = (nbpoints < N) ? nbpoints : N;
+ for (i=0; i<N; i++) vec[i] = (float)(histo[i]) * scale * x->x_scale;
+ //garray_redraw(a);
+ }
+
+}
+
+static void get_sampleset(t_pdp_histo *x, int log_tmp_size, int threshold)
+{
+ int N = 1 << log_tmp_size;
+ int mask = N-1;
+ int index, nbpoints, i;
+ t_atom a[2];
+ float scalex = 1.0f / (float)(x->x_width);
+ float scaley = 1.0f / (float)(x->x_height);
+ t_symbol *s = gensym("list");
+
+ /* store the offsets of the points in a in an oversized array
+ the oversizing is to eliminate a division and to limit the
+ searching for a free location after a random index is generated */
+
+ int offset[N];
+
+ /* float versions of the coordinates */
+ float fx[x->x_sample_size];
+ float fy[x->x_sample_size];
+ float max_x, min_x, max_y, min_y;
+
+ /* reset the array */
+ memset(offset, -1, N * sizeof(int));
+
+ /* get the coordinates of the tempsize brightest points
+ and store them in a random location in the hash */
+ for (i=0; i<x->x_nb_pixels; i++){
+ if (x->x_data[i] >= threshold){
+ /* get a random index */
+ int ri = random();
+ //int ri = 0;
+ /* find an empty spot to store it */
+ while (-1 != offset[ri & mask]) ri++;
+ offset[ri & mask] = i;
+ }
+ }
+
+
+ /* repack the array to get the requested
+ sample size at the start */
+ index = 0;
+ nbpoints = 0;
+ while (nbpoints < x->x_sample_size){
+ while (-1 == offset[index]) index++; // ffwd to next nonepty slot
+ offset[nbpoints++] = offset[index++]; // move slot
+ }
+
+ /* mark output packet samples */
+ memset(x->x_data, 0, 2*x->x_nb_pixels);
+ for (i=0; i<x->x_sample_size; i++){
+ x->x_data[offset[i]] = 0x7fff;
+ }
+
+ /* send packet to left outlet */
+ pdp_pass_if_valid(x->x_outlet0, &x->x_packet0);
+
+
+ /* run the clustering algo */
+ x->x_pixel_offset = offset;
+ compute_clusters(x);
+
+
+}
+
+static void get_brightest(t_pdp_histo *x)
+{
+ int i;
+ int *histo = x->x_histo;
+ int N = 1 << (x->x_logN);
+
+ int index, nsamps;
+
+ /* check requested size */
+ if (x->x_sample_size > x->x_nb_pixels){
+ post("WARNING: more samples requested than pixels in image");
+ x->x_sample_size = x->x_nb_pixels;
+ }
+
+
+ /* find limiting index */
+ index = N;
+ nsamps = 0;
+ while (nsamps < x->x_sample_size){
+ index--;
+ nsamps += histo[index];
+ }
+
+ /* status report */
+ if (x->x_debug){
+ post("found %d samples between h[%d] and h[%d]", nsamps, index, N-1);
+ }
+
+ /* get a representative set from the candidates
+ the tempbuf is the rounded log of the nb of samples + 1
+ so it is at least 50% sparse */
+ get_sampleset(x, round_up_2log(nsamps) + 1, index << (15-x->x_logN));
+
+}
+
+
+static void _pdp_histo_perform(t_pdp_histo *x)
+{
+ short int *pp;
+ int N = 1 << x->x_logN;
+ int nbpixels = x->x_width * x->x_height, i;
+
+ int histo[N];
+
+ /* init */
+ for (i=0; i<N; i++) histo[i] = 0;
+
+ /* build histo */
+ for (i=0; i<nbpixels; i++){
+ int index = x->x_data[i] >> (15 - x->x_logN);
+ if (index < 0) index = 0; /* negative -> zero */
+ histo[index]++;
+ }
+
+ /* save the histo stack location */
+ x->x_histo = histo;
+
+ /* print it */
+ if (x->x_debug){
+ post("histogram:");
+ for (i=0; i<N; i++){
+ fprintf(stderr, "%d\t", histo[i]);
+ if (!(i % 10)) post("");
+ }
+ post("");
+ }
+
+ /* call the processor */
+ x->x_process_method(x);
+
+
+}
+
+
+// packet is an image/*/* packet or invalid */
+static void pdp_histo_perform(t_pdp_histo *x)
+{
+ t_pdp *header0 = pdp_packet_header(x->x_packet0);
+ void *data0 = pdp_packet_data(x->x_packet0);
+ if (!header0 || !data0) return;
+
+ x->x_width = header0->info.image.width;
+ x->x_height = header0->info.image.height;
+ x->x_nb_pixels = x->x_width * x->x_height;
+ x->x_data = data0;
+
+ _pdp_histo_perform(x);
+}
+
+
+
+static void pdp_histo_input_0(t_pdp_histo *x, t_symbol *s, t_floatarg f)
+{
+ int packet = (int)f;
+
+ /* register */
+ if (s == gensym("register_ro")){
+ /* replace if not compatible or we are not interpolating */
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = pdp_packet_convert_rw(packet, pdp_gensym("image/grey/*"));
+
+ }
+
+ if (s == gensym("process")){
+ pdp_histo_perform(x);
+ }
+
+}
+
+
+
+static void pdp_histo_samplesize(t_pdp_histo *x, t_floatarg f)
+{
+ int i = (int)f;
+ if (i >= x->x_nb_clusters ) x->x_sample_size = i;
+}
+
+static void pdp_histo_clusters(t_pdp_histo *x, t_floatarg f)
+{
+ int i = (int)f;
+ if (i>=2 && i<= x->x_sample_size){
+ x->x_nb_clusters = i;
+ if (x->x_cluster) pdp_dealloc(x->x_cluster);
+ x->x_cluster = 0;
+ }
+}
+static void pdp_histo_scale(t_pdp_histo *x, t_floatarg f){x->x_scale = f;}
+
+
+
+static void pdp_histo_size(t_pdp_histo *x, t_floatarg f)
+{
+ int i = (int)f;
+ if (i < 1) return;
+ x->x_logN = round_up_2log(i);
+}
+
+
+static void pdp_histo_array(t_pdp_histo *x, t_symbol *s)
+{
+ //post("setting symbol %x", s);
+ x->x_array_sym = s;
+}
+
+
+static void pdp_histo_free(t_pdp_histo *x)
+{
+ pdp_packet_mark_unused(x->x_packet0);
+ if (x->x_cluster) pdp_dealloc(x->x_cluster);
+}
+
+
+t_class *pdp_histo_class;
+
+
+
+void *pdp_histo_new(t_floatarg f)
+{
+
+ t_pdp_histo *x = (t_pdp_histo *)pd_new(pdp_histo_class);
+ if (f == 0.0f) f = 64;
+ pdp_histo_size(x, f);
+ x->x_packet0 = -1;
+ x->x_debug = 0;
+ x->x_sample_size = 16;
+ x->x_nb_clusters = 3;
+ x->x_cluster = 0;
+ return (void *)x;
+}
+
+
+void *pdp_histo_array_new(t_symbol *s, t_float f, t_float f2)
+{
+ t_pdp_histo *x = (t_pdp_histo *)pdp_histo_new(f);
+ if (f2 == 0.0f) f2 = 1.0f;
+ pdp_histo_scale(x, f2);
+ pdp_histo_array(x, s);
+ x->x_process_method = dump_to_array;
+ return (void *)x;
+}
+
+void *pdp_histo_sample_new(t_float nbsamples, t_float histosize)
+{
+ t_pdp_histo *x;
+ if (histosize == 0.0f) histosize = 256.0f;
+ x = (t_pdp_histo *)pdp_histo_new(histosize);
+ if (nbsamples == 0.0f) nbsamples = 16.0f;
+ pdp_histo_samplesize(x, nbsamples);
+ x->x_process_method = get_brightest;
+ x->x_outlet0 = outlet_new(&x->x_obj, gensym("anything"));
+ //x->x_outlet1 = outlet_new(&x->x_obj, gensym("anything"));
+
+ inlet_new((t_object *)x, (t_pd *)&x->x_obj, gensym("float"), gensym("nbpoints"));
+
+ return (void *)x;
+}
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+void pdp_histo_setup(void)
+{
+
+ pdp_histo_class = class_new(gensym("pdp_histo"), (t_newmethod)pdp_histo_array_new,
+ (t_method)pdp_histo_free, sizeof(t_pdp_histo), 0, A_DEFSYMBOL, A_DEFFLOAT, A_DEFFLOAT, A_NULL);
+
+ class_addcreator((t_newmethod)pdp_histo_sample_new, gensym("pdp_pointcloud"), A_DEFFLOAT, A_DEFFLOAT, A_NULL);
+
+ class_addmethod(pdp_histo_class, (t_method)pdp_histo_input_0, gensym("pdp"), A_SYMBOL, A_DEFFLOAT, A_NULL);
+
+ class_addmethod(pdp_histo_class, (t_method)pdp_histo_size, gensym("size"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_histo_class, (t_method)pdp_histo_size, gensym("scale"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_histo_class, (t_method)pdp_histo_array, gensym("array"), A_SYMBOL, A_NULL);
+ class_addmethod(pdp_histo_class, (t_method)pdp_histo_samplesize, gensym("nbpoints"), A_FLOAT, A_NULL);
+ class_addmethod(pdp_histo_class, (t_method)pdp_histo_clusters, gensym("nbclusters"), A_FLOAT, A_NULL);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+
+
diff --git a/modules/matrix_basic/pdp_mat_lu.c b/modules/matrix_basic/pdp_mat_lu.c new file mode 100644 index 0000000..af8931d --- /dev/null +++ b/modules/matrix_basic/pdp_mat_lu.c @@ -0,0 +1,143 @@ +/* + * Pure Data Packet module. LU decomposition 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. + * + */ + +//#include <gsl/gsl_block.h> +//#include <gsl/gsl_vector.h> +//#include <gsl/gsl_matrix.h> +//#include <gsl/gsl_blas.h> +#include "pdp.h" +#include "pdp_base.h" + + +typedef struct pdp_mat_LU_struct +{ + t_pdp_base x_base; + +} t_pdp_mat_LU; + + + +static void pdp_mat_LU_process_LU_inverse(t_pdp_mat_LU *x) +{ + int p = pdp_base_get_packet(x, 0); + int p_LU = pdp_packet_matrix_LU_to_inverse(p); + pdp_base_set_packet(x, 0, p_LU); // replace packet +} + +static void pdp_mat_LU_process_LU(t_pdp_mat_LU *x) +{ + int p = pdp_base_get_packet(x, 0); + int p_LU = pdp_packet_matrix_LU(p); + pdp_base_set_packet(x, 0, p_LU); // replace packet +} + +static void pdp_mat_LU_process_LU_solve(t_pdp_mat_LU *x) +{ + int p0 = pdp_base_get_packet(x, 0); + int p1 = pdp_base_get_packet(x, 1); + int pvr, pm, pv; + + /* determine which is vector and which is matrix */ + if (pdp_packet_matrix_ismatrix(p0) && pdp_packet_matrix_isvector(p1)){ + pm = p0; + pv = p1; + } + else { + pm = p1; + pv = p0; + } + + /* create the result vector */ + pvr = pdp_packet_matrix_LU_solve(pm, pv); + + /* replace the active packet */ + pdp_base_set_packet(x, 0, pvr); +} + +static void pdp_mat_LU_free(t_pdp_mat_LU *x) +{ + /* remove process method from queue before deleting data */ + pdp_base_free(x); +} + +t_class *pdp_mat_LU_class; + + +/* common new methods */ +t_pdp_mat_LU *pdp_mat_LU_base_new(void) +{ + t_pdp_mat_LU *x = (t_pdp_mat_LU *)pd_new(pdp_mat_LU_class); + pdp_base_init(x); + pdp_base_add_pdp_outlet(x); + return x; +} + +void *pdp_mat_LU_inverse_new(void) +{ + t_pdp_mat_LU *x = pdp_mat_LU_base_new(); + pdp_base_set_process_method(x,(t_pdp_method)pdp_mat_LU_process_LU_inverse); + pdp_base_readonly_active_inlet(x); + return (void *)x; +} + + +void *pdp_mat_LU_new(void) +{ + t_pdp_mat_LU *x = pdp_mat_LU_base_new(); + pdp_base_set_process_method(x,(t_pdp_method)pdp_mat_LU_process_LU); + pdp_base_readonly_active_inlet(x); + return (void *)x; +} + +void *pdp_mat_LU_solve_new(void) +{ + t_pdp_mat_LU *x = pdp_mat_LU_base_new(); + pdp_base_set_process_method(x,(t_pdp_method)pdp_mat_LU_process_LU_solve); + pdp_base_readonly_active_inlet(x); + pdp_base_add_pdp_inlet(x); + return (void *)x; +} + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_mat_lu_setup(void) +{ + + + pdp_mat_LU_class = class_new(gensym("pdp_m_LU_inverse"), (t_newmethod)pdp_mat_LU_inverse_new, + (t_method)pdp_mat_LU_free, sizeof(t_pdp_mat_LU), 0, A_NULL); + + pdp_base_setup(pdp_mat_LU_class); + + + class_addcreator((t_newmethod)pdp_mat_LU_new, gensym("pdp_m_LU"), A_NULL); + class_addcreator((t_newmethod)pdp_mat_LU_solve_new, gensym("pdp_m_LU_solve"), A_NULL); + + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/matrix_basic/pdp_mat_mul.c b/modules/matrix_basic/pdp_mat_mul.c new file mode 100644 index 0000000..329813f --- /dev/null +++ b/modules/matrix_basic/pdp_mat_mul.c @@ -0,0 +1,308 @@ +/* + * Pure Data Packet module. Matrix multiplication 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. + * + */ + +//#include <gsl/gsl_block.h> +//#include <gsl/gsl_vector.h> +//#include <gsl/gsl_matrix.h> +//#include <gsl/gsl_blas.h> +#include "pdp.h" +#include "pdp_base.h" + + +typedef struct pdp_mat_mm_struct +{ + t_pdp_base x_base; + CBLAS_TRANSPOSE_t x_T0; + CBLAS_TRANSPOSE_t x_T1; + int x_M0; + int x_M1; + + float x_scale_r; + float x_scale_i; + +} t_pdp_mat_mm; + + +static void pdp_mat_mm_rscale(t_pdp_mat_mm *x, t_floatarg r) +{ + x->x_scale_r = r; + x->x_scale_i = 0.0f; +} + +static void pdp_mat_mm_cscale(t_pdp_mat_mm *x, t_floatarg r, t_floatarg i) +{ + x->x_scale_r = r; + x->x_scale_i = i; +} + + +/* matrix multilpy */ +static void pdp_mat_mv_process_mul(t_pdp_mat_mm *x) +{ + int pA = pdp_base_get_packet(x, 0); + int pB = pdp_base_get_packet(x, 1); + int p0, p1, pR; + + /* determine which one is the vector */ + if (pdp_packet_matrix_isvector(pA)){ + p0 = pB; + p1 = pA; + } + else { + p1 = pB; + p0 = pA; + } + + pR = pdp_packet_new_matrix_product_result(x->x_T0, CblasNoTrans, p0, p1); + + if (-1 != pR){ + pdp_packet_matrix_setzero(pR); + if (pdp_packet_matrix_blas_mv(x->x_T0, p0, p1, pR, x->x_scale_r, x->x_scale_i)){ + //post("pdp_packet_matrix_blas_mm failed"); + pdp_packet_mark_unused(pR); + pR = -1; + } + } + else { + //post("pdp_packet_new_matrix_product_result failed"); + } + + /* replace with result */ + pdp_base_set_packet(x, 0, pR); + +} + +/* matrix vector multilpy */ +static void pdp_mat_mm_process_mul(t_pdp_mat_mm *x) +{ + int pA = pdp_base_get_packet(x, 0); + int pB = pdp_base_get_packet(x, 1); + int p0, p1, pR; + + p0 = (x->x_M0) ? pB : pA; + p1 = (x->x_M1) ? pB : pA; + + pR = pdp_packet_new_matrix_product_result(x->x_T0, x->x_T1, p0, p1); + + if (-1 != pR){ + pdp_packet_matrix_setzero(pR); + if (pdp_packet_matrix_blas_mm(x->x_T0, x->x_T1, p0, p1, pR, x->x_scale_r, x->x_scale_i)){ + //post("pdp_packet_matrix_blas_mm failed"); + pdp_packet_mark_unused(pR); + pR = -1; + } + } + else { + //post("pdp_packet_new_matrix_product_result failed"); + } + + /* replace with result */ + pdp_base_set_packet(x, 0, pR); + +} +/* matrix macc */ +static void pdp_mat_mm_process_mac(t_pdp_mat_mm *x) +{ + int pC = pdp_base_get_packet(x, 0); + int pA = pdp_base_get_packet(x, 1); + int pB = pdp_base_get_packet(x, 2); + int p0, p1; + + p0 = (x->x_M0) ? pB : pA; + p1 = (x->x_M1) ? pB : pA; + + if (pdp_packet_matrix_blas_mm(x->x_T0, x->x_T1, p0, p1, pC, x->x_scale_r, x->x_scale_i)){ + //post("pdp_packet_matrix_blas_mm failed"); + pdp_base_set_packet(x, 0, -1); // delete packet + } + +} + + +static void pdp_mat_mm_free(t_pdp_mat_mm *x) +{ + /* remove process method from queue before deleting data */ + pdp_base_free(x); +} + +t_class *pdp_mat_mm_class; + + +/* common new method */ +void *pdp_mat_mm_new(void) +{ + int i; + t_pdp_mat_mm *x = (t_pdp_mat_mm *)pd_new(pdp_mat_mm_class); + + /* super init */ + pdp_base_init(x); + + /* outlet */ + pdp_base_add_pdp_outlet(x); + + + return (void *)x; +} + + +static int pdp_mat_mm_setup_routing_M0(t_pdp_mat_mm *x, t_symbol *s0) +{ + if ('A' == s0->s_name[0]){x->x_M0 = 0;} else if ('B' == s0->s_name[0]) {x->x_M0 = 1;} else return 0; + + if ((gensym("A") == s0) || (gensym("B") == s0)) x->x_T0 = CblasNoTrans; + else if ((gensym("A^T") == s0) || (gensym("B^T") == s0)) x->x_T0 = CblasConjTrans; + else if ((gensym("A^H") == s0) || (gensym("B^H") == s0)) x->x_T0 = CblasConjTrans; + else return 0; + + return 1; +} + +static int pdp_mat_mm_setup_routing_M1(t_pdp_mat_mm *x, t_symbol *s1) +{ + + if ('A' == s1->s_name[0]){x->x_M1 = 0;} else if ('B' == s1->s_name[0]) {x->x_M1 = 1;} else return 0; + + /* setup second matrix transpose operation */ + if ((gensym("A") == s1) || (gensym("B") == s1)) x->x_T1 = CblasNoTrans; + else if ((gensym("A^T") == s1) || (gensym("B^T") == s1)) x->x_T1 = CblasConjTrans; + else if ((gensym("A^H") == s1) || (gensym("B^H") == s1)) x->x_T1 = CblasConjTrans; + else return 0; + + return 1; +} + + +static int pdp_mat_mm_setup_scaling(t_pdp_mat_mm *x, t_symbol *scale) +{ + int success = 1; + + /* setup scaling inlet */ + if ((gensym ("rscale") == scale) || (gensym("r") == scale)){ + pdp_base_add_gen_inlet(x, gensym("float"), gensym("rscale")); + } + else if ((gensym ("cscale") == scale) || (gensym("c") == scale)){ + pdp_base_add_gen_inlet(x, gensym("list"), gensym("cscale")); + } + else if (gensym ("") != scale) success = 0; + + return success; +} + +void *pdp_mat_mm_new_mul_common(t_symbol *s0, t_symbol *s1, t_symbol *scale, int ein) +{ + t_pdp_mat_mm *x = pdp_mat_mm_new(); + + /* add extra pdp inlets */ + while (ein--) pdp_base_add_pdp_inlet(x); + + /* setup routing */ + if (!pdp_mat_mm_setup_routing_M0(x, s0)) goto error; + if (!pdp_mat_mm_setup_routing_M1(x, s1)) goto error; + if (!pdp_mat_mm_setup_scaling(x, scale)) goto error; + + /* default scale = 1 */ + pdp_mat_mm_cscale(x, 1.0f, 0.0f); + return (void *)x; + + error: + pd_free((void *)x); + return 0; +} + +void *pdp_mat_mv_new_mul_common(t_symbol *s0, t_symbol *scale, int ein) +{ + t_pdp_mat_mm *x = pdp_mat_mm_new(); + + /* add extra pdp inlets */ + while (ein--) pdp_base_add_pdp_inlet(x); + + /* setup routing */ + if (!pdp_mat_mm_setup_routing_M0(x, s0)) goto error; + if (!pdp_mat_mm_setup_scaling(x, scale)) goto error; + + /* default scale = 1 */ + pdp_mat_mm_cscale(x, 1.0f, 0.0f); + return (void *)x; + + error: + pd_free((void *)x); + return 0; +} + +void *pdp_mat_mm_new_mul(t_symbol *s0, t_symbol *s1, t_symbol *scale) +{ + t_pdp_mat_mm *x = pdp_mat_mm_new_mul_common(s0, s1, scale, 1); + if(x){ + pdp_base_set_process_method(x, (t_pdp_method)pdp_mat_mm_process_mul); + pdp_base_readonly_active_inlet(x); + } + return x; +} + +void *pdp_mat_mv_new_mul(t_symbol *s0, t_symbol *scale) +{ + t_pdp_mat_mm *x = pdp_mat_mv_new_mul_common(s0, scale, 1); + if(x){ + pdp_base_set_process_method(x, (t_pdp_method)pdp_mat_mv_process_mul); + pdp_base_readonly_active_inlet(x); + } + return x; +} + +void *pdp_mat_mm_new_mac(t_symbol *s0, t_symbol *s1, t_symbol *scale) +{ + t_pdp_mat_mm *x = pdp_mat_mm_new_mul_common(s0, s1, scale, 2); + if (x){ + pdp_base_set_process_method(x, (t_pdp_method)pdp_mat_mm_process_mac); + } + return x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_mat_mul_setup(void) +{ + + + pdp_mat_mm_class = class_new(gensym("pdp_m_mm"), (t_newmethod)pdp_mat_mm_new_mul, + (t_method)pdp_mat_mm_free, sizeof(t_pdp_mat_mm), 0, A_SYMBOL, A_SYMBOL, A_DEFSYMBOL, A_NULL); + + pdp_base_setup(pdp_mat_mm_class); + + class_addcreator((t_newmethod)pdp_mat_mm_new_mac, gensym("pdp_m_+=mm"), + A_SYMBOL, A_SYMBOL, A_DEFSYMBOL, A_NULL); + + class_addcreator((t_newmethod)pdp_mat_mv_new_mul, gensym("pdp_m_mv"), + A_SYMBOL, A_DEFSYMBOL, A_NULL); + + + class_addmethod(pdp_mat_mm_class, (t_method)pdp_mat_mm_rscale, gensym("rscale"), A_FLOAT, A_NULL); + class_addmethod(pdp_mat_mm_class, (t_method)pdp_mat_mm_cscale, gensym("cscale"), A_FLOAT, A_FLOAT, A_NULL); + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/matrix_basic/pdp_mat_vec.c b/modules/matrix_basic/pdp_mat_vec.c new file mode 100644 index 0000000..16257c8 --- /dev/null +++ b/modules/matrix_basic/pdp_mat_vec.c @@ -0,0 +1,213 @@ +/* + * Pure Data Packet module. Vector modules. + * 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 <gsl/gsl_block.h> +//#include <gsl/gsl_vector.h> +//#include <gsl/gsl_matrix.h> +//#include <gsl/gsl_blas.h> +#include "pdp.h" +#include "pdp_base.h" + + +typedef struct pdp_mat_vec_struct +{ + t_pdp_base x_base; + int x_type; + t_outlet *x_out; + int x_accept_list; + int x_list_size; + t_atom *x_list; + +} t_pdp_mat_vec; + + +#define GETFLOAT(x) ((x)->a_type == A_FLOAT ? (x)->a_w.w_float : 0.0f) +#define GETDOUBLE(x) (double)GETFLOAT(x) + + +static void pdp_mat_vec_list_in(t_pdp_mat_vec *x, t_symbol *s, int argc, t_atom *argv) +{ + int i; + int vp = -1; + int f; + int dim = argc; + + if (!x->x_accept_list) return; //check if this handler is enabled + if (!argc) return; //reject empty list + + switch(x->x_type){ + case PDP_MATRIX_TYPE_CFLOAT: + if (argc & 1) return; //reject odd nb elements + dim >>= 1; //halve dimension + case PDP_MATRIX_TYPE_RFLOAT: + vp = pdp_packet_new_matrix(dim, 1, x->x_type); + if (-1 != vp){ + float *data = (float *)pdp_packet_data(vp); + for (i=0; i<argc; i++) data[i] = GETFLOAT(&argv[i]); + } + break; + case PDP_MATRIX_TYPE_CDOUBLE: + if (argc & 1) return; //reject odd nb elements + dim >>= 1; //halve dimension + case PDP_MATRIX_TYPE_RDOUBLE: + vp = pdp_packet_new_matrix(dim, 1, x->x_type); + if (-1 != vp){ + double *data = (double *)pdp_packet_data(vp); + for (i=0; i<argc; i++) data[i] = GETDOUBLE(&argv[i]); + } + break; + default: + break; + } + + if (-1 != vp){ + /* store vector packet */ + pdp_base_set_packet(x, 0, vp); + pdp_base_bang(x); + } +} + +static void pdp_mat_vec_list_out(t_pdp_mat_vec *x) +{ + int p = pdp_base_move_packet(x, 0); + int type = pdp_packet_matrix_get_type(p); + int outlist_size; + float *fdata = 0; + double *ddata = 0; + int i; + + /* check if it's a vector */ + gsl_vector *m = (gsl_vector *)pdp_packet_matrix_get_gsl_vector(p, type); + if (!pdp_packet_matrix_isvector(p)) return; + + /* get list size */ + outlist_size = m->size; + if ((type == PDP_MATRIX_TYPE_CFLOAT) + || (type == PDP_MATRIX_TYPE_CDOUBLE)) + outlist_size <<= 1; + + /* realloc list if necessary */ + if (outlist_size > x->x_list_size){ + free(x->x_list); + x->x_list = (t_atom *)pdp_alloc(sizeof(t_atom) * outlist_size); + x->x_list_size = outlist_size; + } + + /* copy data */ + switch(type){ + case PDP_MATRIX_TYPE_RFLOAT: + case PDP_MATRIX_TYPE_CFLOAT: + fdata = (float *)pdp_packet_data(p); + for (i=0; i<outlist_size; i++) + SETFLOAT(&x->x_list[i], fdata[i]); + break; + + case PDP_MATRIX_TYPE_RDOUBLE: + case PDP_MATRIX_TYPE_CDOUBLE: + ddata = (double *)pdp_packet_data(p); + for (i=0; i<outlist_size; i++) + SETFLOAT(&x->x_list[i], (float)ddata[i]); + break; + + } + + /* dispose of vector packet and output list */ + pdp_packet_mark_unused(p); + outlet_list(x->x_out, &s_list, outlist_size, x->x_list); +} + +static void pdp_mat_vec_free(t_pdp_mat_vec *x) +{ + /* remove process method from queue before deleting data */ + pdp_base_free(x); + + /* delete list */ + if (x->x_list) pdp_dealloc (x->x_list); +} + +t_class *pdp_mat_vec_class; + + +/* common new methods */ +t_pdp_mat_vec *pdp_mat_vec_base_new(void) +{ + t_pdp_mat_vec *x = (t_pdp_mat_vec *)pd_new(pdp_mat_vec_class); + pdp_base_init(x); + x->x_type = PDP_MATRIX_TYPE_CFLOAT; + x->x_accept_list = 0; + x->x_list_size = 0; + x->x_list = 0; + return x; +} + +void *pdp_mat_vec_list2vec_new(t_symbol *type) +{ + t_pdp_mat_vec *x = pdp_mat_vec_base_new(); + pdp_base_disable_active_inlet(x); + pdp_base_add_pdp_outlet(x); + x->x_accept_list = 1; + if ((gensym ("") == type) || (gensym ("double/real") == type)) x->x_type = PDP_MATRIX_TYPE_RDOUBLE; + else if (gensym ("double/complex") == type) x->x_type = PDP_MATRIX_TYPE_CDOUBLE; + else if (gensym ("float/real") == type) x->x_type = PDP_MATRIX_TYPE_RFLOAT; + else if (gensym ("float/complex") == type) x->x_type = PDP_MATRIX_TYPE_CFLOAT; + else { + pd_free((t_pd *)x); + x = 0; + } + return (void *)x; +} + + +void *pdp_mat_vec_vec2list_new(t_symbol *type) +{ + t_pdp_mat_vec *x = pdp_mat_vec_base_new(); + x->x_out = outlet_new((t_object *)x, &s_anything); + pdp_base_set_postproc_method(x,(t_pdp_method)pdp_mat_vec_list_out); + pdp_base_readonly_active_inlet(x); + return (void *)x; +} + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_mat_vec_setup(void) +{ + + + pdp_mat_vec_class = class_new(gensym("pdp_m_list2vec"), (t_newmethod)pdp_mat_vec_list2vec_new, + (t_method)pdp_mat_vec_free, sizeof(t_pdp_mat_vec), 0, A_DEFSYMBOL, A_NULL); + + pdp_base_setup(pdp_mat_vec_class); + + + class_addcreator((t_newmethod)pdp_mat_vec_vec2list_new, gensym("pdp_m_vec2list"), A_NULL); + + class_addlist(pdp_mat_vec_class, (t_method)pdp_mat_vec_list_in); + + +} + +#ifdef __cplusplus +} +#endif diff --git a/modules/test/Makefile b/modules/test/Makefile new file mode 100644 index 0000000..12819a3 --- /dev/null +++ b/modules/test/Makefile @@ -0,0 +1,13 @@ +#current: all_modules +current: clean +include ../../Makefile.config + +all_modules: pdp_forthtest.o + +# build test modules +# all_modules: $(PDP_MOD) + +clean: + rm -f *~ + rm -f *.o + diff --git a/modules/test/README b/modules/test/README new file mode 100644 index 0000000..eb35faa --- /dev/null +++ b/modules/test/README @@ -0,0 +1 @@ +This directory contains test modules. diff --git a/modules/test/pdp_dpd_test.c b/modules/test/pdp_dpd_test.c new file mode 100644 index 0000000..1d85bde --- /dev/null +++ b/modules/test/pdp_dpd_test.c @@ -0,0 +1,104 @@ +/* + * 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. + * + */ + + + +#include "pdp.h" +#include "pdp_dpd_base.h" + +typedef struct pdp_dpd_test_struct +{ + t_pdp_dpd_base x_base; + +} t_pdp_dpd_test; + + + +/* outlet methods */ +static void pdp_dpd_test_1(t_pdp_dpd_test *x){post("%x: one", x);} +static void pdp_dpd_test_2(t_pdp_dpd_test *x){post("%x: two", x);} +static void pdp_dpd_test_3(t_pdp_dpd_test *x){post("%x: three", x);} +static void pdp_dpd_test_cleanup(t_pdp_dpd_test *x){post("%x: cleanup", x);} +static void pdp_dpd_test_inspect(t_pdp_dpd_test *x){post("%x: inspect", x);} + + + +static void pdp_dpd_test_bang(t_pdp_dpd_test *x) +{ + /* store a dummy packet */ + pdp_dpd_base_set_context_packet(x, pdp_packet_new(PDP_IMAGE, 4096)); + + /* bang base */ + pdp_dpd_base_bang(x); +} + + +static void pdp_dpd_test_free(t_pdp_dpd_test *x) +{ + pdp_dpd_base_free(x); + +} + +t_class *pdp_dpd_test_class; + + +void *pdp_dpd_test_new(void) +{ + /* allocate */ + t_pdp_dpd_test *x = (t_pdp_dpd_test *)pd_new(pdp_dpd_test_class); + + /* init super: this is mandatory */ + pdp_dpd_base_init(x); + + /* set the dpd processing methods & outlets */ + pdp_dpd_base_add_outlet(x, (t_pdp_method)pdp_dpd_test_1); + pdp_dpd_base_add_outlet(x, (t_pdp_method)pdp_dpd_test_2); + pdp_dpd_base_add_outlet(x, (t_pdp_method)pdp_dpd_test_3); + + pdp_dpd_base_add_cleanup(x, (t_pdp_method)pdp_dpd_test_cleanup); + pdp_dpd_base_add_inspector(x, (t_pdp_method)pdp_dpd_test_inspect); + + return (void *)x; +} + + + +#ifdef __cplusplus +extern "C" +{ +#endif + + +void pdp_dpd_test_setup(void) +{ + /* create a standard pd class */ + pdp_dpd_test_class = class_new(gensym("pdp_dpd_test"), (t_newmethod)pdp_dpd_test_new, + (t_method)pdp_dpd_test_free, sizeof(t_pdp_dpd_test), 0, A_NULL); + + /* inherit pdp base class methods */ + pdp_dpd_base_setup(pdp_dpd_test_class); + + /* add bang method */ + class_addbang(pdp_dpd_test_class, pdp_dpd_test_bang); +} + +#ifdef __cplusplus +} +#endif diff --git a/opengl/Makefile b/opengl/Makefile new file mode 100644 index 0000000..d62358e --- /dev/null +++ b/opengl/Makefile @@ -0,0 +1,27 @@ +include Makefile.config + +all: $(TARGET) + +linux: pdp_opengl.pd_linux + +darwin: pdp_opengl.pd_darwin + +subdirs: + make -C system + make -C modules + make -C include + +clean: + make -C system clean + make -C modules clean + make -C include clean + rm -f pdp_opengl.pd_linux + rm -f *~ + +pdp_opengl.pd_linux: subdirs + rm -f pdp_opengl.pd_linux + $(CC) -export_dynamic -shared -o pdp_opengl.pd_linux modules/*.o system/*.o $(LDFLAGS) -g + +pdp_opengl.pd_darwin: subdirs + rm -f pdp_opengl.pd_linux + $(CC) -o pdp_opengl.pd_pd_darwin modules/*.o system/*.o $(LDFLAGS) -g -bundle -bundle_loader $(PD_EXECUTABLE) diff --git a/opengl/Makefile.config b/opengl/Makefile.config new file mode 100644 index 0000000..4e59a4c --- /dev/null +++ b/opengl/Makefile.config @@ -0,0 +1,25 @@ +PD_DIR = /home/tom/pd/distro/pd/src +PDP_DIR = /home/tom/pd/packet/include +PDP_OGL_DIR = /home/tom/pd/packet/opengl/include + + + +CFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer -ffast-math \ + -Wall -W -Wstrict-prototypes -Werror \ + -Wno-unused -Wno-parentheses -Wno-switch -g + +CPPFLAGS = -I$(PD_DIR) -I$(PDP_DIR) -I$(PDP_OGL_DIR) -I/usr/X11R6/include +LDFLAGS = -lGL -lglut + +TARGET=linux + +#uncomment these for darwin: +#TARGET=darwin +#CPPFLAGS+=-I/sw/include +#PD_EXECUTABLE=/usr/local/bin/pd +#LDFLAGS = -lGL -lGLU -lglut -lX11 -L/sw/lib -L/usr/X11R6/lib + + + +.c.o: + $(CC) $(CFLAGS) $(CPPFLAGS) -o $*.o -c $*.c diff --git a/opengl/README b/opengl/README new file mode 100644 index 0000000..fb756b3 --- /dev/null +++ b/opengl/README @@ -0,0 +1,239 @@ +pdp_opengl (3dp): opengl extensions for pdp +warning: this is still experimental and incomplete + +this library extends pdp with texture and render context packets, +to use some of the power of current video hardware. + +it is very much like gem (sort of a redesign, filled with my own +idiosyncrasies). gem, like it is now, is not very suited for interaction +with pdp image processing because of its hardcoded window centric rendering, +so i thought i'd write some gem like stuff on top of pdp myself. (3dp right +now only supports window centric rendering itself, but adding pbuffer +or pixmap rendering is easy enough, though it requires a rather new glx +library) + +so, 3dp is an experimental gem clone with tight pdp integration. unless +you like experiments, use gem, since it is better supported, has more +features and runs on linux, windows and mac osx. + +requires glx (opengl on x window). + +building: + +edit Makefile.config to reflect your system and run make. the library is +pdp_opengl.pd_linux + +some of the examples use the abstractions in opengl/abstractions. best to +add this to your pd path. + + +there are some fatal X bugs floating around, so be careful with resizing +windows and closing patches. there are a lot of other, non-fatal bugs +or badly implemented features. if you have remarks, just let me know. + + +i'll move to autoconf once i no longer consider it experimental. + + +TIPS & TRICKS + + +* the 3dp_windowcontext creates a window to draw in. the window packet +will be be output to the left outlet when a bang is received. control +flow for context is right to left, this means if a 3dp object has 2 +context outlets, the rightmost will be propagated before the leftmost. +there is no fanout. all operations are accumulative, including the +geometric transformations. if you need to branch use a 3dp_push object. + + +* geometric transformations can be done using the 3dp_view object. +the first argument is the kind of transformation: scale, scalex, +scaley, scalez, rotx, roty, rotz, rota, transx, transy, transz, +transxyz. + +repectively this scales the object, the x, y, z axis, rotates +around the x, y, z axis, rotates around an arbitrary axis, translates +in the x, y, z directions and translates along a vector. the initial +parameters can be specified as creation arguments, i.e.: + +[3dp_view rota 1 1 1 90] rotates by 90 degrees around axis (1,1,1). + + +* some simple objects can be drawn using the pdp_draw object. the +first argument is the object: square, cube, sphere, torus, cone, +teapot, dodeca, icosa, octa, tetra. prepending the names with a w +draws the wireframe version. (i.e. wcube is a wireframe cube). the +first inlet of the object is a texture inlet. not all objects support +this, but cube, square and sphere do. other inlets set some parameters +like size, nb of segments for sphere, etc.. they can be specified +by creation arguments too. + + +* saving a (matrix) state can be accomplished by the 3dp_push object. +the default matrix is the modelview matrix. it works as follows: both +the right and the left outlet propagate the same matrix state as the +input. so in short you can use 3dp_push to split your rendering tree +into parallel branches. the matrix types that can be pushed are: +modelview, texture, color, projection. + +* setting a current matrix can be done using the 3dp_mode object. +i.e. [3dp_mode texture] will map all geometric transforms to the +texture matrix, for texture coordinate animation. the left outlet +restores the current matrix back to the modelview matrix. + +* it is possible to send a render context trough a drawing/view +transforming object multiple times. the easy way is to use 3dp_for, +but other ways are legal too. this can be done to draw different +versions of the same object. have a look at example01 how this can +be done. + +it is also possible to send multiple render contexts trought the same +rendering tree (i.e. multiple windows). if you use different viewing +transformations on the top of the rendering chain, you can view a scene +trough different angles. + + +* light sources can be introduces using 3dp_light. they can be moved +with oridinary 3dp_view objects. + + +* 3dp_color changes the current color. right outlet is new color, +left outlet is the previous color. + +* 3dp_toggle toggles a boolean opengl switch. enabled now are: +depth_test, blend_add, blend_mix. more to come later. + + + +* couping 3dp and pdp can be done using 3dp_snap and pdp_convert. +the correct way to do it is to put 3dp_snap in a rendering +chain and give it arguments like this: + +[3dp_snap image/*/* 320 240] + +if you specify the subtype to be image/multi/*, the packet +will not be colour space converted: it will stay rgb. +if you want to make a snapshot to store as a high quality +png image, snap to bitmap/rgb/* and store it in pdp_reg to save. +to convert an image back to a texture, use + +[pdp_convert texture/*/*] + +if you snap to a texture (which is the default) +the dimensions don't need to be specified. a texture will be +allocated that can contain the entire screen. this is because +texture coordinates are relative and data is always interpolated. + +snapping only works correctly when the window is not covered +by other windows. + + +* textures can have any dimensions, but only those which have +dimensions that are integral powers of two will be tiled correctly. +i.e. by using pdp_mode to change to texture mode and doing some +coordinate transforms using pdp_view (see example06: this +uses a tilable animated cellular automata texture) + + +* multipass rendering is supported trough the objects +3dp_subcontext, "3dp_draw clear" and "3dp_view reset". the idea +is as follows: before rendering the final scene, you use +(a part of) the drawing buffer to render some things and store +them in a texture to be used in your final drawing pass. have +a look at examples 11, 12 and 13. in theory you could build a +"texture processing" framework on top of 3dp, by using the window +buffer as an accumulator and using textures as auxilary registers, +and drawing your final texture result using i.e. +3dp_display_texture. while this can be much faster than ordinary +pdp image processing, it also requires you to do more bookkeping +yourself, since you can only use a "serial" approach because +you can't modify textures directly, only trough the use of the +render buffer. + + +* 3dp has it's own thread, which is enabled by default. you +can enable/disable this by sending a "3dthread 0/1" message +to pdp_control. in most cases there's no reason to disable +the thread processing, except when you are doing a lot of +pdp<->3dp conversions. in that case the delay introduced by +the thread processing might become problematic. (the same +goes for pdp btw. feedback and threads don't mix too well). +it's a tradeoff. thread processing gives priority to the audio, +so you can obtain low latency, and you don't have to care +about locking up pd by doing too much processing. (instead +frames will be dropped). the drawback is of course that video +timing becomes unpredictable because it is governed by the system +scheduler. so sometimes it can be useful to disable threads +and increase the audio latency, so you can have snappy audio +and video at the same time. getting this to work well usually +requires some experimenting. + + +enjoy, + +tom + + + + +--- +some political ranting: gem vs. pdp/3dp + +first a disclaimer. i'm not into dissing people. i respect and appreciate +all the work that has gone into gem, but at the same time i'm not too happy +with what gem has become. not so much the functionality, but the way it is +built. the original design as a 3d processing extension is not flexible enough +to incorporate what's in there now and what could be added in the future.. + +instead of complaining about it, i decided to be pragmatic and write something +from scratch. i think, sometimes this has to be done. for me writing pdp/3dp +has been an extremely interesting learning experience. i think i understand +the trade-offs better now, and maybe these remarks can be useful. + +opengl is not a pure dataflow language. it operates on a global machine +state next to the obvious drawing buffer that is passed around. +representing opengl code with a graphic tree view has some advantages, +though it has a different "feel" than normal pd patches. the fact that +opengl transforms object coordinates to screen coordinates, and not vice +versa, gives graphicly represented opengl code an "upside down" feel. + +one of the things i don't like about gem is that it has both the opengl +"upside down drawing and coordinate transformation tree" and the pix +"data flow image processing tree" in the same network, which i find +counterintuitive. it is too monolytic to be truly flexible. in pdp/3dp +i try to separate the two explicitly: dataflow where possible (image +processing), and serial context based processing where needed (opengl). + +another disadvantage of gem is its window centric context. by building +3dp around explicit context passing, with explicit context creation objects, +i try to avoid this. an advantage of this is the possibility to send +multiple contexts trough a rendering tree, i.e. to have 2 different views +of the same scene. + +pdp has implicit fanout for all packets. i think that is its great +strength. it enables you to use any kind of media packet like you would +use floats. it is very intuitive. however, 3d rendering does not fit +this shoe. at least not when you just wrap opengl in the most straightforward +way. 3dp is a test case for pdp's "accumulation packets", which is a formal +abstraction of a context. pdp processors are noting more than serial programs +working on a context, so in fact it's nothing special. in fact, i'm still +in doubt if it is useful besides being able to support opengl.. + +so, the idea was to improve upon gem a bit, from different angles. i did +not succeed in my primary goal: making 3dp more intuitive than gem. it seems +this is only possible if you limit some of the flexibility. just wrapping +opengl keeps a lot of flexibility, but get's infected with the opengl +paradigm. by separating pdp (image processing) and 3dp (drawing and geometry +processing) as much as possible i think i've solved some intuition problems, +but 3dp itself is still basicly gem, with imho a better overall design, +but due to its more low level approach, maybe harder to understand. it seems +to me that knowing how opengl works is rather necessary to use 3dp. the same +is true for gem. + +one last philo remark: to me it seems the biggest drawback of gem's design is +the processor centric aproach: the only objects are processors, the data is +implicit and seemingly global. in pdp/3dp processors and objects are 2 separate +entities. i tried to unify them in one object model, but to me it seems the two +should really be treated as different complementary entities, to stay close to +the dataflow paradigm which makes pd such an intuitive tool. + diff --git a/opengl/TODO b/opengl/TODO new file mode 100644 index 0000000..dc61332 --- /dev/null +++ b/opengl/TODO @@ -0,0 +1,121 @@ +bugs: WARNING: there are still fatal bugs lurking around. + +* segfault when combining 3dp_windowcontext and pdp_xv. probably a mistake +in the teture<->image conversion or window event handling. +* 3dp is not robust against running out of video card resources. if you +manage to crash it please consider sending a bug report. +* 3dp_dlist triggered a segfault in pdp_mutex(un?)lock. can't reproduce. +this also happens on some other occasions where a post() statement is involved: +crash in pdp_mutex_lock, called by putc(). +* pd hangs on save? what happens during save?? -> 0.35 prob? 0.36 seems to work fine. + + +todo: + +* fix flow control for texture conversion +* fix display event routing for multiple windows +* finish mesh object + + +general: + +* prevent display list recursion: add some state data to the context packet to solve this. + +redesign: +* unify pbuf & window contexts (postponed pbufs because of subcontexts) +* cube mapping +* bubble object (model + wave equation) +* finish 3dp light + +performance: +* why is texture upload so slow? and is there a way to work around it? +* (why) is context switching slow? and how to introduce the render context differently. (direct to window?) + + + +DESIGN NOTES + +3dp is basicly my idea of gem on top of pdp. it is a simple layer around opengl. +3dp_* render and transform objects accept a reference to a rendering context and pass this along. + +3dp_* objects DO NOT SUPPORT FANOUT. they do support fanin. multiple contexts can be sent to an object, which +will result in drawing in (or other manipulations of) the respective contexts. + +texture packets can be used to pass bitmap data around. 3dp_tdraw accepts a texture on its +second inlet. 3dp_snap dumps the current state of the buffer into a texture packet. + +object classes: + +-drawing objects + +[3dp_draw cube] [3dp_draw sphere] .. + +-manipulation objects + +[3dp_view rot2d] + +-opengl stack objects + +[3dp_push color view texture] + +3dp vs pdp design: + +a context packet is an accumulation packet, and the order of operations on it (serial) is important. it has +elements from a readonly packet (only one instance is passed around) and from a rw packet (it is legal to change it's +state and pass it on). + +opengl in dataflow formulation seems to be read bottom to top. i first thought +this was a big drawback of gem, but now that i finally understand how opengl works i think it is +ok. it suddenly dawned on me, usually the number of eyes is much smaller than the number of objects +in a scene, so it is much more straghtforward to start at the eye instead of the objects. since a +simple serial stack mechanism can be used. + +so opengl is really serial, while pd "looks" parallel, but is serial too. i still think this +is a good thing, since it keeps things simple, but for some reason the "upside down & serial" +thing about opengl in pd is rather strange.. + +once you get used to it, and think about rendering a set as a tree rooted at the "context source" +like is done in gem,it seems to work out.. + +i think i'm going to stick to the depth first backtracking a tree serial rendering +way of looking at it. since i don't see a way of making this more intuitive without complicating +it too much. so no legal fanout of a context packet. + +------------------ + +accumulation packets (buckets) & backtracking + +buckets add support for "context based" single threaded programs. it +basicly allows you to construct data processors represented as a control +flow tree. i.e. function calls can be represented by branches in a tree, +with each branch rooted at an object's outlet, executed from right to +left. a "context packet" (or accumulation packet or bucket) is passed +along and acted upon. + +* fanout is illegal for bucket processors (at least, if they are non +cummutative, as is usually the case). this is not explicitly prohibited, +since it is hard to check in pd and it allows some hacks. the reason for +this is obvious: if you do not know which branch of a tree is executed +first, results are undefined. + +* a new communication protocol needs to be introduced, incompatible +with the existing 3 phase protocol: + +(1) register_ro +(2) register_rw +(3) process + +the new dpd (bucket / accumulation packet) protocol is 2 phase: +(1) inspect +(2) accumulate + +(1) is only present to give priority to bucket inspectors over +accumulators (processors) + +an accumulation packet will be owned by its creator all the time. +accumulation processors do not increase the reference count. only ro +inspectors will. + +note: it is legal for a packet to have a broken register_rw (missing +copy constructor, i.e. if a copy is too expensive or impossible. this +must be set in the pdp packet header flags by the packet constructor) |