From 513389b4c1bc8afe4b2dc9947c534bfeb105e3da Mon Sep 17 00:00:00 2001 From: Peter D'Hoye Date: Fri, 22 May 2009 21:58:48 +0000 Subject: Add FS #10214. Initial commit of the original PDa code for the GSoC Pure Data plugin project of Wincent Balin. Stripped some non-sourcefiles and added a rockbox readme that needs a bit more info from Wincent. Is added to CATEGORIES and viewers, but not yet to SUBDIRS (ie doesn't build yet) git-svn-id: svn://svn.rockbox.org/rockbox/trunk@21044 a1c6a512-1295-4272-9138-f99709370657 --- apps/plugins/pdbox/PDa/src/build.ipod | 10 + apps/plugins/pdbox/PDa/src/d_arithmetic.c | 1684 +++++++ apps/plugins/pdbox/PDa/src/d_array.c | 2148 ++++++++ apps/plugins/pdbox/PDa/src/d_ctl.c | 1568 ++++++ apps/plugins/pdbox/PDa/src/d_dac.c | 368 ++ apps/plugins/pdbox/PDa/src/d_delay.c | 638 +++ apps/plugins/pdbox/PDa/src/d_fft.c | 688 +++ apps/plugins/pdbox/PDa/src/d_fftroutine.c | 2002 ++++++++ apps/plugins/pdbox/PDa/src/d_filter.c | 1094 +++++ apps/plugins/pdbox/PDa/src/d_global.c | 616 +++ apps/plugins/pdbox/PDa/src/d_imayer_fft.c | 1032 ++++ apps/plugins/pdbox/PDa/src/d_imayer_tables.h | 100 + apps/plugins/pdbox/PDa/src/d_math.c | 1146 +++++ apps/plugins/pdbox/PDa/src/d_mayer_fft.c | 838 ++++ apps/plugins/pdbox/PDa/src/d_misc.c | 524 ++ apps/plugins/pdbox/PDa/src/d_osc.c | 1070 ++++ apps/plugins/pdbox/PDa/src/d_resample.c | 450 ++ apps/plugins/pdbox/PDa/src/d_soundfile.c | 4734 ++++++++++++++++++ apps/plugins/pdbox/PDa/src/d_ugen.c | 2252 +++++++++ apps/plugins/pdbox/PDa/src/delme.pd | 16 + apps/plugins/pdbox/PDa/src/g_all_guis.c | 1324 +++++ apps/plugins/pdbox/PDa/src/g_all_guis.h | 658 +++ apps/plugins/pdbox/PDa/src/g_array.c | 2734 +++++++++++ apps/plugins/pdbox/PDa/src/g_bang.c | 1108 +++++ apps/plugins/pdbox/PDa/src/g_canvas.c | 2952 +++++++++++ apps/plugins/pdbox/PDa/src/g_canvas.h | 1204 +++++ apps/plugins/pdbox/PDa/src/g_editor.c | 4548 +++++++++++++++++ apps/plugins/pdbox/PDa/src/g_graph.c | 2224 +++++++++ apps/plugins/pdbox/PDa/src/g_guiconnect.c | 188 + apps/plugins/pdbox/PDa/src/g_hdial.c | 1470 ++++++ apps/plugins/pdbox/PDa/src/g_hslider.c | 1308 +++++ apps/plugins/pdbox/PDa/src/g_io.c | 1224 +++++ apps/plugins/pdbox/PDa/src/g_mycanvas.c | 770 +++ apps/plugins/pdbox/PDa/src/g_numbox.c | 1814 +++++++ apps/plugins/pdbox/PDa/src/g_readwrite.c | 1446 ++++++ apps/plugins/pdbox/PDa/src/g_rtext.c | 972 ++++ apps/plugins/pdbox/PDa/src/g_scalar.c | 802 +++ apps/plugins/pdbox/PDa/src/g_template.c | 3358 +++++++++++++ apps/plugins/pdbox/PDa/src/g_text.c | 2632 ++++++++++ apps/plugins/pdbox/PDa/src/g_toggle.c | 948 ++++ apps/plugins/pdbox/PDa/src/g_traversal.c | 2168 +++++++++ apps/plugins/pdbox/PDa/src/g_vdial.c | 1432 ++++++ apps/plugins/pdbox/PDa/src/g_vslider.c | 1254 +++++ apps/plugins/pdbox/PDa/src/g_vumeter.c | 1426 ++++++ apps/plugins/pdbox/PDa/src/m_atom.c | 258 + apps/plugins/pdbox/PDa/src/m_binbuf.c | 2438 ++++++++++ apps/plugins/pdbox/PDa/src/m_class.c | 1648 +++++++ apps/plugins/pdbox/PDa/src/m_conf.c | 202 + apps/plugins/pdbox/PDa/src/m_fixed.c | 252 + apps/plugins/pdbox/PDa/src/m_fixed.h | 110 + apps/plugins/pdbox/PDa/src/m_glob.c | 210 + apps/plugins/pdbox/PDa/src/m_imp.h | 156 + apps/plugins/pdbox/PDa/src/m_memory.c | 178 + apps/plugins/pdbox/PDa/src/m_obj.c | 1394 ++++++ apps/plugins/pdbox/PDa/src/m_pd.c | 612 +++ apps/plugins/pdbox/PDa/src/m_pd.h | 1300 +++++ apps/plugins/pdbox/PDa/src/m_sched.c | 1162 +++++ apps/plugins/pdbox/PDa/src/makecostab.c | 50 + apps/plugins/pdbox/PDa/src/makefile | 354 ++ apps/plugins/pdbox/PDa/src/s_audio.c | 1746 +++++++ apps/plugins/pdbox/PDa/src/s_audio_alsa.c | 1890 ++++++++ apps/plugins/pdbox/PDa/src/s_audio_mmio.c | 1588 ++++++ apps/plugins/pdbox/PDa/src/s_audio_oss.c | 1688 +++++++ apps/plugins/pdbox/PDa/src/s_audio_pa.c | 584 +++ apps/plugins/pdbox/PDa/src/s_entry.c | 102 + apps/plugins/pdbox/PDa/src/s_file.c | 110 + apps/plugins/pdbox/PDa/src/s_inter.c | 2000 ++++++++ apps/plugins/pdbox/PDa/src/s_loader.c | 338 ++ apps/plugins/pdbox/PDa/src/s_main.c | 1676 +++++++ apps/plugins/pdbox/PDa/src/s_midi.c | 1282 +++++ apps/plugins/pdbox/PDa/src/s_midi_oss.c | 718 +++ apps/plugins/pdbox/PDa/src/s_midi_pm.c | 332 ++ apps/plugins/pdbox/PDa/src/s_midi_sgi.c | 376 ++ apps/plugins/pdbox/PDa/src/s_path.c | 820 ++++ apps/plugins/pdbox/PDa/src/s_print.c | 300 ++ apps/plugins/pdbox/PDa/src/s_stuff.h | 430 ++ apps/plugins/pdbox/PDa/src/s_watchdog.c | 94 + apps/plugins/pdbox/PDa/src/t_main.c | 240 + apps/plugins/pdbox/PDa/src/t_tk.h | 20 + apps/plugins/pdbox/PDa/src/t_tkcmd.c | 796 +++ apps/plugins/pdbox/PDa/src/u_main.tk | 6734 ++++++++++++++++++++++++++ apps/plugins/pdbox/PDa/src/u_pdreceive.c | 650 +++ apps/plugins/pdbox/PDa/src/u_pdsend.c | 314 ++ apps/plugins/pdbox/PDa/src/x_acoustics.c | 386 ++ apps/plugins/pdbox/PDa/src/x_arithmetic.c | 1792 +++++++ apps/plugins/pdbox/PDa/src/x_connective.c | 2904 +++++++++++ apps/plugins/pdbox/PDa/src/x_gui.c | 754 +++ apps/plugins/pdbox/PDa/src/x_interface.c | 156 + apps/plugins/pdbox/PDa/src/x_midi.c | 2626 ++++++++++ apps/plugins/pdbox/PDa/src/x_misc.c | 642 +++ apps/plugins/pdbox/PDa/src/x_net.c | 726 +++ apps/plugins/pdbox/PDa/src/x_qlist.c | 690 +++ apps/plugins/pdbox/PDa/src/x_time.c | 1040 ++++ 93 files changed, 109810 insertions(+) create mode 100644 apps/plugins/pdbox/PDa/src/build.ipod create mode 100644 apps/plugins/pdbox/PDa/src/d_arithmetic.c create mode 100644 apps/plugins/pdbox/PDa/src/d_array.c create mode 100644 apps/plugins/pdbox/PDa/src/d_ctl.c create mode 100644 apps/plugins/pdbox/PDa/src/d_dac.c create mode 100644 apps/plugins/pdbox/PDa/src/d_delay.c create mode 100644 apps/plugins/pdbox/PDa/src/d_fft.c create mode 100644 apps/plugins/pdbox/PDa/src/d_fftroutine.c create mode 100644 apps/plugins/pdbox/PDa/src/d_filter.c create mode 100644 apps/plugins/pdbox/PDa/src/d_global.c create mode 100644 apps/plugins/pdbox/PDa/src/d_imayer_fft.c create mode 100644 apps/plugins/pdbox/PDa/src/d_imayer_tables.h create mode 100644 apps/plugins/pdbox/PDa/src/d_math.c create mode 100644 apps/plugins/pdbox/PDa/src/d_mayer_fft.c create mode 100644 apps/plugins/pdbox/PDa/src/d_misc.c create mode 100644 apps/plugins/pdbox/PDa/src/d_osc.c create mode 100644 apps/plugins/pdbox/PDa/src/d_resample.c create mode 100644 apps/plugins/pdbox/PDa/src/d_soundfile.c create mode 100644 apps/plugins/pdbox/PDa/src/d_ugen.c create mode 100644 apps/plugins/pdbox/PDa/src/delme.pd create mode 100644 apps/plugins/pdbox/PDa/src/g_all_guis.c create mode 100644 apps/plugins/pdbox/PDa/src/g_all_guis.h create mode 100644 apps/plugins/pdbox/PDa/src/g_array.c create mode 100644 apps/plugins/pdbox/PDa/src/g_bang.c create mode 100644 apps/plugins/pdbox/PDa/src/g_canvas.c create mode 100644 apps/plugins/pdbox/PDa/src/g_canvas.h create mode 100644 apps/plugins/pdbox/PDa/src/g_editor.c create mode 100644 apps/plugins/pdbox/PDa/src/g_graph.c create mode 100644 apps/plugins/pdbox/PDa/src/g_guiconnect.c create mode 100644 apps/plugins/pdbox/PDa/src/g_hdial.c create mode 100644 apps/plugins/pdbox/PDa/src/g_hslider.c create mode 100644 apps/plugins/pdbox/PDa/src/g_io.c create mode 100644 apps/plugins/pdbox/PDa/src/g_mycanvas.c create mode 100644 apps/plugins/pdbox/PDa/src/g_numbox.c create mode 100644 apps/plugins/pdbox/PDa/src/g_readwrite.c create mode 100644 apps/plugins/pdbox/PDa/src/g_rtext.c create mode 100644 apps/plugins/pdbox/PDa/src/g_scalar.c create mode 100644 apps/plugins/pdbox/PDa/src/g_template.c create mode 100644 apps/plugins/pdbox/PDa/src/g_text.c create mode 100644 apps/plugins/pdbox/PDa/src/g_toggle.c create mode 100644 apps/plugins/pdbox/PDa/src/g_traversal.c create mode 100644 apps/plugins/pdbox/PDa/src/g_vdial.c create mode 100644 apps/plugins/pdbox/PDa/src/g_vslider.c create mode 100644 apps/plugins/pdbox/PDa/src/g_vumeter.c create mode 100644 apps/plugins/pdbox/PDa/src/m_atom.c create mode 100644 apps/plugins/pdbox/PDa/src/m_binbuf.c create mode 100644 apps/plugins/pdbox/PDa/src/m_class.c create mode 100644 apps/plugins/pdbox/PDa/src/m_conf.c create mode 100644 apps/plugins/pdbox/PDa/src/m_fixed.c create mode 100644 apps/plugins/pdbox/PDa/src/m_fixed.h create mode 100644 apps/plugins/pdbox/PDa/src/m_glob.c create mode 100644 apps/plugins/pdbox/PDa/src/m_imp.h create mode 100644 apps/plugins/pdbox/PDa/src/m_memory.c create mode 100644 apps/plugins/pdbox/PDa/src/m_obj.c create mode 100644 apps/plugins/pdbox/PDa/src/m_pd.c create mode 100644 apps/plugins/pdbox/PDa/src/m_pd.h create mode 100644 apps/plugins/pdbox/PDa/src/m_sched.c create mode 100644 apps/plugins/pdbox/PDa/src/makecostab.c create mode 100644 apps/plugins/pdbox/PDa/src/makefile create mode 100644 apps/plugins/pdbox/PDa/src/s_audio.c create mode 100644 apps/plugins/pdbox/PDa/src/s_audio_alsa.c create mode 100644 apps/plugins/pdbox/PDa/src/s_audio_mmio.c create mode 100644 apps/plugins/pdbox/PDa/src/s_audio_oss.c create mode 100644 apps/plugins/pdbox/PDa/src/s_audio_pa.c create mode 100644 apps/plugins/pdbox/PDa/src/s_entry.c create mode 100644 apps/plugins/pdbox/PDa/src/s_file.c create mode 100644 apps/plugins/pdbox/PDa/src/s_inter.c create mode 100644 apps/plugins/pdbox/PDa/src/s_loader.c create mode 100644 apps/plugins/pdbox/PDa/src/s_main.c create mode 100644 apps/plugins/pdbox/PDa/src/s_midi.c create mode 100644 apps/plugins/pdbox/PDa/src/s_midi_oss.c create mode 100644 apps/plugins/pdbox/PDa/src/s_midi_pm.c create mode 100644 apps/plugins/pdbox/PDa/src/s_midi_sgi.c create mode 100644 apps/plugins/pdbox/PDa/src/s_path.c create mode 100644 apps/plugins/pdbox/PDa/src/s_print.c create mode 100644 apps/plugins/pdbox/PDa/src/s_stuff.h create mode 100644 apps/plugins/pdbox/PDa/src/s_watchdog.c create mode 100644 apps/plugins/pdbox/PDa/src/t_main.c create mode 100644 apps/plugins/pdbox/PDa/src/t_tk.h create mode 100644 apps/plugins/pdbox/PDa/src/t_tkcmd.c create mode 100644 apps/plugins/pdbox/PDa/src/u_main.tk create mode 100644 apps/plugins/pdbox/PDa/src/u_pdreceive.c create mode 100644 apps/plugins/pdbox/PDa/src/u_pdsend.c create mode 100644 apps/plugins/pdbox/PDa/src/x_acoustics.c create mode 100644 apps/plugins/pdbox/PDa/src/x_arithmetic.c create mode 100644 apps/plugins/pdbox/PDa/src/x_connective.c create mode 100644 apps/plugins/pdbox/PDa/src/x_gui.c create mode 100644 apps/plugins/pdbox/PDa/src/x_interface.c create mode 100644 apps/plugins/pdbox/PDa/src/x_midi.c create mode 100644 apps/plugins/pdbox/PDa/src/x_misc.c create mode 100644 apps/plugins/pdbox/PDa/src/x_net.c create mode 100644 apps/plugins/pdbox/PDa/src/x_qlist.c create mode 100644 apps/plugins/pdbox/PDa/src/x_time.c (limited to 'apps/plugins/pdbox/PDa/src') diff --git a/apps/plugins/pdbox/PDa/src/build.ipod b/apps/plugins/pdbox/PDa/src/build.ipod new file mode 100644 index 0000000..bb78632 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/build.ipod @@ -0,0 +1,10 @@ + +# The compiler for iPod has a bug with -O6, thats why we try to compile twice + +make CFLAGS="-O6 -ffast-math -fexpensive-optimizations -mcpu=arm7tdmi " -k ipod +make CFLAGS="-O2 -ffast-math -fexpensive-optimizations -mcpu=arm7tdmi" ipod + +# The compiler for iPod has a bug with -O6, thats why we try to compile twice + +make CFLAGS="-O6 -ffast-math -fexpensive-optimizations -mcpu=arm7tdmi " -k ipod +make CFLAGS="-O2 -ffast-math -fexpensive-optimizations -mcpu=arm7tdmi" ipod diff --git a/apps/plugins/pdbox/PDa/src/d_arithmetic.c b/apps/plugins/pdbox/PDa/src/d_arithmetic.c new file mode 100644 index 0000000..72404ba --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_arithmetic.c @@ -0,0 +1,1684 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* arithmetic binops (+, -, *, /). +If no creation argument is given, there are two signal inlets for vector/vector +operation; otherwise it's vector/scalar and the second inlet takes a float +to reset the value. +*/ + +#include "m_pd.h" + +/* ----------------------------- plus ----------------------------- */ +static t_class *plus_class, *scalarplus_class; + +typedef struct _plus +{ + t_object x_obj; + float x_f; +} t_plus; + +typedef struct _scalarplus +{ + t_object x_obj; + float x_f; + t_float x_g; /* inlet value */ +} t_scalarplus; + +static void *plus_new(t_symbol *s, int argc, t_atom *argv) +{ + if (argc > 1) post("+~: extra arguments ignored"); + if (argc) + { + t_scalarplus *x = (t_scalarplus *)pd_new(scalarplus_class); + floatinlet_new(&x->x_obj, &x->x_g); + x->x_g = atom_getfloatarg(0, argc, argv); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } + else + { + t_plus *x = (t_plus *)pd_new(plus_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } +} + +t_int *plus_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in1++ + *in2++; + return (w+5); +} + +t_int *plus_perf8(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; + + out[0] = f0 + g0; out[1] = f1 + g1; out[2] = f2 + g2; out[3] = f3 + g3; + out[4] = f4 + g4; out[5] = f5 + g5; out[6] = f6 + g6; out[7] = f7 + g7; + } + return (w+5); +} + +t_int *scalarplus_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample f = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in++ + f; + return (w+5); +} + +t_int *scalarplus_perf8(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample g = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; + + out[0] = f0 + g; out[1] = f1 + g; out[2] = f2 + g; out[3] = f3 + g; + out[4] = f4 + g; out[5] = f5 + g; out[6] = f6 + g; out[7] = f7 + g; + } + return (w+5); +} + +void dsp_add_plus(t_sample *in1, t_sample *in2, t_sample *out, int n) +{ + if (n&7) + dsp_add(plus_perform, 4, in1, in2, out, n); + else + dsp_add(plus_perf8, 4, in1, in2, out, n); +} + +static void plus_dsp(t_plus *x, t_signal **sp) +{ + dsp_add_plus(sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +static void scalarplus_dsp(t_scalarplus *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(scalarplus_perform, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); + else + dsp_add(scalarplus_perf8, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); +} + +static void plus_setup(void) +{ + plus_class = class_new(gensym("+~"), (t_newmethod)plus_new, 0, + sizeof(t_plus), 0, A_GIMME, 0); + class_addmethod(plus_class, (t_method)plus_dsp, gensym("dsp"), 0); + CLASS_MAINSIGNALIN(plus_class, t_plus, x_f); + class_sethelpsymbol(plus_class, gensym("sigbinops")); + scalarplus_class = class_new(gensym("+~"), 0, 0, + sizeof(t_scalarplus), 0, 0); + CLASS_MAINSIGNALIN(scalarplus_class, t_scalarplus, x_f); + class_addmethod(scalarplus_class, (t_method)scalarplus_dsp, gensym("dsp"), + 0); + class_sethelpsymbol(scalarplus_class, gensym("sigbinops")); +} + +/* ----------------------------- minus ----------------------------- */ +static t_class *minus_class, *scalarminus_class; + +typedef struct _minus +{ + t_object x_obj; + float x_f; +} t_minus; + +typedef struct _scalarminus +{ + t_object x_obj; + float x_f; + t_float x_g; +} t_scalarminus; + +static void *minus_new(t_symbol *s, int argc, t_atom *argv) +{ + if (argc > 1) post("-~: extra arguments ignored"); + if (argc) + { + t_scalarminus *x = (t_scalarminus *)pd_new(scalarminus_class); + floatinlet_new(&x->x_obj, &x->x_g); + x->x_g = atom_getfloatarg(0, argc, argv); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } + else + { + t_minus *x = (t_minus *)pd_new(minus_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } +} + +t_int *minus_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in1++ - *in2++; + return (w+5); +} + +t_int *minus_perf8(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; + + out[0] = f0 - g0; out[1] = f1 - g1; out[2] = f2 - g2; out[3] = f3 - g3; + out[4] = f4 - g4; out[5] = f5 - g5; out[6] = f6 - g6; out[7] = f7 - g7; + } + return (w+5); +} + +t_int *scalarminus_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample f = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in++ - f; + return (w+5); +} + +t_int *scalarminus_perf8(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample g = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; + + out[0] = f0 - g; out[1] = f1 - g; out[2] = f2 - g; out[3] = f3 - g; + out[4] = f4 - g; out[5] = f5 - g; out[6] = f6 - g; out[7] = f7 - g; + } + return (w+5); +} + +static void minus_dsp(t_minus *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(minus_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); + else + dsp_add(minus_perf8, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +static void scalarminus_dsp(t_scalarminus *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(scalarminus_perform, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); + else + dsp_add(scalarminus_perf8, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); +} + +static void minus_setup(void) +{ + minus_class = class_new(gensym("-~"), (t_newmethod)minus_new, 0, + sizeof(t_minus), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(minus_class, t_minus, x_f); + class_addmethod(minus_class, (t_method)minus_dsp, gensym("dsp"), 0); + class_sethelpsymbol(minus_class, gensym("sigbinops")); + scalarminus_class = class_new(gensym("-~"), 0, 0, + sizeof(t_scalarminus), 0, 0); + CLASS_MAINSIGNALIN(scalarminus_class, t_scalarminus, x_f); + class_addmethod(scalarminus_class, (t_method)scalarminus_dsp, gensym("dsp"), + 0); + class_sethelpsymbol(scalarminus_class, gensym("sigbinops")); +} + +/* ----------------------------- times ----------------------------- */ + +static t_class *times_class, *scalartimes_class; + +typedef struct _times +{ + t_object x_obj; + float x_f; +} t_times; + +typedef struct _scalartimes +{ + t_object x_obj; + float x_f; + t_float x_g; +} t_scalartimes; + +static void *times_new(t_symbol *s, int argc, t_atom *argv) +{ + if (argc > 1) post("*~: extra arguments ignored"); + if (argc) + { + t_scalartimes *x = (t_scalartimes *)pd_new(scalartimes_class); + floatinlet_new(&x->x_obj, &x->x_g); + x->x_g = atom_getfloatarg(0, argc, argv); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } + else + { + t_times *x = (t_times *)pd_new(times_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } +} + +t_int *times_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = mult(*in1++,*in2++); + return (w+5); +} + +t_int *times_perf8(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; + + out[0] = mult(f0,g0); out[1] = mult(f1,g1); out[2] = mult(f2,g2); out[3] = mult(f3,g3); + out[4] = mult(f4,g4); out[5] = mult(f5,g5); out[6] = mult(f6,g6); out[7] = mult(f7,g7); + } + return (w+5); +} + +t_int *scalartimes_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample f = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = mult(*in++,f); + return (w+5); +} + +t_int *scalartimes_perf8(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample g = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; + + out[0] = mult(f0,g); out[1] = mult(f1,g); out[2] = mult(f2,g); out[3] = mult(f3,g); + out[4] = mult(f4,g); out[5] = mult(f5,g); out[6] = mult(f6,g); out[7] = mult(f7,g); + } + return (w+5); +} + +static void times_dsp(t_times *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(times_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); + else + dsp_add(times_perf8, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +static void scalartimes_dsp(t_scalartimes *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(scalartimes_perform, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); + else + dsp_add(scalartimes_perf8, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); +} + +static void times_setup(void) +{ + times_class = class_new(gensym("*~"), (t_newmethod)times_new, 0, + sizeof(t_times), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(times_class, t_times, x_f); + class_addmethod(times_class, (t_method)times_dsp, gensym("dsp"), 0); + class_sethelpsymbol(times_class, gensym("sigbinops")); + scalartimes_class = class_new(gensym("*~"), 0, 0, + sizeof(t_scalartimes), 0, 0); + CLASS_MAINSIGNALIN(scalartimes_class, t_scalartimes, x_f); + class_addmethod(scalartimes_class, (t_method)scalartimes_dsp, gensym("dsp"), + 0); + class_sethelpsymbol(scalartimes_class, gensym("sigbinops")); +} + +/* ----------------------------- over ----------------------------- */ +static t_class *over_class, *scalarover_class; + +typedef struct _over +{ + t_object x_obj; + float x_f; +} t_over; + +typedef struct _scalarover +{ + t_object x_obj; + float x_f; + t_float x_g; +} t_scalarover; + +static void *over_new(t_symbol *s, int argc, t_atom *argv) +{ + if (argc > 1) post("/~: extra arguments ignored"); + if (argc) + { + t_scalarover *x = (t_scalarover *)pd_new(scalarover_class); + floatinlet_new(&x->x_obj, &x->x_g); + x->x_g = atom_getfloatarg(0, argc, argv); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } + else + { + t_over *x = (t_over *)pd_new(over_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } +} + +t_int *over_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + float g = *in2++; + *out++ = (g ? *in1++ / g : 0); + } + return (w+5); +} + +t_int *over_perf8(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; + + out[0] = (g0? idiv(f0,g0) : 0); + out[1] = (g1? idiv(f1,g1) : 0); + out[2] = (g2? idiv(f2,g2) : 0); + out[3] = (g3? idiv(f3,g3) : 0); + out[4] = (g4? idiv(f4,g4) : 0); + out[5] = (g5? idiv(f5,g5) : 0); + out[6] = (g6? idiv(f6,g6) : 0); + out[7] = (g7? idiv(f7,g7) : 0); + } + return (w+5); +} + +t_int *scalarover_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample f = idiv(ftofix(1.),ftofix(*(t_float *)(w[2]))); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = mult(*in++,f); + return (w+5); +} + +t_int *scalarover_perf8(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample g = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + if (g) g = idiv(ftofix(1.f),g); + for (; n; n -= 8, in += 8, out += 8) + { + t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; + + out[0] = mult(f0,g); out[1] = mult(f1,g); out[2] = mult(f2,g); out[3] = mult(f3,g); + out[4] = mult(f4,g); out[5] = mult(f5,g); out[6] = mult(f6,g); out[7] = mult(f7,g); + } + return (w+5); +} + +static void over_dsp(t_over *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(over_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); + else + dsp_add(over_perf8, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +static void scalarover_dsp(t_scalarover *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(scalarover_perform, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); + else + dsp_add(scalarover_perf8, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); +} + +static void over_setup(void) +{ + over_class = class_new(gensym("/~"), (t_newmethod)over_new, 0, + sizeof(t_over), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(over_class, t_over, x_f); + class_addmethod(over_class, (t_method)over_dsp, gensym("dsp"), 0); + class_sethelpsymbol(over_class, gensym("sigbinops")); + scalarover_class = class_new(gensym("/~"), 0, 0, + sizeof(t_scalarover), 0, 0); + CLASS_MAINSIGNALIN(scalarover_class, t_scalarover, x_f); + class_addmethod(scalarover_class, (t_method)scalarover_dsp, gensym("dsp"), + 0); + class_sethelpsymbol(scalarover_class, gensym("sigbinops")); +} + +/* ----------------------------- max ----------------------------- */ +static t_class *max_class, *scalarmax_class; + +typedef struct _max +{ + t_object x_obj; + float x_f; +} t_max; + +typedef struct _scalarmax +{ + t_object x_obj; + float x_f; + t_float x_g; +} t_scalarmax; + +static void *max_new(t_symbol *s, int argc, t_atom *argv) +{ + if (argc > 1) post("max~: extra arguments ignored"); + if (argc) + { + t_scalarmax *x = (t_scalarmax *)pd_new(scalarmax_class); + floatinlet_new(&x->x_obj, &x->x_g); + x->x_g = atom_getfloatarg(0, argc, argv); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } + else + { + t_max *x = (t_max *)pd_new(max_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } +} + +t_int *max_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + t_sample f = *in1++, g = *in2++; + *out++ = (f > g ? f : g); + } + return (w+5); +} + +t_int *max_perf8(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; + + out[0] = (f0 > g0 ? f0 : g0); out[1] = (f1 > g1 ? f1 : g1); + out[2] = (f2 > g2 ? f2 : g2); out[3] = (f3 > g3 ? f3 : g3); + out[4] = (f4 > g4 ? f4 : g4); out[5] = (f5 > g5 ? f5 : g5); + out[6] = (f6 > g6 ? f6 : g6); out[7] = (f7 > g7 ? f7 : g7); + } + return (w+5); +} + +t_int *scalarmax_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample f = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + t_sample g = *in++; + *out++ = (f > g ? f : g); + } + return (w+5); +} + +t_int *scalarmax_perf8(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample g = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; + + out[0] = (f0 > g ? f0 : g); out[1] = (f1 > g ? f1 : g); + out[2] = (f2 > g ? f2 : g); out[3] = (f3 > g ? f3 : g); + out[4] = (f4 > g ? f4 : g); out[5] = (f5 > g ? f5 : g); + out[6] = (f6 > g ? f6 : g); out[7] = (f7 > g ? f7 : g); + } + return (w+5); +} + +static void max_dsp(t_max *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(max_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); + else + dsp_add(max_perf8, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +static void scalarmax_dsp(t_scalarmax *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(scalarmax_perform, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); + else + dsp_add(scalarmax_perf8, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); +} + +static void max_setup(void) +{ + max_class = class_new(gensym("max~"), (t_newmethod)max_new, 0, + sizeof(t_max), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(max_class, t_max, x_f); + class_addmethod(max_class, (t_method)max_dsp, gensym("dsp"), 0); + class_sethelpsymbol(max_class, gensym("sigbinops")); + scalarmax_class = class_new(gensym("max~"), 0, 0, + sizeof(t_scalarmax), 0, 0); + CLASS_MAINSIGNALIN(scalarmax_class, t_scalarmax, x_f); + class_addmethod(scalarmax_class, (t_method)scalarmax_dsp, gensym("dsp"), + 0); + class_sethelpsymbol(scalarmax_class, gensym("sigbinops")); +} + +/* ----------------------------- min ----------------------------- */ +static t_class *min_class, *scalarmin_class; + +typedef struct _min +{ + t_object x_obj; + float x_f; +} t_min; + +typedef struct _scalarmin +{ + t_object x_obj; + t_float x_g; + float x_f; +} t_scalarmin; + +static void *min_new(t_symbol *s, int argc, t_atom *argv) +{ + if (argc > 1) post("min~: extra arguments ignored"); + if (argc) + { + t_scalarmin *x = (t_scalarmin *)pd_new(scalarmin_class); + floatinlet_new(&x->x_obj, &x->x_g); + x->x_g = atom_getfloatarg(0, argc, argv); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } + else + { + t_min *x = (t_min *)pd_new(min_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } +} + +t_int *min_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + t_sample f = *in1++, g = *in2++; + *out++ = (f < g ? f : g); + } + return (w+5); +} + +t_int *min_perf8(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; + + out[0] = (f0 < g0 ? f0 : g0); out[1] = (f1 < g1 ? f1 : g1); + out[2] = (f2 < g2 ? f2 : g2); out[3] = (f3 < g3 ? f3 : g3); + out[4] = (f4 < g4 ? f4 : g4); out[5] = (f5 < g5 ? f5 : g5); + out[6] = (f6 < g6 ? f6 : g6); out[7] = (f7 < g7 ? f7 : g7); + } + return (w+5); +} + +t_int *scalarmin_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample f = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + t_sample g = *in++; + *out++ = (f < g ? f : g); + } + return (w+5); +} + +t_int *scalarmin_perf8(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample g = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; + + out[0] = (f0 < g ? f0 : g); out[1] = (f1 < g ? f1 : g); + out[2] = (f2 < g ? f2 : g); out[3] = (f3 < g ? f3 : g); + out[4] = (f4 < g ? f4 : g); out[5] = (f5 < g ? f5 : g); + out[6] = (f6 < g ? f6 : g); out[7] = (f7 < g ? f7 : g); + } + return (w+5); +} + +static void min_dsp(t_min *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(min_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); + else + dsp_add(min_perf8, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +static void scalarmin_dsp(t_scalarmin *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(scalarmin_perform, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); + else + dsp_add(scalarmin_perf8, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); +} + +static void min_setup(void) +{ + min_class = class_new(gensym("min~"), (t_newmethod)min_new, 0, + sizeof(t_min), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(min_class, t_min, x_f); + class_addmethod(min_class, (t_method)min_dsp, gensym("dsp"), 0); + class_sethelpsymbol(min_class, gensym("sigbinops")); + scalarmin_class = class_new(gensym("min~"), 0, 0, + sizeof(t_scalarmin), 0, 0); + CLASS_MAINSIGNALIN(scalarmin_class, t_scalarmin, x_f); + class_addmethod(scalarmin_class, (t_method)scalarmin_dsp, gensym("dsp"), + 0); + class_sethelpsymbol(scalarmin_class, gensym("sigbinops")); +} + +/* ----------------------- global setup routine ---------------- */ +void d_arithmetic_setup(void) +{ + plus_setup(); + minus_setup(); + times_setup(); + over_setup(); + max_setup(); + min_setup(); +} + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* arithmetic binops (+, -, *, /). +If no creation argument is given, there are two signal inlets for vector/vector +operation; otherwise it's vector/scalar and the second inlet takes a float +to reset the value. +*/ + +#include "m_pd.h" + +/* ----------------------------- plus ----------------------------- */ +static t_class *plus_class, *scalarplus_class; + +typedef struct _plus +{ + t_object x_obj; + float x_f; +} t_plus; + +typedef struct _scalarplus +{ + t_object x_obj; + float x_f; + t_float x_g; /* inlet value */ +} t_scalarplus; + +static void *plus_new(t_symbol *s, int argc, t_atom *argv) +{ + if (argc > 1) post("+~: extra arguments ignored"); + if (argc) + { + t_scalarplus *x = (t_scalarplus *)pd_new(scalarplus_class); + floatinlet_new(&x->x_obj, &x->x_g); + x->x_g = atom_getfloatarg(0, argc, argv); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } + else + { + t_plus *x = (t_plus *)pd_new(plus_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } +} + +t_int *plus_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in1++ + *in2++; + return (w+5); +} + +t_int *plus_perf8(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; + + out[0] = f0 + g0; out[1] = f1 + g1; out[2] = f2 + g2; out[3] = f3 + g3; + out[4] = f4 + g4; out[5] = f5 + g5; out[6] = f6 + g6; out[7] = f7 + g7; + } + return (w+5); +} + +t_int *scalarplus_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample f = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in++ + f; + return (w+5); +} + +t_int *scalarplus_perf8(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample g = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; + + out[0] = f0 + g; out[1] = f1 + g; out[2] = f2 + g; out[3] = f3 + g; + out[4] = f4 + g; out[5] = f5 + g; out[6] = f6 + g; out[7] = f7 + g; + } + return (w+5); +} + +void dsp_add_plus(t_sample *in1, t_sample *in2, t_sample *out, int n) +{ + if (n&7) + dsp_add(plus_perform, 4, in1, in2, out, n); + else + dsp_add(plus_perf8, 4, in1, in2, out, n); +} + +static void plus_dsp(t_plus *x, t_signal **sp) +{ + dsp_add_plus(sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +static void scalarplus_dsp(t_scalarplus *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(scalarplus_perform, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); + else + dsp_add(scalarplus_perf8, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); +} + +static void plus_setup(void) +{ + plus_class = class_new(gensym("+~"), (t_newmethod)plus_new, 0, + sizeof(t_plus), 0, A_GIMME, 0); + class_addmethod(plus_class, (t_method)plus_dsp, gensym("dsp"), 0); + CLASS_MAINSIGNALIN(plus_class, t_plus, x_f); + class_sethelpsymbol(plus_class, gensym("sigbinops")); + scalarplus_class = class_new(gensym("+~"), 0, 0, + sizeof(t_scalarplus), 0, 0); + CLASS_MAINSIGNALIN(scalarplus_class, t_scalarplus, x_f); + class_addmethod(scalarplus_class, (t_method)scalarplus_dsp, gensym("dsp"), + 0); + class_sethelpsymbol(scalarplus_class, gensym("sigbinops")); +} + +/* ----------------------------- minus ----------------------------- */ +static t_class *minus_class, *scalarminus_class; + +typedef struct _minus +{ + t_object x_obj; + float x_f; +} t_minus; + +typedef struct _scalarminus +{ + t_object x_obj; + float x_f; + t_float x_g; +} t_scalarminus; + +static void *minus_new(t_symbol *s, int argc, t_atom *argv) +{ + if (argc > 1) post("-~: extra arguments ignored"); + if (argc) + { + t_scalarminus *x = (t_scalarminus *)pd_new(scalarminus_class); + floatinlet_new(&x->x_obj, &x->x_g); + x->x_g = atom_getfloatarg(0, argc, argv); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } + else + { + t_minus *x = (t_minus *)pd_new(minus_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } +} + +t_int *minus_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in1++ - *in2++; + return (w+5); +} + +t_int *minus_perf8(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; + + out[0] = f0 - g0; out[1] = f1 - g1; out[2] = f2 - g2; out[3] = f3 - g3; + out[4] = f4 - g4; out[5] = f5 - g5; out[6] = f6 - g6; out[7] = f7 - g7; + } + return (w+5); +} + +t_int *scalarminus_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample f = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in++ - f; + return (w+5); +} + +t_int *scalarminus_perf8(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample g = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; + + out[0] = f0 - g; out[1] = f1 - g; out[2] = f2 - g; out[3] = f3 - g; + out[4] = f4 - g; out[5] = f5 - g; out[6] = f6 - g; out[7] = f7 - g; + } + return (w+5); +} + +static void minus_dsp(t_minus *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(minus_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); + else + dsp_add(minus_perf8, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +static void scalarminus_dsp(t_scalarminus *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(scalarminus_perform, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); + else + dsp_add(scalarminus_perf8, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); +} + +static void minus_setup(void) +{ + minus_class = class_new(gensym("-~"), (t_newmethod)minus_new, 0, + sizeof(t_minus), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(minus_class, t_minus, x_f); + class_addmethod(minus_class, (t_method)minus_dsp, gensym("dsp"), 0); + class_sethelpsymbol(minus_class, gensym("sigbinops")); + scalarminus_class = class_new(gensym("-~"), 0, 0, + sizeof(t_scalarminus), 0, 0); + CLASS_MAINSIGNALIN(scalarminus_class, t_scalarminus, x_f); + class_addmethod(scalarminus_class, (t_method)scalarminus_dsp, gensym("dsp"), + 0); + class_sethelpsymbol(scalarminus_class, gensym("sigbinops")); +} + +/* ----------------------------- times ----------------------------- */ + +static t_class *times_class, *scalartimes_class; + +typedef struct _times +{ + t_object x_obj; + float x_f; +} t_times; + +typedef struct _scalartimes +{ + t_object x_obj; + float x_f; + t_float x_g; +} t_scalartimes; + +static void *times_new(t_symbol *s, int argc, t_atom *argv) +{ + if (argc > 1) post("*~: extra arguments ignored"); + if (argc) + { + t_scalartimes *x = (t_scalartimes *)pd_new(scalartimes_class); + floatinlet_new(&x->x_obj, &x->x_g); + x->x_g = atom_getfloatarg(0, argc, argv); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } + else + { + t_times *x = (t_times *)pd_new(times_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } +} + +t_int *times_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = mult(*in1++,*in2++); + return (w+5); +} + +t_int *times_perf8(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; + + out[0] = mult(f0,g0); out[1] = mult(f1,g1); out[2] = mult(f2,g2); out[3] = mult(f3,g3); + out[4] = mult(f4,g4); out[5] = mult(f5,g5); out[6] = mult(f6,g6); out[7] = mult(f7,g7); + } + return (w+5); +} + +t_int *scalartimes_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample f = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = mult(*in++,f); + return (w+5); +} + +t_int *scalartimes_perf8(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample g = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; + + out[0] = mult(f0,g); out[1] = mult(f1,g); out[2] = mult(f2,g); out[3] = mult(f3,g); + out[4] = mult(f4,g); out[5] = mult(f5,g); out[6] = mult(f6,g); out[7] = mult(f7,g); + } + return (w+5); +} + +static void times_dsp(t_times *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(times_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); + else + dsp_add(times_perf8, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +static void scalartimes_dsp(t_scalartimes *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(scalartimes_perform, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); + else + dsp_add(scalartimes_perf8, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); +} + +static void times_setup(void) +{ + times_class = class_new(gensym("*~"), (t_newmethod)times_new, 0, + sizeof(t_times), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(times_class, t_times, x_f); + class_addmethod(times_class, (t_method)times_dsp, gensym("dsp"), 0); + class_sethelpsymbol(times_class, gensym("sigbinops")); + scalartimes_class = class_new(gensym("*~"), 0, 0, + sizeof(t_scalartimes), 0, 0); + CLASS_MAINSIGNALIN(scalartimes_class, t_scalartimes, x_f); + class_addmethod(scalartimes_class, (t_method)scalartimes_dsp, gensym("dsp"), + 0); + class_sethelpsymbol(scalartimes_class, gensym("sigbinops")); +} + +/* ----------------------------- over ----------------------------- */ +static t_class *over_class, *scalarover_class; + +typedef struct _over +{ + t_object x_obj; + float x_f; +} t_over; + +typedef struct _scalarover +{ + t_object x_obj; + float x_f; + t_float x_g; +} t_scalarover; + +static void *over_new(t_symbol *s, int argc, t_atom *argv) +{ + if (argc > 1) post("/~: extra arguments ignored"); + if (argc) + { + t_scalarover *x = (t_scalarover *)pd_new(scalarover_class); + floatinlet_new(&x->x_obj, &x->x_g); + x->x_g = atom_getfloatarg(0, argc, argv); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } + else + { + t_over *x = (t_over *)pd_new(over_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } +} + +t_int *over_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + float g = *in2++; + *out++ = (g ? *in1++ / g : 0); + } + return (w+5); +} + +t_int *over_perf8(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; + + out[0] = (g0? idiv(f0,g0) : 0); + out[1] = (g1? idiv(f1,g1) : 0); + out[2] = (g2? idiv(f2,g2) : 0); + out[3] = (g3? idiv(f3,g3) : 0); + out[4] = (g4? idiv(f4,g4) : 0); + out[5] = (g5? idiv(f5,g5) : 0); + out[6] = (g6? idiv(f6,g6) : 0); + out[7] = (g7? idiv(f7,g7) : 0); + } + return (w+5); +} + +t_int *scalarover_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample f = idiv(ftofix(1.),ftofix(*(t_float *)(w[2]))); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = mult(*in++,f); + return (w+5); +} + +t_int *scalarover_perf8(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample g = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + if (g) g = idiv(ftofix(1.f),g); + for (; n; n -= 8, in += 8, out += 8) + { + t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; + + out[0] = mult(f0,g); out[1] = mult(f1,g); out[2] = mult(f2,g); out[3] = mult(f3,g); + out[4] = mult(f4,g); out[5] = mult(f5,g); out[6] = mult(f6,g); out[7] = mult(f7,g); + } + return (w+5); +} + +static void over_dsp(t_over *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(over_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); + else + dsp_add(over_perf8, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +static void scalarover_dsp(t_scalarover *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(scalarover_perform, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); + else + dsp_add(scalarover_perf8, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); +} + +static void over_setup(void) +{ + over_class = class_new(gensym("/~"), (t_newmethod)over_new, 0, + sizeof(t_over), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(over_class, t_over, x_f); + class_addmethod(over_class, (t_method)over_dsp, gensym("dsp"), 0); + class_sethelpsymbol(over_class, gensym("sigbinops")); + scalarover_class = class_new(gensym("/~"), 0, 0, + sizeof(t_scalarover), 0, 0); + CLASS_MAINSIGNALIN(scalarover_class, t_scalarover, x_f); + class_addmethod(scalarover_class, (t_method)scalarover_dsp, gensym("dsp"), + 0); + class_sethelpsymbol(scalarover_class, gensym("sigbinops")); +} + +/* ----------------------------- max ----------------------------- */ +static t_class *max_class, *scalarmax_class; + +typedef struct _max +{ + t_object x_obj; + float x_f; +} t_max; + +typedef struct _scalarmax +{ + t_object x_obj; + float x_f; + t_float x_g; +} t_scalarmax; + +static void *max_new(t_symbol *s, int argc, t_atom *argv) +{ + if (argc > 1) post("max~: extra arguments ignored"); + if (argc) + { + t_scalarmax *x = (t_scalarmax *)pd_new(scalarmax_class); + floatinlet_new(&x->x_obj, &x->x_g); + x->x_g = atom_getfloatarg(0, argc, argv); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } + else + { + t_max *x = (t_max *)pd_new(max_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } +} + +t_int *max_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + t_sample f = *in1++, g = *in2++; + *out++ = (f > g ? f : g); + } + return (w+5); +} + +t_int *max_perf8(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; + + out[0] = (f0 > g0 ? f0 : g0); out[1] = (f1 > g1 ? f1 : g1); + out[2] = (f2 > g2 ? f2 : g2); out[3] = (f3 > g3 ? f3 : g3); + out[4] = (f4 > g4 ? f4 : g4); out[5] = (f5 > g5 ? f5 : g5); + out[6] = (f6 > g6 ? f6 : g6); out[7] = (f7 > g7 ? f7 : g7); + } + return (w+5); +} + +t_int *scalarmax_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample f = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + t_sample g = *in++; + *out++ = (f > g ? f : g); + } + return (w+5); +} + +t_int *scalarmax_perf8(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample g = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; + + out[0] = (f0 > g ? f0 : g); out[1] = (f1 > g ? f1 : g); + out[2] = (f2 > g ? f2 : g); out[3] = (f3 > g ? f3 : g); + out[4] = (f4 > g ? f4 : g); out[5] = (f5 > g ? f5 : g); + out[6] = (f6 > g ? f6 : g); out[7] = (f7 > g ? f7 : g); + } + return (w+5); +} + +static void max_dsp(t_max *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(max_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); + else + dsp_add(max_perf8, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +static void scalarmax_dsp(t_scalarmax *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(scalarmax_perform, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); + else + dsp_add(scalarmax_perf8, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); +} + +static void max_setup(void) +{ + max_class = class_new(gensym("max~"), (t_newmethod)max_new, 0, + sizeof(t_max), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(max_class, t_max, x_f); + class_addmethod(max_class, (t_method)max_dsp, gensym("dsp"), 0); + class_sethelpsymbol(max_class, gensym("sigbinops")); + scalarmax_class = class_new(gensym("max~"), 0, 0, + sizeof(t_scalarmax), 0, 0); + CLASS_MAINSIGNALIN(scalarmax_class, t_scalarmax, x_f); + class_addmethod(scalarmax_class, (t_method)scalarmax_dsp, gensym("dsp"), + 0); + class_sethelpsymbol(scalarmax_class, gensym("sigbinops")); +} + +/* ----------------------------- min ----------------------------- */ +static t_class *min_class, *scalarmin_class; + +typedef struct _min +{ + t_object x_obj; + float x_f; +} t_min; + +typedef struct _scalarmin +{ + t_object x_obj; + t_float x_g; + float x_f; +} t_scalarmin; + +static void *min_new(t_symbol *s, int argc, t_atom *argv) +{ + if (argc > 1) post("min~: extra arguments ignored"); + if (argc) + { + t_scalarmin *x = (t_scalarmin *)pd_new(scalarmin_class); + floatinlet_new(&x->x_obj, &x->x_g); + x->x_g = atom_getfloatarg(0, argc, argv); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } + else + { + t_min *x = (t_min *)pd_new(min_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); + } +} + +t_int *min_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + t_sample f = *in1++, g = *in2++; + *out++ = (f < g ? f : g); + } + return (w+5); +} + +t_int *min_perf8(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + t_sample f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + t_sample f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + t_sample g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + t_sample g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; + + out[0] = (f0 < g0 ? f0 : g0); out[1] = (f1 < g1 ? f1 : g1); + out[2] = (f2 < g2 ? f2 : g2); out[3] = (f3 < g3 ? f3 : g3); + out[4] = (f4 < g4 ? f4 : g4); out[5] = (f5 < g5 ? f5 : g5); + out[6] = (f6 < g6 ? f6 : g6); out[7] = (f7 < g7 ? f7 : g7); + } + return (w+5); +} + +t_int *scalarmin_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample f = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + t_sample g = *in++; + *out++ = (f < g ? f : g); + } + return (w+5); +} + +t_int *scalarmin_perf8(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample g = ftofix(*(t_float *)(w[2])); + t_sample *out = (t_sample *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + t_sample f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + t_sample f4 = in[4], f5 = in[5], f6 = in[6], f7 = in[7]; + + out[0] = (f0 < g ? f0 : g); out[1] = (f1 < g ? f1 : g); + out[2] = (f2 < g ? f2 : g); out[3] = (f3 < g ? f3 : g); + out[4] = (f4 < g ? f4 : g); out[5] = (f5 < g ? f5 : g); + out[6] = (f6 < g ? f6 : g); out[7] = (f7 < g ? f7 : g); + } + return (w+5); +} + +static void min_dsp(t_min *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(min_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); + else + dsp_add(min_perf8, 4, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[0]->s_n); +} + +static void scalarmin_dsp(t_scalarmin *x, t_signal **sp) +{ + if (sp[0]->s_n&7) + dsp_add(scalarmin_perform, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); + else + dsp_add(scalarmin_perf8, 4, sp[0]->s_vec, &x->x_g, + sp[1]->s_vec, sp[0]->s_n); +} + +static void min_setup(void) +{ + min_class = class_new(gensym("min~"), (t_newmethod)min_new, 0, + sizeof(t_min), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(min_class, t_min, x_f); + class_addmethod(min_class, (t_method)min_dsp, gensym("dsp"), 0); + class_sethelpsymbol(min_class, gensym("sigbinops")); + scalarmin_class = class_new(gensym("min~"), 0, 0, + sizeof(t_scalarmin), 0, 0); + CLASS_MAINSIGNALIN(scalarmin_class, t_scalarmin, x_f); + class_addmethod(scalarmin_class, (t_method)scalarmin_dsp, gensym("dsp"), + 0); + class_sethelpsymbol(scalarmin_class, gensym("sigbinops")); +} + +/* ----------------------- global setup routine ---------------- */ +void d_arithmetic_setup(void) +{ + plus_setup(); + minus_setup(); + times_setup(); + over_setup(); + max_setup(); + min_setup(); +} + diff --git a/apps/plugins/pdbox/PDa/src/d_array.c b/apps/plugins/pdbox/PDa/src/d_array.c new file mode 100644 index 0000000..14ae464 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_array.c @@ -0,0 +1,2148 @@ +/* Copyright (c) 1997-1999 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* sampling */ + +/* LATER make tabread4 and tabread~ */ + +#include "m_pd.h" + + +/* ------------------------- tabwrite~ -------------------------- */ + +static t_class *tabwrite_tilde_class; + +typedef struct _tabwrite_tilde +{ + t_object x_obj; + int x_phase; + int x_nsampsintab; + float *x_vec; + t_symbol *x_arrayname; + t_clock *x_clock; + float x_f; +} t_tabwrite_tilde; + +static void tabwrite_tilde_tick(t_tabwrite_tilde *x); + +static void *tabwrite_tilde_new(t_symbol *s) +{ + t_tabwrite_tilde *x = (t_tabwrite_tilde *)pd_new(tabwrite_tilde_class); + x->x_clock = clock_new(x, (t_method)tabwrite_tilde_tick); + x->x_phase = 0x7fffffff; + x->x_arrayname = s; + x->x_f = 0; + return (x); +} + +static t_int *tabwrite_tilde_perform(t_int *w) +{ + t_tabwrite_tilde *x = (t_tabwrite_tilde *)(w[1]); + t_float *in = (t_float *)(w[2]); + int n = (int)(w[3]), phase = x->x_phase, endphase = x->x_nsampsintab; + if (!x->x_vec) goto bad; + + if (endphase > phase) + { + int nxfer = endphase - phase; + float *fp = x->x_vec + phase; + if (nxfer > n) nxfer = n; + phase += nxfer; + while (nxfer--) + { + float f = *in++; + if (PD_BIGORSMALL(f)) + f = 0; + *fp++ = f; + } + if (phase >= endphase) + { + clock_delay(x->x_clock, 0); + phase = 0x7fffffff; + } + x->x_phase = phase; + } +bad: + return (w+4); +} + +void tabwrite_tilde_set(t_tabwrite_tilde *x, t_symbol *s) +{ + t_garray *a; + + x->x_arrayname = s; + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + { + if (*s->s_name) pd_error(x, "tabwrite~: %s: no such array", + x->x_arrayname->s_name); + x->x_vec = 0; + } + else if (!garray_getfloatarray(a, &x->x_nsampsintab, &x->x_vec)) + { + pd_error(x, "%s: bad template for tabwrite~", x->x_arrayname->s_name); + x->x_vec = 0; + } + else garray_usedindsp(a); +} + +static void tabwrite_tilde_dsp(t_tabwrite_tilde *x, t_signal **sp) +{ + tabwrite_tilde_set(x, x->x_arrayname); + dsp_add(tabwrite_tilde_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); +} + +static void tabwrite_tilde_bang(t_tabwrite_tilde *x) +{ + x->x_phase = 0; +} + +static void tabwrite_tilde_stop(t_tabwrite_tilde *x) +{ + if (x->x_phase != 0x7fffffff) + { + tabwrite_tilde_tick(x); + x->x_phase = 0x7fffffff; + } +} + +static void tabwrite_tilde_tick(t_tabwrite_tilde *x) +{ + t_garray *a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class); + if (!a) bug("tabwrite_tilde_tick"); + else garray_redraw(a); +} + +static void tabwrite_tilde_free(t_tabwrite_tilde *x) +{ + clock_free(x->x_clock); +} + +static void tabwrite_tilde_setup(void) +{ + tabwrite_tilde_class = class_new(gensym("tabwrite~"), + (t_newmethod)tabwrite_tilde_new, (t_method)tabwrite_tilde_free, + sizeof(t_tabwrite_tilde), 0, A_DEFSYM, 0); + CLASS_MAINSIGNALIN(tabwrite_tilde_class, t_tabwrite_tilde, x_f); + class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_set, + gensym("set"), A_SYMBOL, 0); + class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_stop, + gensym("stop"), 0); + class_addbang(tabwrite_tilde_class, tabwrite_tilde_bang); +} + +/* ------------ tabplay~ - non-transposing sample playback --------------- */ + +static t_class *tabplay_tilde_class; + +typedef struct _tabplay_tilde +{ + t_object x_obj; + t_outlet *x_bangout; + int x_phase; + int x_nsampsintab; + int x_limit; + float *x_vec; + t_symbol *x_arrayname; + t_clock *x_clock; +} t_tabplay_tilde; + +static void tabplay_tilde_tick(t_tabplay_tilde *x); + +static void *tabplay_tilde_new(t_symbol *s) +{ + t_tabplay_tilde *x = (t_tabplay_tilde *)pd_new(tabplay_tilde_class); + x->x_clock = clock_new(x, (t_method)tabplay_tilde_tick); + x->x_phase = 0x7fffffff; + x->x_limit = 0; + x->x_arrayname = s; + outlet_new(&x->x_obj, &s_signal); + x->x_bangout = outlet_new(&x->x_obj, &s_bang); + return (x); +} + +static t_int *tabplay_tilde_perform(t_int *w) +{ + t_tabplay_tilde *x = (t_tabplay_tilde *)(w[1]); + t_float *out = (t_float *)(w[2]), *fp; + int n = (int)(w[3]), phase = x->x_phase, + endphase = (x->x_nsampsintab < x->x_limit ? + x->x_nsampsintab : x->x_limit), nxfer, n3; + if (!x->x_vec || phase >= endphase) + goto zero; + + nxfer = endphase - phase; + fp = x->x_vec + phase; + if (nxfer > n) + nxfer = n; + n3 = n - nxfer; + phase += nxfer; + while (nxfer--) + *out++ = *fp++; + if (phase >= endphase) + { + clock_delay(x->x_clock, 0); + x->x_phase = 0x7fffffff; + while (n3--) + *out++ = 0; + } + else x->x_phase = phase; + + return (w+4); +zero: + while (n--) *out++ = 0; + return (w+4); +} + +void tabplay_tilde_set(t_tabplay_tilde *x, t_symbol *s) +{ + t_garray *a; + + x->x_arrayname = s; + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + { + if (*s->s_name) pd_error(x, "tabplay~: %s: no such array", + x->x_arrayname->s_name); + x->x_vec = 0; + } + else if (!garray_getfloatarray(a, &x->x_nsampsintab, &x->x_vec)) + { + pd_error(x, "%s: bad template for tabplay~", x->x_arrayname->s_name); + x->x_vec = 0; + } + else garray_usedindsp(a); +} + +static void tabplay_tilde_dsp(t_tabplay_tilde *x, t_signal **sp) +{ + tabplay_tilde_set(x, x->x_arrayname); + dsp_add(tabplay_tilde_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); +} + +static void tabplay_tilde_list(t_tabplay_tilde *x, t_symbol *s, + int argc, t_atom *argv) +{ + long start = atom_getfloatarg(0, argc, argv); + long length = atom_getfloatarg(1, argc, argv); + if (start < 0) start = 0; + if (length <= 0) + x->x_limit = 0x7fffffff; + else + x->x_limit = start + length; + x->x_phase = start; +} + +static void tabplay_tilde_stop(t_tabplay_tilde *x) +{ + x->x_phase = 0x7fffffff; +} + +static void tabplay_tilde_tick(t_tabplay_tilde *x) +{ + outlet_bang(x->x_bangout); +} + +static void tabplay_tilde_free(t_tabplay_tilde *x) +{ + clock_free(x->x_clock); +} + +static void tabplay_tilde_setup(void) +{ + tabplay_tilde_class = class_new(gensym("tabplay~"), + (t_newmethod)tabplay_tilde_new, (t_method)tabplay_tilde_free, + sizeof(t_tabplay_tilde), 0, A_DEFSYM, 0); + class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_stop, + gensym("stop"), 0); + class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_set, + gensym("set"), A_DEFSYM, 0); + class_addlist(tabplay_tilde_class, tabplay_tilde_list); +} + +/******************** tabread~ ***********************/ + +static t_class *tabread_tilde_class; + +typedef struct _tabread_tilde +{ + t_object x_obj; + int x_npoints; + float *x_vec; + t_symbol *x_arrayname; + float x_f; +} t_tabread_tilde; + +static void *tabread_tilde_new(t_symbol *s) +{ + t_tabread_tilde *x = (t_tabread_tilde *)pd_new(tabread_tilde_class); + x->x_arrayname = s; + x->x_vec = 0; + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *tabread_tilde_perform(t_int *w) +{ + t_tabread_tilde *x = (t_tabread_tilde *)(w[1]); + t_float *in = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + int maxindex; + float *buf = x->x_vec, *fp; + int i; + + maxindex = x->x_npoints - 1; + if (!buf) goto zero; + + for (i = 0; i < n; i++) + { + int index = *in++; + if (index < 0) + index = 0; + else if (index > maxindex) + index = maxindex; + *out++ = buf[index]; + } + return (w+5); + zero: + while (n--) *out++ = 0; + + return (w+5); +} + +void tabread_tilde_set(t_tabread_tilde *x, t_symbol *s) +{ + t_garray *a; + + x->x_arrayname = s; + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + { + if (*s->s_name) + pd_error(x, "tabread~: %s: no such array", x->x_arrayname->s_name); + x->x_vec = 0; + } + else if (!garray_getfloatarray(a, &x->x_npoints, &x->x_vec)) + { + pd_error(x, "%s: bad template for tabread~", x->x_arrayname->s_name); + x->x_vec = 0; + } + else garray_usedindsp(a); +} + +static void tabread_tilde_dsp(t_tabread_tilde *x, t_signal **sp) +{ + tabread_tilde_set(x, x->x_arrayname); + + dsp_add(tabread_tilde_perform, 4, x, + sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); + +} + +static void tabread_tilde_free(t_tabread_tilde *x) +{ +} + +static void tabread_tilde_setup(void) +{ + tabread_tilde_class = class_new(gensym("tabread~"), + (t_newmethod)tabread_tilde_new, (t_method)tabread_tilde_free, + sizeof(t_tabread_tilde), 0, A_DEFSYM, 0); + CLASS_MAINSIGNALIN(tabread_tilde_class, t_tabread_tilde, x_f); + class_addmethod(tabread_tilde_class, (t_method)tabread_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(tabread_tilde_class, (t_method)tabread_tilde_set, + gensym("set"), A_SYMBOL, 0); +} + +/******************** tabread4~ ***********************/ + +static t_class *tabread4_tilde_class; + +typedef struct _tabread4_tilde +{ + t_object x_obj; + int x_npoints; + float *x_vec; + t_symbol *x_arrayname; + float x_f; +} t_tabread4_tilde; + +static void *tabread4_tilde_new(t_symbol *s) +{ + t_tabread4_tilde *x = (t_tabread4_tilde *)pd_new(tabread4_tilde_class); + x->x_arrayname = s; + x->x_vec = 0; + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *tabread4_tilde_perform(t_int *w) +{ + t_tabread4_tilde *x = (t_tabread4_tilde *)(w[1]); + t_float *in = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + int maxindex; + float *buf = x->x_vec, *fp; + int i; + + maxindex = x->x_npoints - 3; + + if (!buf) goto zero; + +#if 0 /* test for spam -- I'm not ready to deal with this */ + for (i = 0, xmax = 0, xmin = maxindex, fp = in1; i < n; i++, fp++) + { + float f = *in1; + if (f < xmin) xmin = f; + else if (f > xmax) xmax = f; + } + if (xmax < xmin + x->c_maxextent) xmax = xmin + x->c_maxextent; + for (i = 0, splitlo = xmin+ x->c_maxextent, splithi = xmax - x->c_maxextent, + fp = in1; i < n; i++, fp++) + { + float f = *in1; + if (f > splitlo && f < splithi) goto zero; + } +#endif + + for (i = 0; i < n; i++) + { + float findex = *in++; + int index = findex; + float frac, a, b, c, d, cminusb; + static int count; + if (index < 1) + index = 1, frac = 0; + else if (index > maxindex) + index = maxindex, frac = 1; + else frac = findex - index; + fp = buf + index; + a = fp[-1]; + b = fp[0]; + c = fp[1]; + d = fp[2]; + /* if (!i && !(count++ & 1023)) + post("fp = %lx, shit = %lx, b = %f", fp, buf->b_shit, b); */ + cminusb = c-b; + *out++ = b + frac * ( + cminusb - 0.1666667f * (1.-frac) * ( + (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b) + ) + ); + } + return (w+5); + zero: + while (n--) *out++ = 0; + + return (w+5); +} + +void tabread4_tilde_set(t_tabread4_tilde *x, t_symbol *s) +{ + t_garray *a; + + x->x_arrayname = s; + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + { + if (*s->s_name) + pd_error(x, "tabread4~: %s: no such array", x->x_arrayname->s_name); + x->x_vec = 0; + } + else if (!garray_getfloatarray(a, &x->x_npoints, &x->x_vec)) + { + pd_error(x, "%s: bad template for tabread4~", x->x_arrayname->s_name); + x->x_vec = 0; + } + else garray_usedindsp(a); +} + +static void tabread4_tilde_dsp(t_tabread4_tilde *x, t_signal **sp) +{ + tabread4_tilde_set(x, x->x_arrayname); + + dsp_add(tabread4_tilde_perform, 4, x, + sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); + +} + +static void tabread4_tilde_free(t_tabread4_tilde *x) +{ +} + +static void tabread4_tilde_setup(void) +{ + tabread4_tilde_class = class_new(gensym("tabread4~"), + (t_newmethod)tabread4_tilde_new, (t_method)tabread4_tilde_free, + sizeof(t_tabread4_tilde), 0, A_DEFSYM, 0); + CLASS_MAINSIGNALIN(tabread4_tilde_class, t_tabread4_tilde, x_f); + class_addmethod(tabread4_tilde_class, (t_method)tabread4_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(tabread4_tilde_class, (t_method)tabread4_tilde_set, + gensym("set"), A_SYMBOL, 0); +} + +/******************** tabosc4~ ***********************/ + +/* this is all copied from d_osc.c... what include file could this go in? */ +#define UNITBIT32 1572864. /* 3*2^19; bit 32 has place value 1 */ + + /* machine-dependent definitions. These ifdefs really + should have been by CPU type and not by operating system! */ +#ifdef IRIX + /* big-endian. Most significant byte is at low address in memory */ +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#define int32 long /* a data type that has 32 bits */ +#else +#ifdef MSW + /* little-endian; most significant byte is at highest address */ +#define HIOFFSET 1 +#define LOWOFFSET 0 +#define int32 long +#else +#ifdef __FreeBSD__ +#include +#if BYTE_ORDER == LITTLE_ENDIAN +#define HIOFFSET 1 +#define LOWOFFSET 0 +#else +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#endif /* BYTE_ORDER */ +#include +#define int32 int32_t +#endif + +#ifdef __linux__ +#include +#if !defined(__BYTE_ORDER) || !defined(__LITTLE_ENDIAN) +#error No byte order defined +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define HIOFFSET 1 +#define LOWOFFSET 0 +#else +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#endif /* __BYTE_ORDER */ + +#include +#define int32 int32_t + +#else +#ifdef MACOSX +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#define int32 int /* a data type that has 32 bits */ + +#endif /* MACOSX */ +#endif /* __linux__ */ +#endif /* MSW */ +#endif /* SGI */ + +union tabfudge +{ + double tf_d; + int32 tf_i[2]; +}; + +static t_class *tabosc4_tilde_class; + +typedef struct _tabosc4_tilde +{ + t_object x_obj; + float x_fnpoints; + float x_finvnpoints; + float *x_vec; + t_symbol *x_arrayname; + float x_f; + double x_phase; + float x_conv; +} t_tabosc4_tilde; + +static void *tabosc4_tilde_new(t_symbol *s) +{ + t_tabosc4_tilde *x = (t_tabosc4_tilde *)pd_new(tabosc4_tilde_class); + x->x_arrayname = s; + x->x_vec = 0; + x->x_fnpoints = 512.; + x->x_finvnpoints = (1./512.); + outlet_new(&x->x_obj, gensym("signal")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1")); + x->x_f = 0; + return (x); +} + +static t_int *tabosc4_tilde_perform(t_int *w) +{ + t_tabosc4_tilde *x = (t_tabosc4_tilde *)(w[1]); + t_float *in = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + int normhipart; + union tabfudge tf; + float fnpoints = x->x_fnpoints; + int mask = fnpoints - 1; + float conv = fnpoints * x->x_conv; + int maxindex; + float *tab = x->x_vec, *addr; + int i; + double dphase = fnpoints * x->x_phase + UNITBIT32; + + if (!tab) goto zero; + tf.tf_d = UNITBIT32; + normhipart = tf.tf_i[HIOFFSET]; + +#if 1 + while (n--) + { + float frac, a, b, c, d, cminusb; + tf.tf_d = dphase; + dphase += *in++ * conv; + addr = tab + (tf.tf_i[HIOFFSET] & mask); + tf.tf_i[HIOFFSET] = normhipart; + frac = tf.tf_d - UNITBIT32; + a = addr[0]; + b = addr[1]; + c = addr[2]; + d = addr[3]; + cminusb = c-b; + *out++ = b + frac * ( + cminusb - 0.1666667f * (1.-frac) * ( + (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b) + ) + ); + } +#endif + + tf.tf_d = UNITBIT32 * fnpoints; + normhipart = tf.tf_i[HIOFFSET]; + tf.tf_d = dphase + (UNITBIT32 * fnpoints - UNITBIT32); + tf.tf_i[HIOFFSET] = normhipart; + x->x_phase = (tf.tf_d - UNITBIT32 * fnpoints) * x->x_finvnpoints; + return (w+5); + zero: + while (n--) *out++ = 0; + + return (w+5); +} + +void tabosc4_tilde_set(t_tabosc4_tilde *x, t_symbol *s) +{ + t_garray *a; + int npoints, pointsinarray; + + x->x_arrayname = s; + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + { + if (*s->s_name) + pd_error(x, "tabosc4~: %s: no such array", x->x_arrayname->s_name); + x->x_vec = 0; + } + else if (!garray_getfloatarray(a, &pointsinarray, &x->x_vec)) + { + pd_error(x, "%s: bad template for tabosc4~", x->x_arrayname->s_name); + x->x_vec = 0; + } + else if ((npoints = pointsinarray - 3) != (1 << ilog2(pointsinarray - 3))) + { + pd_error(x, "%s: number of points (%d) not a power of 2 plus three", + x->x_arrayname->s_name, pointsinarray); + x->x_vec = 0; + garray_usedindsp(a); + } + else + { + x->x_fnpoints = npoints; + x->x_finvnpoints = 1./npoints; + garray_usedindsp(a); + } +} + +static void tabosc4_tilde_ft1(t_tabosc4_tilde *x, t_float f) +{ + x->x_phase = f; +} + +static void tabosc4_tilde_dsp(t_tabosc4_tilde *x, t_signal **sp) +{ + x->x_conv = 1. / sp[0]->s_sr; + tabosc4_tilde_set(x, x->x_arrayname); + + dsp_add(tabosc4_tilde_perform, 4, x, + sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +static void tabosc4_tilde_setup(void) +{ + tabosc4_tilde_class = class_new(gensym("tabosc4~"), + (t_newmethod)tabosc4_tilde_new, 0, + sizeof(t_tabosc4_tilde), 0, A_DEFSYM, 0); + CLASS_MAINSIGNALIN(tabosc4_tilde_class, t_tabosc4_tilde, x_f); + class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_set, + gensym("set"), A_SYMBOL, 0); + class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_ft1, + gensym("ft1"), A_FLOAT, 0); +} + +/* ------------------------ tabsend~ ------------------------- */ + +static t_class *tabsend_class; + +typedef struct _tabsend +{ + t_object x_obj; + float *x_vec; + int x_graphperiod; + int x_graphcount; + t_symbol *x_arrayname; + t_clock *x_clock; + float x_f; +} t_tabsend; + +static void tabsend_tick(t_tabsend *x); + +static void *tabsend_new(t_symbol *s) +{ + t_tabsend *x = (t_tabsend *)pd_new(tabsend_class); + x->x_graphcount = 0; + x->x_arrayname = s; + x->x_clock = clock_new(x, (t_method)tabsend_tick); + x->x_f = 0; + return (x); +} + +static t_int *tabsend_perform(t_int *w) +{ + t_tabsend *x = (t_tabsend *)(w[1]); + t_float *in = (t_float *)(w[2]); + int n = w[3]; + t_float *dest = x->x_vec; + int i = x->x_graphcount; + if (!x->x_vec) goto bad; + + while (n--) + { + float f = *in++; + if (PD_BIGORSMALL(f)) + f = 0; + *dest++ = f; + } + if (!i--) + { + clock_delay(x->x_clock, 0); + i = x->x_graphperiod; + } + x->x_graphcount = i; +bad: + return (w+4); +} + +static void tabsend_dsp(t_tabsend *x, t_signal **sp) +{ + int i, vecsize; + t_garray *a; + + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + { + if (*x->x_arrayname->s_name) + pd_error(x, "tabsend~: %s: no such array", x->x_arrayname->s_name); + } + else if (!garray_getfloatarray(a, &vecsize, &x->x_vec)) + pd_error(x, "%s: bad template for tabsend~", x->x_arrayname->s_name); + else + { + int n = sp[0]->s_n; + int ticksper = sp[0]->s_sr/n; + if (ticksper < 1) ticksper = 1; + x->x_graphperiod = ticksper; + if (x->x_graphcount > ticksper) x->x_graphcount = ticksper; + if (n < vecsize) vecsize = n; + garray_usedindsp(a); + dsp_add(tabsend_perform, 3, x, sp[0]->s_vec, vecsize); + } +} + +static void tabsend_tick(t_tabsend *x) +{ + t_garray *a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class); + if (!a) bug("tabsend_tick"); + else garray_redraw(a); +} + +static void tabsend_free(t_tabsend *x) +{ + clock_free(x->x_clock); +} + +static void tabsend_setup(void) +{ + tabsend_class = class_new(gensym("tabsend~"), (t_newmethod)tabsend_new, + (t_method)tabsend_free, sizeof(t_tabsend), 0, A_DEFSYM, 0); + CLASS_MAINSIGNALIN(tabsend_class, t_tabsend, x_f); + class_addmethod(tabsend_class, (t_method)tabsend_dsp, gensym("dsp"), 0); +} + +/* ------------------------ tabreceive~ ------------------------- */ + +static t_class *tabreceive_class; + +typedef struct _tabreceive +{ + t_object x_obj; + float *x_vec; + t_symbol *x_arrayname; +} t_tabreceive; + +static t_int *tabreceive_perform(t_int *w) +{ + t_tabreceive *x = (t_tabreceive *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = w[3]; + t_float *from = x->x_vec; + if (from) while (n--) *out++ = *from++; + else while (n--) *out++ = 0; + return (w+4); +} + +static void tabreceive_dsp(t_tabreceive *x, t_signal **sp) +{ + t_garray *a; + int vecsize; + + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + { + if (*x->x_arrayname->s_name) + pd_error(x, "tabsend~: %s: no such array", x->x_arrayname->s_name); + } + else if (!garray_getfloatarray(a, &vecsize, &x->x_vec)) + pd_error(x, "%s: bad template for tabreceive~", x->x_arrayname->s_name); + else + { + int n = sp[0]->s_n; + if (n < vecsize) vecsize = n; + garray_usedindsp(a); + dsp_add(tabreceive_perform, 3, x, sp[0]->s_vec, vecsize); + } +} + +static void *tabreceive_new(t_symbol *s) +{ + t_tabreceive *x = (t_tabreceive *)pd_new(tabreceive_class); + x->x_arrayname = s; + outlet_new(&x->x_obj, &s_signal); + return (x); +} + +static void tabreceive_setup(void) +{ + tabreceive_class = class_new(gensym("tabreceive~"), + (t_newmethod)tabreceive_new, 0, + sizeof(t_tabreceive), 0, A_DEFSYM, 0); + class_addmethod(tabreceive_class, (t_method)tabreceive_dsp, + gensym("dsp"), 0); +} + + +/* ---------- tabread: control, non-interpolating ------------------------ */ + +static t_class *tabread_class; + +typedef struct _tabread +{ + t_object x_obj; + t_symbol *x_arrayname; +} t_tabread; + +static void tabread_float(t_tabread *x, t_float f) +{ + t_garray *a; + int npoints; + t_float *vec; + + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + pd_error(x, "%s: no such array", x->x_arrayname->s_name); + else if (!garray_getfloatarray(a, &npoints, &vec)) + pd_error(x, "%s: bad template for tabread", x->x_arrayname->s_name); + else + { + int n = f; + if (n < 0) n = 0; + else if (n >= npoints) n = npoints - 1; + outlet_float(x->x_obj.ob_outlet, (npoints ? vec[n] : 0)); + } +} + +static void tabread_set(t_tabread *x, t_symbol *s) +{ + x->x_arrayname = s; +} + +static void *tabread_new(t_symbol *s) +{ + t_tabread *x = (t_tabread *)pd_new(tabread_class); + x->x_arrayname = s; + outlet_new(&x->x_obj, &s_float); + return (x); +} + +static void tabread_setup(void) +{ + tabread_class = class_new(gensym("tabread"), (t_newmethod)tabread_new, + 0, sizeof(t_tabread), 0, A_DEFSYM, 0); + class_addfloat(tabread_class, (t_method)tabread_float); + class_addmethod(tabread_class, (t_method)tabread_set, gensym("set"), + A_SYMBOL, 0); +} + +/* ---------- tabread4: control, non-interpolating ------------------------ */ + +static t_class *tabread4_class; + +typedef struct _tabread4 +{ + t_object x_obj; + t_symbol *x_arrayname; +} t_tabread4; + +static void tabread4_float(t_tabread4 *x, t_float f) +{ + t_garray *a; + int npoints; + t_float *vec; + + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + pd_error(x, "%s: no such array", x->x_arrayname->s_name); + else if (!garray_getfloatarray(a, &npoints, &vec)) + pd_error(x, "%s: bad template for tabread4", x->x_arrayname->s_name); + else if (npoints < 4) + outlet_float(x->x_obj.ob_outlet, 0); + else if (f <= 1) + outlet_float(x->x_obj.ob_outlet, vec[1]); + else if (f >= npoints - 2) + outlet_float(x->x_obj.ob_outlet, vec[npoints - 2]); + else + { + int n = f; + float a, b, c, d, cminusb, frac, *fp; + if (n >= npoints - 2) + n = npoints - 3; + fp = vec + n; + frac = f - n; + a = fp[-1]; + b = fp[0]; + c = fp[1]; + d = fp[2]; + cminusb = c-b; + outlet_float(x->x_obj.ob_outlet, b + frac * ( + cminusb - 0.1666667f * (1.-frac) * ( + (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b)))); + } +} + +static void tabread4_set(t_tabread4 *x, t_symbol *s) +{ + x->x_arrayname = s; +} + +static void *tabread4_new(t_symbol *s) +{ + t_tabread4 *x = (t_tabread4 *)pd_new(tabread4_class); + x->x_arrayname = s; + outlet_new(&x->x_obj, &s_float); + return (x); +} + +static void tabread4_setup(void) +{ + tabread4_class = class_new(gensym("tabread4"), (t_newmethod)tabread4_new, + 0, sizeof(t_tabread4), 0, A_DEFSYM, 0); + class_addfloat(tabread4_class, (t_method)tabread4_float); + class_addmethod(tabread4_class, (t_method)tabread4_set, gensym("set"), + A_SYMBOL, 0); +} + +/* ------------------ tabwrite: control ------------------------ */ + +static t_class *tabwrite_class; + +typedef struct _tabwrite +{ + t_object x_obj; + t_symbol *x_arrayname; + t_clock *x_clock; + float x_ft1; + double x_updtime; + int x_set; +} t_tabwrite; + +static void tabwrite_tick(t_tabwrite *x) +{ + t_garray *a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class); + if (!a) bug("tabwrite_tick"); + else garray_redraw(a); + x->x_set = 0; + x->x_updtime = clock_getsystime(); +} + +static void tabwrite_float(t_tabwrite *x, t_float f) +{ + int i, vecsize; + t_garray *a; + t_float *vec; + + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + pd_error(x, "%s: no such array", x->x_arrayname->s_name); + else if (!garray_getfloatarray(a, &vecsize, &vec)) + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname->s_name); + else + { + int n = x->x_ft1; + double timesince = clock_gettimesince(x->x_updtime); + if (n < 0) n = 0; + else if (n >= vecsize) n = vecsize-1; + vec[n] = f; + if (timesince > 1000) + { + tabwrite_tick(x); + } + else + { + if (x->x_set == 0) + { + clock_delay(x->x_clock, 1000 - timesince); + x->x_set = 1; + } + } + } +} + +static void tabwrite_set(t_tabwrite *x, t_symbol *s) +{ + x->x_arrayname = s; +} + +static void tabwrite_free(t_tabwrite *x) +{ + clock_free(x->x_clock); +} + +static void *tabwrite_new(t_symbol *s) +{ + t_tabwrite *x = (t_tabwrite *)pd_new(tabwrite_class); + x->x_ft1 = 0; + x->x_arrayname = s; + x->x_updtime = clock_getsystime(); + x->x_clock = clock_new(x, (t_method)tabwrite_tick); + floatinlet_new(&x->x_obj, &x->x_ft1); + return (x); +} + +void tabwrite_setup(void) +{ + tabwrite_class = class_new(gensym("tabwrite"), (t_newmethod)tabwrite_new, + (t_method)tabwrite_free, sizeof(t_tabwrite), 0, A_DEFSYM, 0); + class_addfloat(tabwrite_class, (t_method)tabwrite_float); + class_addmethod(tabwrite_class, (t_method)tabwrite_set, gensym("set"), A_SYMBOL, 0); +} + +/* ------------------------ global setup routine ------------------------- */ + +void d_array_setup(void) +{ + tabwrite_tilde_setup(); + tabplay_tilde_setup(); + tabread_tilde_setup(); + tabread4_tilde_setup(); + tabosc4_tilde_setup(); + tabsend_setup(); + tabreceive_setup(); + tabread_setup(); + tabread4_setup(); + tabwrite_setup(); +} + +/* Copyright (c) 1997-1999 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* sampling */ + +/* LATER make tabread4 and tabread~ */ + +#include "m_pd.h" + + +/* ------------------------- tabwrite~ -------------------------- */ + +static t_class *tabwrite_tilde_class; + +typedef struct _tabwrite_tilde +{ + t_object x_obj; + int x_phase; + int x_nsampsintab; + float *x_vec; + t_symbol *x_arrayname; + t_clock *x_clock; + float x_f; +} t_tabwrite_tilde; + +static void tabwrite_tilde_tick(t_tabwrite_tilde *x); + +static void *tabwrite_tilde_new(t_symbol *s) +{ + t_tabwrite_tilde *x = (t_tabwrite_tilde *)pd_new(tabwrite_tilde_class); + x->x_clock = clock_new(x, (t_method)tabwrite_tilde_tick); + x->x_phase = 0x7fffffff; + x->x_arrayname = s; + x->x_f = 0; + return (x); +} + +static t_int *tabwrite_tilde_perform(t_int *w) +{ + t_tabwrite_tilde *x = (t_tabwrite_tilde *)(w[1]); + t_float *in = (t_float *)(w[2]); + int n = (int)(w[3]), phase = x->x_phase, endphase = x->x_nsampsintab; + if (!x->x_vec) goto bad; + + if (endphase > phase) + { + int nxfer = endphase - phase; + float *fp = x->x_vec + phase; + if (nxfer > n) nxfer = n; + phase += nxfer; + while (nxfer--) + { + float f = *in++; + if (PD_BIGORSMALL(f)) + f = 0; + *fp++ = f; + } + if (phase >= endphase) + { + clock_delay(x->x_clock, 0); + phase = 0x7fffffff; + } + x->x_phase = phase; + } +bad: + return (w+4); +} + +void tabwrite_tilde_set(t_tabwrite_tilde *x, t_symbol *s) +{ + t_garray *a; + + x->x_arrayname = s; + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + { + if (*s->s_name) pd_error(x, "tabwrite~: %s: no such array", + x->x_arrayname->s_name); + x->x_vec = 0; + } + else if (!garray_getfloatarray(a, &x->x_nsampsintab, &x->x_vec)) + { + pd_error(x, "%s: bad template for tabwrite~", x->x_arrayname->s_name); + x->x_vec = 0; + } + else garray_usedindsp(a); +} + +static void tabwrite_tilde_dsp(t_tabwrite_tilde *x, t_signal **sp) +{ + tabwrite_tilde_set(x, x->x_arrayname); + dsp_add(tabwrite_tilde_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); +} + +static void tabwrite_tilde_bang(t_tabwrite_tilde *x) +{ + x->x_phase = 0; +} + +static void tabwrite_tilde_stop(t_tabwrite_tilde *x) +{ + if (x->x_phase != 0x7fffffff) + { + tabwrite_tilde_tick(x); + x->x_phase = 0x7fffffff; + } +} + +static void tabwrite_tilde_tick(t_tabwrite_tilde *x) +{ + t_garray *a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class); + if (!a) bug("tabwrite_tilde_tick"); + else garray_redraw(a); +} + +static void tabwrite_tilde_free(t_tabwrite_tilde *x) +{ + clock_free(x->x_clock); +} + +static void tabwrite_tilde_setup(void) +{ + tabwrite_tilde_class = class_new(gensym("tabwrite~"), + (t_newmethod)tabwrite_tilde_new, (t_method)tabwrite_tilde_free, + sizeof(t_tabwrite_tilde), 0, A_DEFSYM, 0); + CLASS_MAINSIGNALIN(tabwrite_tilde_class, t_tabwrite_tilde, x_f); + class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_set, + gensym("set"), A_SYMBOL, 0); + class_addmethod(tabwrite_tilde_class, (t_method)tabwrite_tilde_stop, + gensym("stop"), 0); + class_addbang(tabwrite_tilde_class, tabwrite_tilde_bang); +} + +/* ------------ tabplay~ - non-transposing sample playback --------------- */ + +static t_class *tabplay_tilde_class; + +typedef struct _tabplay_tilde +{ + t_object x_obj; + t_outlet *x_bangout; + int x_phase; + int x_nsampsintab; + int x_limit; + float *x_vec; + t_symbol *x_arrayname; + t_clock *x_clock; +} t_tabplay_tilde; + +static void tabplay_tilde_tick(t_tabplay_tilde *x); + +static void *tabplay_tilde_new(t_symbol *s) +{ + t_tabplay_tilde *x = (t_tabplay_tilde *)pd_new(tabplay_tilde_class); + x->x_clock = clock_new(x, (t_method)tabplay_tilde_tick); + x->x_phase = 0x7fffffff; + x->x_limit = 0; + x->x_arrayname = s; + outlet_new(&x->x_obj, &s_signal); + x->x_bangout = outlet_new(&x->x_obj, &s_bang); + return (x); +} + +static t_int *tabplay_tilde_perform(t_int *w) +{ + t_tabplay_tilde *x = (t_tabplay_tilde *)(w[1]); + t_float *out = (t_float *)(w[2]), *fp; + int n = (int)(w[3]), phase = x->x_phase, + endphase = (x->x_nsampsintab < x->x_limit ? + x->x_nsampsintab : x->x_limit), nxfer, n3; + if (!x->x_vec || phase >= endphase) + goto zero; + + nxfer = endphase - phase; + fp = x->x_vec + phase; + if (nxfer > n) + nxfer = n; + n3 = n - nxfer; + phase += nxfer; + while (nxfer--) + *out++ = *fp++; + if (phase >= endphase) + { + clock_delay(x->x_clock, 0); + x->x_phase = 0x7fffffff; + while (n3--) + *out++ = 0; + } + else x->x_phase = phase; + + return (w+4); +zero: + while (n--) *out++ = 0; + return (w+4); +} + +void tabplay_tilde_set(t_tabplay_tilde *x, t_symbol *s) +{ + t_garray *a; + + x->x_arrayname = s; + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + { + if (*s->s_name) pd_error(x, "tabplay~: %s: no such array", + x->x_arrayname->s_name); + x->x_vec = 0; + } + else if (!garray_getfloatarray(a, &x->x_nsampsintab, &x->x_vec)) + { + pd_error(x, "%s: bad template for tabplay~", x->x_arrayname->s_name); + x->x_vec = 0; + } + else garray_usedindsp(a); +} + +static void tabplay_tilde_dsp(t_tabplay_tilde *x, t_signal **sp) +{ + tabplay_tilde_set(x, x->x_arrayname); + dsp_add(tabplay_tilde_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); +} + +static void tabplay_tilde_list(t_tabplay_tilde *x, t_symbol *s, + int argc, t_atom *argv) +{ + long start = atom_getfloatarg(0, argc, argv); + long length = atom_getfloatarg(1, argc, argv); + if (start < 0) start = 0; + if (length <= 0) + x->x_limit = 0x7fffffff; + else + x->x_limit = start + length; + x->x_phase = start; +} + +static void tabplay_tilde_stop(t_tabplay_tilde *x) +{ + x->x_phase = 0x7fffffff; +} + +static void tabplay_tilde_tick(t_tabplay_tilde *x) +{ + outlet_bang(x->x_bangout); +} + +static void tabplay_tilde_free(t_tabplay_tilde *x) +{ + clock_free(x->x_clock); +} + +static void tabplay_tilde_setup(void) +{ + tabplay_tilde_class = class_new(gensym("tabplay~"), + (t_newmethod)tabplay_tilde_new, (t_method)tabplay_tilde_free, + sizeof(t_tabplay_tilde), 0, A_DEFSYM, 0); + class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_stop, + gensym("stop"), 0); + class_addmethod(tabplay_tilde_class, (t_method)tabplay_tilde_set, + gensym("set"), A_DEFSYM, 0); + class_addlist(tabplay_tilde_class, tabplay_tilde_list); +} + +/******************** tabread~ ***********************/ + +static t_class *tabread_tilde_class; + +typedef struct _tabread_tilde +{ + t_object x_obj; + int x_npoints; + float *x_vec; + t_symbol *x_arrayname; + float x_f; +} t_tabread_tilde; + +static void *tabread_tilde_new(t_symbol *s) +{ + t_tabread_tilde *x = (t_tabread_tilde *)pd_new(tabread_tilde_class); + x->x_arrayname = s; + x->x_vec = 0; + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *tabread_tilde_perform(t_int *w) +{ + t_tabread_tilde *x = (t_tabread_tilde *)(w[1]); + t_float *in = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + int maxindex; + float *buf = x->x_vec, *fp; + int i; + + maxindex = x->x_npoints - 1; + if (!buf) goto zero; + + for (i = 0; i < n; i++) + { + int index = *in++; + if (index < 0) + index = 0; + else if (index > maxindex) + index = maxindex; + *out++ = buf[index]; + } + return (w+5); + zero: + while (n--) *out++ = 0; + + return (w+5); +} + +void tabread_tilde_set(t_tabread_tilde *x, t_symbol *s) +{ + t_garray *a; + + x->x_arrayname = s; + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + { + if (*s->s_name) + pd_error(x, "tabread~: %s: no such array", x->x_arrayname->s_name); + x->x_vec = 0; + } + else if (!garray_getfloatarray(a, &x->x_npoints, &x->x_vec)) + { + pd_error(x, "%s: bad template for tabread~", x->x_arrayname->s_name); + x->x_vec = 0; + } + else garray_usedindsp(a); +} + +static void tabread_tilde_dsp(t_tabread_tilde *x, t_signal **sp) +{ + tabread_tilde_set(x, x->x_arrayname); + + dsp_add(tabread_tilde_perform, 4, x, + sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); + +} + +static void tabread_tilde_free(t_tabread_tilde *x) +{ +} + +static void tabread_tilde_setup(void) +{ + tabread_tilde_class = class_new(gensym("tabread~"), + (t_newmethod)tabread_tilde_new, (t_method)tabread_tilde_free, + sizeof(t_tabread_tilde), 0, A_DEFSYM, 0); + CLASS_MAINSIGNALIN(tabread_tilde_class, t_tabread_tilde, x_f); + class_addmethod(tabread_tilde_class, (t_method)tabread_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(tabread_tilde_class, (t_method)tabread_tilde_set, + gensym("set"), A_SYMBOL, 0); +} + +/******************** tabread4~ ***********************/ + +static t_class *tabread4_tilde_class; + +typedef struct _tabread4_tilde +{ + t_object x_obj; + int x_npoints; + float *x_vec; + t_symbol *x_arrayname; + float x_f; +} t_tabread4_tilde; + +static void *tabread4_tilde_new(t_symbol *s) +{ + t_tabread4_tilde *x = (t_tabread4_tilde *)pd_new(tabread4_tilde_class); + x->x_arrayname = s; + x->x_vec = 0; + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *tabread4_tilde_perform(t_int *w) +{ + t_tabread4_tilde *x = (t_tabread4_tilde *)(w[1]); + t_float *in = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + int maxindex; + float *buf = x->x_vec, *fp; + int i; + + maxindex = x->x_npoints - 3; + + if (!buf) goto zero; + +#if 0 /* test for spam -- I'm not ready to deal with this */ + for (i = 0, xmax = 0, xmin = maxindex, fp = in1; i < n; i++, fp++) + { + float f = *in1; + if (f < xmin) xmin = f; + else if (f > xmax) xmax = f; + } + if (xmax < xmin + x->c_maxextent) xmax = xmin + x->c_maxextent; + for (i = 0, splitlo = xmin+ x->c_maxextent, splithi = xmax - x->c_maxextent, + fp = in1; i < n; i++, fp++) + { + float f = *in1; + if (f > splitlo && f < splithi) goto zero; + } +#endif + + for (i = 0; i < n; i++) + { + float findex = *in++; + int index = findex; + float frac, a, b, c, d, cminusb; + static int count; + if (index < 1) + index = 1, frac = 0; + else if (index > maxindex) + index = maxindex, frac = 1; + else frac = findex - index; + fp = buf + index; + a = fp[-1]; + b = fp[0]; + c = fp[1]; + d = fp[2]; + /* if (!i && !(count++ & 1023)) + post("fp = %lx, shit = %lx, b = %f", fp, buf->b_shit, b); */ + cminusb = c-b; + *out++ = b + frac * ( + cminusb - 0.1666667f * (1.-frac) * ( + (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b) + ) + ); + } + return (w+5); + zero: + while (n--) *out++ = 0; + + return (w+5); +} + +void tabread4_tilde_set(t_tabread4_tilde *x, t_symbol *s) +{ + t_garray *a; + + x->x_arrayname = s; + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + { + if (*s->s_name) + pd_error(x, "tabread4~: %s: no such array", x->x_arrayname->s_name); + x->x_vec = 0; + } + else if (!garray_getfloatarray(a, &x->x_npoints, &x->x_vec)) + { + pd_error(x, "%s: bad template for tabread4~", x->x_arrayname->s_name); + x->x_vec = 0; + } + else garray_usedindsp(a); +} + +static void tabread4_tilde_dsp(t_tabread4_tilde *x, t_signal **sp) +{ + tabread4_tilde_set(x, x->x_arrayname); + + dsp_add(tabread4_tilde_perform, 4, x, + sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); + +} + +static void tabread4_tilde_free(t_tabread4_tilde *x) +{ +} + +static void tabread4_tilde_setup(void) +{ + tabread4_tilde_class = class_new(gensym("tabread4~"), + (t_newmethod)tabread4_tilde_new, (t_method)tabread4_tilde_free, + sizeof(t_tabread4_tilde), 0, A_DEFSYM, 0); + CLASS_MAINSIGNALIN(tabread4_tilde_class, t_tabread4_tilde, x_f); + class_addmethod(tabread4_tilde_class, (t_method)tabread4_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(tabread4_tilde_class, (t_method)tabread4_tilde_set, + gensym("set"), A_SYMBOL, 0); +} + +/******************** tabosc4~ ***********************/ + +/* this is all copied from d_osc.c... what include file could this go in? */ +#define UNITBIT32 1572864. /* 3*2^19; bit 32 has place value 1 */ + + /* machine-dependent definitions. These ifdefs really + should have been by CPU type and not by operating system! */ +#ifdef IRIX + /* big-endian. Most significant byte is at low address in memory */ +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#define int32 long /* a data type that has 32 bits */ +#else +#ifdef MSW + /* little-endian; most significant byte is at highest address */ +#define HIOFFSET 1 +#define LOWOFFSET 0 +#define int32 long +#else +#ifdef __FreeBSD__ +#include +#if BYTE_ORDER == LITTLE_ENDIAN +#define HIOFFSET 1 +#define LOWOFFSET 0 +#else +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#endif /* BYTE_ORDER */ +#include +#define int32 int32_t +#endif + +#ifdef __linux__ +#include +#if !defined(__BYTE_ORDER) || !defined(__LITTLE_ENDIAN) +#error No byte order defined +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define HIOFFSET 1 +#define LOWOFFSET 0 +#else +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#endif /* __BYTE_ORDER */ + +#include +#define int32 int32_t + +#else +#ifdef MACOSX +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#define int32 int /* a data type that has 32 bits */ + +#endif /* MACOSX */ +#endif /* __linux__ */ +#endif /* MSW */ +#endif /* SGI */ + +union tabfudge +{ + double tf_d; + int32 tf_i[2]; +}; + +static t_class *tabosc4_tilde_class; + +typedef struct _tabosc4_tilde +{ + t_object x_obj; + float x_fnpoints; + float x_finvnpoints; + float *x_vec; + t_symbol *x_arrayname; + float x_f; + double x_phase; + float x_conv; +} t_tabosc4_tilde; + +static void *tabosc4_tilde_new(t_symbol *s) +{ + t_tabosc4_tilde *x = (t_tabosc4_tilde *)pd_new(tabosc4_tilde_class); + x->x_arrayname = s; + x->x_vec = 0; + x->x_fnpoints = 512.; + x->x_finvnpoints = (1./512.); + outlet_new(&x->x_obj, gensym("signal")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1")); + x->x_f = 0; + return (x); +} + +static t_int *tabosc4_tilde_perform(t_int *w) +{ + t_tabosc4_tilde *x = (t_tabosc4_tilde *)(w[1]); + t_float *in = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + int normhipart; + union tabfudge tf; + float fnpoints = x->x_fnpoints; + int mask = fnpoints - 1; + float conv = fnpoints * x->x_conv; + int maxindex; + float *tab = x->x_vec, *addr; + int i; + double dphase = fnpoints * x->x_phase + UNITBIT32; + + if (!tab) goto zero; + tf.tf_d = UNITBIT32; + normhipart = tf.tf_i[HIOFFSET]; + +#if 1 + while (n--) + { + float frac, a, b, c, d, cminusb; + tf.tf_d = dphase; + dphase += *in++ * conv; + addr = tab + (tf.tf_i[HIOFFSET] & mask); + tf.tf_i[HIOFFSET] = normhipart; + frac = tf.tf_d - UNITBIT32; + a = addr[0]; + b = addr[1]; + c = addr[2]; + d = addr[3]; + cminusb = c-b; + *out++ = b + frac * ( + cminusb - 0.1666667f * (1.-frac) * ( + (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b) + ) + ); + } +#endif + + tf.tf_d = UNITBIT32 * fnpoints; + normhipart = tf.tf_i[HIOFFSET]; + tf.tf_d = dphase + (UNITBIT32 * fnpoints - UNITBIT32); + tf.tf_i[HIOFFSET] = normhipart; + x->x_phase = (tf.tf_d - UNITBIT32 * fnpoints) * x->x_finvnpoints; + return (w+5); + zero: + while (n--) *out++ = 0; + + return (w+5); +} + +void tabosc4_tilde_set(t_tabosc4_tilde *x, t_symbol *s) +{ + t_garray *a; + int npoints, pointsinarray; + + x->x_arrayname = s; + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + { + if (*s->s_name) + pd_error(x, "tabosc4~: %s: no such array", x->x_arrayname->s_name); + x->x_vec = 0; + } + else if (!garray_getfloatarray(a, &pointsinarray, &x->x_vec)) + { + pd_error(x, "%s: bad template for tabosc4~", x->x_arrayname->s_name); + x->x_vec = 0; + } + else if ((npoints = pointsinarray - 3) != (1 << ilog2(pointsinarray - 3))) + { + pd_error(x, "%s: number of points (%d) not a power of 2 plus three", + x->x_arrayname->s_name, pointsinarray); + x->x_vec = 0; + garray_usedindsp(a); + } + else + { + x->x_fnpoints = npoints; + x->x_finvnpoints = 1./npoints; + garray_usedindsp(a); + } +} + +static void tabosc4_tilde_ft1(t_tabosc4_tilde *x, t_float f) +{ + x->x_phase = f; +} + +static void tabosc4_tilde_dsp(t_tabosc4_tilde *x, t_signal **sp) +{ + x->x_conv = 1. / sp[0]->s_sr; + tabosc4_tilde_set(x, x->x_arrayname); + + dsp_add(tabosc4_tilde_perform, 4, x, + sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +static void tabosc4_tilde_setup(void) +{ + tabosc4_tilde_class = class_new(gensym("tabosc4~"), + (t_newmethod)tabosc4_tilde_new, 0, + sizeof(t_tabosc4_tilde), 0, A_DEFSYM, 0); + CLASS_MAINSIGNALIN(tabosc4_tilde_class, t_tabosc4_tilde, x_f); + class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_set, + gensym("set"), A_SYMBOL, 0); + class_addmethod(tabosc4_tilde_class, (t_method)tabosc4_tilde_ft1, + gensym("ft1"), A_FLOAT, 0); +} + +/* ------------------------ tabsend~ ------------------------- */ + +static t_class *tabsend_class; + +typedef struct _tabsend +{ + t_object x_obj; + float *x_vec; + int x_graphperiod; + int x_graphcount; + t_symbol *x_arrayname; + t_clock *x_clock; + float x_f; +} t_tabsend; + +static void tabsend_tick(t_tabsend *x); + +static void *tabsend_new(t_symbol *s) +{ + t_tabsend *x = (t_tabsend *)pd_new(tabsend_class); + x->x_graphcount = 0; + x->x_arrayname = s; + x->x_clock = clock_new(x, (t_method)tabsend_tick); + x->x_f = 0; + return (x); +} + +static t_int *tabsend_perform(t_int *w) +{ + t_tabsend *x = (t_tabsend *)(w[1]); + t_float *in = (t_float *)(w[2]); + int n = w[3]; + t_float *dest = x->x_vec; + int i = x->x_graphcount; + if (!x->x_vec) goto bad; + + while (n--) + { + float f = *in++; + if (PD_BIGORSMALL(f)) + f = 0; + *dest++ = f; + } + if (!i--) + { + clock_delay(x->x_clock, 0); + i = x->x_graphperiod; + } + x->x_graphcount = i; +bad: + return (w+4); +} + +static void tabsend_dsp(t_tabsend *x, t_signal **sp) +{ + int i, vecsize; + t_garray *a; + + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + { + if (*x->x_arrayname->s_name) + pd_error(x, "tabsend~: %s: no such array", x->x_arrayname->s_name); + } + else if (!garray_getfloatarray(a, &vecsize, &x->x_vec)) + pd_error(x, "%s: bad template for tabsend~", x->x_arrayname->s_name); + else + { + int n = sp[0]->s_n; + int ticksper = sp[0]->s_sr/n; + if (ticksper < 1) ticksper = 1; + x->x_graphperiod = ticksper; + if (x->x_graphcount > ticksper) x->x_graphcount = ticksper; + if (n < vecsize) vecsize = n; + garray_usedindsp(a); + dsp_add(tabsend_perform, 3, x, sp[0]->s_vec, vecsize); + } +} + +static void tabsend_tick(t_tabsend *x) +{ + t_garray *a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class); + if (!a) bug("tabsend_tick"); + else garray_redraw(a); +} + +static void tabsend_free(t_tabsend *x) +{ + clock_free(x->x_clock); +} + +static void tabsend_setup(void) +{ + tabsend_class = class_new(gensym("tabsend~"), (t_newmethod)tabsend_new, + (t_method)tabsend_free, sizeof(t_tabsend), 0, A_DEFSYM, 0); + CLASS_MAINSIGNALIN(tabsend_class, t_tabsend, x_f); + class_addmethod(tabsend_class, (t_method)tabsend_dsp, gensym("dsp"), 0); +} + +/* ------------------------ tabreceive~ ------------------------- */ + +static t_class *tabreceive_class; + +typedef struct _tabreceive +{ + t_object x_obj; + float *x_vec; + t_symbol *x_arrayname; +} t_tabreceive; + +static t_int *tabreceive_perform(t_int *w) +{ + t_tabreceive *x = (t_tabreceive *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = w[3]; + t_float *from = x->x_vec; + if (from) while (n--) *out++ = *from++; + else while (n--) *out++ = 0; + return (w+4); +} + +static void tabreceive_dsp(t_tabreceive *x, t_signal **sp) +{ + t_garray *a; + int vecsize; + + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + { + if (*x->x_arrayname->s_name) + pd_error(x, "tabsend~: %s: no such array", x->x_arrayname->s_name); + } + else if (!garray_getfloatarray(a, &vecsize, &x->x_vec)) + pd_error(x, "%s: bad template for tabreceive~", x->x_arrayname->s_name); + else + { + int n = sp[0]->s_n; + if (n < vecsize) vecsize = n; + garray_usedindsp(a); + dsp_add(tabreceive_perform, 3, x, sp[0]->s_vec, vecsize); + } +} + +static void *tabreceive_new(t_symbol *s) +{ + t_tabreceive *x = (t_tabreceive *)pd_new(tabreceive_class); + x->x_arrayname = s; + outlet_new(&x->x_obj, &s_signal); + return (x); +} + +static void tabreceive_setup(void) +{ + tabreceive_class = class_new(gensym("tabreceive~"), + (t_newmethod)tabreceive_new, 0, + sizeof(t_tabreceive), 0, A_DEFSYM, 0); + class_addmethod(tabreceive_class, (t_method)tabreceive_dsp, + gensym("dsp"), 0); +} + + +/* ---------- tabread: control, non-interpolating ------------------------ */ + +static t_class *tabread_class; + +typedef struct _tabread +{ + t_object x_obj; + t_symbol *x_arrayname; +} t_tabread; + +static void tabread_float(t_tabread *x, t_float f) +{ + t_garray *a; + int npoints; + t_float *vec; + + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + pd_error(x, "%s: no such array", x->x_arrayname->s_name); + else if (!garray_getfloatarray(a, &npoints, &vec)) + pd_error(x, "%s: bad template for tabread", x->x_arrayname->s_name); + else + { + int n = f; + if (n < 0) n = 0; + else if (n >= npoints) n = npoints - 1; + outlet_float(x->x_obj.ob_outlet, (npoints ? vec[n] : 0)); + } +} + +static void tabread_set(t_tabread *x, t_symbol *s) +{ + x->x_arrayname = s; +} + +static void *tabread_new(t_symbol *s) +{ + t_tabread *x = (t_tabread *)pd_new(tabread_class); + x->x_arrayname = s; + outlet_new(&x->x_obj, &s_float); + return (x); +} + +static void tabread_setup(void) +{ + tabread_class = class_new(gensym("tabread"), (t_newmethod)tabread_new, + 0, sizeof(t_tabread), 0, A_DEFSYM, 0); + class_addfloat(tabread_class, (t_method)tabread_float); + class_addmethod(tabread_class, (t_method)tabread_set, gensym("set"), + A_SYMBOL, 0); +} + +/* ---------- tabread4: control, non-interpolating ------------------------ */ + +static t_class *tabread4_class; + +typedef struct _tabread4 +{ + t_object x_obj; + t_symbol *x_arrayname; +} t_tabread4; + +static void tabread4_float(t_tabread4 *x, t_float f) +{ + t_garray *a; + int npoints; + t_float *vec; + + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + pd_error(x, "%s: no such array", x->x_arrayname->s_name); + else if (!garray_getfloatarray(a, &npoints, &vec)) + pd_error(x, "%s: bad template for tabread4", x->x_arrayname->s_name); + else if (npoints < 4) + outlet_float(x->x_obj.ob_outlet, 0); + else if (f <= 1) + outlet_float(x->x_obj.ob_outlet, vec[1]); + else if (f >= npoints - 2) + outlet_float(x->x_obj.ob_outlet, vec[npoints - 2]); + else + { + int n = f; + float a, b, c, d, cminusb, frac, *fp; + if (n >= npoints - 2) + n = npoints - 3; + fp = vec + n; + frac = f - n; + a = fp[-1]; + b = fp[0]; + c = fp[1]; + d = fp[2]; + cminusb = c-b; + outlet_float(x->x_obj.ob_outlet, b + frac * ( + cminusb - 0.1666667f * (1.-frac) * ( + (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b)))); + } +} + +static void tabread4_set(t_tabread4 *x, t_symbol *s) +{ + x->x_arrayname = s; +} + +static void *tabread4_new(t_symbol *s) +{ + t_tabread4 *x = (t_tabread4 *)pd_new(tabread4_class); + x->x_arrayname = s; + outlet_new(&x->x_obj, &s_float); + return (x); +} + +static void tabread4_setup(void) +{ + tabread4_class = class_new(gensym("tabread4"), (t_newmethod)tabread4_new, + 0, sizeof(t_tabread4), 0, A_DEFSYM, 0); + class_addfloat(tabread4_class, (t_method)tabread4_float); + class_addmethod(tabread4_class, (t_method)tabread4_set, gensym("set"), + A_SYMBOL, 0); +} + +/* ------------------ tabwrite: control ------------------------ */ + +static t_class *tabwrite_class; + +typedef struct _tabwrite +{ + t_object x_obj; + t_symbol *x_arrayname; + t_clock *x_clock; + float x_ft1; + double x_updtime; + int x_set; +} t_tabwrite; + +static void tabwrite_tick(t_tabwrite *x) +{ + t_garray *a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class); + if (!a) bug("tabwrite_tick"); + else garray_redraw(a); + x->x_set = 0; + x->x_updtime = clock_getsystime(); +} + +static void tabwrite_float(t_tabwrite *x, t_float f) +{ + int i, vecsize; + t_garray *a; + t_float *vec; + + if (!(a = (t_garray *)pd_findbyclass(x->x_arrayname, garray_class))) + pd_error(x, "%s: no such array", x->x_arrayname->s_name); + else if (!garray_getfloatarray(a, &vecsize, &vec)) + pd_error(x, "%s: bad template for tabwrite", x->x_arrayname->s_name); + else + { + int n = x->x_ft1; + double timesince = clock_gettimesince(x->x_updtime); + if (n < 0) n = 0; + else if (n >= vecsize) n = vecsize-1; + vec[n] = f; + if (timesince > 1000) + { + tabwrite_tick(x); + } + else + { + if (x->x_set == 0) + { + clock_delay(x->x_clock, 1000 - timesince); + x->x_set = 1; + } + } + } +} + +static void tabwrite_set(t_tabwrite *x, t_symbol *s) +{ + x->x_arrayname = s; +} + +static void tabwrite_free(t_tabwrite *x) +{ + clock_free(x->x_clock); +} + +static void *tabwrite_new(t_symbol *s) +{ + t_tabwrite *x = (t_tabwrite *)pd_new(tabwrite_class); + x->x_ft1 = 0; + x->x_arrayname = s; + x->x_updtime = clock_getsystime(); + x->x_clock = clock_new(x, (t_method)tabwrite_tick); + floatinlet_new(&x->x_obj, &x->x_ft1); + return (x); +} + +void tabwrite_setup(void) +{ + tabwrite_class = class_new(gensym("tabwrite"), (t_newmethod)tabwrite_new, + (t_method)tabwrite_free, sizeof(t_tabwrite), 0, A_DEFSYM, 0); + class_addfloat(tabwrite_class, (t_method)tabwrite_float); + class_addmethod(tabwrite_class, (t_method)tabwrite_set, gensym("set"), A_SYMBOL, 0); +} + +/* ------------------------ global setup routine ------------------------- */ + +void d_array_setup(void) +{ + tabwrite_tilde_setup(); + tabplay_tilde_setup(); + tabread_tilde_setup(); + tabread4_tilde_setup(); + tabosc4_tilde_setup(); + tabsend_setup(); + tabreceive_setup(); + tabread_setup(); + tabread4_setup(); + tabwrite_setup(); +} + diff --git a/apps/plugins/pdbox/PDa/src/d_ctl.c b/apps/plugins/pdbox/PDa/src/d_ctl.c new file mode 100644 index 0000000..d3262af --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_ctl.c @@ -0,0 +1,1568 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* sig~ and line~ control-to-signal converters; + snapshot~ signal-to-control converter. +*/ + +#include "m_pd.h" +#include "math.h" + +/* -------------------------- sig~ ------------------------------ */ +static t_class *sig_tilde_class; + +typedef struct _sig +{ + t_object x_obj; + float x_f; +} t_sig; + +static t_int *sig_tilde_perform(t_int *w) +{ + t_float f = *(t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + while (n--) + *out++ = f; + return (w+4); +} + +static t_int *sig_tilde_perf8(t_int *w) +{ + t_float f = *(t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + + for (; n; n -= 8, out += 8) + { + out[0] = f; + out[1] = f; + out[2] = f; + out[3] = f; + out[4] = f; + out[5] = f; + out[6] = f; + out[7] = f; + } + return (w+4); +} + +void dsp_add_scalarcopy(t_sample *in, t_sample *out, int n) +{ + if (n&7) + dsp_add(sig_tilde_perform, 3, in, out, n); + else + dsp_add(sig_tilde_perf8, 3, in, out, n); +} + +static void sig_tilde_float(t_sig *x, t_float f) +{ + x->x_f = f; +} + +static void sig_tilde_dsp(t_sig *x, t_signal **sp) +{ + dsp_add(sig_tilde_perform, 3, &x->x_f, sp[0]->s_vec, sp[0]->s_n); +} + +static void *sig_tilde_new(t_floatarg f) +{ + t_sig *x = (t_sig *)pd_new(sig_tilde_class); + x->x_f = f; + outlet_new(&x->x_obj, gensym("signal")); + return (x); +} + +static void sig_tilde_setup(void) +{ + sig_tilde_class = class_new(gensym("sig~"), (t_newmethod)sig_tilde_new, 0, + sizeof(t_sig), 0, A_DEFFLOAT, 0); + class_addfloat(sig_tilde_class, (t_method)sig_tilde_float); + class_addmethod(sig_tilde_class, (t_method)sig_tilde_dsp, gensym("dsp"), 0); +} + + +#ifndef FIXEDPOINT + +/* -------------------------- line~ ------------------------------ */ +static t_class *line_tilde_class; + +typedef struct _line +{ + t_object x_obj; + float x_target; + float x_value; + float x_biginc; + float x_inc; + float x_1overn; + float x_dspticktomsec; + float x_inletvalue; + float x_inletwas; + int x_ticksleft; + int x_retarget; +} t_line; + +static t_int *line_tilde_perform(t_int *w) +{ + t_line *x = (t_line *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + float f = x->x_value; + + if (PD_BIGORSMALL(f)) + x->x_value = f = 0; + if (x->x_retarget) + { + int nticks = x->x_inletwas * x->x_dspticktomsec; + if (!nticks) nticks = 1; + x->x_ticksleft = nticks; + x->x_biginc = (x->x_target - x->x_value)/(float)nticks; + x->x_inc = x->x_1overn * x->x_biginc; + x->x_retarget = 0; + } + if (x->x_ticksleft) + { + float f = x->x_value; + while (n--) *out++ = f, f += x->x_inc; + x->x_value += x->x_biginc; + x->x_ticksleft--; + } + else + { + x->x_value = x->x_target; + while (n--) *out++ = x->x_value; + } + return (w+4); +} + +static void line_tilde_float(t_line *x, t_float f) +{ + if (x->x_inletvalue <= 0) + { + x->x_target = x->x_value = f; + x->x_ticksleft = x->x_retarget = 0; + } + else + { + x->x_target = f; + x->x_retarget = 1; + x->x_inletwas = x->x_inletvalue; + x->x_inletvalue = 0; + } +} + +static void line_tilde_stop(t_line *x) +{ + x->x_target = x->x_value; + x->x_ticksleft = x->x_retarget = 0; +} + +static void line_tilde_dsp(t_line *x, t_signal **sp) +{ + dsp_add(line_tilde_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); + x->x_1overn = 1./sp[0]->s_n; + x->x_dspticktomsec = sp[0]->s_sr / (1000 * sp[0]->s_n); +} + +static void *line_tilde_new(void) +{ + t_line *x = (t_line *)pd_new(line_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + floatinlet_new(&x->x_obj, &x->x_inletvalue); + x->x_ticksleft = x->x_retarget = 0; + x->x_value = x->x_target = x->x_inletvalue = x->x_inletwas = 0; + return (x); +} + +static void line_tilde_setup(void) +{ + line_tilde_class = class_new(gensym("line~"), line_tilde_new, 0, + sizeof(t_line), 0, 0); + class_addfloat(line_tilde_class, (t_method)line_tilde_float); + class_addmethod(line_tilde_class, (t_method)line_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(line_tilde_class, (t_method)line_tilde_stop, + gensym("stop"), 0); +} + +/* -------------------------- vline~ ------------------------------ */ +static t_class *vline_tilde_class; + +typedef struct _vseg +{ + double s_targettime; + double s_starttime; + float s_target; + struct _vseg *s_next; +} t_vseg; + +typedef struct _vline +{ + t_object x_obj; + double x_value; + double x_inc; + double x_referencetime; + double x_samppermsec; + double x_msecpersamp; + double x_targettime; + float x_target; + float x_inlet1; + float x_inlet2; + t_vseg *x_list; +} t_vline; + +static t_int *vline_tilde_perform(t_int *w) +{ + t_vline *x = (t_vline *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]), i; + double f = x->x_value; + double inc = x->x_inc; + double msecpersamp = x->x_msecpersamp; + double samppermsec = x->x_samppermsec; + double timenow = clock_gettimesince(x->x_referencetime) - n * msecpersamp; + t_vseg *s = x->x_list; + for (i = 0; i < n; i++) + { + double timenext = timenow + msecpersamp; + checknext: + if (s) + { + /* has starttime elapsed? If so update value and increment */ + if (s->s_starttime < timenext) + { + if (x->x_targettime <= timenext) + f = x->x_target, inc = 0; + /* if zero-length segment bash output value */ + if (s->s_targettime <= s->s_starttime) + { + f = s->s_target; + inc = 0; + } + else + { + double incpermsec = (s->s_target - f)/ + (s->s_targettime - s->s_starttime); + f = f + incpermsec * (timenext - s->s_starttime); + inc = incpermsec * msecpersamp; + } + x->x_inc = inc; + x->x_target = s->s_target; + x->x_targettime = s->s_targettime; + x->x_list = s->s_next; + t_freebytes(s, sizeof(*s)); + s = x->x_list; + goto checknext; + } + } + if (x->x_targettime <= timenext) + f = x->x_target, inc = x->x_inc = 0, x->x_targettime = 1e20; + *out++ = f; + f = f + inc; + timenow = timenext; + } + x->x_value = f; + return (w+4); +} + +static void vline_tilde_stop(t_vline *x) +{ + t_vseg *s1, *s2; + for (s1 = x->x_list; s1; s1 = s2) + s2 = s1->s_next, t_freebytes(s1, sizeof(*s1)); + x->x_list = 0; + x->x_inc = 0; + x->x_inlet1 = x->x_inlet2 = 0; + x->x_target = x->x_value; + x->x_targettime = 1e20; +} + +static void vline_tilde_float(t_vline *x, t_float f) +{ + double timenow = clock_gettimesince(x->x_referencetime); + float inlet1 = (x->x_inlet1 < 0 ? 0 : x->x_inlet1); + float inlet2 = x->x_inlet2; + double starttime = timenow + inlet2; + t_vseg *s1, *s2, *deletefrom = 0, *snew; + if (PD_BIGORSMALL(f)) + f = 0; + + /* negative delay input means stop and jump immediately to new value */ + if (inlet2 < 0) + { + x->x_value = f; + vline_tilde_stop(x); + return; + } + snew = (t_vseg *)t_getbytes(sizeof(*snew)); + /* check if we supplant the first item in the list. We supplant + an item by having an earlier starttime, or an equal starttime unless + the equal one was instantaneous and the new one isn't (in which case + we'll do a jump-and-slide starting at that time.) */ + if (!x->x_list || x->x_list->s_starttime > starttime || + (x->x_list->s_starttime == starttime && + (x->x_list->s_targettime > x->x_list->s_starttime || inlet1 <= 0))) + { + deletefrom = x->x_list; + x->x_list = snew; + } + else + { + for (s1 = x->x_list; s2 = s1->s_next; s1 = s2) + { + if (s2->s_starttime > starttime || + (s2->s_starttime == starttime && + (s2->s_targettime > s2->s_starttime || inlet1 <= 0))) + { + deletefrom = s2; + s1->s_next = snew; + goto didit; + } + } + s1->s_next = snew; + deletefrom = 0; + didit: ; + } + while (deletefrom) + { + s1 = deletefrom->s_next; + t_freebytes(deletefrom, sizeof(*deletefrom)); + deletefrom = s1; + } + snew->s_next = 0; + snew->s_target = f; + snew->s_starttime = starttime; + snew->s_targettime = starttime + inlet1; + x->x_inlet1 = x->x_inlet2 = 0; +} + +static void vline_tilde_dsp(t_vline *x, t_signal **sp) +{ + dsp_add(vline_tilde_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); + x->x_samppermsec = ((double)(sp[0]->s_sr)) / 1000; + x->x_msecpersamp = ((double)1000) / sp[0]->s_sr; +} + +static void *vline_tilde_new(void) +{ + t_vline *x = (t_vline *)pd_new(vline_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + floatinlet_new(&x->x_obj, &x->x_inlet1); + floatinlet_new(&x->x_obj, &x->x_inlet2); + x->x_inlet1 = x->x_inlet2 = 0; + x->x_value = x->x_inc = 0; + x->x_referencetime = clock_getlogicaltime(); + x->x_list = 0; + x->x_samppermsec = 0; + x->x_targettime = 1e20; + return (x); +} + +static void vline_tilde_setup(void) +{ + vline_tilde_class = class_new(gensym("vline~"), vline_tilde_new, + (t_method)vline_tilde_stop, sizeof(t_vline), 0, 0); + class_addfloat(vline_tilde_class, (t_method)vline_tilde_float); + class_addmethod(vline_tilde_class, (t_method)vline_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(vline_tilde_class, (t_method)vline_tilde_stop, + gensym("stop"), 0); +} + +/* -------------------------- snapshot~ ------------------------------ */ +static t_class *snapshot_tilde_class; + +typedef struct _snapshot +{ + t_object x_obj; + t_sample x_value; + float x_f; +} t_snapshot; + +static void *snapshot_tilde_new(void) +{ + t_snapshot *x = (t_snapshot *)pd_new(snapshot_tilde_class); + x->x_value = 0; + outlet_new(&x->x_obj, &s_float); + x->x_f = 0; + return (x); +} + +static t_int *snapshot_tilde_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + *out = *in; + return (w+3); +} + +static void snapshot_tilde_dsp(t_snapshot *x, t_signal **sp) +{ + dsp_add(snapshot_tilde_perform, 2, sp[0]->s_vec + (sp[0]->s_n-1), + &x->x_value); +} + +static void snapshot_tilde_bang(t_snapshot *x) +{ + outlet_float(x->x_obj.ob_outlet, x->x_value); +} + +static void snapshot_tilde_set(t_snapshot *x, t_floatarg f) +{ + x->x_value = f; +} + +static void snapshot_tilde_setup(void) +{ + snapshot_tilde_class = class_new(gensym("snapshot~"), snapshot_tilde_new, 0, + sizeof(t_snapshot), 0, 0); + CLASS_MAINSIGNALIN(snapshot_tilde_class, t_snapshot, x_f); + class_addmethod(snapshot_tilde_class, (t_method)snapshot_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(snapshot_tilde_class, (t_method)snapshot_tilde_set, + gensym("set"), A_DEFFLOAT, 0); + class_addbang(snapshot_tilde_class, snapshot_tilde_bang); +} + +/* -------------------------- vsnapshot~ ------------------------------ */ +static t_class *vsnapshot_tilde_class; + +typedef struct _vsnapshot +{ + t_object x_obj; + int x_n; + int x_gotone; + t_sample *x_vec; + float x_f; + float x_sampspermsec; + double x_time; +} t_vsnapshot; + +static void *vsnapshot_tilde_new(void) +{ + t_vsnapshot *x = (t_vsnapshot *)pd_new(vsnapshot_tilde_class); + outlet_new(&x->x_obj, &s_float); + x->x_f = 0; + x->x_n = 0; + x->x_vec = 0; + x->x_gotone = 0; + return (x); +} + +static t_int *vsnapshot_tilde_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_vsnapshot *x = (t_vsnapshot *)(w[2]); + t_float *out = x->x_vec; + int n = x->x_n, i; + for (i = 0; i < n; i++) + out[i] = in[i]; + x->x_time = clock_getlogicaltime(); + x->x_gotone = 1; + return (w+3); +} + +static void vsnapshot_tilde_dsp(t_vsnapshot *x, t_signal **sp) +{ + int n = sp[0]->s_n; + if (n != x->x_n) + { + if (x->x_vec) + t_freebytes(x->x_vec, x->x_n * sizeof(t_sample)); + x->x_vec = (t_sample *)getbytes(n * sizeof(t_sample)); + x->x_gotone = 0; + x->x_n = n; + } + x->x_sampspermsec = sp[0]->s_sr / 1000; + dsp_add(vsnapshot_tilde_perform, 2, sp[0]->s_vec, x); +} + +static void vsnapshot_tilde_bang(t_vsnapshot *x) +{ + float val; + if (x->x_gotone) + { + int indx = clock_gettimesince(x->x_time) * x->x_sampspermsec; + if (indx < 0) + indx = 0; + else if (indx >= x->x_n) + indx = x->x_n - 1; + val = x->x_vec[indx]; + } + else val = 0; + outlet_float(x->x_obj.ob_outlet, val); +} + +static void vsnapshot_tilde_ff(t_vsnapshot *x) +{ + if (x->x_vec) + t_freebytes(x->x_vec, x->x_n * sizeof(t_sample)); +} + +static void vsnapshot_tilde_setup(void) +{ + vsnapshot_tilde_class = class_new(gensym("vsnapshot~"), + vsnapshot_tilde_new, (t_method)vsnapshot_tilde_ff, + sizeof(t_vsnapshot), 0, 0); + CLASS_MAINSIGNALIN(vsnapshot_tilde_class, t_vsnapshot, x_f); + class_addmethod(vsnapshot_tilde_class, (t_method)vsnapshot_tilde_dsp, gensym("dsp"), 0); + class_addbang(vsnapshot_tilde_class, vsnapshot_tilde_bang); +} + + +/* ---------------- env~ - simple envelope follower. ----------------- */ + +#define MAXOVERLAP 10 +#define MAXVSTAKEN 64 + +typedef struct sigenv +{ + t_object x_obj; /* header */ + void *x_outlet; /* a "float" outlet */ + void *x_clock; /* a "clock" object */ + float *x_buf; /* a Hanning window */ + int x_phase; /* number of points since last output */ + int x_period; /* requested period of output */ + int x_realperiod; /* period rounded up to vecsize multiple */ + int x_npoints; /* analysis window size in samples */ + float x_result; /* result to output */ + float x_sumbuf[MAXOVERLAP]; /* summing buffer */ + float x_f; +} t_sigenv; + +t_class *env_tilde_class; +static void env_tilde_tick(t_sigenv *x); + +static void *env_tilde_new(t_floatarg fnpoints, t_floatarg fperiod) +{ + int npoints = fnpoints; + int period = fperiod; + t_sigenv *x; + float *buf; + int i; + + if (npoints < 1) npoints = 1024; + if (period < 1) period = npoints/2; + if (period < npoints / MAXOVERLAP + 1) + period = npoints / MAXOVERLAP + 1; + if (!(buf = getbytes(sizeof(float) * (npoints + MAXVSTAKEN)))) + { + error("env: couldn't allocate buffer"); + return (0); + } + x = (t_sigenv *)pd_new(env_tilde_class); + x->x_buf = buf; + x->x_npoints = npoints; + x->x_phase = 0; + x->x_period = period; + for (i = 0; i < MAXOVERLAP; i++) x->x_sumbuf[i] = 0; + for (i = 0; i < npoints; i++) + buf[i] = (1. - cos((2 * 3.14159 * i) / npoints))/npoints; + for (; i < npoints+MAXVSTAKEN; i++) buf[i] = 0; + x->x_clock = clock_new(x, (t_method)env_tilde_tick); + x->x_outlet = outlet_new(&x->x_obj, gensym("float")); + x->x_f = 0; + return (x); +} + +static t_int *env_tilde_perform(t_int *w) +{ + t_sigenv *x = (t_sigenv *)(w[1]); + t_float *in = (t_float *)(w[2]); + int n = (int)(w[3]); + int count; + float *sump; + in += n; + for (count = x->x_phase, sump = x->x_sumbuf; + count < x->x_npoints; count += x->x_realperiod, sump++) + { + float *hp = x->x_buf + count; + float *fp = in; + float sum = *sump; + int i; + + for (i = 0; i < n; i++) + { + fp--; + sum += *hp++ * (*fp * *fp); + } + *sump = sum; + } + sump[0] = 0; + x->x_phase -= n; + if (x->x_phase < 0) + { + x->x_result = x->x_sumbuf[0]; + for (count = x->x_realperiod, sump = x->x_sumbuf; + count < x->x_npoints; count += x->x_realperiod, sump++) + sump[0] = sump[1]; + sump[0] = 0; + x->x_phase = x->x_realperiod - n; + clock_delay(x->x_clock, 0L); + } + return (w+4); +} + +static void env_tilde_dsp(t_sigenv *x, t_signal **sp) +{ + if (x->x_period % sp[0]->s_n) x->x_realperiod = + x->x_period + sp[0]->s_n - (x->x_period % sp[0]->s_n); + else x->x_realperiod = x->x_period; + dsp_add(env_tilde_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); + if (sp[0]->s_n > MAXVSTAKEN) bug("env_tilde_dsp"); +} + +static void env_tilde_tick(t_sigenv *x) /* callback function for the clock */ +{ + outlet_float(x->x_outlet, powtodb(x->x_result)); +} + +static void env_tilde_ff(t_sigenv *x) /* cleanup on free */ +{ + clock_free(x->x_clock); + freebytes(x->x_buf, (x->x_npoints + MAXVSTAKEN) * sizeof(float)); +} + + +void env_tilde_setup(void ) +{ + env_tilde_class = class_new(gensym("env~"), (t_newmethod)env_tilde_new, + (t_method)env_tilde_ff, sizeof(t_sigenv), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(env_tilde_class, t_sigenv, x_f); + class_addmethod(env_tilde_class, (t_method)env_tilde_dsp, gensym("dsp"), 0); +} + +/* --------------------- threshold~ ----------------------------- */ + +static t_class *threshold_tilde_class; + +typedef struct _threshold_tilde +{ + t_object x_obj; + t_outlet *x_outlet1; /* bang out for high thresh */ + t_outlet *x_outlet2; /* bang out for low thresh */ + t_clock *x_clock; /* wakeup for message output */ + float x_f; /* scalar inlet */ + int x_state; /* 1 = high, 0 = low */ + float x_hithresh; /* value of high threshold */ + float x_lothresh; /* value of low threshold */ + float x_deadwait; /* msec remaining in dead period */ + float x_msecpertick; /* msec per DSP tick */ + float x_hideadtime; /* hi dead time in msec */ + float x_lodeadtime; /* lo dead time in msec */ +} t_threshold_tilde; + +static void threshold_tilde_tick(t_threshold_tilde *x); +static void threshold_tilde_set(t_threshold_tilde *x, + t_floatarg hithresh, t_floatarg hideadtime, + t_floatarg lothresh, t_floatarg lodeadtime); + +static t_threshold_tilde *threshold_tilde_new(t_floatarg hithresh, + t_floatarg hideadtime, t_floatarg lothresh, t_floatarg lodeadtime) +{ + t_threshold_tilde *x = (t_threshold_tilde *) + pd_new(threshold_tilde_class); + x->x_state = 0; /* low state */ + x->x_deadwait = 0; /* no dead time */ + x->x_clock = clock_new(x, (t_method)threshold_tilde_tick); + x->x_outlet1 = outlet_new(&x->x_obj, &s_bang); + x->x_outlet2 = outlet_new(&x->x_obj, &s_bang); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1")); + x->x_msecpertick = 0.; + x->x_f = 0; + threshold_tilde_set(x, hithresh, hideadtime, lothresh, lodeadtime); + return (x); +} + + /* "set" message to specify thresholds and dead times */ +static void threshold_tilde_set(t_threshold_tilde *x, + t_floatarg hithresh, t_floatarg hideadtime, + t_floatarg lothresh, t_floatarg lodeadtime) +{ + if (lothresh > hithresh) + lothresh = hithresh; + x->x_hithresh = hithresh; + x->x_hideadtime = hideadtime; + x->x_lothresh = lothresh; + x->x_lodeadtime = lodeadtime; +} + + /* number in inlet sets state -- note incompatible with JMAX which used + "int" message for this, impossible here because of auto signal conversion */ +static void threshold_tilde_ft1(t_threshold_tilde *x, t_floatarg f) +{ + x->x_state = (f != 0); + x->x_deadwait = 0; +} + +static void threshold_tilde_tick(t_threshold_tilde *x) +{ + if (x->x_state) + outlet_bang(x->x_outlet1); + else outlet_bang(x->x_outlet2); +} + +static t_int *threshold_tilde_perform(t_int *w) +{ + float *in1 = (float *)(w[1]); + t_threshold_tilde *x = (t_threshold_tilde *)(w[2]); + int n = (t_int)(w[3]); + if (x->x_deadwait > 0) + x->x_deadwait -= x->x_msecpertick; + else if (x->x_state) + { + /* we're high; look for low sample */ + for (; n--; in1++) + { + if (*in1 < x->x_lothresh) + { + clock_delay(x->x_clock, 0L); + x->x_state = 0; + x->x_deadwait = x->x_lodeadtime; + goto done; + } + } + } + else + { + /* we're low; look for high sample */ + for (; n--; in1++) + { + if (*in1 >= x->x_hithresh) + { + clock_delay(x->x_clock, 0L); + x->x_state = 1; + x->x_deadwait = x->x_hideadtime; + goto done; + } + } + } +done: + return (w+4); +} + +void threshold_tilde_dsp(t_threshold_tilde *x, t_signal **sp) +{ + x->x_msecpertick = 1000. * sp[0]->s_n / sp[0]->s_sr; + dsp_add(threshold_tilde_perform, 3, sp[0]->s_vec, x, sp[0]->s_n); +} + +static void threshold_tilde_ff(t_threshold_tilde *x) +{ + clock_free(x->x_clock); +} + +static void threshold_tilde_setup( void) +{ + threshold_tilde_class = class_new(gensym("threshold~"), + (t_newmethod)threshold_tilde_new, (t_method)threshold_tilde_ff, + sizeof(t_threshold_tilde), 0, + A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(threshold_tilde_class, t_threshold_tilde, x_f); + class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_set, + gensym("set"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_ft1, + gensym("ft1"), A_FLOAT, 0); + class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_dsp, + gensym("dsp"), 0); +} + +/* ------------------------ global setup routine ------------------------- */ + +void d_ctl_setup(void) +{ + sig_tilde_setup(); + line_tilde_setup(); + vline_tilde_setup(); + snapshot_tilde_setup(); + vsnapshot_tilde_setup(); + env_tilde_setup(); + threshold_tilde_setup(); +} + +#endif +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* sig~ and line~ control-to-signal converters; + snapshot~ signal-to-control converter. +*/ + +#include "m_pd.h" +#include "math.h" + +/* -------------------------- sig~ ------------------------------ */ +static t_class *sig_tilde_class; + +typedef struct _sig +{ + t_object x_obj; + float x_f; +} t_sig; + +static t_int *sig_tilde_perform(t_int *w) +{ + t_float f = *(t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + while (n--) + *out++ = f; + return (w+4); +} + +static t_int *sig_tilde_perf8(t_int *w) +{ + t_float f = *(t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + + for (; n; n -= 8, out += 8) + { + out[0] = f; + out[1] = f; + out[2] = f; + out[3] = f; + out[4] = f; + out[5] = f; + out[6] = f; + out[7] = f; + } + return (w+4); +} + +void dsp_add_scalarcopy(t_sample *in, t_sample *out, int n) +{ + if (n&7) + dsp_add(sig_tilde_perform, 3, in, out, n); + else + dsp_add(sig_tilde_perf8, 3, in, out, n); +} + +static void sig_tilde_float(t_sig *x, t_float f) +{ + x->x_f = f; +} + +static void sig_tilde_dsp(t_sig *x, t_signal **sp) +{ + dsp_add(sig_tilde_perform, 3, &x->x_f, sp[0]->s_vec, sp[0]->s_n); +} + +static void *sig_tilde_new(t_floatarg f) +{ + t_sig *x = (t_sig *)pd_new(sig_tilde_class); + x->x_f = f; + outlet_new(&x->x_obj, gensym("signal")); + return (x); +} + +static void sig_tilde_setup(void) +{ + sig_tilde_class = class_new(gensym("sig~"), (t_newmethod)sig_tilde_new, 0, + sizeof(t_sig), 0, A_DEFFLOAT, 0); + class_addfloat(sig_tilde_class, (t_method)sig_tilde_float); + class_addmethod(sig_tilde_class, (t_method)sig_tilde_dsp, gensym("dsp"), 0); +} + + +#ifndef FIXEDPOINT + +/* -------------------------- line~ ------------------------------ */ +static t_class *line_tilde_class; + +typedef struct _line +{ + t_object x_obj; + float x_target; + float x_value; + float x_biginc; + float x_inc; + float x_1overn; + float x_dspticktomsec; + float x_inletvalue; + float x_inletwas; + int x_ticksleft; + int x_retarget; +} t_line; + +static t_int *line_tilde_perform(t_int *w) +{ + t_line *x = (t_line *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + float f = x->x_value; + + if (PD_BIGORSMALL(f)) + x->x_value = f = 0; + if (x->x_retarget) + { + int nticks = x->x_inletwas * x->x_dspticktomsec; + if (!nticks) nticks = 1; + x->x_ticksleft = nticks; + x->x_biginc = (x->x_target - x->x_value)/(float)nticks; + x->x_inc = x->x_1overn * x->x_biginc; + x->x_retarget = 0; + } + if (x->x_ticksleft) + { + float f = x->x_value; + while (n--) *out++ = f, f += x->x_inc; + x->x_value += x->x_biginc; + x->x_ticksleft--; + } + else + { + x->x_value = x->x_target; + while (n--) *out++ = x->x_value; + } + return (w+4); +} + +static void line_tilde_float(t_line *x, t_float f) +{ + if (x->x_inletvalue <= 0) + { + x->x_target = x->x_value = f; + x->x_ticksleft = x->x_retarget = 0; + } + else + { + x->x_target = f; + x->x_retarget = 1; + x->x_inletwas = x->x_inletvalue; + x->x_inletvalue = 0; + } +} + +static void line_tilde_stop(t_line *x) +{ + x->x_target = x->x_value; + x->x_ticksleft = x->x_retarget = 0; +} + +static void line_tilde_dsp(t_line *x, t_signal **sp) +{ + dsp_add(line_tilde_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); + x->x_1overn = 1./sp[0]->s_n; + x->x_dspticktomsec = sp[0]->s_sr / (1000 * sp[0]->s_n); +} + +static void *line_tilde_new(void) +{ + t_line *x = (t_line *)pd_new(line_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + floatinlet_new(&x->x_obj, &x->x_inletvalue); + x->x_ticksleft = x->x_retarget = 0; + x->x_value = x->x_target = x->x_inletvalue = x->x_inletwas = 0; + return (x); +} + +static void line_tilde_setup(void) +{ + line_tilde_class = class_new(gensym("line~"), line_tilde_new, 0, + sizeof(t_line), 0, 0); + class_addfloat(line_tilde_class, (t_method)line_tilde_float); + class_addmethod(line_tilde_class, (t_method)line_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(line_tilde_class, (t_method)line_tilde_stop, + gensym("stop"), 0); +} + +/* -------------------------- vline~ ------------------------------ */ +static t_class *vline_tilde_class; + +typedef struct _vseg +{ + double s_targettime; + double s_starttime; + float s_target; + struct _vseg *s_next; +} t_vseg; + +typedef struct _vline +{ + t_object x_obj; + double x_value; + double x_inc; + double x_referencetime; + double x_samppermsec; + double x_msecpersamp; + double x_targettime; + float x_target; + float x_inlet1; + float x_inlet2; + t_vseg *x_list; +} t_vline; + +static t_int *vline_tilde_perform(t_int *w) +{ + t_vline *x = (t_vline *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]), i; + double f = x->x_value; + double inc = x->x_inc; + double msecpersamp = x->x_msecpersamp; + double samppermsec = x->x_samppermsec; + double timenow = clock_gettimesince(x->x_referencetime) - n * msecpersamp; + t_vseg *s = x->x_list; + for (i = 0; i < n; i++) + { + double timenext = timenow + msecpersamp; + checknext: + if (s) + { + /* has starttime elapsed? If so update value and increment */ + if (s->s_starttime < timenext) + { + if (x->x_targettime <= timenext) + f = x->x_target, inc = 0; + /* if zero-length segment bash output value */ + if (s->s_targettime <= s->s_starttime) + { + f = s->s_target; + inc = 0; + } + else + { + double incpermsec = (s->s_target - f)/ + (s->s_targettime - s->s_starttime); + f = f + incpermsec * (timenext - s->s_starttime); + inc = incpermsec * msecpersamp; + } + x->x_inc = inc; + x->x_target = s->s_target; + x->x_targettime = s->s_targettime; + x->x_list = s->s_next; + t_freebytes(s, sizeof(*s)); + s = x->x_list; + goto checknext; + } + } + if (x->x_targettime <= timenext) + f = x->x_target, inc = x->x_inc = 0, x->x_targettime = 1e20; + *out++ = f; + f = f + inc; + timenow = timenext; + } + x->x_value = f; + return (w+4); +} + +static void vline_tilde_stop(t_vline *x) +{ + t_vseg *s1, *s2; + for (s1 = x->x_list; s1; s1 = s2) + s2 = s1->s_next, t_freebytes(s1, sizeof(*s1)); + x->x_list = 0; + x->x_inc = 0; + x->x_inlet1 = x->x_inlet2 = 0; + x->x_target = x->x_value; + x->x_targettime = 1e20; +} + +static void vline_tilde_float(t_vline *x, t_float f) +{ + double timenow = clock_gettimesince(x->x_referencetime); + float inlet1 = (x->x_inlet1 < 0 ? 0 : x->x_inlet1); + float inlet2 = x->x_inlet2; + double starttime = timenow + inlet2; + t_vseg *s1, *s2, *deletefrom = 0, *snew; + if (PD_BIGORSMALL(f)) + f = 0; + + /* negative delay input means stop and jump immediately to new value */ + if (inlet2 < 0) + { + x->x_value = f; + vline_tilde_stop(x); + return; + } + snew = (t_vseg *)t_getbytes(sizeof(*snew)); + /* check if we supplant the first item in the list. We supplant + an item by having an earlier starttime, or an equal starttime unless + the equal one was instantaneous and the new one isn't (in which case + we'll do a jump-and-slide starting at that time.) */ + if (!x->x_list || x->x_list->s_starttime > starttime || + (x->x_list->s_starttime == starttime && + (x->x_list->s_targettime > x->x_list->s_starttime || inlet1 <= 0))) + { + deletefrom = x->x_list; + x->x_list = snew; + } + else + { + for (s1 = x->x_list; s2 = s1->s_next; s1 = s2) + { + if (s2->s_starttime > starttime || + (s2->s_starttime == starttime && + (s2->s_targettime > s2->s_starttime || inlet1 <= 0))) + { + deletefrom = s2; + s1->s_next = snew; + goto didit; + } + } + s1->s_next = snew; + deletefrom = 0; + didit: ; + } + while (deletefrom) + { + s1 = deletefrom->s_next; + t_freebytes(deletefrom, sizeof(*deletefrom)); + deletefrom = s1; + } + snew->s_next = 0; + snew->s_target = f; + snew->s_starttime = starttime; + snew->s_targettime = starttime + inlet1; + x->x_inlet1 = x->x_inlet2 = 0; +} + +static void vline_tilde_dsp(t_vline *x, t_signal **sp) +{ + dsp_add(vline_tilde_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); + x->x_samppermsec = ((double)(sp[0]->s_sr)) / 1000; + x->x_msecpersamp = ((double)1000) / sp[0]->s_sr; +} + +static void *vline_tilde_new(void) +{ + t_vline *x = (t_vline *)pd_new(vline_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + floatinlet_new(&x->x_obj, &x->x_inlet1); + floatinlet_new(&x->x_obj, &x->x_inlet2); + x->x_inlet1 = x->x_inlet2 = 0; + x->x_value = x->x_inc = 0; + x->x_referencetime = clock_getlogicaltime(); + x->x_list = 0; + x->x_samppermsec = 0; + x->x_targettime = 1e20; + return (x); +} + +static void vline_tilde_setup(void) +{ + vline_tilde_class = class_new(gensym("vline~"), vline_tilde_new, + (t_method)vline_tilde_stop, sizeof(t_vline), 0, 0); + class_addfloat(vline_tilde_class, (t_method)vline_tilde_float); + class_addmethod(vline_tilde_class, (t_method)vline_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(vline_tilde_class, (t_method)vline_tilde_stop, + gensym("stop"), 0); +} + +/* -------------------------- snapshot~ ------------------------------ */ +static t_class *snapshot_tilde_class; + +typedef struct _snapshot +{ + t_object x_obj; + t_sample x_value; + float x_f; +} t_snapshot; + +static void *snapshot_tilde_new(void) +{ + t_snapshot *x = (t_snapshot *)pd_new(snapshot_tilde_class); + x->x_value = 0; + outlet_new(&x->x_obj, &s_float); + x->x_f = 0; + return (x); +} + +static t_int *snapshot_tilde_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + *out = *in; + return (w+3); +} + +static void snapshot_tilde_dsp(t_snapshot *x, t_signal **sp) +{ + dsp_add(snapshot_tilde_perform, 2, sp[0]->s_vec + (sp[0]->s_n-1), + &x->x_value); +} + +static void snapshot_tilde_bang(t_snapshot *x) +{ + outlet_float(x->x_obj.ob_outlet, x->x_value); +} + +static void snapshot_tilde_set(t_snapshot *x, t_floatarg f) +{ + x->x_value = f; +} + +static void snapshot_tilde_setup(void) +{ + snapshot_tilde_class = class_new(gensym("snapshot~"), snapshot_tilde_new, 0, + sizeof(t_snapshot), 0, 0); + CLASS_MAINSIGNALIN(snapshot_tilde_class, t_snapshot, x_f); + class_addmethod(snapshot_tilde_class, (t_method)snapshot_tilde_dsp, + gensym("dsp"), 0); + class_addmethod(snapshot_tilde_class, (t_method)snapshot_tilde_set, + gensym("set"), A_DEFFLOAT, 0); + class_addbang(snapshot_tilde_class, snapshot_tilde_bang); +} + +/* -------------------------- vsnapshot~ ------------------------------ */ +static t_class *vsnapshot_tilde_class; + +typedef struct _vsnapshot +{ + t_object x_obj; + int x_n; + int x_gotone; + t_sample *x_vec; + float x_f; + float x_sampspermsec; + double x_time; +} t_vsnapshot; + +static void *vsnapshot_tilde_new(void) +{ + t_vsnapshot *x = (t_vsnapshot *)pd_new(vsnapshot_tilde_class); + outlet_new(&x->x_obj, &s_float); + x->x_f = 0; + x->x_n = 0; + x->x_vec = 0; + x->x_gotone = 0; + return (x); +} + +static t_int *vsnapshot_tilde_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_vsnapshot *x = (t_vsnapshot *)(w[2]); + t_float *out = x->x_vec; + int n = x->x_n, i; + for (i = 0; i < n; i++) + out[i] = in[i]; + x->x_time = clock_getlogicaltime(); + x->x_gotone = 1; + return (w+3); +} + +static void vsnapshot_tilde_dsp(t_vsnapshot *x, t_signal **sp) +{ + int n = sp[0]->s_n; + if (n != x->x_n) + { + if (x->x_vec) + t_freebytes(x->x_vec, x->x_n * sizeof(t_sample)); + x->x_vec = (t_sample *)getbytes(n * sizeof(t_sample)); + x->x_gotone = 0; + x->x_n = n; + } + x->x_sampspermsec = sp[0]->s_sr / 1000; + dsp_add(vsnapshot_tilde_perform, 2, sp[0]->s_vec, x); +} + +static void vsnapshot_tilde_bang(t_vsnapshot *x) +{ + float val; + if (x->x_gotone) + { + int indx = clock_gettimesince(x->x_time) * x->x_sampspermsec; + if (indx < 0) + indx = 0; + else if (indx >= x->x_n) + indx = x->x_n - 1; + val = x->x_vec[indx]; + } + else val = 0; + outlet_float(x->x_obj.ob_outlet, val); +} + +static void vsnapshot_tilde_ff(t_vsnapshot *x) +{ + if (x->x_vec) + t_freebytes(x->x_vec, x->x_n * sizeof(t_sample)); +} + +static void vsnapshot_tilde_setup(void) +{ + vsnapshot_tilde_class = class_new(gensym("vsnapshot~"), + vsnapshot_tilde_new, (t_method)vsnapshot_tilde_ff, + sizeof(t_vsnapshot), 0, 0); + CLASS_MAINSIGNALIN(vsnapshot_tilde_class, t_vsnapshot, x_f); + class_addmethod(vsnapshot_tilde_class, (t_method)vsnapshot_tilde_dsp, gensym("dsp"), 0); + class_addbang(vsnapshot_tilde_class, vsnapshot_tilde_bang); +} + + +/* ---------------- env~ - simple envelope follower. ----------------- */ + +#define MAXOVERLAP 10 +#define MAXVSTAKEN 64 + +typedef struct sigenv +{ + t_object x_obj; /* header */ + void *x_outlet; /* a "float" outlet */ + void *x_clock; /* a "clock" object */ + float *x_buf; /* a Hanning window */ + int x_phase; /* number of points since last output */ + int x_period; /* requested period of output */ + int x_realperiod; /* period rounded up to vecsize multiple */ + int x_npoints; /* analysis window size in samples */ + float x_result; /* result to output */ + float x_sumbuf[MAXOVERLAP]; /* summing buffer */ + float x_f; +} t_sigenv; + +t_class *env_tilde_class; +static void env_tilde_tick(t_sigenv *x); + +static void *env_tilde_new(t_floatarg fnpoints, t_floatarg fperiod) +{ + int npoints = fnpoints; + int period = fperiod; + t_sigenv *x; + float *buf; + int i; + + if (npoints < 1) npoints = 1024; + if (period < 1) period = npoints/2; + if (period < npoints / MAXOVERLAP + 1) + period = npoints / MAXOVERLAP + 1; + if (!(buf = getbytes(sizeof(float) * (npoints + MAXVSTAKEN)))) + { + error("env: couldn't allocate buffer"); + return (0); + } + x = (t_sigenv *)pd_new(env_tilde_class); + x->x_buf = buf; + x->x_npoints = npoints; + x->x_phase = 0; + x->x_period = period; + for (i = 0; i < MAXOVERLAP; i++) x->x_sumbuf[i] = 0; + for (i = 0; i < npoints; i++) + buf[i] = (1. - cos((2 * 3.14159 * i) / npoints))/npoints; + for (; i < npoints+MAXVSTAKEN; i++) buf[i] = 0; + x->x_clock = clock_new(x, (t_method)env_tilde_tick); + x->x_outlet = outlet_new(&x->x_obj, gensym("float")); + x->x_f = 0; + return (x); +} + +static t_int *env_tilde_perform(t_int *w) +{ + t_sigenv *x = (t_sigenv *)(w[1]); + t_float *in = (t_float *)(w[2]); + int n = (int)(w[3]); + int count; + float *sump; + in += n; + for (count = x->x_phase, sump = x->x_sumbuf; + count < x->x_npoints; count += x->x_realperiod, sump++) + { + float *hp = x->x_buf + count; + float *fp = in; + float sum = *sump; + int i; + + for (i = 0; i < n; i++) + { + fp--; + sum += *hp++ * (*fp * *fp); + } + *sump = sum; + } + sump[0] = 0; + x->x_phase -= n; + if (x->x_phase < 0) + { + x->x_result = x->x_sumbuf[0]; + for (count = x->x_realperiod, sump = x->x_sumbuf; + count < x->x_npoints; count += x->x_realperiod, sump++) + sump[0] = sump[1]; + sump[0] = 0; + x->x_phase = x->x_realperiod - n; + clock_delay(x->x_clock, 0L); + } + return (w+4); +} + +static void env_tilde_dsp(t_sigenv *x, t_signal **sp) +{ + if (x->x_period % sp[0]->s_n) x->x_realperiod = + x->x_period + sp[0]->s_n - (x->x_period % sp[0]->s_n); + else x->x_realperiod = x->x_period; + dsp_add(env_tilde_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); + if (sp[0]->s_n > MAXVSTAKEN) bug("env_tilde_dsp"); +} + +static void env_tilde_tick(t_sigenv *x) /* callback function for the clock */ +{ + outlet_float(x->x_outlet, powtodb(x->x_result)); +} + +static void env_tilde_ff(t_sigenv *x) /* cleanup on free */ +{ + clock_free(x->x_clock); + freebytes(x->x_buf, (x->x_npoints + MAXVSTAKEN) * sizeof(float)); +} + + +void env_tilde_setup(void ) +{ + env_tilde_class = class_new(gensym("env~"), (t_newmethod)env_tilde_new, + (t_method)env_tilde_ff, sizeof(t_sigenv), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(env_tilde_class, t_sigenv, x_f); + class_addmethod(env_tilde_class, (t_method)env_tilde_dsp, gensym("dsp"), 0); +} + +/* --------------------- threshold~ ----------------------------- */ + +static t_class *threshold_tilde_class; + +typedef struct _threshold_tilde +{ + t_object x_obj; + t_outlet *x_outlet1; /* bang out for high thresh */ + t_outlet *x_outlet2; /* bang out for low thresh */ + t_clock *x_clock; /* wakeup for message output */ + float x_f; /* scalar inlet */ + int x_state; /* 1 = high, 0 = low */ + float x_hithresh; /* value of high threshold */ + float x_lothresh; /* value of low threshold */ + float x_deadwait; /* msec remaining in dead period */ + float x_msecpertick; /* msec per DSP tick */ + float x_hideadtime; /* hi dead time in msec */ + float x_lodeadtime; /* lo dead time in msec */ +} t_threshold_tilde; + +static void threshold_tilde_tick(t_threshold_tilde *x); +static void threshold_tilde_set(t_threshold_tilde *x, + t_floatarg hithresh, t_floatarg hideadtime, + t_floatarg lothresh, t_floatarg lodeadtime); + +static t_threshold_tilde *threshold_tilde_new(t_floatarg hithresh, + t_floatarg hideadtime, t_floatarg lothresh, t_floatarg lodeadtime) +{ + t_threshold_tilde *x = (t_threshold_tilde *) + pd_new(threshold_tilde_class); + x->x_state = 0; /* low state */ + x->x_deadwait = 0; /* no dead time */ + x->x_clock = clock_new(x, (t_method)threshold_tilde_tick); + x->x_outlet1 = outlet_new(&x->x_obj, &s_bang); + x->x_outlet2 = outlet_new(&x->x_obj, &s_bang); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1")); + x->x_msecpertick = 0.; + x->x_f = 0; + threshold_tilde_set(x, hithresh, hideadtime, lothresh, lodeadtime); + return (x); +} + + /* "set" message to specify thresholds and dead times */ +static void threshold_tilde_set(t_threshold_tilde *x, + t_floatarg hithresh, t_floatarg hideadtime, + t_floatarg lothresh, t_floatarg lodeadtime) +{ + if (lothresh > hithresh) + lothresh = hithresh; + x->x_hithresh = hithresh; + x->x_hideadtime = hideadtime; + x->x_lothresh = lothresh; + x->x_lodeadtime = lodeadtime; +} + + /* number in inlet sets state -- note incompatible with JMAX which used + "int" message for this, impossible here because of auto signal conversion */ +static void threshold_tilde_ft1(t_threshold_tilde *x, t_floatarg f) +{ + x->x_state = (f != 0); + x->x_deadwait = 0; +} + +static void threshold_tilde_tick(t_threshold_tilde *x) +{ + if (x->x_state) + outlet_bang(x->x_outlet1); + else outlet_bang(x->x_outlet2); +} + +static t_int *threshold_tilde_perform(t_int *w) +{ + float *in1 = (float *)(w[1]); + t_threshold_tilde *x = (t_threshold_tilde *)(w[2]); + int n = (t_int)(w[3]); + if (x->x_deadwait > 0) + x->x_deadwait -= x->x_msecpertick; + else if (x->x_state) + { + /* we're high; look for low sample */ + for (; n--; in1++) + { + if (*in1 < x->x_lothresh) + { + clock_delay(x->x_clock, 0L); + x->x_state = 0; + x->x_deadwait = x->x_lodeadtime; + goto done; + } + } + } + else + { + /* we're low; look for high sample */ + for (; n--; in1++) + { + if (*in1 >= x->x_hithresh) + { + clock_delay(x->x_clock, 0L); + x->x_state = 1; + x->x_deadwait = x->x_hideadtime; + goto done; + } + } + } +done: + return (w+4); +} + +void threshold_tilde_dsp(t_threshold_tilde *x, t_signal **sp) +{ + x->x_msecpertick = 1000. * sp[0]->s_n / sp[0]->s_sr; + dsp_add(threshold_tilde_perform, 3, sp[0]->s_vec, x, sp[0]->s_n); +} + +static void threshold_tilde_ff(t_threshold_tilde *x) +{ + clock_free(x->x_clock); +} + +static void threshold_tilde_setup( void) +{ + threshold_tilde_class = class_new(gensym("threshold~"), + (t_newmethod)threshold_tilde_new, (t_method)threshold_tilde_ff, + sizeof(t_threshold_tilde), 0, + A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(threshold_tilde_class, t_threshold_tilde, x_f); + class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_set, + gensym("set"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_ft1, + gensym("ft1"), A_FLOAT, 0); + class_addmethod(threshold_tilde_class, (t_method)threshold_tilde_dsp, + gensym("dsp"), 0); +} + +/* ------------------------ global setup routine ------------------------- */ + +void d_ctl_setup(void) +{ + sig_tilde_setup(); + line_tilde_setup(); + vline_tilde_setup(); + snapshot_tilde_setup(); + vsnapshot_tilde_setup(); + env_tilde_setup(); + threshold_tilde_setup(); +} + +#endif diff --git a/apps/plugins/pdbox/PDa/src/d_dac.c b/apps/plugins/pdbox/PDa/src/d_dac.c new file mode 100644 index 0000000..ce7c712 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_dac.c @@ -0,0 +1,368 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* The dac~ and adc~ routines. +*/ + +#include "m_pd.h" +#include "s_stuff.h" + +/* ----------------------------- dac~ --------------------------- */ +static t_class *dac_class; + +typedef struct _dac +{ + t_object x_obj; + t_int x_n; + t_int *x_vec; + float x_f; +} t_dac; + +static void *dac_new(t_symbol *s, int argc, t_atom *argv) +{ + t_dac *x = (t_dac *)pd_new(dac_class); + t_atom defarg[2], *ap; + int i; + if (!argc) + { + argv = defarg; + argc = 2; + SETFLOAT(&defarg[0], 1); + SETFLOAT(&defarg[1], 2); + } + x->x_n = argc; + x->x_vec = (t_int *)getbytes(argc * sizeof(*x->x_vec)); + for (i = 0; i < argc; i++) + x->x_vec[i] = atom_getintarg(i, argc, argv); + for (i = 1; i < argc; i++) + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + x->x_f = 0; + return (x); +} + +static void dac_dsp(t_dac *x, t_signal **sp) +{ + t_int i, *ip; + t_signal **sp2; + for (i = x->x_n, ip = x->x_vec, sp2 = sp; i--; ip++, sp2++) + { + int ch = *ip - 1; + if ((*sp2)->s_n != DEFDACBLKSIZE) + error("dac~: bad vector size"); + else if (ch >= 0 && ch < sys_get_outchannels()) + dsp_add(plus_perform, 4, sys_soundout + DEFDACBLKSIZE*ch, + (*sp2)->s_vec, sys_soundout + DEFDACBLKSIZE*ch, DEFDACBLKSIZE); + } +} + +static void dac_free(t_dac *x) +{ + freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec)); +} + +static void dac_setup(void) +{ + dac_class = class_new(gensym("dac~"), (t_newmethod)dac_new, + (t_method)dac_free, sizeof(t_dac), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(dac_class, t_dac, x_f); + class_addmethod(dac_class, (t_method)dac_dsp, gensym("dsp"), A_CANT, 0); + class_sethelpsymbol(dac_class, gensym("adc~_dac~")); +} + +/* ----------------------------- adc~ --------------------------- */ +static t_class *adc_class; + +typedef struct _adc +{ + t_object x_obj; + t_int x_n; + t_int *x_vec; +} t_adc; + +static void *adc_new(t_symbol *s, int argc, t_atom *argv) +{ + t_adc *x = (t_adc *)pd_new(adc_class); + t_atom defarg[2], *ap; + int i; + if (!argc) + { + argv = defarg; + argc = 2; + SETFLOAT(&defarg[0], 1); + SETFLOAT(&defarg[1], 2); + } + x->x_n = argc; + x->x_vec = (t_int *)getbytes(argc * sizeof(*x->x_vec)); + for (i = 0; i < argc; i++) + x->x_vec[i] = atom_getintarg(i, argc, argv); + for (i = 0; i < argc; i++) + outlet_new(&x->x_obj, &s_signal); + return (x); +} + +t_int *copy_perform(t_int *w) +{ + t_float *in1 = (t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + while (n--) *out++ = *in1++; + return (w+4); +} + +t_int *copy_perf8(t_int *w) +{ + t_float *in1 = (t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + + for (; n; n -= 8, in1 += 8, out += 8) + { + float f0 = in1[0]; + float f1 = in1[1]; + float f2 = in1[2]; + float f3 = in1[3]; + float f4 = in1[4]; + float f5 = in1[5]; + float f6 = in1[6]; + float f7 = in1[7]; + + out[0] = f0; + out[1] = f1; + out[2] = f2; + out[3] = f3; + out[4] = f4; + out[5] = f5; + out[6] = f6; + out[7] = f7; + } + return (w+4); +} + +void dsp_add_copy(t_sample *in, t_sample *out, int n) +{ + if (n&7) + dsp_add(copy_perform, 3, in, out, n); + else + dsp_add(copy_perf8, 3, in, out, n); +} + +static void adc_dsp(t_adc *x, t_signal **sp) +{ + t_int i, *ip; + t_signal **sp2; + for (i = x->x_n, ip = x->x_vec, sp2 = sp; i--; ip++, sp2++) + { + int ch = *ip - 1; + if ((*sp2)->s_n != DEFDACBLKSIZE) + error("adc~: bad vector size"); + else if (ch >= 0 && ch < sys_get_inchannels()) + dsp_add_copy(sys_soundin + DEFDACBLKSIZE*ch, + (*sp2)->s_vec, DEFDACBLKSIZE); + else dsp_add_zero((*sp2)->s_vec, DEFDACBLKSIZE); + } +} + +static void adc_free(t_adc *x) +{ + freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec)); +} + +static void adc_setup(void) +{ + adc_class = class_new(gensym("adc~"), (t_newmethod)adc_new, + (t_method)adc_free, sizeof(t_adc), 0, A_GIMME, 0); + class_addmethod(adc_class, (t_method)adc_dsp, gensym("dsp"), A_CANT, 0); + class_sethelpsymbol(adc_class, gensym("adc~_dac~")); +} + +void d_dac_setup(void) +{ + dac_setup(); + adc_setup(); +} + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* The dac~ and adc~ routines. +*/ + +#include "m_pd.h" +#include "s_stuff.h" + +/* ----------------------------- dac~ --------------------------- */ +static t_class *dac_class; + +typedef struct _dac +{ + t_object x_obj; + t_int x_n; + t_int *x_vec; + float x_f; +} t_dac; + +static void *dac_new(t_symbol *s, int argc, t_atom *argv) +{ + t_dac *x = (t_dac *)pd_new(dac_class); + t_atom defarg[2], *ap; + int i; + if (!argc) + { + argv = defarg; + argc = 2; + SETFLOAT(&defarg[0], 1); + SETFLOAT(&defarg[1], 2); + } + x->x_n = argc; + x->x_vec = (t_int *)getbytes(argc * sizeof(*x->x_vec)); + for (i = 0; i < argc; i++) + x->x_vec[i] = atom_getintarg(i, argc, argv); + for (i = 1; i < argc; i++) + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + x->x_f = 0; + return (x); +} + +static void dac_dsp(t_dac *x, t_signal **sp) +{ + t_int i, *ip; + t_signal **sp2; + for (i = x->x_n, ip = x->x_vec, sp2 = sp; i--; ip++, sp2++) + { + int ch = *ip - 1; + if ((*sp2)->s_n != DEFDACBLKSIZE) + error("dac~: bad vector size"); + else if (ch >= 0 && ch < sys_get_outchannels()) + dsp_add(plus_perform, 4, sys_soundout + DEFDACBLKSIZE*ch, + (*sp2)->s_vec, sys_soundout + DEFDACBLKSIZE*ch, DEFDACBLKSIZE); + } +} + +static void dac_free(t_dac *x) +{ + freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec)); +} + +static void dac_setup(void) +{ + dac_class = class_new(gensym("dac~"), (t_newmethod)dac_new, + (t_method)dac_free, sizeof(t_dac), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(dac_class, t_dac, x_f); + class_addmethod(dac_class, (t_method)dac_dsp, gensym("dsp"), A_CANT, 0); + class_sethelpsymbol(dac_class, gensym("adc~_dac~")); +} + +/* ----------------------------- adc~ --------------------------- */ +static t_class *adc_class; + +typedef struct _adc +{ + t_object x_obj; + t_int x_n; + t_int *x_vec; +} t_adc; + +static void *adc_new(t_symbol *s, int argc, t_atom *argv) +{ + t_adc *x = (t_adc *)pd_new(adc_class); + t_atom defarg[2], *ap; + int i; + if (!argc) + { + argv = defarg; + argc = 2; + SETFLOAT(&defarg[0], 1); + SETFLOAT(&defarg[1], 2); + } + x->x_n = argc; + x->x_vec = (t_int *)getbytes(argc * sizeof(*x->x_vec)); + for (i = 0; i < argc; i++) + x->x_vec[i] = atom_getintarg(i, argc, argv); + for (i = 0; i < argc; i++) + outlet_new(&x->x_obj, &s_signal); + return (x); +} + +t_int *copy_perform(t_int *w) +{ + t_float *in1 = (t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + while (n--) *out++ = *in1++; + return (w+4); +} + +t_int *copy_perf8(t_int *w) +{ + t_float *in1 = (t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + + for (; n; n -= 8, in1 += 8, out += 8) + { + float f0 = in1[0]; + float f1 = in1[1]; + float f2 = in1[2]; + float f3 = in1[3]; + float f4 = in1[4]; + float f5 = in1[5]; + float f6 = in1[6]; + float f7 = in1[7]; + + out[0] = f0; + out[1] = f1; + out[2] = f2; + out[3] = f3; + out[4] = f4; + out[5] = f5; + out[6] = f6; + out[7] = f7; + } + return (w+4); +} + +void dsp_add_copy(t_sample *in, t_sample *out, int n) +{ + if (n&7) + dsp_add(copy_perform, 3, in, out, n); + else + dsp_add(copy_perf8, 3, in, out, n); +} + +static void adc_dsp(t_adc *x, t_signal **sp) +{ + t_int i, *ip; + t_signal **sp2; + for (i = x->x_n, ip = x->x_vec, sp2 = sp; i--; ip++, sp2++) + { + int ch = *ip - 1; + if ((*sp2)->s_n != DEFDACBLKSIZE) + error("adc~: bad vector size"); + else if (ch >= 0 && ch < sys_get_inchannels()) + dsp_add_copy(sys_soundin + DEFDACBLKSIZE*ch, + (*sp2)->s_vec, DEFDACBLKSIZE); + else dsp_add_zero((*sp2)->s_vec, DEFDACBLKSIZE); + } +} + +static void adc_free(t_adc *x) +{ + freebytes(x->x_vec, x->x_n * sizeof(*x->x_vec)); +} + +static void adc_setup(void) +{ + adc_class = class_new(gensym("adc~"), (t_newmethod)adc_new, + (t_method)adc_free, sizeof(t_adc), 0, A_GIMME, 0); + class_addmethod(adc_class, (t_method)adc_dsp, gensym("dsp"), A_CANT, 0); + class_sethelpsymbol(adc_class, gensym("adc~_dac~")); +} + +void d_dac_setup(void) +{ + dac_setup(); + adc_setup(); +} + diff --git a/apps/plugins/pdbox/PDa/src/d_delay.c b/apps/plugins/pdbox/PDa/src/d_delay.c new file mode 100644 index 0000000..37fb90e --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_delay.c @@ -0,0 +1,638 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* send~, delread~, throw~, catch~ */ + +#include "m_pd.h" +extern int ugen_getsortno(void); + +#define DEFDELVS 64 /* LATER get this from canvas at DSP time */ +static int delread_zero = 0; /* four bytes of zero for delread~, vd~ */ + +/* ----------------------------- delwrite~ ----------------------------- */ +static t_class *sigdelwrite_class; + +typedef struct delwritectl +{ + int c_n; + float *c_vec; + int c_phase; +} t_delwritectl; + +typedef struct _sigdelwrite +{ + t_object x_obj; + t_symbol *x_sym; + t_delwritectl x_cspace; + int x_sortno; /* DSP sort number at which this was last put on chain */ + int x_rsortno; /* DSP sort # for first delread or write in chain */ + int x_vecsize; /* vector size for delread~ to use */ + float x_f; +} t_sigdelwrite; + +#define XTRASAMPS 4 +#define SAMPBLK 4 + + /* routine to check that all delwrites/delreads/vds have same vecsize */ +static void sigdelwrite_checkvecsize(t_sigdelwrite *x, int vecsize) +{ + /* + LATER this should really check sample rate and blocking, once that is + supported. Probably we don't actually care about vecsize. + For now just suppress this check... */ +#if 0 + if (x->x_rsortno != ugen_getsortno()) + { + x->x_vecsize = vecsize; + x->x_rsortno = ugen_getsortno(); + } + else if (vecsize != x->x_vecsize) + pd_error(x, "delread/delwrite/vd vector size mismatch"); +#endif +} + +static void *sigdelwrite_new(t_symbol *s, t_floatarg msec) +{ + int nsamps; + t_sigdelwrite *x = (t_sigdelwrite *)pd_new(sigdelwrite_class); + if (!*s->s_name) s = gensym("delwrite~"); + pd_bind(&x->x_obj.ob_pd, s); + x->x_sym = s; + nsamps = msec * sys_getsr() * (float)(0.001f); + if (nsamps < 1) nsamps = 1; + nsamps += ((- nsamps) & (SAMPBLK - 1)); + nsamps += DEFDELVS; + x->x_cspace.c_n = nsamps; + x->x_cspace.c_vec = + (float *)getbytes((nsamps + XTRASAMPS) * sizeof(float)); + x->x_cspace.c_phase = XTRASAMPS; + x->x_sortno = 0; + x->x_vecsize = 0; + x->x_f = 0; + return (x); +} + +static t_int *sigdelwrite_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_delwritectl *c = (t_delwritectl *)(w[2]); + int n = (int)(w[3]); + int phase = c->c_phase, nsamps = c->c_n; + float *vp = c->c_vec, *bp = vp + phase, *ep = vp + (c->c_n + XTRASAMPS); + phase += n; + while (n--) + { + float f = *in++; + if (PD_BIGORSMALL(f)) + f = 0; + *bp++ = f; + if (bp == ep) + { + vp[0] = ep[-4]; + vp[1] = ep[-3]; + vp[2] = ep[-2]; + vp[3] = ep[-1]; + bp = vp + XTRASAMPS; + phase -= nsamps; + } + } + c->c_phase = phase; + return (w+4); +} + +static void sigdelwrite_dsp(t_sigdelwrite *x, t_signal **sp) +{ + dsp_add(sigdelwrite_perform, 3, sp[0]->s_vec, &x->x_cspace, sp[0]->s_n); + x->x_sortno = ugen_getsortno(); + sigdelwrite_checkvecsize(x, sp[0]->s_n); +} + +static void sigdelwrite_free(t_sigdelwrite *x) +{ + pd_unbind(&x->x_obj.ob_pd, x->x_sym); + freebytes(x->x_cspace.c_vec, + (x->x_cspace.c_n + XTRASAMPS) * sizeof(float)); +} + +static void sigdelwrite_setup(void) +{ + sigdelwrite_class = class_new(gensym("delwrite~"), + (t_newmethod)sigdelwrite_new, (t_method)sigdelwrite_free, + sizeof(t_sigdelwrite), 0, A_DEFSYM, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(sigdelwrite_class, t_sigdelwrite, x_f); + class_addmethod(sigdelwrite_class, (t_method)sigdelwrite_dsp, + gensym("dsp"), 0); +} + +/* ----------------------------- delread~ ----------------------------- */ +static t_class *sigdelread_class; + +typedef struct _sigdelread +{ + t_object x_obj; + t_symbol *x_sym; + t_float x_deltime; /* delay in msec */ + int x_delsamps; /* delay in samples */ + t_float x_sr; /* samples per msec */ + t_float x_n; /* vector size */ + int x_zerodel; /* 0 or vecsize depending on read/write order */ +} t_sigdelread; + +static void sigdelread_float(t_sigdelread *x, t_float f); + +static void *sigdelread_new(t_symbol *s, t_floatarg f) +{ + t_sigdelread *x = (t_sigdelread *)pd_new(sigdelread_class); + x->x_sym = s; + x->x_sr = 1; + x->x_n = 1; + x->x_zerodel = 0; + sigdelread_float(x, f); + outlet_new(&x->x_obj, &s_signal); + return (x); +} + +static void sigdelread_float(t_sigdelread *x, t_float f) +{ + int samps; + t_sigdelwrite *delwriter = + (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class); + x->x_deltime = f; + if (delwriter) + { + int delsize = delwriter->x_cspace.c_n; + x->x_delsamps = (int)(0.5 + x->x_sr * x->x_deltime) + + x->x_n - x->x_zerodel; + if (x->x_delsamps < x->x_n) x->x_delsamps = x->x_n; + else if (x->x_delsamps > delwriter->x_cspace.c_n - DEFDELVS) + x->x_delsamps = delwriter->x_cspace.c_n - DEFDELVS; + } +} + +static t_int *sigdelread_perform(t_int *w) +{ + t_float *out = (t_float *)(w[1]); + t_delwritectl *c = (t_delwritectl *)(w[2]); + int delsamps = *(int *)(w[3]); + int n = (int)(w[4]); + int phase = c->c_phase - delsamps, nsamps = c->c_n; + float *vp = c->c_vec, *bp, *ep = vp + (c->c_n + XTRASAMPS); + + if (phase < 0) phase += nsamps; + bp = vp + phase; + while (n--) + { + *out++ = *bp++; + if (bp == ep) bp -= nsamps; + } + return (w+5); +} + +static void sigdelread_dsp(t_sigdelread *x, t_signal **sp) +{ + t_sigdelwrite *delwriter = + (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class); + x->x_sr = sp[0]->s_sr * 0.001; + x->x_n = sp[0]->s_n; + if (delwriter) + { + sigdelwrite_checkvecsize(delwriter, sp[0]->s_n); + x->x_zerodel = (delwriter->x_sortno == ugen_getsortno() ? + 0 : delwriter->x_vecsize); + sigdelread_float(x, x->x_deltime); + dsp_add(sigdelread_perform, 4, + sp[0]->s_vec, &delwriter->x_cspace, &x->x_delsamps, sp[0]->s_n); + } + else if (*x->x_sym->s_name) + error("delread~: %s: no such delwrite~",x->x_sym->s_name); +} + +static void sigdelread_setup(void) +{ + sigdelread_class = class_new(gensym("delread~"), + (t_newmethod)sigdelread_new, 0, + sizeof(t_sigdelread), 0, A_DEFSYM, A_DEFFLOAT, 0); + class_addmethod(sigdelread_class, (t_method)sigdelread_dsp, + gensym("dsp"), 0); + class_addfloat(sigdelread_class, (t_method)sigdelread_float); +} + + +/* ----------------------------- vd~ ----------------------------- */ +static t_class *sigvd_class; + +typedef struct _sigvd +{ + t_object x_obj; + t_symbol *x_sym; + t_float x_sr; /* samples per msec */ + int x_zerodel; /* 0 or vecsize depending on read/write order */ + float x_f; +} t_sigvd; + +static void *sigvd_new(t_symbol *s) +{ + t_sigvd *x = (t_sigvd *)pd_new(sigvd_class); + if (!*s->s_name) s = gensym("vd~"); + x->x_sym = s; + x->x_sr = 1; + x->x_zerodel = 0; + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); +} + +static t_int *sigvd_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + t_delwritectl *ctl = (t_delwritectl *)(w[3]); + t_sigvd *x = (t_sigvd *)(w[4]); + int n = (int)(w[5]); + + int nsamps = ctl->c_n; + float limit = nsamps - n - 1; + float fn = n-1; + float *vp = ctl->c_vec, *bp, *wp = vp + ctl->c_phase; + float zerodel = x->x_zerodel; + while (n--) + { + float delsamps = x->x_sr * *in++ - zerodel, frac; + int idelsamps; + float a, b, c, d, cminusb; + if (delsamps < 1.00001f) delsamps = 1.00001f; + if (delsamps > limit) delsamps = limit; + delsamps += fn; + fn = fn - 1.0f; + idelsamps = delsamps; + frac = delsamps - (float)idelsamps; + bp = wp - idelsamps; + if (bp < vp + 4) bp += nsamps; + d = bp[-3]; + c = bp[-2]; + b = bp[-1]; + a = bp[0]; + cminusb = c-b; + *out++ = b + frac * ( + cminusb - 0.1666667f * (1.-frac) * ( + (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b) + ) + ); + } + return (w+6); +} + +static void sigvd_dsp(t_sigvd *x, t_signal **sp) +{ + t_sigdelwrite *delwriter = + (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class); + x->x_sr = sp[0]->s_sr * 0.001; + if (delwriter) + { + sigdelwrite_checkvecsize(delwriter, sp[0]->s_n); + x->x_zerodel = (delwriter->x_sortno == ugen_getsortno() ? + 0 : delwriter->x_vecsize); + dsp_add(sigvd_perform, 5, + sp[0]->s_vec, sp[1]->s_vec, + &delwriter->x_cspace, x, sp[0]->s_n); + } + else error("vd~: %s: no such delwrite~",x->x_sym->s_name); +} + +static void sigvd_setup(void) +{ + sigvd_class = class_new(gensym("vd~"), (t_newmethod)sigvd_new, 0, + sizeof(t_sigvd), 0, A_DEFSYM, 0); + class_addmethod(sigvd_class, (t_method)sigvd_dsp, gensym("dsp"), 0); + CLASS_MAINSIGNALIN(sigvd_class, t_sigvd, x_f); +} + +/* ----------------------- global setup routine ---------------- */ + +void d_delay_setup(void) +{ + sigdelwrite_setup(); + sigdelread_setup(); + sigvd_setup(); +} + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* send~, delread~, throw~, catch~ */ + +#include "m_pd.h" +extern int ugen_getsortno(void); + +#define DEFDELVS 64 /* LATER get this from canvas at DSP time */ +static int delread_zero = 0; /* four bytes of zero for delread~, vd~ */ + +/* ----------------------------- delwrite~ ----------------------------- */ +static t_class *sigdelwrite_class; + +typedef struct delwritectl +{ + int c_n; + float *c_vec; + int c_phase; +} t_delwritectl; + +typedef struct _sigdelwrite +{ + t_object x_obj; + t_symbol *x_sym; + t_delwritectl x_cspace; + int x_sortno; /* DSP sort number at which this was last put on chain */ + int x_rsortno; /* DSP sort # for first delread or write in chain */ + int x_vecsize; /* vector size for delread~ to use */ + float x_f; +} t_sigdelwrite; + +#define XTRASAMPS 4 +#define SAMPBLK 4 + + /* routine to check that all delwrites/delreads/vds have same vecsize */ +static void sigdelwrite_checkvecsize(t_sigdelwrite *x, int vecsize) +{ + /* + LATER this should really check sample rate and blocking, once that is + supported. Probably we don't actually care about vecsize. + For now just suppress this check... */ +#if 0 + if (x->x_rsortno != ugen_getsortno()) + { + x->x_vecsize = vecsize; + x->x_rsortno = ugen_getsortno(); + } + else if (vecsize != x->x_vecsize) + pd_error(x, "delread/delwrite/vd vector size mismatch"); +#endif +} + +static void *sigdelwrite_new(t_symbol *s, t_floatarg msec) +{ + int nsamps; + t_sigdelwrite *x = (t_sigdelwrite *)pd_new(sigdelwrite_class); + if (!*s->s_name) s = gensym("delwrite~"); + pd_bind(&x->x_obj.ob_pd, s); + x->x_sym = s; + nsamps = msec * sys_getsr() * (float)(0.001f); + if (nsamps < 1) nsamps = 1; + nsamps += ((- nsamps) & (SAMPBLK - 1)); + nsamps += DEFDELVS; + x->x_cspace.c_n = nsamps; + x->x_cspace.c_vec = + (float *)getbytes((nsamps + XTRASAMPS) * sizeof(float)); + x->x_cspace.c_phase = XTRASAMPS; + x->x_sortno = 0; + x->x_vecsize = 0; + x->x_f = 0; + return (x); +} + +static t_int *sigdelwrite_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_delwritectl *c = (t_delwritectl *)(w[2]); + int n = (int)(w[3]); + int phase = c->c_phase, nsamps = c->c_n; + float *vp = c->c_vec, *bp = vp + phase, *ep = vp + (c->c_n + XTRASAMPS); + phase += n; + while (n--) + { + float f = *in++; + if (PD_BIGORSMALL(f)) + f = 0; + *bp++ = f; + if (bp == ep) + { + vp[0] = ep[-4]; + vp[1] = ep[-3]; + vp[2] = ep[-2]; + vp[3] = ep[-1]; + bp = vp + XTRASAMPS; + phase -= nsamps; + } + } + c->c_phase = phase; + return (w+4); +} + +static void sigdelwrite_dsp(t_sigdelwrite *x, t_signal **sp) +{ + dsp_add(sigdelwrite_perform, 3, sp[0]->s_vec, &x->x_cspace, sp[0]->s_n); + x->x_sortno = ugen_getsortno(); + sigdelwrite_checkvecsize(x, sp[0]->s_n); +} + +static void sigdelwrite_free(t_sigdelwrite *x) +{ + pd_unbind(&x->x_obj.ob_pd, x->x_sym); + freebytes(x->x_cspace.c_vec, + (x->x_cspace.c_n + XTRASAMPS) * sizeof(float)); +} + +static void sigdelwrite_setup(void) +{ + sigdelwrite_class = class_new(gensym("delwrite~"), + (t_newmethod)sigdelwrite_new, (t_method)sigdelwrite_free, + sizeof(t_sigdelwrite), 0, A_DEFSYM, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(sigdelwrite_class, t_sigdelwrite, x_f); + class_addmethod(sigdelwrite_class, (t_method)sigdelwrite_dsp, + gensym("dsp"), 0); +} + +/* ----------------------------- delread~ ----------------------------- */ +static t_class *sigdelread_class; + +typedef struct _sigdelread +{ + t_object x_obj; + t_symbol *x_sym; + t_float x_deltime; /* delay in msec */ + int x_delsamps; /* delay in samples */ + t_float x_sr; /* samples per msec */ + t_float x_n; /* vector size */ + int x_zerodel; /* 0 or vecsize depending on read/write order */ +} t_sigdelread; + +static void sigdelread_float(t_sigdelread *x, t_float f); + +static void *sigdelread_new(t_symbol *s, t_floatarg f) +{ + t_sigdelread *x = (t_sigdelread *)pd_new(sigdelread_class); + x->x_sym = s; + x->x_sr = 1; + x->x_n = 1; + x->x_zerodel = 0; + sigdelread_float(x, f); + outlet_new(&x->x_obj, &s_signal); + return (x); +} + +static void sigdelread_float(t_sigdelread *x, t_float f) +{ + int samps; + t_sigdelwrite *delwriter = + (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class); + x->x_deltime = f; + if (delwriter) + { + int delsize = delwriter->x_cspace.c_n; + x->x_delsamps = (int)(0.5 + x->x_sr * x->x_deltime) + + x->x_n - x->x_zerodel; + if (x->x_delsamps < x->x_n) x->x_delsamps = x->x_n; + else if (x->x_delsamps > delwriter->x_cspace.c_n - DEFDELVS) + x->x_delsamps = delwriter->x_cspace.c_n - DEFDELVS; + } +} + +static t_int *sigdelread_perform(t_int *w) +{ + t_float *out = (t_float *)(w[1]); + t_delwritectl *c = (t_delwritectl *)(w[2]); + int delsamps = *(int *)(w[3]); + int n = (int)(w[4]); + int phase = c->c_phase - delsamps, nsamps = c->c_n; + float *vp = c->c_vec, *bp, *ep = vp + (c->c_n + XTRASAMPS); + + if (phase < 0) phase += nsamps; + bp = vp + phase; + while (n--) + { + *out++ = *bp++; + if (bp == ep) bp -= nsamps; + } + return (w+5); +} + +static void sigdelread_dsp(t_sigdelread *x, t_signal **sp) +{ + t_sigdelwrite *delwriter = + (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class); + x->x_sr = sp[0]->s_sr * 0.001; + x->x_n = sp[0]->s_n; + if (delwriter) + { + sigdelwrite_checkvecsize(delwriter, sp[0]->s_n); + x->x_zerodel = (delwriter->x_sortno == ugen_getsortno() ? + 0 : delwriter->x_vecsize); + sigdelread_float(x, x->x_deltime); + dsp_add(sigdelread_perform, 4, + sp[0]->s_vec, &delwriter->x_cspace, &x->x_delsamps, sp[0]->s_n); + } + else if (*x->x_sym->s_name) + error("delread~: %s: no such delwrite~",x->x_sym->s_name); +} + +static void sigdelread_setup(void) +{ + sigdelread_class = class_new(gensym("delread~"), + (t_newmethod)sigdelread_new, 0, + sizeof(t_sigdelread), 0, A_DEFSYM, A_DEFFLOAT, 0); + class_addmethod(sigdelread_class, (t_method)sigdelread_dsp, + gensym("dsp"), 0); + class_addfloat(sigdelread_class, (t_method)sigdelread_float); +} + + +/* ----------------------------- vd~ ----------------------------- */ +static t_class *sigvd_class; + +typedef struct _sigvd +{ + t_object x_obj; + t_symbol *x_sym; + t_float x_sr; /* samples per msec */ + int x_zerodel; /* 0 or vecsize depending on read/write order */ + float x_f; +} t_sigvd; + +static void *sigvd_new(t_symbol *s) +{ + t_sigvd *x = (t_sigvd *)pd_new(sigvd_class); + if (!*s->s_name) s = gensym("vd~"); + x->x_sym = s; + x->x_sr = 1; + x->x_zerodel = 0; + outlet_new(&x->x_obj, &s_signal); + x->x_f = 0; + return (x); +} + +static t_int *sigvd_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + t_delwritectl *ctl = (t_delwritectl *)(w[3]); + t_sigvd *x = (t_sigvd *)(w[4]); + int n = (int)(w[5]); + + int nsamps = ctl->c_n; + float limit = nsamps - n - 1; + float fn = n-1; + float *vp = ctl->c_vec, *bp, *wp = vp + ctl->c_phase; + float zerodel = x->x_zerodel; + while (n--) + { + float delsamps = x->x_sr * *in++ - zerodel, frac; + int idelsamps; + float a, b, c, d, cminusb; + if (delsamps < 1.00001f) delsamps = 1.00001f; + if (delsamps > limit) delsamps = limit; + delsamps += fn; + fn = fn - 1.0f; + idelsamps = delsamps; + frac = delsamps - (float)idelsamps; + bp = wp - idelsamps; + if (bp < vp + 4) bp += nsamps; + d = bp[-3]; + c = bp[-2]; + b = bp[-1]; + a = bp[0]; + cminusb = c-b; + *out++ = b + frac * ( + cminusb - 0.1666667f * (1.-frac) * ( + (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b) + ) + ); + } + return (w+6); +} + +static void sigvd_dsp(t_sigvd *x, t_signal **sp) +{ + t_sigdelwrite *delwriter = + (t_sigdelwrite *)pd_findbyclass(x->x_sym, sigdelwrite_class); + x->x_sr = sp[0]->s_sr * 0.001; + if (delwriter) + { + sigdelwrite_checkvecsize(delwriter, sp[0]->s_n); + x->x_zerodel = (delwriter->x_sortno == ugen_getsortno() ? + 0 : delwriter->x_vecsize); + dsp_add(sigvd_perform, 5, + sp[0]->s_vec, sp[1]->s_vec, + &delwriter->x_cspace, x, sp[0]->s_n); + } + else error("vd~: %s: no such delwrite~",x->x_sym->s_name); +} + +static void sigvd_setup(void) +{ + sigvd_class = class_new(gensym("vd~"), (t_newmethod)sigvd_new, 0, + sizeof(t_sigvd), 0, A_DEFSYM, 0); + class_addmethod(sigvd_class, (t_method)sigvd_dsp, gensym("dsp"), 0); + CLASS_MAINSIGNALIN(sigvd_class, t_sigvd, x_f); +} + +/* ----------------------- global setup routine ---------------- */ + +void d_delay_setup(void) +{ + sigdelwrite_setup(); + sigdelread_setup(); + sigvd_setup(); +} + diff --git a/apps/plugins/pdbox/PDa/src/d_fft.c b/apps/plugins/pdbox/PDa/src/d_fft.c new file mode 100644 index 0000000..9916fdc --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_fft.c @@ -0,0 +1,688 @@ +/* Copyright (c) 1997-1999 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include "m_pd.h" + +/* ------------------------ fft~ and ifft~ -------------------------------- */ +static t_class *sigfft_class, *sigifft_class; + +typedef struct fft +{ + t_object x_obj; + float x_f; +} t_sigfft; + +static void *sigfft_new(void) +{ + t_sigfft *x = (t_sigfft *)pd_new(sigfft_class); + outlet_new(&x->x_obj, gensym("signal")); + outlet_new(&x->x_obj, gensym("signal")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + x->x_f = 0; + return (x); +} + +static void *sigifft_new(void) +{ + t_sigfft *x = (t_sigfft *)pd_new(sigifft_class); + outlet_new(&x->x_obj, gensym("signal")); + outlet_new(&x->x_obj, gensym("signal")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + x->x_f = 0; + return (x); +} + +static t_int *sigfft_swap(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + int n = w[3]; + for (;n--; in1++, in2++) + { + float f = *in1; + *in1 = *in2; + *in2 = f; + } + return (w+4); +} + +static t_int *sigfft_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + int n = w[3]; + mayer_fft(n, in1, in2); + return (w+4); +} + +static t_int *sigifft_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + int n = w[3]; + mayer_ifft(n, in1, in2); + return (w+4); +} + +static void sigfft_dspx(t_sigfft *x, t_signal **sp, t_int *(*f)(t_int *w)) +{ + int n = sp[0]->s_n; + t_sample *in1 = sp[0]->s_vec; + t_sample *in2 = sp[1]->s_vec; + t_sample *out1 = sp[2]->s_vec; + t_sample *out2 = sp[3]->s_vec; + if (out1 == in2 && out2 == in1) + dsp_add(sigfft_swap, 3, out1, out2, n); + else if (out1 == in2) + { + dsp_add(copy_perform, 3, in2, out2, n); + dsp_add(copy_perform, 3, in1, out1, n); + } + else + { + if (out1 != in1) dsp_add(copy_perform, 3, in1, out1, n); + if (out2 != in2) dsp_add(copy_perform, 3, in2, out2, n); + } + dsp_add(f, 3, sp[2]->s_vec, sp[3]->s_vec, n); +} + +static void sigfft_dsp(t_sigfft *x, t_signal **sp) +{ + sigfft_dspx(x, sp, sigfft_perform); +} + +static void sigifft_dsp(t_sigfft *x, t_signal **sp) +{ + sigfft_dspx(x, sp, sigifft_perform); +} + +static void sigfft_setup(void) +{ + sigfft_class = class_new(gensym("fft~"), sigfft_new, 0, + sizeof(t_sigfft), 0, 0); + CLASS_MAINSIGNALIN(sigfft_class, t_sigfft, x_f); + class_addmethod(sigfft_class, (t_method)sigfft_dsp, gensym("dsp"), 0); + + sigifft_class = class_new(gensym("ifft~"), sigifft_new, 0, + sizeof(t_sigfft), 0, 0); + CLASS_MAINSIGNALIN(sigifft_class, t_sigfft, x_f); + class_addmethod(sigifft_class, (t_method)sigifft_dsp, gensym("dsp"), 0); + class_sethelpsymbol(sigifft_class, gensym("fft~")); +} + +/* ----------------------- rfft~ -------------------------------- */ + +static t_class *sigrfft_class; + +typedef struct rfft +{ + t_object x_obj; + float x_f; +} t_sigrfft; + +static void *sigrfft_new(void) +{ + t_sigrfft *x = (t_sigrfft *)pd_new(sigrfft_class); + outlet_new(&x->x_obj, gensym("signal")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *sigrfft_flip(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample *out = (t_sample *)(w[2]); + int n = w[3]; + while (n--) *(--out) = *in++; + *(--out) = 0; /* to hell with it */ + return (w+4); +} + +static t_int *sigrfft_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + int n = w[2]; + mayer_realfft(n, in); + return (w+3); +} + +static void sigrfft_dsp(t_sigrfft *x, t_signal **sp) +{ + int n = sp[0]->s_n, n2 = (n>>1); + t_sample *in1 = sp[0]->s_vec; + t_sample *out1 = sp[1]->s_vec; + t_sample *out2 = sp[2]->s_vec; + if (n < 4) + { + error("fft: minimum 4 points"); + return; + } + if (in1 == out2) /* this probably never happens */ + { + dsp_add(sigrfft_perform, 2, out2, n); + dsp_add(copy_perform, 3, out2, out1, n2); + dsp_add(sigrfft_flip, 3, out2 + (n2+1), out2 + n2, n2-1); + } + else + { + if (in1 != out1) dsp_add(copy_perform, 3, in1, out1, n); + dsp_add(sigrfft_perform, 2, out1, n); + dsp_add(sigrfft_flip, 3, out1 + (n2+1), out2 + n2, n2-1); + } + dsp_add_zero(out1 + n2, n2); + dsp_add_zero(out2 + n2, n2); +} + +static void sigrfft_setup(void) +{ + sigrfft_class = class_new(gensym("rfft~"), sigrfft_new, 0, + sizeof(t_sigrfft), 0, 0); + CLASS_MAINSIGNALIN(sigrfft_class, t_sigrfft, x_f); + class_addmethod(sigrfft_class, (t_method)sigrfft_dsp, gensym("dsp"), 0); + class_sethelpsymbol(sigrfft_class, gensym("fft~")); +} + +/* ----------------------- rifft~ -------------------------------- */ + +static t_class *sigrifft_class; + +typedef struct rifft +{ + t_object x_obj; + float x_f; +} t_sigrifft; + +static void *sigrifft_new(void) +{ + t_sigrifft *x = (t_sigrifft *)pd_new(sigrifft_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *sigrifft_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + int n = w[2]; + mayer_realifft(n, in); + return (w+3); +} + +static void sigrifft_dsp(t_sigrifft *x, t_signal **sp) +{ + int n = sp[0]->s_n, n2 = (n>>1); + t_sample *in1 = sp[0]->s_vec; + t_sample *in2 = sp[1]->s_vec; + t_sample *out1 = sp[2]->s_vec; + if (n < 4) + { + error("fft: minimum 4 points"); + return; + } + if (in2 == out1) + { + dsp_add(sigrfft_flip, 3, out1+1, out1 + n, (n2-1)); + dsp_add(copy_perform, 3, in1, out1, n2); + } + else + { + if (in1 != out1) dsp_add(copy_perform, 3, in1, out1, n2); + dsp_add(sigrfft_flip, 3, in2+1, out1 + n, n2-1); + } + dsp_add(sigrifft_perform, 2, out1, n); +} + +static void sigrifft_setup(void) +{ + sigrifft_class = class_new(gensym("rifft~"), sigrifft_new, 0, + sizeof(t_sigrifft), 0, 0); + CLASS_MAINSIGNALIN(sigrifft_class, t_sigrifft, x_f); + class_addmethod(sigrifft_class, (t_method)sigrifft_dsp, gensym("dsp"), 0); + class_sethelpsymbol(sigrifft_class, gensym("fft~")); +} + +/* ----------------------- framp~ -------------------------------- */ + +#if 0 +static t_class *sigframp_class; + +typedef struct framp +{ + t_object x_obj; + float x_f; +} t_sigframp; + +static void *sigframp_new(void) +{ + t_sigframp *x = (t_sigframp *)pd_new(sigframp_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, gensym("signal")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *sigframp_perform(t_int *w) +{ + float *inreal = (t_float *)(w[1]); + float *inimag = (t_float *)(w[2]); + float *outfreq = (t_float *)(w[3]); + float *outamp = (t_float *)(w[4]); + float lastreal = 0, currentreal = inreal[0], nextreal = inreal[1]; + float lastimag = 0, currentimag = inimag[0], nextimag = inimag[1]; + int n = w[5]; + int m = n + 1; + float fbin = 1, oneovern2 = 1.f/((float)n * (float)n); + + inreal += 2; + inimag += 2; + *outamp++ = *outfreq++ = 0; + n -= 2; + while (n--) + { + float re, im, pow, freq; + lastreal = currentreal; + currentreal = nextreal; + nextreal = *inreal++; + lastimag = currentimag; + currentimag = nextimag; + nextimag = *inimag++; + re = currentreal - 0.5f * (lastreal + nextreal); + im = currentimag - 0.5f * (lastimag + nextimag); + pow = re * re + im * im; + if (pow > 1e-19) + { + float detune = ((lastreal - nextreal) * re + + (lastimag - nextimag) * im) / (2.0f * pow); + if (detune > 2 || detune < -2) freq = pow = 0; + else freq = fbin + detune; + } + else freq = pow = 0; + *outfreq++ = freq; + *outamp++ = oneovern2 * pow; + fbin += 1.0f; + } + while (m--) *outamp++ = *outfreq++ = 0; + return (w+6); +} + +t_int *sigsqrt_perform(t_int *w); + +static void sigframp_dsp(t_sigframp *x, t_signal **sp) +{ + int n = sp[0]->s_n, n2 = (n>>1); + if (n < 4) + { + error("framp: minimum 4 points"); + return; + } + dsp_add(sigframp_perform, 5, sp[0]->s_vec, sp[1]->s_vec, + sp[2]->s_vec, sp[3]->s_vec, n2); + dsp_add(sigsqrt_perform, 3, sp[3]->s_vec, sp[3]->s_vec, n2); +} + +static void sigframp_setup(void) +{ + sigframp_class = class_new(gensym("framp~"), sigframp_new, 0, + sizeof(t_sigframp), 0, 0); + CLASS_MAINSIGNALIN(sigframp_class, t_sigframp, x_f); + class_addmethod(sigframp_class, (t_method)sigframp_dsp, gensym("dsp"), 0); +} +#endif + +/* ------------------------ global setup routine ------------------------- */ + +void d_fft_setup(void) +{ + sigfft_setup(); + sigrfft_setup(); + sigrifft_setup(); +// sigframp_setup(); +} +/* Copyright (c) 1997-1999 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include "m_pd.h" + +/* ------------------------ fft~ and ifft~ -------------------------------- */ +static t_class *sigfft_class, *sigifft_class; + +typedef struct fft +{ + t_object x_obj; + float x_f; +} t_sigfft; + +static void *sigfft_new(void) +{ + t_sigfft *x = (t_sigfft *)pd_new(sigfft_class); + outlet_new(&x->x_obj, gensym("signal")); + outlet_new(&x->x_obj, gensym("signal")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + x->x_f = 0; + return (x); +} + +static void *sigifft_new(void) +{ + t_sigfft *x = (t_sigfft *)pd_new(sigifft_class); + outlet_new(&x->x_obj, gensym("signal")); + outlet_new(&x->x_obj, gensym("signal")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + x->x_f = 0; + return (x); +} + +static t_int *sigfft_swap(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + int n = w[3]; + for (;n--; in1++, in2++) + { + float f = *in1; + *in1 = *in2; + *in2 = f; + } + return (w+4); +} + +static t_int *sigfft_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + int n = w[3]; + mayer_fft(n, in1, in2); + return (w+4); +} + +static t_int *sigifft_perform(t_int *w) +{ + t_sample *in1 = (t_sample *)(w[1]); + t_sample *in2 = (t_sample *)(w[2]); + int n = w[3]; + mayer_ifft(n, in1, in2); + return (w+4); +} + +static void sigfft_dspx(t_sigfft *x, t_signal **sp, t_int *(*f)(t_int *w)) +{ + int n = sp[0]->s_n; + t_sample *in1 = sp[0]->s_vec; + t_sample *in2 = sp[1]->s_vec; + t_sample *out1 = sp[2]->s_vec; + t_sample *out2 = sp[3]->s_vec; + if (out1 == in2 && out2 == in1) + dsp_add(sigfft_swap, 3, out1, out2, n); + else if (out1 == in2) + { + dsp_add(copy_perform, 3, in2, out2, n); + dsp_add(copy_perform, 3, in1, out1, n); + } + else + { + if (out1 != in1) dsp_add(copy_perform, 3, in1, out1, n); + if (out2 != in2) dsp_add(copy_perform, 3, in2, out2, n); + } + dsp_add(f, 3, sp[2]->s_vec, sp[3]->s_vec, n); +} + +static void sigfft_dsp(t_sigfft *x, t_signal **sp) +{ + sigfft_dspx(x, sp, sigfft_perform); +} + +static void sigifft_dsp(t_sigfft *x, t_signal **sp) +{ + sigfft_dspx(x, sp, sigifft_perform); +} + +static void sigfft_setup(void) +{ + sigfft_class = class_new(gensym("fft~"), sigfft_new, 0, + sizeof(t_sigfft), 0, 0); + CLASS_MAINSIGNALIN(sigfft_class, t_sigfft, x_f); + class_addmethod(sigfft_class, (t_method)sigfft_dsp, gensym("dsp"), 0); + + sigifft_class = class_new(gensym("ifft~"), sigifft_new, 0, + sizeof(t_sigfft), 0, 0); + CLASS_MAINSIGNALIN(sigifft_class, t_sigfft, x_f); + class_addmethod(sigifft_class, (t_method)sigifft_dsp, gensym("dsp"), 0); + class_sethelpsymbol(sigifft_class, gensym("fft~")); +} + +/* ----------------------- rfft~ -------------------------------- */ + +static t_class *sigrfft_class; + +typedef struct rfft +{ + t_object x_obj; + float x_f; +} t_sigrfft; + +static void *sigrfft_new(void) +{ + t_sigrfft *x = (t_sigrfft *)pd_new(sigrfft_class); + outlet_new(&x->x_obj, gensym("signal")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *sigrfft_flip(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample *out = (t_sample *)(w[2]); + int n = w[3]; + while (n--) *(--out) = *in++; + *(--out) = 0; /* to hell with it */ + return (w+4); +} + +static t_int *sigrfft_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + int n = w[2]; + mayer_realfft(n, in); + return (w+3); +} + +static void sigrfft_dsp(t_sigrfft *x, t_signal **sp) +{ + int n = sp[0]->s_n, n2 = (n>>1); + t_sample *in1 = sp[0]->s_vec; + t_sample *out1 = sp[1]->s_vec; + t_sample *out2 = sp[2]->s_vec; + if (n < 4) + { + error("fft: minimum 4 points"); + return; + } + if (in1 == out2) /* this probably never happens */ + { + dsp_add(sigrfft_perform, 2, out2, n); + dsp_add(copy_perform, 3, out2, out1, n2); + dsp_add(sigrfft_flip, 3, out2 + (n2+1), out2 + n2, n2-1); + } + else + { + if (in1 != out1) dsp_add(copy_perform, 3, in1, out1, n); + dsp_add(sigrfft_perform, 2, out1, n); + dsp_add(sigrfft_flip, 3, out1 + (n2+1), out2 + n2, n2-1); + } + dsp_add_zero(out1 + n2, n2); + dsp_add_zero(out2 + n2, n2); +} + +static void sigrfft_setup(void) +{ + sigrfft_class = class_new(gensym("rfft~"), sigrfft_new, 0, + sizeof(t_sigrfft), 0, 0); + CLASS_MAINSIGNALIN(sigrfft_class, t_sigrfft, x_f); + class_addmethod(sigrfft_class, (t_method)sigrfft_dsp, gensym("dsp"), 0); + class_sethelpsymbol(sigrfft_class, gensym("fft~")); +} + +/* ----------------------- rifft~ -------------------------------- */ + +static t_class *sigrifft_class; + +typedef struct rifft +{ + t_object x_obj; + float x_f; +} t_sigrifft; + +static void *sigrifft_new(void) +{ + t_sigrifft *x = (t_sigrifft *)pd_new(sigrifft_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *sigrifft_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + int n = w[2]; + mayer_realifft(n, in); + return (w+3); +} + +static void sigrifft_dsp(t_sigrifft *x, t_signal **sp) +{ + int n = sp[0]->s_n, n2 = (n>>1); + t_sample *in1 = sp[0]->s_vec; + t_sample *in2 = sp[1]->s_vec; + t_sample *out1 = sp[2]->s_vec; + if (n < 4) + { + error("fft: minimum 4 points"); + return; + } + if (in2 == out1) + { + dsp_add(sigrfft_flip, 3, out1+1, out1 + n, (n2-1)); + dsp_add(copy_perform, 3, in1, out1, n2); + } + else + { + if (in1 != out1) dsp_add(copy_perform, 3, in1, out1, n2); + dsp_add(sigrfft_flip, 3, in2+1, out1 + n, n2-1); + } + dsp_add(sigrifft_perform, 2, out1, n); +} + +static void sigrifft_setup(void) +{ + sigrifft_class = class_new(gensym("rifft~"), sigrifft_new, 0, + sizeof(t_sigrifft), 0, 0); + CLASS_MAINSIGNALIN(sigrifft_class, t_sigrifft, x_f); + class_addmethod(sigrifft_class, (t_method)sigrifft_dsp, gensym("dsp"), 0); + class_sethelpsymbol(sigrifft_class, gensym("fft~")); +} + +/* ----------------------- framp~ -------------------------------- */ + +#if 0 +static t_class *sigframp_class; + +typedef struct framp +{ + t_object x_obj; + float x_f; +} t_sigframp; + +static void *sigframp_new(void) +{ + t_sigframp *x = (t_sigframp *)pd_new(sigframp_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, gensym("signal")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *sigframp_perform(t_int *w) +{ + float *inreal = (t_float *)(w[1]); + float *inimag = (t_float *)(w[2]); + float *outfreq = (t_float *)(w[3]); + float *outamp = (t_float *)(w[4]); + float lastreal = 0, currentreal = inreal[0], nextreal = inreal[1]; + float lastimag = 0, currentimag = inimag[0], nextimag = inimag[1]; + int n = w[5]; + int m = n + 1; + float fbin = 1, oneovern2 = 1.f/((float)n * (float)n); + + inreal += 2; + inimag += 2; + *outamp++ = *outfreq++ = 0; + n -= 2; + while (n--) + { + float re, im, pow, freq; + lastreal = currentreal; + currentreal = nextreal; + nextreal = *inreal++; + lastimag = currentimag; + currentimag = nextimag; + nextimag = *inimag++; + re = currentreal - 0.5f * (lastreal + nextreal); + im = currentimag - 0.5f * (lastimag + nextimag); + pow = re * re + im * im; + if (pow > 1e-19) + { + float detune = ((lastreal - nextreal) * re + + (lastimag - nextimag) * im) / (2.0f * pow); + if (detune > 2 || detune < -2) freq = pow = 0; + else freq = fbin + detune; + } + else freq = pow = 0; + *outfreq++ = freq; + *outamp++ = oneovern2 * pow; + fbin += 1.0f; + } + while (m--) *outamp++ = *outfreq++ = 0; + return (w+6); +} + +t_int *sigsqrt_perform(t_int *w); + +static void sigframp_dsp(t_sigframp *x, t_signal **sp) +{ + int n = sp[0]->s_n, n2 = (n>>1); + if (n < 4) + { + error("framp: minimum 4 points"); + return; + } + dsp_add(sigframp_perform, 5, sp[0]->s_vec, sp[1]->s_vec, + sp[2]->s_vec, sp[3]->s_vec, n2); + dsp_add(sigsqrt_perform, 3, sp[3]->s_vec, sp[3]->s_vec, n2); +} + +static void sigframp_setup(void) +{ + sigframp_class = class_new(gensym("framp~"), sigframp_new, 0, + sizeof(t_sigframp), 0, 0); + CLASS_MAINSIGNALIN(sigframp_class, t_sigframp, x_f); + class_addmethod(sigframp_class, (t_method)sigframp_dsp, gensym("dsp"), 0); +} +#endif + +/* ------------------------ global setup routine ------------------------- */ + +void d_fft_setup(void) +{ + sigfft_setup(); + sigrfft_setup(); + sigrifft_setup(); +// sigframp_setup(); +} diff --git a/apps/plugins/pdbox/PDa/src/d_fftroutine.c b/apps/plugins/pdbox/PDa/src/d_fftroutine.c new file mode 100644 index 0000000..dde24fa --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_fftroutine.c @@ -0,0 +1,2002 @@ +/*****************************************************************************/ +/* */ +/* Fast Fourier Transform */ +/* Network Abstraction, Definitions */ +/* Kevin Peterson, MIT Media Lab, EMS */ +/* UROP - Fall '86 */ +/* REV: 6/12/87(KHP) - To incorporate link list of different sized networks */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* added debug option 5/91 brown@nadia */ +/* change sign at AAA */ +/* */ +/* Fast Fourier Transform */ +/* FFT Network Interaction and Support Modules */ +/* Kevin Peterson, MIT Media Lab, EMS */ +/* UROP - Fall '86 */ +/* REV: 6/12/87(KHP) - Generalized to one procedure call with typed I/O */ +/* */ +/*****************************************************************************/ + +/* Overview: + + My realization of the FFT involves a representation of a network of + "butterfly" elements that takes a set of 'N' sound samples as input and + computes the discrete Fourier transform. This network consists of a + series of stages (log2 N), each stage consisting of N/2 parallel butterfly + elements. Consecutive stages are connected by specific, predetermined flow + paths, (see Oppenheim, Schafer for details) and each butterfly element has + an associated multiplicative coefficient. + + FFT NETWORK: + ----------- + ____ _ ____ _ ____ _ ____ _ ____ + o--| |o-| |-o| |o-| |-o| |o-| |-o| |o-| |-o| |--o + |reg1| | | |W^r1| | | |reg1| | | |W^r1| | | |reg1| + | | | | | | | | | | | | | | | | | | ..... + | | | | | | | | | | | | | | | | | | + o--|____|o-| |-o|____|o-| |-o|____|o-| |-o|____|o-| |-o|____|--o + | | | | | | | | + | | | | | | | | + ____ | | ____ | | ____ | | ____ | | ____ + o--| |o-| |-o| |o-| |-o| |o-| |-o| |o-| |-o| |--o + |reg2| | | |W^r2| | | |reg2| | | |W^r2| | | |reg2| + | | | | | | | | | | | | | | | | | | ..... + | | | | | | | | | | | | | | | | | | + o--|____|o-| |-o|____|o-| |-o|____|o-| |-o|____|o-| |-o|____|--o + | | | | | | | | + | | | | | | | | + : : : : : : : : : + : : : : : : : : : + : : : : : : : : : + : : : : : : : : : + : : : : : : : : : + + ____ | | ____ | | ____ | | ____ | | ____ + o--| |o-| |-o| |o-| |-o| |o-| |-o| |o-| |-o| |--o + |reg | | | |W^r | | | |reg | | | |W^r | | | |reg | + | N/2| | | | N/2| | | | N/2| | | | N/2| | | | N/2| ..... + | | | | | | | | | | | | | | | | | | + o--|____|o-|_|-o|____|o-|_|-o|____|o-|_|-o|____|o-|_|-o|____|--o + + ^ ^ ^ ^ + Initial | Bttrfly | Rd/Wrt | Bttrfly | Rd/Wrt + Buffer | | Register | | Register + |____________|____________|____________| + | + | + Interconnect + Paths + + The use of "in-place" computation permits one to use only one set of + registers realized by an array of complex number structures. To describe + the coefficients for each butterfly I am using a two dimensional array + (stage, butterfly) of complex numbers. The predetermined stage connections + will be described in a two dimensional array of indicies. These indicies + will be used to determine the order of reading at each stage of the + computation. +*/ + + +/*****************************************************************************/ +/* INCLUDE FILES */ +/*****************************************************************************/ + +#include +#include +#include + + /* the following is needed only to declare pd_fft() as exportable in MSW */ +#include "m_pd.h" + +/* some basic definitions */ +#ifndef BOOL +#define BOOL int +#define TRUE 1 +#define FALSE 0 +#endif + +#define SAMPLE float /* data type used in calculation */ + +#define SHORT_SIZE sizeof(short) +#define INT_SIZE sizeof(int) +#define FLOAT_SIZE sizeof(float) +#define SAMPLE_SIZE sizeof(SAMPLE) +#define PNTR_SIZE sizeof(char *) + +#define PI 3.1415927 +#define TWO_PI 6.2831854 + +/* type definitions for I/O buffers */ +#define REAL 0 /* real only */ +#define IMAG 2 /* imaginary only */ +#define RECT 8 /* real and imaginary */ +#define MAG 16 /* magnitude only */ +#define PHASE 32 /* phase only */ +#define POLAR 64 /* magnitude and phase*/ + +/* scale definitions for I/O buffers */ +#define LINEAR 0 +#define DB 1 /* 20log10 */ + +/* transform direction definition */ +#define FORWARD 1 /* Forward FFT */ +#define INVERSE 2 /* Inverse FFT */ + +/* window type definitions */ +#define HANNING 1 +#define RECTANGULAR 0 + + + +/* network structure definition */ + +typedef struct Tfft_net { + int n; + int stages; + int bps; + int direction; + int window_type; + int *load_index; + SAMPLE *window, *inv_window; + SAMPLE *regr; + SAMPLE *regi; + SAMPLE **indexpr; + SAMPLE **indexpi; + SAMPLE **indexqr; + SAMPLE **indexqi; + SAMPLE *coeffr, *inv_coeffr; + SAMPLE *coeffi, *inv_coeffi; + struct Tfft_net *next; +} FFT_NET; + + +void cfft(int trnsfrm_dir, int npnt, int window, + float *source_buf, int source_form, int source_scale, + float *result_buf, int result_form, int result_scale, int debug); + + +/*****************************************************************************/ +/* GLOBAL DECLARATIONS */ +/*****************************************************************************/ + +static FFT_NET *firstnet; + +/* prototypes */ + +void net_alloc(FFT_NET *fft_net); +void net_dealloc(FFT_NET *fft_net); +int power_of_two(int n); +void create_hanning(SAMPLE *window, int n, SAMPLE scale); +void create_rectangular(SAMPLE *window, int n, SAMPLE scale); +void short_to_float(short *short_buf, float *float_buf, int n); +void load_registers(FFT_NET *fft_net, float *buf, int buf_form, + int buf_scale, int trnsfrm_dir); +void compute_fft(FFT_NET *fft_net); +void store_registers(FFT_NET *fft_net, float *buf, int buf_form, + int buf_scale, int debug); +void build_fft_network(FFT_NET *fft_net, int n, int window_type); + +/*****************************************************************************/ +/* GENERALIZED FAST FOURIER TRANSFORM MODULE */ +/*****************************************************************************/ + +void cfft(int trnsfrm_dir, int npnt, int window, + float *source_buf, int source_form, int source_scale, + float *result_buf, int result_form, int result_scale, int debug) + +/* modifies: result_buf + effects: Computes npnt FFT specified by form, scale, and dir parameters. + Source samples (single precision float) are taken from soure_buf and + the transfrmd representation is stored in result_buf (single precision + float). The parameters are defined as follows: + + trnsfrm_dir = FORWARD | INVERSE + npnt = 2^k for some any positive integer k + window = HANNING | RECTANGULAR + (RECT = real and imag parts, POLAR = magnitude and phase) + source_form = REAL | IMAG | RECT | POLAR + result_form = REAL | IMAG | RECT | MAG | PHASE | POLAR + xxxxxx_scale= LINEAR | DB ( 20log10 |mag| ) + + The input/output buffers are stored in a form appropriate to the type. + For example: REAL => {real, real, real ...}, + MAG => {mag, mag, mag, ... }, + RECT => {real, imag, real, imag, ... }, + POLAR => {mag, phase, mag, phase, ... }. + + To look at the magnitude (in db) of a 1024 point FFT of a real time + signal we have: + + fft(FORWARD, 1024, RECTANGULAR, input, REAL, LINEAR, output, MAG, DB) + + All possible input and output combinations are possible given the + choice of type and scale parameters. +*/ + +{ + FFT_NET *thisnet = (FFT_NET *)0; + FFT_NET *lastnet = (FFT_NET *)0; + + /* A linked list of fft networks of different sizes is maintained to + avoid building with every call. The network is built on the first + call but reused for subsequent calls requesting the same size + transformation. + */ + + thisnet=firstnet; + while (thisnet) { + if (!(thisnet->n == npnt) || !(thisnet->window_type == window)) { + /* current net doesn't match size or window type */ + lastnet=thisnet; + thisnet=thisnet->next; + continue; /* keep looking */ + } + + else { /* network matches desired size */ + load_registers(thisnet, source_buf, source_form, source_scale, + trnsfrm_dir); + compute_fft(thisnet); /* do transformation */ + store_registers(thisnet, result_buf, result_form, result_scale,debug); + return; + } + } + + /* none of existing networks match required size*/ + + if (lastnet) { /* add new network to end of list */ + thisnet = (FFT_NET *)malloc(sizeof(FFT_NET)); /* allocate */ + thisnet->next = 0; + lastnet->next = thisnet; /* add to end of list */ + } + else { /* first network to be created */ + thisnet=firstnet=(FFT_NET *)malloc(sizeof(FFT_NET)); /* alloc. */ + thisnet->next = 0; + } + + /* build new network and compute transformation */ + build_fft_network(thisnet, npnt, window); + load_registers(thisnet, source_buf, source_form, source_scale, + trnsfrm_dir); + compute_fft(thisnet); + store_registers(thisnet, result_buf, result_form, result_scale,debug); + return; +} + +void fft_clear(void) + +/* effects: Deallocates all preserved FFT networks. Should be used when + finished with all computations. +*/ + +{ + FFT_NET *thisnet, *nextnet; + + if (firstnet) { + thisnet=firstnet; + do { + nextnet = thisnet->next; + net_dealloc(thisnet); + free((char *)thisnet); + } while (thisnet = nextnet); + } +} + + +/*****************************************************************************/ +/* NETWORK CONSTRUCTION */ +/*****************************************************************************/ + +void build_fft_network(FFT_NET *fft_net, int n, int window_type) + + +/* modifies:fft_net + effects: Constructs the fft network as described in fft.h. Butterfly + coefficients, read/write indicies, bit reversed load indicies, + and array allocations are computed. +*/ + +{ + int cntr, i, j, s; + int stages, bps; + int **p, **q, *pp, *qp; + SAMPLE two_pi_div_n = TWO_PI / n; + + + /* network definition */ + fft_net->n = n; + fft_net->bps = bps = n/2; + for (i = 0, j = n; j > 1; j >>= 1, i++); + fft_net->stages = stages = i; + fft_net->direction = FORWARD; + fft_net->window_type = window_type; + fft_net->next = (FFT_NET *)0; + + /* allocate registers, index, coefficient arrays */ + net_alloc(fft_net); + + + /* create appropriate windows */ + if (window_type==HANNING) { + create_hanning(fft_net->window, n, 1.); + create_hanning(fft_net->inv_window, n, 1./n); + } + else { + create_rectangular(fft_net->window, n, 1.); + create_rectangular(fft_net->inv_window, n, 1./n); + } + + + /* calculate butterfly coefficients */ { + + int num_diff_coeffs, power_inc, power; + SAMPLE *coeffpr = fft_net->coeffr; + SAMPLE *coeffpi = fft_net->coeffi; + SAMPLE *inv_coeffpr = fft_net->inv_coeffr; + SAMPLE *inv_coeffpi = fft_net->inv_coeffi; + + /* stage one coeffs are 1 + 0j */ + for (i = 0; i < bps; i++) { + *coeffpr = *inv_coeffpr = 1.; + *coeffpi = *inv_coeffpi = 0.; + coeffpr++; inv_coeffpr++; + coeffpi++; inv_coeffpi++; + } + + /* stage 2 to last stage coeffs need calculation */ + /* (1< 2^r */ + for (s = 2; s <= stages; s++) { + + num_diff_coeffs = n / (1 << (stages - s + 1)); + power_inc = 1 << (stages -s); + cntr = 0; + + for (i = bps/num_diff_coeffs; i > 0; i--) { + + power = 0; + + for (j = num_diff_coeffs; j > 0; j--) { + *coeffpr = cos(two_pi_div_n*power); + *inv_coeffpr = cos(two_pi_div_n*power); +/* AAA change these signs */ *coeffpi = -sin(two_pi_div_n*power); +/* change back */ *inv_coeffpi = sin(two_pi_div_n*power); + power += power_inc; + coeffpr++; inv_coeffpr++; + coeffpi++; inv_coeffpi++; + } + } + } + } + + /* calculate network indicies: stage exchange indicies are + calculated and then used as offset values from the base + register locations. The final addresses are then stored in + fft_net. + */ { + + int index, inc; + SAMPLE **indexpr = fft_net->indexpr; + SAMPLE **indexpi = fft_net->indexpi; + SAMPLE **indexqr = fft_net->indexqr; + SAMPLE **indexqi = fft_net->indexqi; + SAMPLE *regr = fft_net->regr; + SAMPLE *regi = fft_net->regi; + + + /* allocate temporary 2d stage exchange index, 1d temp + load index */ + p = (int **)malloc(stages * PNTR_SIZE); + q = (int **)malloc(stages * PNTR_SIZE); + + for (s = 0; s < stages; s++) { + p[s] = (int *)malloc(bps * INT_SIZE); + q[s] = (int *)malloc(bps * INT_SIZE); + } + + /* calculate stage exchange indicies: */ + for (s = 0; s < stages; s++) { + pp = p[s]; + qp = q[s]; + inc = 1 << s; + cntr = 1 << (stages-s-1); + i = j = index = 0; + + do { + do { + qp[i] = index + inc; + pp[i++] = index++; + } while (++j < inc); + index = qp[i-1] + 1; + j = 0; + } while (--cntr); + } + + /* compute actual address values using indicies as offsets */ + for (s = 0; s < stages; s++) { + for (i = 0; i < bps; i++) { + *indexpr++ = regr + p[s][i]; + *indexpi++ = regi + p[s][i]; + *indexqr++ = regr + q[s][i]; + *indexqi++ = regi + q[s][i]; + } + } + } + + + /* calculate load indicies (bit reverse ordering) */ + /* bit reverse ordering achieved by passing normal + order indicies backwards through the network */ + + /* init to normal order indicies */ { + int *load_index,*load_indexp; + int *temp_indexp, *temp_index; + temp_index=temp_indexp=(int *)malloc(n * INT_SIZE); + + i = 0; j = n; + load_index = load_indexp = fft_net->load_index; + + while (j--) + *load_indexp++ = i++; + + /* pass indicies backwards through net */ + for (s = stages - 1; s > 0; s--) { + pp = p[s]; + qp = q[s]; + + for (i = 0; i < bps; i++) { + temp_index[pp[i]]=load_index[2*i]; + temp_index[qp[i]]=load_index[2*i+1]; + } + j = n; + load_indexp = load_index; + temp_indexp = temp_index; + while (j--) + *load_indexp++ = *temp_indexp++; + } + + /* free all temporary arrays */ + free((char *)temp_index); + for (s = 0; s < stages; s++) { + free((char *)p[s]);free((char *)q[s]); + } + free((char *)p);free((char *)q); + } +} + + + +/*****************************************************************************/ +/* REGISTER LOAD AND STORE */ +/*****************************************************************************/ + +void load_registers(FFT_NET *fft_net, float *buf, int buf_form, + int buf_scale, int trnsfrm_dir) + +/* effects: Multiplies the input buffer with the appropriate window and + stores the resulting values in the initial registers of the + network. Input buffer must contain values appropriate to form. + For RECT, the buffer contains real num. followed by imag num, + and for POLAR, it contains magnitude followed by phase. Pure + inputs are listed normally. Both LINEAR and DB scales are + interpreted. +*/ + +{ + int *load_index = fft_net->load_index; + SAMPLE *window; + int index, i = 0, n = fft_net->n; + + if (trnsfrm_dir==FORWARD) window = fft_net->window; + else if (trnsfrm_dir==INVERSE) window = fft_net->inv_window; + else { + fprintf(stderr, "load_registers:illegal transform direction\n"); + exit(0); + } + fft_net->direction = trnsfrm_dir; + + switch(buf_scale) { + case LINEAR: { + + switch (buf_form) { + case REAL: { /* pure REAL */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=(SAMPLE)buf[index] * window[index]; + fft_net->regi[i]=0.; + i++; + } + } break; + + case IMAG: { /* pure IMAGinary */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=0; + fft_net->regi[i]=(SAMPLE)buf[index] * window[index]; + i++; + } + } break; + + case RECT: { /* both REAL and IMAGinary */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=(SAMPLE)buf[index*2] * window[index]; + fft_net->regi[i]=(SAMPLE)buf[index*2+1] * window[index]; + i++; + } + } break; + + case POLAR: { /* magnitude followed by phase */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=(SAMPLE)(buf[index*2] * cos(buf[index*2+1])) + * window[index]; + fft_net->regi[i]=(SAMPLE)(buf[index*2] * sin(buf[index*2+1])) + * window[index]; + i++; + } + } break; + + default: { + fprintf(stderr, "load_registers:illegal input form\n"); + exit(0); + } break; + } + } break; + + case DB: { + + switch (buf_form) { + case REAL: { /* log pure REAL */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=(SAMPLE)pow(10., (1./20.)*buf[index]) + * window[index]; /* window scaling after linearization */ + fft_net->regi[i]=0.; + i++; + } + } break; + + case IMAG: { /* log pure IMAGinary */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=0.; + fft_net->regi[i]=(SAMPLE)pow(10., (1./20.)*buf[index]) + * window[index]; + i++; + } + } break; + + case RECT: { /* log REAL and log IMAGinary */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=(SAMPLE)pow(10., (1./20.)*buf[index*2]) + * window[index]; + fft_net->regi[i]=(SAMPLE)pow(10., (1./20.)*buf[index*2+1]) + * window[index]; + i++; + } + } break; + + case POLAR: { /* log mag followed by phase */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=(SAMPLE)(pow(10., (1./20.)*buf[index*2]) + * cos(buf[index*2+1])) * window[index]; + fft_net->regi[i]=(SAMPLE)(pow(10., (1./20.)*buf[index*2]) + * sin(buf[index*2+1])) * window[index]; + i++; + } + } break; + + default: { + fprintf(stderr, "load_registers:illegal input form\n"); + exit(0); + } break; + } + } break; + + default: { + fprintf(stderr, "load_registers:illegal input scale\n"); + exit(0); + } break; + } +} + + +void store_registers(FFT_NET *fft_net, float *buf, int buf_form, + int buf_scale, int debug) + +/* modifies: buf + effects: Writes the final contents of the network registers into buf in + either linear or db scale, polar or rectangular form. If any of + the pure forms(REAL, IMAG, MAG, or PHASE) are used then only the + corresponding part of the registers is stored in buf. +*/ + +{ + int i; + SAMPLE real, imag, mag, phase; + int n; + + i = 0; + n = fft_net->n; + + switch (buf_scale) { + case LINEAR: { + + switch (buf_form) { + case REAL: { /* pure REAL */ + do { + *buf++ = (float)fft_net->regr[i]; + } while (++i < n); + } break; + + case IMAG: { /* pure IMAGinary */ + do { + *buf++ = (float)fft_net->regi[i]; + } while (++i < n); + } break; + + case RECT: { /* both REAL and IMAGinary */ + do { + *buf++ = (float)fft_net->regr[i]; + *buf++ = (float)fft_net->regi[i]; + } while (++i < n); + } break; + + case MAG: { /* magnitude only */ + do { + real = fft_net->regr[i]; + imag = fft_net->regi[i]; + *buf++ = (float)sqrt(real*real+imag*imag); + } while (++i < n); + } break; + + case PHASE: { /* phase only */ + do { + real = fft_net->regr[i]; + imag = fft_net->regi[i]; + if (real > .00001) + *buf++ = (float)atan2(imag, real); + else { /* deal with bad case */ + if (imag > 0){ *buf++ = PI / 2.; + if(debug) fprintf(stderr,"real=0 and imag > 0\n");} + else if (imag < 0){ *buf++ = -PI / 2.; + if(debug) fprintf(stderr,"real=0 and imag < 0\n");} + else { *buf++ = 0; + if(debug) fprintf(stderr,"real=0 and imag=0\n");} + } + } while (++i < n); + } break; + + case POLAR: { /* magnitude and phase */ + do { + real = fft_net->regr[i]; + imag = fft_net->regi[i]; + *buf++ = (float)sqrt(real*real+imag*imag); + if (real) /* a hack to avoid div by zero */ + *buf++ = (float)atan2(imag, real); + else { /* deal with bad case */ + if (imag > 0) *buf++ = PI / 2.; + else if (imag < 0) *buf++ = -PI / 2.; + else *buf++ = 0; + } + } while (++i < n); + } break; + + default: { + fprintf(stderr, "store_registers:illegal output form\n"); + exit(0); + } break; + } + } break; + + case DB: { + + switch (buf_form) { + case REAL: { /* real only */ + do { + *buf++ = (float)20.*log10(fft_net->regr[i]); + } while (++i < n); + } break; + + case IMAG: { /* imag only */ + do { + *buf++ = (float)20.*log10(fft_net->regi[i]); + } while (++i < n); + } break; + + case RECT: { /* real and imag */ + do { + *buf++ = (float)20.*log10(fft_net->regr[i]); + *buf++ = (float)20.*log10(fft_net->regi[i]); + } while (++i < n); + } break; + + case MAG: { /* magnitude only */ + do { + real = fft_net->regr[i]; + imag = fft_net->regi[i]; + *buf++ = (float)20.*log10(sqrt(real*real+imag*imag)); + } while (++i < n); + } break; + + case PHASE: { /* phase only */ + do { + real = fft_net->regr[i]; + imag = fft_net->regi[i]; + if (real) + *buf++ = (float)atan2(imag, real); + else { /* deal with bad case */ + if (imag > 0) *buf++ = PI / 2.; + else if (imag < 0) *buf++ = -PI / 2.; + else *buf++ = 0; + } + } while (++i < n); + } break; + + case POLAR: { /* magnitude and phase */ + do { + real = fft_net->regr[i]; + imag = fft_net->regi[i]; + *buf++ = (float)20.*log10(sqrt(real*real+imag*imag)); + if (real) + *buf++ = (float)atan2(imag, real); + else { /* deal with bad case */ + if (imag > 0) *buf++ = PI / 2.; + else if (imag < 0) *buf++ = -PI / 2.; + else *buf++ = 0; + } + } while (++i < n); + } break; + + default: { + fprintf(stderr, "store_registers:illegal output form\n"); + exit(0); + } break; + } + } break; + + default: { + fprintf(stderr, "store_registers:illegal output scale\n"); + exit(0); + } break; + } +} + + + +/*****************************************************************************/ +/* COMPUTE TRANSFORMATION */ +/*****************************************************************************/ + +void compute_fft(FFT_NET *fft_net) + + +/* modifies: fft_net + effects: Passes the values (already loaded) in the registers through + the network, multiplying with appropriate coefficients at each + stage. The fft result will be in the registers at the end of + the computation. The direction of the transformation is indicated + by the network flag 'direction'. The form of the computation is: + + X(pn) = X(p) + C*X(q) + X(qn) = X(p) - C*X(q) + + where X(pn,qn) represents the output of the registers at each stage. + The calculations are actually done in place. Register pointers are + used to speed up the calculations. + + Register and coefficient addresses involved in the calculations + are stored sequentially and are accessed as such. fft_net->indexp, + indexq contain pointers to the relevant addresses, and fft_net->coeffs, + inv_coeffs points to the appropriate coefficients at each stage of the + computation. +*/ + +{ + SAMPLE **xpr, **xpi, **xqr, **xqi, *cr, *ci; + int i; + SAMPLE tpr, tpi, tqr, tqi; + int bps = fft_net->bps; + int cnt = bps * (fft_net->stages - 1); + + /* predetermined register addresses and coefficients */ + xpr = fft_net->indexpr; + xpi = fft_net->indexpi; + xqr = fft_net->indexqr; + xqi = fft_net->indexqi; + + if (fft_net->direction==FORWARD) { /* FORWARD FFT coefficients */ + cr = fft_net->coeffr; + ci = fft_net->coeffi; + } + else { /* INVERSE FFT coefficients */ + cr = fft_net->inv_coeffr; + ci = fft_net->inv_coeffi; + } + + /* stage one coefficients are 1 + 0j so C*X(q)=X(q) */ + /* bps mults can be avoided */ + + for (i = 0; i < bps; i++) { + + /* add X(p) and X(q) */ + tpr = **xpr + **xqr; + tpi = **xpi + **xqi; + tqr = **xpr - **xqr; + tqi = **xpi - **xqi; + + /* exchange register with temp */ + **xpr = tpr; + **xpi = tpi; + **xqr = tqr; + **xqi = tqi; + + /* next set of register for calculations: */ + xpr++; xpi++; xqr++; xqi++; cr++; ci++; + + } + + for (i = 0; i < cnt; i++) { + + /* mult X(q) by coeff C */ + tqr = **xqr * *cr - **xqi * *ci; + tqi = **xqr * *ci + **xqi * *cr; + + /* exchange register with temp */ + **xqr = tqr; + **xqi = tqi; + + /* add X(p) and X(q) */ + tpr = **xpr + **xqr; + tpi = **xpi + **xqi; + tqr = **xpr - **xqr; + tqi = **xpi - **xqi; + + /* exchange register with temp */ + **xpr = tpr; + **xpi = tpi; + **xqr = tqr; + **xqi = tqi; + /* next set of register for calculations: */ + xpr++; xpi++; xqr++; xqi++; cr++; ci++; + } +} + + +/****************************************************************************/ +/* SUPPORT MODULES */ +/****************************************************************************/ + +void net_alloc(FFT_NET *fft_net) + + +/* effects: Allocates appropriate two dimensional arrays and assigns + correct internal pointers. +*/ + +{ + + int stages, bps, n; + + n = fft_net->n; + stages = fft_net->stages; + bps = fft_net->bps; + + + /* two dimensional arrays with elements stored sequentially */ + + fft_net->load_index = (int *)malloc(n * INT_SIZE); + fft_net->regr = (SAMPLE *)malloc(n * SAMPLE_SIZE); + fft_net->regi = (SAMPLE *)malloc(n * SAMPLE_SIZE); + fft_net->coeffr = (SAMPLE *)malloc(stages*bps*SAMPLE_SIZE); + fft_net->coeffi = (SAMPLE *)malloc(stages*bps*SAMPLE_SIZE); + fft_net->inv_coeffr = (SAMPLE *)malloc(stages*bps*SAMPLE_SIZE); + fft_net->inv_coeffi = (SAMPLE *)malloc(stages*bps*SAMPLE_SIZE); + fft_net->indexpr = (SAMPLE **)malloc(stages * bps * PNTR_SIZE); + fft_net->indexpi = (SAMPLE **)malloc(stages * bps * PNTR_SIZE); + fft_net->indexqr = (SAMPLE **)malloc(stages * bps * PNTR_SIZE); + fft_net->indexqi = (SAMPLE **)malloc(stages * bps * PNTR_SIZE); + + /* one dimensional load window */ + fft_net->window = (SAMPLE *)malloc(n * SAMPLE_SIZE); + fft_net->inv_window = (SAMPLE *)malloc(n * SAMPLE_SIZE); +} + +void net_dealloc(FFT_NET *fft_net) + + +/* effects: Deallocates given FFT network. +*/ + +{ + + free((char *)fft_net->load_index); + free((char *)fft_net->regr); + free((char *)fft_net->regi); + free((char *)fft_net->coeffr); + free((char *)fft_net->coeffi); + free((char *)fft_net->inv_coeffr); + free((char *)fft_net->inv_coeffi); + free((char *)fft_net->indexpr); + free((char *)fft_net->indexpi); + free((char *)fft_net->indexqr); + free((char *)fft_net->indexqi); + free((char *)fft_net->window); + free((char *)fft_net->inv_window); +} + + +BOOL power_of_two(n) + +int n; + +/* effects: Returns TRUE if n is a power of two, otherwise FALSE. +*/ + +{ + int i; + + for (i = n; i > 1; i >>= 1) + if (i & 1) return FALSE; /* more than one bit high */ + return TRUE; +} + + +void create_hanning(SAMPLE *window, int n, SAMPLE scale) + +/* effects: Fills the buffer window with a hanning window of the appropriate + size scaled by scale. +*/ + +{ + SAMPLE a, pi_div_n = PI/n; + int k; + + for (k=1; k <= n; k++) { + a = sin(k * pi_div_n); + *window++ = scale * a * a; + } +} + + +void create_rectangular(SAMPLE *window, int n, SAMPLE scale) + +/* effects: Fills the buffer window with a rectangular window of the + appropriate size of height scale. +*/ + +{ + while (n--) + *window++ = scale; +} + + +void short_to_float(short *short_buf, float *float_buf, int n) + +/* effects; Converts short_buf to floats and stores them in float_buf. +*/ + +{ + while (n--) { + *float_buf++ = (float)*short_buf++; + } +} + + +/* here's the meat: */ + +void pd_fft(float *buf, int npoints, int inverse) +{ + double renorm; + float *fp, *fp2; + int i; + renorm = (inverse ? npoints : 1.); + cfft((inverse ? INVERSE : FORWARD), npoints, RECTANGULAR, + buf, RECT, LINEAR, buf, RECT, LINEAR, 0); + for (i = npoints << 1, fp = buf; i--; fp++) *fp *= renorm; +} +/*****************************************************************************/ +/* */ +/* Fast Fourier Transform */ +/* Network Abstraction, Definitions */ +/* Kevin Peterson, MIT Media Lab, EMS */ +/* UROP - Fall '86 */ +/* REV: 6/12/87(KHP) - To incorporate link list of different sized networks */ +/* */ +/*****************************************************************************/ + +/*****************************************************************************/ +/* added debug option 5/91 brown@nadia */ +/* change sign at AAA */ +/* */ +/* Fast Fourier Transform */ +/* FFT Network Interaction and Support Modules */ +/* Kevin Peterson, MIT Media Lab, EMS */ +/* UROP - Fall '86 */ +/* REV: 6/12/87(KHP) - Generalized to one procedure call with typed I/O */ +/* */ +/*****************************************************************************/ + +/* Overview: + + My realization of the FFT involves a representation of a network of + "butterfly" elements that takes a set of 'N' sound samples as input and + computes the discrete Fourier transform. This network consists of a + series of stages (log2 N), each stage consisting of N/2 parallel butterfly + elements. Consecutive stages are connected by specific, predetermined flow + paths, (see Oppenheim, Schafer for details) and each butterfly element has + an associated multiplicative coefficient. + + FFT NETWORK: + ----------- + ____ _ ____ _ ____ _ ____ _ ____ + o--| |o-| |-o| |o-| |-o| |o-| |-o| |o-| |-o| |--o + |reg1| | | |W^r1| | | |reg1| | | |W^r1| | | |reg1| + | | | | | | | | | | | | | | | | | | ..... + | | | | | | | | | | | | | | | | | | + o--|____|o-| |-o|____|o-| |-o|____|o-| |-o|____|o-| |-o|____|--o + | | | | | | | | + | | | | | | | | + ____ | | ____ | | ____ | | ____ | | ____ + o--| |o-| |-o| |o-| |-o| |o-| |-o| |o-| |-o| |--o + |reg2| | | |W^r2| | | |reg2| | | |W^r2| | | |reg2| + | | | | | | | | | | | | | | | | | | ..... + | | | | | | | | | | | | | | | | | | + o--|____|o-| |-o|____|o-| |-o|____|o-| |-o|____|o-| |-o|____|--o + | | | | | | | | + | | | | | | | | + : : : : : : : : : + : : : : : : : : : + : : : : : : : : : + : : : : : : : : : + : : : : : : : : : + + ____ | | ____ | | ____ | | ____ | | ____ + o--| |o-| |-o| |o-| |-o| |o-| |-o| |o-| |-o| |--o + |reg | | | |W^r | | | |reg | | | |W^r | | | |reg | + | N/2| | | | N/2| | | | N/2| | | | N/2| | | | N/2| ..... + | | | | | | | | | | | | | | | | | | + o--|____|o-|_|-o|____|o-|_|-o|____|o-|_|-o|____|o-|_|-o|____|--o + + ^ ^ ^ ^ + Initial | Bttrfly | Rd/Wrt | Bttrfly | Rd/Wrt + Buffer | | Register | | Register + |____________|____________|____________| + | + | + Interconnect + Paths + + The use of "in-place" computation permits one to use only one set of + registers realized by an array of complex number structures. To describe + the coefficients for each butterfly I am using a two dimensional array + (stage, butterfly) of complex numbers. The predetermined stage connections + will be described in a two dimensional array of indicies. These indicies + will be used to determine the order of reading at each stage of the + computation. +*/ + + +/*****************************************************************************/ +/* INCLUDE FILES */ +/*****************************************************************************/ + +#include +#include +#include + + /* the following is needed only to declare pd_fft() as exportable in MSW */ +#include "m_pd.h" + +/* some basic definitions */ +#ifndef BOOL +#define BOOL int +#define TRUE 1 +#define FALSE 0 +#endif + +#define SAMPLE float /* data type used in calculation */ + +#define SHORT_SIZE sizeof(short) +#define INT_SIZE sizeof(int) +#define FLOAT_SIZE sizeof(float) +#define SAMPLE_SIZE sizeof(SAMPLE) +#define PNTR_SIZE sizeof(char *) + +#define PI 3.1415927 +#define TWO_PI 6.2831854 + +/* type definitions for I/O buffers */ +#define REAL 0 /* real only */ +#define IMAG 2 /* imaginary only */ +#define RECT 8 /* real and imaginary */ +#define MAG 16 /* magnitude only */ +#define PHASE 32 /* phase only */ +#define POLAR 64 /* magnitude and phase*/ + +/* scale definitions for I/O buffers */ +#define LINEAR 0 +#define DB 1 /* 20log10 */ + +/* transform direction definition */ +#define FORWARD 1 /* Forward FFT */ +#define INVERSE 2 /* Inverse FFT */ + +/* window type definitions */ +#define HANNING 1 +#define RECTANGULAR 0 + + + +/* network structure definition */ + +typedef struct Tfft_net { + int n; + int stages; + int bps; + int direction; + int window_type; + int *load_index; + SAMPLE *window, *inv_window; + SAMPLE *regr; + SAMPLE *regi; + SAMPLE **indexpr; + SAMPLE **indexpi; + SAMPLE **indexqr; + SAMPLE **indexqi; + SAMPLE *coeffr, *inv_coeffr; + SAMPLE *coeffi, *inv_coeffi; + struct Tfft_net *next; +} FFT_NET; + + +void cfft(int trnsfrm_dir, int npnt, int window, + float *source_buf, int source_form, int source_scale, + float *result_buf, int result_form, int result_scale, int debug); + + +/*****************************************************************************/ +/* GLOBAL DECLARATIONS */ +/*****************************************************************************/ + +static FFT_NET *firstnet; + +/* prototypes */ + +void net_alloc(FFT_NET *fft_net); +void net_dealloc(FFT_NET *fft_net); +int power_of_two(int n); +void create_hanning(SAMPLE *window, int n, SAMPLE scale); +void create_rectangular(SAMPLE *window, int n, SAMPLE scale); +void short_to_float(short *short_buf, float *float_buf, int n); +void load_registers(FFT_NET *fft_net, float *buf, int buf_form, + int buf_scale, int trnsfrm_dir); +void compute_fft(FFT_NET *fft_net); +void store_registers(FFT_NET *fft_net, float *buf, int buf_form, + int buf_scale, int debug); +void build_fft_network(FFT_NET *fft_net, int n, int window_type); + +/*****************************************************************************/ +/* GENERALIZED FAST FOURIER TRANSFORM MODULE */ +/*****************************************************************************/ + +void cfft(int trnsfrm_dir, int npnt, int window, + float *source_buf, int source_form, int source_scale, + float *result_buf, int result_form, int result_scale, int debug) + +/* modifies: result_buf + effects: Computes npnt FFT specified by form, scale, and dir parameters. + Source samples (single precision float) are taken from soure_buf and + the transfrmd representation is stored in result_buf (single precision + float). The parameters are defined as follows: + + trnsfrm_dir = FORWARD | INVERSE + npnt = 2^k for some any positive integer k + window = HANNING | RECTANGULAR + (RECT = real and imag parts, POLAR = magnitude and phase) + source_form = REAL | IMAG | RECT | POLAR + result_form = REAL | IMAG | RECT | MAG | PHASE | POLAR + xxxxxx_scale= LINEAR | DB ( 20log10 |mag| ) + + The input/output buffers are stored in a form appropriate to the type. + For example: REAL => {real, real, real ...}, + MAG => {mag, mag, mag, ... }, + RECT => {real, imag, real, imag, ... }, + POLAR => {mag, phase, mag, phase, ... }. + + To look at the magnitude (in db) of a 1024 point FFT of a real time + signal we have: + + fft(FORWARD, 1024, RECTANGULAR, input, REAL, LINEAR, output, MAG, DB) + + All possible input and output combinations are possible given the + choice of type and scale parameters. +*/ + +{ + FFT_NET *thisnet = (FFT_NET *)0; + FFT_NET *lastnet = (FFT_NET *)0; + + /* A linked list of fft networks of different sizes is maintained to + avoid building with every call. The network is built on the first + call but reused for subsequent calls requesting the same size + transformation. + */ + + thisnet=firstnet; + while (thisnet) { + if (!(thisnet->n == npnt) || !(thisnet->window_type == window)) { + /* current net doesn't match size or window type */ + lastnet=thisnet; + thisnet=thisnet->next; + continue; /* keep looking */ + } + + else { /* network matches desired size */ + load_registers(thisnet, source_buf, source_form, source_scale, + trnsfrm_dir); + compute_fft(thisnet); /* do transformation */ + store_registers(thisnet, result_buf, result_form, result_scale,debug); + return; + } + } + + /* none of existing networks match required size*/ + + if (lastnet) { /* add new network to end of list */ + thisnet = (FFT_NET *)malloc(sizeof(FFT_NET)); /* allocate */ + thisnet->next = 0; + lastnet->next = thisnet; /* add to end of list */ + } + else { /* first network to be created */ + thisnet=firstnet=(FFT_NET *)malloc(sizeof(FFT_NET)); /* alloc. */ + thisnet->next = 0; + } + + /* build new network and compute transformation */ + build_fft_network(thisnet, npnt, window); + load_registers(thisnet, source_buf, source_form, source_scale, + trnsfrm_dir); + compute_fft(thisnet); + store_registers(thisnet, result_buf, result_form, result_scale,debug); + return; +} + +void fft_clear(void) + +/* effects: Deallocates all preserved FFT networks. Should be used when + finished with all computations. +*/ + +{ + FFT_NET *thisnet, *nextnet; + + if (firstnet) { + thisnet=firstnet; + do { + nextnet = thisnet->next; + net_dealloc(thisnet); + free((char *)thisnet); + } while (thisnet = nextnet); + } +} + + +/*****************************************************************************/ +/* NETWORK CONSTRUCTION */ +/*****************************************************************************/ + +void build_fft_network(FFT_NET *fft_net, int n, int window_type) + + +/* modifies:fft_net + effects: Constructs the fft network as described in fft.h. Butterfly + coefficients, read/write indicies, bit reversed load indicies, + and array allocations are computed. +*/ + +{ + int cntr, i, j, s; + int stages, bps; + int **p, **q, *pp, *qp; + SAMPLE two_pi_div_n = TWO_PI / n; + + + /* network definition */ + fft_net->n = n; + fft_net->bps = bps = n/2; + for (i = 0, j = n; j > 1; j >>= 1, i++); + fft_net->stages = stages = i; + fft_net->direction = FORWARD; + fft_net->window_type = window_type; + fft_net->next = (FFT_NET *)0; + + /* allocate registers, index, coefficient arrays */ + net_alloc(fft_net); + + + /* create appropriate windows */ + if (window_type==HANNING) { + create_hanning(fft_net->window, n, 1.); + create_hanning(fft_net->inv_window, n, 1./n); + } + else { + create_rectangular(fft_net->window, n, 1.); + create_rectangular(fft_net->inv_window, n, 1./n); + } + + + /* calculate butterfly coefficients */ { + + int num_diff_coeffs, power_inc, power; + SAMPLE *coeffpr = fft_net->coeffr; + SAMPLE *coeffpi = fft_net->coeffi; + SAMPLE *inv_coeffpr = fft_net->inv_coeffr; + SAMPLE *inv_coeffpi = fft_net->inv_coeffi; + + /* stage one coeffs are 1 + 0j */ + for (i = 0; i < bps; i++) { + *coeffpr = *inv_coeffpr = 1.; + *coeffpi = *inv_coeffpi = 0.; + coeffpr++; inv_coeffpr++; + coeffpi++; inv_coeffpi++; + } + + /* stage 2 to last stage coeffs need calculation */ + /* (1< 2^r */ + for (s = 2; s <= stages; s++) { + + num_diff_coeffs = n / (1 << (stages - s + 1)); + power_inc = 1 << (stages -s); + cntr = 0; + + for (i = bps/num_diff_coeffs; i > 0; i--) { + + power = 0; + + for (j = num_diff_coeffs; j > 0; j--) { + *coeffpr = cos(two_pi_div_n*power); + *inv_coeffpr = cos(two_pi_div_n*power); +/* AAA change these signs */ *coeffpi = -sin(two_pi_div_n*power); +/* change back */ *inv_coeffpi = sin(two_pi_div_n*power); + power += power_inc; + coeffpr++; inv_coeffpr++; + coeffpi++; inv_coeffpi++; + } + } + } + } + + /* calculate network indicies: stage exchange indicies are + calculated and then used as offset values from the base + register locations. The final addresses are then stored in + fft_net. + */ { + + int index, inc; + SAMPLE **indexpr = fft_net->indexpr; + SAMPLE **indexpi = fft_net->indexpi; + SAMPLE **indexqr = fft_net->indexqr; + SAMPLE **indexqi = fft_net->indexqi; + SAMPLE *regr = fft_net->regr; + SAMPLE *regi = fft_net->regi; + + + /* allocate temporary 2d stage exchange index, 1d temp + load index */ + p = (int **)malloc(stages * PNTR_SIZE); + q = (int **)malloc(stages * PNTR_SIZE); + + for (s = 0; s < stages; s++) { + p[s] = (int *)malloc(bps * INT_SIZE); + q[s] = (int *)malloc(bps * INT_SIZE); + } + + /* calculate stage exchange indicies: */ + for (s = 0; s < stages; s++) { + pp = p[s]; + qp = q[s]; + inc = 1 << s; + cntr = 1 << (stages-s-1); + i = j = index = 0; + + do { + do { + qp[i] = index + inc; + pp[i++] = index++; + } while (++j < inc); + index = qp[i-1] + 1; + j = 0; + } while (--cntr); + } + + /* compute actual address values using indicies as offsets */ + for (s = 0; s < stages; s++) { + for (i = 0; i < bps; i++) { + *indexpr++ = regr + p[s][i]; + *indexpi++ = regi + p[s][i]; + *indexqr++ = regr + q[s][i]; + *indexqi++ = regi + q[s][i]; + } + } + } + + + /* calculate load indicies (bit reverse ordering) */ + /* bit reverse ordering achieved by passing normal + order indicies backwards through the network */ + + /* init to normal order indicies */ { + int *load_index,*load_indexp; + int *temp_indexp, *temp_index; + temp_index=temp_indexp=(int *)malloc(n * INT_SIZE); + + i = 0; j = n; + load_index = load_indexp = fft_net->load_index; + + while (j--) + *load_indexp++ = i++; + + /* pass indicies backwards through net */ + for (s = stages - 1; s > 0; s--) { + pp = p[s]; + qp = q[s]; + + for (i = 0; i < bps; i++) { + temp_index[pp[i]]=load_index[2*i]; + temp_index[qp[i]]=load_index[2*i+1]; + } + j = n; + load_indexp = load_index; + temp_indexp = temp_index; + while (j--) + *load_indexp++ = *temp_indexp++; + } + + /* free all temporary arrays */ + free((char *)temp_index); + for (s = 0; s < stages; s++) { + free((char *)p[s]);free((char *)q[s]); + } + free((char *)p);free((char *)q); + } +} + + + +/*****************************************************************************/ +/* REGISTER LOAD AND STORE */ +/*****************************************************************************/ + +void load_registers(FFT_NET *fft_net, float *buf, int buf_form, + int buf_scale, int trnsfrm_dir) + +/* effects: Multiplies the input buffer with the appropriate window and + stores the resulting values in the initial registers of the + network. Input buffer must contain values appropriate to form. + For RECT, the buffer contains real num. followed by imag num, + and for POLAR, it contains magnitude followed by phase. Pure + inputs are listed normally. Both LINEAR and DB scales are + interpreted. +*/ + +{ + int *load_index = fft_net->load_index; + SAMPLE *window; + int index, i = 0, n = fft_net->n; + + if (trnsfrm_dir==FORWARD) window = fft_net->window; + else if (trnsfrm_dir==INVERSE) window = fft_net->inv_window; + else { + fprintf(stderr, "load_registers:illegal transform direction\n"); + exit(0); + } + fft_net->direction = trnsfrm_dir; + + switch(buf_scale) { + case LINEAR: { + + switch (buf_form) { + case REAL: { /* pure REAL */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=(SAMPLE)buf[index] * window[index]; + fft_net->regi[i]=0.; + i++; + } + } break; + + case IMAG: { /* pure IMAGinary */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=0; + fft_net->regi[i]=(SAMPLE)buf[index] * window[index]; + i++; + } + } break; + + case RECT: { /* both REAL and IMAGinary */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=(SAMPLE)buf[index*2] * window[index]; + fft_net->regi[i]=(SAMPLE)buf[index*2+1] * window[index]; + i++; + } + } break; + + case POLAR: { /* magnitude followed by phase */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=(SAMPLE)(buf[index*2] * cos(buf[index*2+1])) + * window[index]; + fft_net->regi[i]=(SAMPLE)(buf[index*2] * sin(buf[index*2+1])) + * window[index]; + i++; + } + } break; + + default: { + fprintf(stderr, "load_registers:illegal input form\n"); + exit(0); + } break; + } + } break; + + case DB: { + + switch (buf_form) { + case REAL: { /* log pure REAL */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=(SAMPLE)pow(10., (1./20.)*buf[index]) + * window[index]; /* window scaling after linearization */ + fft_net->regi[i]=0.; + i++; + } + } break; + + case IMAG: { /* log pure IMAGinary */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=0.; + fft_net->regi[i]=(SAMPLE)pow(10., (1./20.)*buf[index]) + * window[index]; + i++; + } + } break; + + case RECT: { /* log REAL and log IMAGinary */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=(SAMPLE)pow(10., (1./20.)*buf[index*2]) + * window[index]; + fft_net->regi[i]=(SAMPLE)pow(10., (1./20.)*buf[index*2+1]) + * window[index]; + i++; + } + } break; + + case POLAR: { /* log mag followed by phase */ + while (i < fft_net->n) { + index = load_index[i]; + fft_net->regr[i]=(SAMPLE)(pow(10., (1./20.)*buf[index*2]) + * cos(buf[index*2+1])) * window[index]; + fft_net->regi[i]=(SAMPLE)(pow(10., (1./20.)*buf[index*2]) + * sin(buf[index*2+1])) * window[index]; + i++; + } + } break; + + default: { + fprintf(stderr, "load_registers:illegal input form\n"); + exit(0); + } break; + } + } break; + + default: { + fprintf(stderr, "load_registers:illegal input scale\n"); + exit(0); + } break; + } +} + + +void store_registers(FFT_NET *fft_net, float *buf, int buf_form, + int buf_scale, int debug) + +/* modifies: buf + effects: Writes the final contents of the network registers into buf in + either linear or db scale, polar or rectangular form. If any of + the pure forms(REAL, IMAG, MAG, or PHASE) are used then only the + corresponding part of the registers is stored in buf. +*/ + +{ + int i; + SAMPLE real, imag, mag, phase; + int n; + + i = 0; + n = fft_net->n; + + switch (buf_scale) { + case LINEAR: { + + switch (buf_form) { + case REAL: { /* pure REAL */ + do { + *buf++ = (float)fft_net->regr[i]; + } while (++i < n); + } break; + + case IMAG: { /* pure IMAGinary */ + do { + *buf++ = (float)fft_net->regi[i]; + } while (++i < n); + } break; + + case RECT: { /* both REAL and IMAGinary */ + do { + *buf++ = (float)fft_net->regr[i]; + *buf++ = (float)fft_net->regi[i]; + } while (++i < n); + } break; + + case MAG: { /* magnitude only */ + do { + real = fft_net->regr[i]; + imag = fft_net->regi[i]; + *buf++ = (float)sqrt(real*real+imag*imag); + } while (++i < n); + } break; + + case PHASE: { /* phase only */ + do { + real = fft_net->regr[i]; + imag = fft_net->regi[i]; + if (real > .00001) + *buf++ = (float)atan2(imag, real); + else { /* deal with bad case */ + if (imag > 0){ *buf++ = PI / 2.; + if(debug) fprintf(stderr,"real=0 and imag > 0\n");} + else if (imag < 0){ *buf++ = -PI / 2.; + if(debug) fprintf(stderr,"real=0 and imag < 0\n");} + else { *buf++ = 0; + if(debug) fprintf(stderr,"real=0 and imag=0\n");} + } + } while (++i < n); + } break; + + case POLAR: { /* magnitude and phase */ + do { + real = fft_net->regr[i]; + imag = fft_net->regi[i]; + *buf++ = (float)sqrt(real*real+imag*imag); + if (real) /* a hack to avoid div by zero */ + *buf++ = (float)atan2(imag, real); + else { /* deal with bad case */ + if (imag > 0) *buf++ = PI / 2.; + else if (imag < 0) *buf++ = -PI / 2.; + else *buf++ = 0; + } + } while (++i < n); + } break; + + default: { + fprintf(stderr, "store_registers:illegal output form\n"); + exit(0); + } break; + } + } break; + + case DB: { + + switch (buf_form) { + case REAL: { /* real only */ + do { + *buf++ = (float)20.*log10(fft_net->regr[i]); + } while (++i < n); + } break; + + case IMAG: { /* imag only */ + do { + *buf++ = (float)20.*log10(fft_net->regi[i]); + } while (++i < n); + } break; + + case RECT: { /* real and imag */ + do { + *buf++ = (float)20.*log10(fft_net->regr[i]); + *buf++ = (float)20.*log10(fft_net->regi[i]); + } while (++i < n); + } break; + + case MAG: { /* magnitude only */ + do { + real = fft_net->regr[i]; + imag = fft_net->regi[i]; + *buf++ = (float)20.*log10(sqrt(real*real+imag*imag)); + } while (++i < n); + } break; + + case PHASE: { /* phase only */ + do { + real = fft_net->regr[i]; + imag = fft_net->regi[i]; + if (real) + *buf++ = (float)atan2(imag, real); + else { /* deal with bad case */ + if (imag > 0) *buf++ = PI / 2.; + else if (imag < 0) *buf++ = -PI / 2.; + else *buf++ = 0; + } + } while (++i < n); + } break; + + case POLAR: { /* magnitude and phase */ + do { + real = fft_net->regr[i]; + imag = fft_net->regi[i]; + *buf++ = (float)20.*log10(sqrt(real*real+imag*imag)); + if (real) + *buf++ = (float)atan2(imag, real); + else { /* deal with bad case */ + if (imag > 0) *buf++ = PI / 2.; + else if (imag < 0) *buf++ = -PI / 2.; + else *buf++ = 0; + } + } while (++i < n); + } break; + + default: { + fprintf(stderr, "store_registers:illegal output form\n"); + exit(0); + } break; + } + } break; + + default: { + fprintf(stderr, "store_registers:illegal output scale\n"); + exit(0); + } break; + } +} + + + +/*****************************************************************************/ +/* COMPUTE TRANSFORMATION */ +/*****************************************************************************/ + +void compute_fft(FFT_NET *fft_net) + + +/* modifies: fft_net + effects: Passes the values (already loaded) in the registers through + the network, multiplying with appropriate coefficients at each + stage. The fft result will be in the registers at the end of + the computation. The direction of the transformation is indicated + by the network flag 'direction'. The form of the computation is: + + X(pn) = X(p) + C*X(q) + X(qn) = X(p) - C*X(q) + + where X(pn,qn) represents the output of the registers at each stage. + The calculations are actually done in place. Register pointers are + used to speed up the calculations. + + Register and coefficient addresses involved in the calculations + are stored sequentially and are accessed as such. fft_net->indexp, + indexq contain pointers to the relevant addresses, and fft_net->coeffs, + inv_coeffs points to the appropriate coefficients at each stage of the + computation. +*/ + +{ + SAMPLE **xpr, **xpi, **xqr, **xqi, *cr, *ci; + int i; + SAMPLE tpr, tpi, tqr, tqi; + int bps = fft_net->bps; + int cnt = bps * (fft_net->stages - 1); + + /* predetermined register addresses and coefficients */ + xpr = fft_net->indexpr; + xpi = fft_net->indexpi; + xqr = fft_net->indexqr; + xqi = fft_net->indexqi; + + if (fft_net->direction==FORWARD) { /* FORWARD FFT coefficients */ + cr = fft_net->coeffr; + ci = fft_net->coeffi; + } + else { /* INVERSE FFT coefficients */ + cr = fft_net->inv_coeffr; + ci = fft_net->inv_coeffi; + } + + /* stage one coefficients are 1 + 0j so C*X(q)=X(q) */ + /* bps mults can be avoided */ + + for (i = 0; i < bps; i++) { + + /* add X(p) and X(q) */ + tpr = **xpr + **xqr; + tpi = **xpi + **xqi; + tqr = **xpr - **xqr; + tqi = **xpi - **xqi; + + /* exchange register with temp */ + **xpr = tpr; + **xpi = tpi; + **xqr = tqr; + **xqi = tqi; + + /* next set of register for calculations: */ + xpr++; xpi++; xqr++; xqi++; cr++; ci++; + + } + + for (i = 0; i < cnt; i++) { + + /* mult X(q) by coeff C */ + tqr = **xqr * *cr - **xqi * *ci; + tqi = **xqr * *ci + **xqi * *cr; + + /* exchange register with temp */ + **xqr = tqr; + **xqi = tqi; + + /* add X(p) and X(q) */ + tpr = **xpr + **xqr; + tpi = **xpi + **xqi; + tqr = **xpr - **xqr; + tqi = **xpi - **xqi; + + /* exchange register with temp */ + **xpr = tpr; + **xpi = tpi; + **xqr = tqr; + **xqi = tqi; + /* next set of register for calculations: */ + xpr++; xpi++; xqr++; xqi++; cr++; ci++; + } +} + + +/****************************************************************************/ +/* SUPPORT MODULES */ +/****************************************************************************/ + +void net_alloc(FFT_NET *fft_net) + + +/* effects: Allocates appropriate two dimensional arrays and assigns + correct internal pointers. +*/ + +{ + + int stages, bps, n; + + n = fft_net->n; + stages = fft_net->stages; + bps = fft_net->bps; + + + /* two dimensional arrays with elements stored sequentially */ + + fft_net->load_index = (int *)malloc(n * INT_SIZE); + fft_net->regr = (SAMPLE *)malloc(n * SAMPLE_SIZE); + fft_net->regi = (SAMPLE *)malloc(n * SAMPLE_SIZE); + fft_net->coeffr = (SAMPLE *)malloc(stages*bps*SAMPLE_SIZE); + fft_net->coeffi = (SAMPLE *)malloc(stages*bps*SAMPLE_SIZE); + fft_net->inv_coeffr = (SAMPLE *)malloc(stages*bps*SAMPLE_SIZE); + fft_net->inv_coeffi = (SAMPLE *)malloc(stages*bps*SAMPLE_SIZE); + fft_net->indexpr = (SAMPLE **)malloc(stages * bps * PNTR_SIZE); + fft_net->indexpi = (SAMPLE **)malloc(stages * bps * PNTR_SIZE); + fft_net->indexqr = (SAMPLE **)malloc(stages * bps * PNTR_SIZE); + fft_net->indexqi = (SAMPLE **)malloc(stages * bps * PNTR_SIZE); + + /* one dimensional load window */ + fft_net->window = (SAMPLE *)malloc(n * SAMPLE_SIZE); + fft_net->inv_window = (SAMPLE *)malloc(n * SAMPLE_SIZE); +} + +void net_dealloc(FFT_NET *fft_net) + + +/* effects: Deallocates given FFT network. +*/ + +{ + + free((char *)fft_net->load_index); + free((char *)fft_net->regr); + free((char *)fft_net->regi); + free((char *)fft_net->coeffr); + free((char *)fft_net->coeffi); + free((char *)fft_net->inv_coeffr); + free((char *)fft_net->inv_coeffi); + free((char *)fft_net->indexpr); + free((char *)fft_net->indexpi); + free((char *)fft_net->indexqr); + free((char *)fft_net->indexqi); + free((char *)fft_net->window); + free((char *)fft_net->inv_window); +} + + +BOOL power_of_two(n) + +int n; + +/* effects: Returns TRUE if n is a power of two, otherwise FALSE. +*/ + +{ + int i; + + for (i = n; i > 1; i >>= 1) + if (i & 1) return FALSE; /* more than one bit high */ + return TRUE; +} + + +void create_hanning(SAMPLE *window, int n, SAMPLE scale) + +/* effects: Fills the buffer window with a hanning window of the appropriate + size scaled by scale. +*/ + +{ + SAMPLE a, pi_div_n = PI/n; + int k; + + for (k=1; k <= n; k++) { + a = sin(k * pi_div_n); + *window++ = scale * a * a; + } +} + + +void create_rectangular(SAMPLE *window, int n, SAMPLE scale) + +/* effects: Fills the buffer window with a rectangular window of the + appropriate size of height scale. +*/ + +{ + while (n--) + *window++ = scale; +} + + +void short_to_float(short *short_buf, float *float_buf, int n) + +/* effects; Converts short_buf to floats and stores them in float_buf. +*/ + +{ + while (n--) { + *float_buf++ = (float)*short_buf++; + } +} + + +/* here's the meat: */ + +void pd_fft(float *buf, int npoints, int inverse) +{ + double renorm; + float *fp, *fp2; + int i; + renorm = (inverse ? npoints : 1.); + cfft((inverse ? INVERSE : FORWARD), npoints, RECTANGULAR, + buf, RECT, LINEAR, buf, RECT, LINEAR, 0); + for (i = npoints << 1, fp = buf; i--; fp++) *fp *= renorm; +} diff --git a/apps/plugins/pdbox/PDa/src/d_filter.c b/apps/plugins/pdbox/PDa/src/d_filter.c new file mode 100644 index 0000000..05bb7cd --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_filter.c @@ -0,0 +1,1094 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* "filters", both linear and nonlinear. +*/ +#include "m_pd.h" +#include + +/* ---------------- hip~ - 1-pole 1-zero hipass filter. ----------------- */ + +typedef struct hipctl +{ + float c_x; + float c_coef; +} t_hipctl; + +typedef struct sighip +{ + t_object x_obj; + float x_sr; + float x_hz; + t_hipctl x_cspace; + t_hipctl *x_ctl; + float x_f; +} t_sighip; + +t_class *sighip_class; +static void sighip_ft1(t_sighip *x, t_floatarg f); + +static void *sighip_new(t_floatarg f) +{ + t_sighip *x = (t_sighip *)pd_new(sighip_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_sr = 44100; + x->x_ctl = &x->x_cspace; + x->x_cspace.c_x = 0; + sighip_ft1(x, f); + x->x_f = 0; + return (x); +} + +static void sighip_ft1(t_sighip *x, t_floatarg f) +{ + if (f < 0) f = 0; + x->x_hz = f; + x->x_ctl->c_coef = 1 - f * (2 * 3.14159) / x->x_sr; + if (x->x_ctl->c_coef < 0) + x->x_ctl->c_coef = 0; + else if (x->x_ctl->c_coef > 1) + x->x_ctl->c_coef = 1; +} + +static t_int *sighip_perform(t_int *w) +{ + float *in = (float *)(w[1]); + float *out = (float *)(w[2]); + t_hipctl *c = (t_hipctl *)(w[3]); + int n = (t_int)(w[4]); + int i; + float last = c->c_x; + float coef = c->c_coef; + if (coef < 1) + { + for (i = 0; i < n; i++) + { + float new = *in++ + coef * last; + *out++ = new - last; + last = new; + } + if (PD_BIGORSMALL(last)) + last = 0; + c->c_x = last; + } + else + { + for (i = 0; i < n; i++) + *out++ = *in++; + c->c_x = 0; + } + return (w+5); +} + +static void sighip_dsp(t_sighip *x, t_signal **sp) +{ + x->x_sr = sp[0]->s_sr; + sighip_ft1(x, x->x_hz); + dsp_add(sighip_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, + x->x_ctl, sp[0]->s_n); + +} + +static void sighip_clear(t_sighip *x, t_floatarg q) +{ + x->x_cspace.c_x = 0; +} + +void sighip_setup(void) +{ + sighip_class = class_new(gensym("hip~"), (t_newmethod)sighip_new, 0, + sizeof(t_sighip), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(sighip_class, t_sighip, x_f); + class_addmethod(sighip_class, (t_method)sighip_dsp, gensym("dsp"), 0); + class_addmethod(sighip_class, (t_method)sighip_ft1, + gensym("ft1"), A_FLOAT, 0); + class_addmethod(sighip_class, (t_method)sighip_clear, gensym("clear"), 0); +} + +/* ---------------- lop~ - 1-pole lopass filter. ----------------- */ + +typedef struct lopctl +{ + float c_x; + float c_coef; +} t_lopctl; + +typedef struct siglop +{ + t_object x_obj; + float x_sr; + float x_hz; + t_lopctl x_cspace; + t_lopctl *x_ctl; + float x_f; +} t_siglop; + +t_class *siglop_class; + +static void siglop_ft1(t_siglop *x, t_floatarg f); + +static void *siglop_new(t_floatarg f) +{ + t_siglop *x = (t_siglop *)pd_new(siglop_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_sr = 44100; + x->x_ctl = &x->x_cspace; + x->x_cspace.c_x = 0; + siglop_ft1(x, f); + x->x_f = 0; + return (x); +} + +static void siglop_ft1(t_siglop *x, t_floatarg f) +{ + if (f < 0) f = 0; + x->x_hz = f; + x->x_ctl->c_coef = f * (2 * 3.14159) / x->x_sr; + if (x->x_ctl->c_coef > 1) + x->x_ctl->c_coef = 1; + else if (x->x_ctl->c_coef < 0) + x->x_ctl->c_coef = 0; +} + +static void siglop_clear(t_siglop *x, t_floatarg q) +{ + x->x_cspace.c_x = 0; +} + +static t_int *siglop_perform(t_int *w) +{ + float *in = (float *)(w[1]); + float *out = (float *)(w[2]); + t_lopctl *c = (t_lopctl *)(w[3]); + int n = (t_int)(w[4]); + int i; + float last = c->c_x; + float coef = c->c_coef; + float feedback = 1 - coef; + for (i = 0; i < n; i++) + last = *out++ = coef * *in++ + feedback * last; + if (PD_BIGORSMALL(last)) + last = 0; + c->c_x = last; + return (w+5); +} + +static void siglop_dsp(t_siglop *x, t_signal **sp) +{ + x->x_sr = sp[0]->s_sr; + siglop_ft1(x, x->x_hz); + dsp_add(siglop_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, + x->x_ctl, sp[0]->s_n); + +} + +void siglop_setup(void) +{ + siglop_class = class_new(gensym("lop~"), (t_newmethod)siglop_new, 0, + sizeof(t_siglop), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(siglop_class, t_siglop, x_f); + class_addmethod(siglop_class, (t_method)siglop_dsp, gensym("dsp"), 0); + class_addmethod(siglop_class, (t_method)siglop_ft1, + gensym("ft1"), A_FLOAT, 0); + class_addmethod(siglop_class, (t_method)siglop_clear, gensym("clear"), 0); +} + +/* ---------------- bp~ - 2-pole bandpass filter. ----------------- */ + +typedef struct bpctl +{ + float c_x1; + float c_x2; + float c_coef1; + float c_coef2; + float c_gain; +} t_bpctl; + +typedef struct sigbp +{ + t_object x_obj; + float x_sr; + float x_freq; + float x_q; + t_bpctl x_cspace; + t_bpctl *x_ctl; + float x_f; +} t_sigbp; + +t_class *sigbp_class; + +static void sigbp_docoef(t_sigbp *x, t_floatarg f, t_floatarg q); + +static void *sigbp_new(t_floatarg f, t_floatarg q) +{ + t_sigbp *x = (t_sigbp *)pd_new(sigbp_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft2")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_sr = 44100; + x->x_ctl = &x->x_cspace; + x->x_cspace.c_x1 = 0; + x->x_cspace.c_x2 = 0; + sigbp_docoef(x, f, q); + x->x_f = 0; + return (x); +} + +static float sigbp_qcos(float f) +{ + if (f >= -(0.5f*3.14159f) && f <= 0.5f*3.14159f) + { + float g = f*f; + return (((g*g*g * (-1.0f/720.0f) + g*g*(1.0f/24.0f)) - g*0.5) + 1); + } + else return (0); +} + +static void sigbp_docoef(t_sigbp *x, t_floatarg f, t_floatarg q) +{ + float r, oneminusr, omega; + if (f < 0.001) f = 10; + if (q < 0) q = 0; + x->x_freq = f; + x->x_q = q; + omega = f * (2.0f * 3.14159f) / x->x_sr; + if (q < 0.001) oneminusr = 1.0f; + else oneminusr = omega/q; + if (oneminusr > 1.0f) oneminusr = 1.0f; + r = 1.0f - oneminusr; + x->x_ctl->c_coef1 = 2.0f * sigbp_qcos(omega) * r; + x->x_ctl->c_coef2 = - r * r; + x->x_ctl->c_gain = 2 * oneminusr * (oneminusr + r * omega); + /* post("r %f, omega %f, coef1 %f, coef2 %f", + r, omega, x->x_ctl->c_coef1, x->x_ctl->c_coef2); */ +} + +static void sigbp_ft1(t_sigbp *x, t_floatarg f) +{ + sigbp_docoef(x, f, x->x_q); +} + +static void sigbp_ft2(t_sigbp *x, t_floatarg q) +{ + sigbp_docoef(x, x->x_freq, q); +} + +static void sigbp_clear(t_sigbp *x, t_floatarg q) +{ + x->x_ctl->c_x1 = x->x_ctl->c_x2 = 0; +} + +static t_int *sigbp_perform(t_int *w) +{ + float *in = (float *)(w[1]); + float *out = (float *)(w[2]); + t_bpctl *c = (t_bpctl *)(w[3]); + int n = (t_int)(w[4]); + int i; + float last = c->c_x1; + float prev = c->c_x2; + float coef1 = c->c_coef1; + float coef2 = c->c_coef2; + float gain = c->c_gain; + for (i = 0; i < n; i++) + { + float output = *in++ + coef1 * last + coef2 * prev; + *out++ = gain * output; + prev = last; + last = output; + } + if (PD_BIGORSMALL(last)) + last = 0; + if (PD_BIGORSMALL(prev)) + prev = 0; + c->c_x1 = last; + c->c_x2 = prev; + return (w+5); +} + +static void sigbp_dsp(t_sigbp *x, t_signal **sp) +{ + x->x_sr = sp[0]->s_sr; + sigbp_docoef(x, x->x_freq, x->x_q); + dsp_add(sigbp_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, + x->x_ctl, sp[0]->s_n); + +} + +void sigbp_setup(void) +{ + sigbp_class = class_new(gensym("bp~"), (t_newmethod)sigbp_new, 0, + sizeof(t_sigbp), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(sigbp_class, t_sigbp, x_f); + class_addmethod(sigbp_class, (t_method)sigbp_dsp, gensym("dsp"), 0); + class_addmethod(sigbp_class, (t_method)sigbp_ft1, + gensym("ft1"), A_FLOAT, 0); + class_addmethod(sigbp_class, (t_method)sigbp_ft2, + gensym("ft2"), A_FLOAT, 0); + class_addmethod(sigbp_class, (t_method)sigbp_clear, gensym("clear"), 0); +} + +/* ---------------- biquad~ - raw biquad filter ----------------- */ + +typedef struct biquadctl +{ + float c_x1; + float c_x2; + float c_fb1; + float c_fb2; + float c_ff1; + float c_ff2; + float c_ff3; +} t_biquadctl; + +typedef struct sigbiquad +{ + t_object x_obj; + float x_f; + t_biquadctl x_cspace; + t_biquadctl *x_ctl; +} t_sigbiquad; + +t_class *sigbiquad_class; + +static void sigbiquad_list(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv); + +static void *sigbiquad_new(t_symbol *s, int argc, t_atom *argv) +{ + t_sigbiquad *x = (t_sigbiquad *)pd_new(sigbiquad_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_ctl = &x->x_cspace; + x->x_cspace.c_x1 = x->x_cspace.c_x2 = 0; + sigbiquad_list(x, s, argc, argv); + x->x_f = 0; + return (x); +} + +static t_int *sigbiquad_perform(t_int *w) +{ + float *in = (float *)(w[1]); + float *out = (float *)(w[2]); + t_biquadctl *c = (t_biquadctl *)(w[3]); + int n = (t_int)(w[4]); + int i; + float last = c->c_x1; + float prev = c->c_x2; + float fb1 = c->c_fb1; + float fb2 = c->c_fb2; + float ff1 = c->c_ff1; + float ff2 = c->c_ff2; + float ff3 = c->c_ff3; + for (i = 0; i < n; i++) + { + float output = *in++ + fb1 * last + fb2 * prev; + if (PD_BIGORSMALL(output)) + output = 0; + *out++ = ff1 * output + ff2 * last + ff3 * prev; + prev = last; + last = output; + } + c->c_x1 = last; + c->c_x2 = prev; + return (w+5); +} + +static void sigbiquad_list(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv) +{ + float fb1 = atom_getfloatarg(0, argc, argv); + float fb2 = atom_getfloatarg(1, argc, argv); + float ff1 = atom_getfloatarg(2, argc, argv); + float ff2 = atom_getfloatarg(3, argc, argv); + float ff3 = atom_getfloatarg(4, argc, argv); + float discriminant = fb1 * fb1 + 4 * fb2; + t_biquadctl *c = x->x_ctl; + if (discriminant < 0) /* imaginary roots -- resonant filter */ + { + /* they're conjugates so we just check that the product + is less than one */ + if (fb2 >= -1.0f) goto stable; + } + else /* real roots */ + { + /* check that the parabola 1 - fb1 x - fb2 x^2 has a + vertex between -1 and 1, and that it's nonnegative + at both ends, which implies both roots are in [1-,1]. */ + if (fb1 <= 2.0f && fb1 >= -2.0f && + 1.0f - fb1 -fb2 >= 0 && 1.0f + fb1 - fb2 >= 0) + goto stable; + } + /* if unstable, just bash to zero */ + fb1 = fb2 = ff1 = ff2 = ff3 = 0; +stable: + c->c_fb1 = fb1; + c->c_fb2 = fb2; + c->c_ff1 = ff1; + c->c_ff2 = ff2; + c->c_ff3 = ff3; +} + +static void sigbiquad_set(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv) +{ + t_biquadctl *c = x->x_ctl; + c->c_x1 = atom_getfloatarg(0, argc, argv); + c->c_x2 = atom_getfloatarg(1, argc, argv); +} + +static void sigbiquad_dsp(t_sigbiquad *x, t_signal **sp) +{ + dsp_add(sigbiquad_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, + x->x_ctl, sp[0]->s_n); + +} + +void sigbiquad_setup(void) +{ + sigbiquad_class = class_new(gensym("biquad~"), (t_newmethod)sigbiquad_new, + 0, sizeof(t_sigbiquad), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(sigbiquad_class, t_sigbiquad, x_f); + class_addmethod(sigbiquad_class, (t_method)sigbiquad_dsp, gensym("dsp"), 0); + class_addlist(sigbiquad_class, sigbiquad_list); + class_addmethod(sigbiquad_class, (t_method)sigbiquad_set, gensym("set"), + A_GIMME, 0); + class_addmethod(sigbiquad_class, (t_method)sigbiquad_set, gensym("clear"), + A_GIMME, 0); +} + +/* ---------------- samphold~ - sample and hold ----------------- */ + +typedef struct sigsamphold +{ + t_object x_obj; + float x_f; + float x_lastin; + float x_lastout; +} t_sigsamphold; + +t_class *sigsamphold_class; + +static void *sigsamphold_new(void) +{ + t_sigsamphold *x = (t_sigsamphold *)pd_new(sigsamphold_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, gensym("signal")); + x->x_lastin = 0; + x->x_lastout = 0; + x->x_f = 0; + return (x); +} + +static t_int *sigsamphold_perform(t_int *w) +{ + float *in1 = (float *)(w[1]); + float *in2 = (float *)(w[2]); + float *out = (float *)(w[3]); + t_sigsamphold *x = (t_sigsamphold *)(w[4]); + int n = (t_int)(w[5]); + int i; + float lastin = x->x_lastin; + float lastout = x->x_lastout; + for (i = 0; i < n; i++, *in1++) + { + float next = *in2++; + if (next < lastin) lastout = *in1; + *out++ = lastout; + lastin = next; + } + x->x_lastin = lastin; + x->x_lastout = lastout; + return (w+6); +} + +static void sigsamphold_dsp(t_sigsamphold *x, t_signal **sp) +{ + dsp_add(sigsamphold_perform, 5, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, + x, sp[0]->s_n); +} + +static void sigsamphold_reset(t_sigsamphold *x) +{ + x->x_lastin = 1e20; +} + +static void sigsamphold_set(t_sigsamphold *x, t_float f) +{ + x->x_lastout = f; +} + +void sigsamphold_setup(void) +{ + sigsamphold_class = class_new(gensym("samphold~"), + (t_newmethod)sigsamphold_new, 0, sizeof(t_sigsamphold), 0, 0); + CLASS_MAINSIGNALIN(sigsamphold_class, t_sigsamphold, x_f); + class_addmethod(sigsamphold_class, (t_method)sigsamphold_set, + gensym("set"), A_FLOAT, 0); + class_addmethod(sigsamphold_class, (t_method)sigsamphold_reset, + gensym("reset"), 0); + class_addmethod(sigsamphold_class, (t_method)sigsamphold_dsp, + gensym("dsp"), 0); +} + +/* ------------------------ setup routine ------------------------- */ + +void d_filter_setup(void) +{ + sighip_setup(); + siglop_setup(); + sigbp_setup(); + sigbiquad_setup(); + sigsamphold_setup(); +} +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* "filters", both linear and nonlinear. +*/ +#include "m_pd.h" +#include + +/* ---------------- hip~ - 1-pole 1-zero hipass filter. ----------------- */ + +typedef struct hipctl +{ + float c_x; + float c_coef; +} t_hipctl; + +typedef struct sighip +{ + t_object x_obj; + float x_sr; + float x_hz; + t_hipctl x_cspace; + t_hipctl *x_ctl; + float x_f; +} t_sighip; + +t_class *sighip_class; +static void sighip_ft1(t_sighip *x, t_floatarg f); + +static void *sighip_new(t_floatarg f) +{ + t_sighip *x = (t_sighip *)pd_new(sighip_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_sr = 44100; + x->x_ctl = &x->x_cspace; + x->x_cspace.c_x = 0; + sighip_ft1(x, f); + x->x_f = 0; + return (x); +} + +static void sighip_ft1(t_sighip *x, t_floatarg f) +{ + if (f < 0) f = 0; + x->x_hz = f; + x->x_ctl->c_coef = 1 - f * (2 * 3.14159) / x->x_sr; + if (x->x_ctl->c_coef < 0) + x->x_ctl->c_coef = 0; + else if (x->x_ctl->c_coef > 1) + x->x_ctl->c_coef = 1; +} + +static t_int *sighip_perform(t_int *w) +{ + float *in = (float *)(w[1]); + float *out = (float *)(w[2]); + t_hipctl *c = (t_hipctl *)(w[3]); + int n = (t_int)(w[4]); + int i; + float last = c->c_x; + float coef = c->c_coef; + if (coef < 1) + { + for (i = 0; i < n; i++) + { + float new = *in++ + coef * last; + *out++ = new - last; + last = new; + } + if (PD_BIGORSMALL(last)) + last = 0; + c->c_x = last; + } + else + { + for (i = 0; i < n; i++) + *out++ = *in++; + c->c_x = 0; + } + return (w+5); +} + +static void sighip_dsp(t_sighip *x, t_signal **sp) +{ + x->x_sr = sp[0]->s_sr; + sighip_ft1(x, x->x_hz); + dsp_add(sighip_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, + x->x_ctl, sp[0]->s_n); + +} + +static void sighip_clear(t_sighip *x, t_floatarg q) +{ + x->x_cspace.c_x = 0; +} + +void sighip_setup(void) +{ + sighip_class = class_new(gensym("hip~"), (t_newmethod)sighip_new, 0, + sizeof(t_sighip), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(sighip_class, t_sighip, x_f); + class_addmethod(sighip_class, (t_method)sighip_dsp, gensym("dsp"), 0); + class_addmethod(sighip_class, (t_method)sighip_ft1, + gensym("ft1"), A_FLOAT, 0); + class_addmethod(sighip_class, (t_method)sighip_clear, gensym("clear"), 0); +} + +/* ---------------- lop~ - 1-pole lopass filter. ----------------- */ + +typedef struct lopctl +{ + float c_x; + float c_coef; +} t_lopctl; + +typedef struct siglop +{ + t_object x_obj; + float x_sr; + float x_hz; + t_lopctl x_cspace; + t_lopctl *x_ctl; + float x_f; +} t_siglop; + +t_class *siglop_class; + +static void siglop_ft1(t_siglop *x, t_floatarg f); + +static void *siglop_new(t_floatarg f) +{ + t_siglop *x = (t_siglop *)pd_new(siglop_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_sr = 44100; + x->x_ctl = &x->x_cspace; + x->x_cspace.c_x = 0; + siglop_ft1(x, f); + x->x_f = 0; + return (x); +} + +static void siglop_ft1(t_siglop *x, t_floatarg f) +{ + if (f < 0) f = 0; + x->x_hz = f; + x->x_ctl->c_coef = f * (2 * 3.14159) / x->x_sr; + if (x->x_ctl->c_coef > 1) + x->x_ctl->c_coef = 1; + else if (x->x_ctl->c_coef < 0) + x->x_ctl->c_coef = 0; +} + +static void siglop_clear(t_siglop *x, t_floatarg q) +{ + x->x_cspace.c_x = 0; +} + +static t_int *siglop_perform(t_int *w) +{ + float *in = (float *)(w[1]); + float *out = (float *)(w[2]); + t_lopctl *c = (t_lopctl *)(w[3]); + int n = (t_int)(w[4]); + int i; + float last = c->c_x; + float coef = c->c_coef; + float feedback = 1 - coef; + for (i = 0; i < n; i++) + last = *out++ = coef * *in++ + feedback * last; + if (PD_BIGORSMALL(last)) + last = 0; + c->c_x = last; + return (w+5); +} + +static void siglop_dsp(t_siglop *x, t_signal **sp) +{ + x->x_sr = sp[0]->s_sr; + siglop_ft1(x, x->x_hz); + dsp_add(siglop_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, + x->x_ctl, sp[0]->s_n); + +} + +void siglop_setup(void) +{ + siglop_class = class_new(gensym("lop~"), (t_newmethod)siglop_new, 0, + sizeof(t_siglop), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(siglop_class, t_siglop, x_f); + class_addmethod(siglop_class, (t_method)siglop_dsp, gensym("dsp"), 0); + class_addmethod(siglop_class, (t_method)siglop_ft1, + gensym("ft1"), A_FLOAT, 0); + class_addmethod(siglop_class, (t_method)siglop_clear, gensym("clear"), 0); +} + +/* ---------------- bp~ - 2-pole bandpass filter. ----------------- */ + +typedef struct bpctl +{ + float c_x1; + float c_x2; + float c_coef1; + float c_coef2; + float c_gain; +} t_bpctl; + +typedef struct sigbp +{ + t_object x_obj; + float x_sr; + float x_freq; + float x_q; + t_bpctl x_cspace; + t_bpctl *x_ctl; + float x_f; +} t_sigbp; + +t_class *sigbp_class; + +static void sigbp_docoef(t_sigbp *x, t_floatarg f, t_floatarg q); + +static void *sigbp_new(t_floatarg f, t_floatarg q) +{ + t_sigbp *x = (t_sigbp *)pd_new(sigbp_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft2")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_sr = 44100; + x->x_ctl = &x->x_cspace; + x->x_cspace.c_x1 = 0; + x->x_cspace.c_x2 = 0; + sigbp_docoef(x, f, q); + x->x_f = 0; + return (x); +} + +static float sigbp_qcos(float f) +{ + if (f >= -(0.5f*3.14159f) && f <= 0.5f*3.14159f) + { + float g = f*f; + return (((g*g*g * (-1.0f/720.0f) + g*g*(1.0f/24.0f)) - g*0.5) + 1); + } + else return (0); +} + +static void sigbp_docoef(t_sigbp *x, t_floatarg f, t_floatarg q) +{ + float r, oneminusr, omega; + if (f < 0.001) f = 10; + if (q < 0) q = 0; + x->x_freq = f; + x->x_q = q; + omega = f * (2.0f * 3.14159f) / x->x_sr; + if (q < 0.001) oneminusr = 1.0f; + else oneminusr = omega/q; + if (oneminusr > 1.0f) oneminusr = 1.0f; + r = 1.0f - oneminusr; + x->x_ctl->c_coef1 = 2.0f * sigbp_qcos(omega) * r; + x->x_ctl->c_coef2 = - r * r; + x->x_ctl->c_gain = 2 * oneminusr * (oneminusr + r * omega); + /* post("r %f, omega %f, coef1 %f, coef2 %f", + r, omega, x->x_ctl->c_coef1, x->x_ctl->c_coef2); */ +} + +static void sigbp_ft1(t_sigbp *x, t_floatarg f) +{ + sigbp_docoef(x, f, x->x_q); +} + +static void sigbp_ft2(t_sigbp *x, t_floatarg q) +{ + sigbp_docoef(x, x->x_freq, q); +} + +static void sigbp_clear(t_sigbp *x, t_floatarg q) +{ + x->x_ctl->c_x1 = x->x_ctl->c_x2 = 0; +} + +static t_int *sigbp_perform(t_int *w) +{ + float *in = (float *)(w[1]); + float *out = (float *)(w[2]); + t_bpctl *c = (t_bpctl *)(w[3]); + int n = (t_int)(w[4]); + int i; + float last = c->c_x1; + float prev = c->c_x2; + float coef1 = c->c_coef1; + float coef2 = c->c_coef2; + float gain = c->c_gain; + for (i = 0; i < n; i++) + { + float output = *in++ + coef1 * last + coef2 * prev; + *out++ = gain * output; + prev = last; + last = output; + } + if (PD_BIGORSMALL(last)) + last = 0; + if (PD_BIGORSMALL(prev)) + prev = 0; + c->c_x1 = last; + c->c_x2 = prev; + return (w+5); +} + +static void sigbp_dsp(t_sigbp *x, t_signal **sp) +{ + x->x_sr = sp[0]->s_sr; + sigbp_docoef(x, x->x_freq, x->x_q); + dsp_add(sigbp_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, + x->x_ctl, sp[0]->s_n); + +} + +void sigbp_setup(void) +{ + sigbp_class = class_new(gensym("bp~"), (t_newmethod)sigbp_new, 0, + sizeof(t_sigbp), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(sigbp_class, t_sigbp, x_f); + class_addmethod(sigbp_class, (t_method)sigbp_dsp, gensym("dsp"), 0); + class_addmethod(sigbp_class, (t_method)sigbp_ft1, + gensym("ft1"), A_FLOAT, 0); + class_addmethod(sigbp_class, (t_method)sigbp_ft2, + gensym("ft2"), A_FLOAT, 0); + class_addmethod(sigbp_class, (t_method)sigbp_clear, gensym("clear"), 0); +} + +/* ---------------- biquad~ - raw biquad filter ----------------- */ + +typedef struct biquadctl +{ + float c_x1; + float c_x2; + float c_fb1; + float c_fb2; + float c_ff1; + float c_ff2; + float c_ff3; +} t_biquadctl; + +typedef struct sigbiquad +{ + t_object x_obj; + float x_f; + t_biquadctl x_cspace; + t_biquadctl *x_ctl; +} t_sigbiquad; + +t_class *sigbiquad_class; + +static void sigbiquad_list(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv); + +static void *sigbiquad_new(t_symbol *s, int argc, t_atom *argv) +{ + t_sigbiquad *x = (t_sigbiquad *)pd_new(sigbiquad_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_ctl = &x->x_cspace; + x->x_cspace.c_x1 = x->x_cspace.c_x2 = 0; + sigbiquad_list(x, s, argc, argv); + x->x_f = 0; + return (x); +} + +static t_int *sigbiquad_perform(t_int *w) +{ + float *in = (float *)(w[1]); + float *out = (float *)(w[2]); + t_biquadctl *c = (t_biquadctl *)(w[3]); + int n = (t_int)(w[4]); + int i; + float last = c->c_x1; + float prev = c->c_x2; + float fb1 = c->c_fb1; + float fb2 = c->c_fb2; + float ff1 = c->c_ff1; + float ff2 = c->c_ff2; + float ff3 = c->c_ff3; + for (i = 0; i < n; i++) + { + float output = *in++ + fb1 * last + fb2 * prev; + if (PD_BIGORSMALL(output)) + output = 0; + *out++ = ff1 * output + ff2 * last + ff3 * prev; + prev = last; + last = output; + } + c->c_x1 = last; + c->c_x2 = prev; + return (w+5); +} + +static void sigbiquad_list(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv) +{ + float fb1 = atom_getfloatarg(0, argc, argv); + float fb2 = atom_getfloatarg(1, argc, argv); + float ff1 = atom_getfloatarg(2, argc, argv); + float ff2 = atom_getfloatarg(3, argc, argv); + float ff3 = atom_getfloatarg(4, argc, argv); + float discriminant = fb1 * fb1 + 4 * fb2; + t_biquadctl *c = x->x_ctl; + if (discriminant < 0) /* imaginary roots -- resonant filter */ + { + /* they're conjugates so we just check that the product + is less than one */ + if (fb2 >= -1.0f) goto stable; + } + else /* real roots */ + { + /* check that the parabola 1 - fb1 x - fb2 x^2 has a + vertex between -1 and 1, and that it's nonnegative + at both ends, which implies both roots are in [1-,1]. */ + if (fb1 <= 2.0f && fb1 >= -2.0f && + 1.0f - fb1 -fb2 >= 0 && 1.0f + fb1 - fb2 >= 0) + goto stable; + } + /* if unstable, just bash to zero */ + fb1 = fb2 = ff1 = ff2 = ff3 = 0; +stable: + c->c_fb1 = fb1; + c->c_fb2 = fb2; + c->c_ff1 = ff1; + c->c_ff2 = ff2; + c->c_ff3 = ff3; +} + +static void sigbiquad_set(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv) +{ + t_biquadctl *c = x->x_ctl; + c->c_x1 = atom_getfloatarg(0, argc, argv); + c->c_x2 = atom_getfloatarg(1, argc, argv); +} + +static void sigbiquad_dsp(t_sigbiquad *x, t_signal **sp) +{ + dsp_add(sigbiquad_perform, 4, + sp[0]->s_vec, sp[1]->s_vec, + x->x_ctl, sp[0]->s_n); + +} + +void sigbiquad_setup(void) +{ + sigbiquad_class = class_new(gensym("biquad~"), (t_newmethod)sigbiquad_new, + 0, sizeof(t_sigbiquad), 0, A_GIMME, 0); + CLASS_MAINSIGNALIN(sigbiquad_class, t_sigbiquad, x_f); + class_addmethod(sigbiquad_class, (t_method)sigbiquad_dsp, gensym("dsp"), 0); + class_addlist(sigbiquad_class, sigbiquad_list); + class_addmethod(sigbiquad_class, (t_method)sigbiquad_set, gensym("set"), + A_GIMME, 0); + class_addmethod(sigbiquad_class, (t_method)sigbiquad_set, gensym("clear"), + A_GIMME, 0); +} + +/* ---------------- samphold~ - sample and hold ----------------- */ + +typedef struct sigsamphold +{ + t_object x_obj; + float x_f; + float x_lastin; + float x_lastout; +} t_sigsamphold; + +t_class *sigsamphold_class; + +static void *sigsamphold_new(void) +{ + t_sigsamphold *x = (t_sigsamphold *)pd_new(sigsamphold_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + outlet_new(&x->x_obj, gensym("signal")); + x->x_lastin = 0; + x->x_lastout = 0; + x->x_f = 0; + return (x); +} + +static t_int *sigsamphold_perform(t_int *w) +{ + float *in1 = (float *)(w[1]); + float *in2 = (float *)(w[2]); + float *out = (float *)(w[3]); + t_sigsamphold *x = (t_sigsamphold *)(w[4]); + int n = (t_int)(w[5]); + int i; + float lastin = x->x_lastin; + float lastout = x->x_lastout; + for (i = 0; i < n; i++, *in1++) + { + float next = *in2++; + if (next < lastin) lastout = *in1; + *out++ = lastout; + lastin = next; + } + x->x_lastin = lastin; + x->x_lastout = lastout; + return (w+6); +} + +static void sigsamphold_dsp(t_sigsamphold *x, t_signal **sp) +{ + dsp_add(sigsamphold_perform, 5, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, + x, sp[0]->s_n); +} + +static void sigsamphold_reset(t_sigsamphold *x) +{ + x->x_lastin = 1e20; +} + +static void sigsamphold_set(t_sigsamphold *x, t_float f) +{ + x->x_lastout = f; +} + +void sigsamphold_setup(void) +{ + sigsamphold_class = class_new(gensym("samphold~"), + (t_newmethod)sigsamphold_new, 0, sizeof(t_sigsamphold), 0, 0); + CLASS_MAINSIGNALIN(sigsamphold_class, t_sigsamphold, x_f); + class_addmethod(sigsamphold_class, (t_method)sigsamphold_set, + gensym("set"), A_FLOAT, 0); + class_addmethod(sigsamphold_class, (t_method)sigsamphold_reset, + gensym("reset"), 0); + class_addmethod(sigsamphold_class, (t_method)sigsamphold_dsp, + gensym("dsp"), 0); +} + +/* ------------------------ setup routine ------------------------- */ + +void d_filter_setup(void) +{ + sighip_setup(); + siglop_setup(); + sigbp_setup(); + sigbiquad_setup(); + sigsamphold_setup(); +} diff --git a/apps/plugins/pdbox/PDa/src/d_global.c b/apps/plugins/pdbox/PDa/src/d_global.c new file mode 100644 index 0000000..2b129ac --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_global.c @@ -0,0 +1,616 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* send~, receive~, throw~, catch~ */ + +#include "m_pd.h" +#include + +#define DEFSENDVS 64 /* LATER get send to get this from canvas */ + +/* ----------------------------- send~ ----------------------------- */ +static t_class *sigsend_class; + +typedef struct _sigsend +{ + t_object x_obj; + t_symbol *x_sym; + int x_n; + t_sample *x_vec; + float x_f; +} t_sigsend; + +static void *sigsend_new(t_symbol *s) +{ + t_sigsend *x = (t_sigsend *)pd_new(sigsend_class); + pd_bind(&x->x_obj.ob_pd, s); + x->x_sym = s; + x->x_n = DEFSENDVS; + x->x_vec = (t_sample *)getbytes(DEFSENDVS * sizeof(t_sample)); + memset((char *)(x->x_vec), 0, DEFSENDVS * sizeof(t_sample)); + x->x_f = 0; + return (x); +} + +static t_int *sigsend_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample *out = (t_sample *)(w[2]); + int n = (int)(w[3]); + while (n--) + { + *out = (PD_BIGORSMALL(*in) ? 0 : *in); + out++; + in++; + } + return (w+4); +} + +static void sigsend_dsp(t_sigsend *x, t_signal **sp) +{ + if (x->x_n == sp[0]->s_n) + dsp_add(sigsend_perform, 3, sp[0]->s_vec, x->x_vec, sp[0]->s_n); + else error("sigsend %s: unexpected vector size", x->x_sym->s_name); +} + +static void sigsend_free(t_sigsend *x) +{ + pd_unbind(&x->x_obj.ob_pd, x->x_sym); + freebytes(x->x_vec, x->x_n * sizeof(float)); +} + +static void sigsend_setup(void) +{ + sigsend_class = class_new(gensym("send~"), (t_newmethod)sigsend_new, + (t_method)sigsend_free, sizeof(t_sigsend), 0, A_DEFSYM, 0); + class_addcreator((t_newmethod)sigsend_new, gensym("s~"), A_DEFSYM, 0); + CLASS_MAINSIGNALIN(sigsend_class, t_sigsend, x_f); + class_addmethod(sigsend_class, (t_method)sigsend_dsp, gensym("dsp"), 0); +} + +/* ----------------------------- receive~ ----------------------------- */ +static t_class *sigreceive_class; + +typedef struct _sigreceive +{ + t_object x_obj; + t_symbol *x_sym; + t_sample *x_wherefrom; + int x_n; +} t_sigreceive; + +static void *sigreceive_new(t_symbol *s) +{ + t_sigreceive *x = (t_sigreceive *)pd_new(sigreceive_class); + x->x_n = DEFSENDVS; /* LATER find our vector size correctly */ + x->x_sym = s; + x->x_wherefrom = 0; + outlet_new(&x->x_obj, &s_signal); + return (x); +} + +static t_int *sigreceive_perform(t_int *w) +{ + t_sigreceive *x = (t_sigreceive *)(w[1]); + t_sample *out = (t_sample *)(w[2]); + int n = (int)(w[3]); + t_sample *in = x->x_wherefrom; + if (in) + { + while (n--) + *out++ = *in++; + } + else + { + while (n--) + *out++ = 0; + } + return (w+4); +} + +static void sigreceive_set(t_sigreceive *x, t_symbol *s) +{ + t_sigsend *sender = (t_sigsend *)pd_findbyclass((x->x_sym = s), + sigsend_class); + if (sender) + { + if (sender->x_n == x->x_n) + x->x_wherefrom = sender->x_vec; + else + { + pd_error(x, "receive~ %s: vector size mismatch", x->x_sym->s_name); + x->x_wherefrom = 0; + } + } + else + { + pd_error(x, "receive~ %s: no matching send", x->x_sym->s_name); + x->x_wherefrom = 0; + } +} + +static void sigreceive_dsp(t_sigreceive *x, t_signal **sp) +{ + if (sp[0]->s_n != x->x_n) + { + pd_error(x, "receive~ %s: vector size mismatch", x->x_sym->s_name); + } + else + { + sigreceive_set(x, x->x_sym); + dsp_add(sigreceive_perform, 3, + x, sp[0]->s_vec, sp[0]->s_n); + } +} + +static void sigreceive_setup(void) +{ + sigreceive_class = class_new(gensym("receive~"), + (t_newmethod)sigreceive_new, 0, + sizeof(t_sigreceive), 0, A_DEFSYM, 0); + class_addcreator((t_newmethod)sigreceive_new, gensym("r~"), A_DEFSYM, 0); + class_addmethod(sigreceive_class, (t_method)sigreceive_set, gensym("set"), + A_SYMBOL, 0); + class_addmethod(sigreceive_class, (t_method)sigreceive_dsp, gensym("dsp"), + 0); + class_sethelpsymbol(sigreceive_class, gensym("send~")); +} + +/* ----------------------------- catch~ ----------------------------- */ +static t_class *sigcatch_class; + +typedef struct _sigcatch +{ + t_object x_obj; + t_symbol *x_sym; + int x_n; + t_sample *x_vec; +} t_sigcatch; + +static void *sigcatch_new(t_symbol *s) +{ + t_sigcatch *x = (t_sigcatch *)pd_new(sigcatch_class); + pd_bind(&x->x_obj.ob_pd, s); + x->x_sym = s; + x->x_n = DEFSENDVS; + x->x_vec = (t_sample *)getbytes(DEFSENDVS * sizeof(t_sample)); + memset((char *)(x->x_vec), 0, DEFSENDVS * sizeof(t_sample)); + outlet_new(&x->x_obj, &s_signal); + return (x); +} + +static t_int *sigcatch_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample *out = (t_sample *)(w[2]); + int n = (int)(w[3]); + while (n--) *out++ = *in, *in++ = 0; + return (w+4); +} + +static void sigcatch_dsp(t_sigcatch *x, t_signal **sp) +{ + if (x->x_n == sp[0]->s_n) + dsp_add(sigcatch_perform, 3, x->x_vec, sp[0]->s_vec, sp[0]->s_n); + else error("sigcatch %s: unexpected vector size", x->x_sym->s_name); +} + +static void sigcatch_free(t_sigcatch *x) +{ + pd_unbind(&x->x_obj.ob_pd, x->x_sym); + freebytes(x->x_vec, x->x_n * sizeof(float)); +} + +static void sigcatch_setup(void) +{ + sigcatch_class = class_new(gensym("catch~"), (t_newmethod)sigcatch_new, + (t_method)sigcatch_free, sizeof(t_sigcatch), CLASS_NOINLET, A_DEFSYM, 0); + class_addmethod(sigcatch_class, (t_method)sigcatch_dsp, gensym("dsp"), 0); + class_sethelpsymbol(sigcatch_class, gensym("throw~")); +} + +/* ----------------------------- throw~ ----------------------------- */ +static t_class *sigthrow_class; + +typedef struct _sigthrow +{ + t_object x_obj; + t_symbol *x_sym; + t_sample *x_whereto; + int x_n; + t_float x_f; +} t_sigthrow; + +static void *sigthrow_new(t_symbol *s) +{ + t_sigthrow *x = (t_sigthrow *)pd_new(sigthrow_class); + x->x_sym = s; + x->x_whereto = 0; + x->x_n = DEFSENDVS; + x->x_f = 0; + return (x); +} + +static t_int *sigthrow_perform(t_int *w) +{ + t_sigthrow *x = (t_sigthrow *)(w[1]); + t_sample *in = (t_sample *)(w[2]); + int n = (int)(w[3]); + t_sample *out = x->x_whereto; + if (out) + { + while (n--) + { + *out += (PD_BIGORSMALL(*in) ? 0 : *in); + out++; + in++; + } + } + return (w+4); +} + +static void sigthrow_set(t_sigthrow *x, t_symbol *s) +{ + t_sigcatch *catcher = (t_sigcatch *)pd_findbyclass((x->x_sym = s), + sigcatch_class); + if (catcher) + { + if (catcher->x_n == x->x_n) + x->x_whereto = catcher->x_vec; + else + { + pd_error(x, "throw~ %s: vector size mismatch", x->x_sym->s_name); + x->x_whereto = 0; + } + } + else + { + pd_error(x, "throw~ %s: no matching catch", x->x_sym->s_name); + x->x_whereto = 0; + } +} + +static void sigthrow_dsp(t_sigthrow *x, t_signal **sp) +{ + if (sp[0]->s_n != x->x_n) + { + pd_error(x, "throw~ %s: vector size mismatch", x->x_sym->s_name); + } + else + { + sigthrow_set(x, x->x_sym); + dsp_add(sigthrow_perform, 3, + x, sp[0]->s_vec, sp[0]->s_n); + } +} + +static void sigthrow_setup(void) +{ + sigthrow_class = class_new(gensym("throw~"), (t_newmethod)sigthrow_new, 0, + sizeof(t_sigthrow), 0, A_DEFSYM, 0); + class_addcreator((t_newmethod)sigthrow_new, gensym("r~"), A_DEFSYM, 0); + class_addmethod(sigthrow_class, (t_method)sigthrow_set, gensym("set"), + A_SYMBOL, 0); + CLASS_MAINSIGNALIN(sigthrow_class, t_sigthrow, x_f); + class_addmethod(sigthrow_class, (t_method)sigthrow_dsp, gensym("dsp"), 0); +} + +/* ----------------------- global setup routine ---------------- */ + +void d_global_setup(void) +{ + sigsend_setup(); + sigreceive_setup(); + sigcatch_setup(); + sigthrow_setup(); +} + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* send~, receive~, throw~, catch~ */ + +#include "m_pd.h" +#include + +#define DEFSENDVS 64 /* LATER get send to get this from canvas */ + +/* ----------------------------- send~ ----------------------------- */ +static t_class *sigsend_class; + +typedef struct _sigsend +{ + t_object x_obj; + t_symbol *x_sym; + int x_n; + t_sample *x_vec; + float x_f; +} t_sigsend; + +static void *sigsend_new(t_symbol *s) +{ + t_sigsend *x = (t_sigsend *)pd_new(sigsend_class); + pd_bind(&x->x_obj.ob_pd, s); + x->x_sym = s; + x->x_n = DEFSENDVS; + x->x_vec = (t_sample *)getbytes(DEFSENDVS * sizeof(t_sample)); + memset((char *)(x->x_vec), 0, DEFSENDVS * sizeof(t_sample)); + x->x_f = 0; + return (x); +} + +static t_int *sigsend_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample *out = (t_sample *)(w[2]); + int n = (int)(w[3]); + while (n--) + { + *out = (PD_BIGORSMALL(*in) ? 0 : *in); + out++; + in++; + } + return (w+4); +} + +static void sigsend_dsp(t_sigsend *x, t_signal **sp) +{ + if (x->x_n == sp[0]->s_n) + dsp_add(sigsend_perform, 3, sp[0]->s_vec, x->x_vec, sp[0]->s_n); + else error("sigsend %s: unexpected vector size", x->x_sym->s_name); +} + +static void sigsend_free(t_sigsend *x) +{ + pd_unbind(&x->x_obj.ob_pd, x->x_sym); + freebytes(x->x_vec, x->x_n * sizeof(float)); +} + +static void sigsend_setup(void) +{ + sigsend_class = class_new(gensym("send~"), (t_newmethod)sigsend_new, + (t_method)sigsend_free, sizeof(t_sigsend), 0, A_DEFSYM, 0); + class_addcreator((t_newmethod)sigsend_new, gensym("s~"), A_DEFSYM, 0); + CLASS_MAINSIGNALIN(sigsend_class, t_sigsend, x_f); + class_addmethod(sigsend_class, (t_method)sigsend_dsp, gensym("dsp"), 0); +} + +/* ----------------------------- receive~ ----------------------------- */ +static t_class *sigreceive_class; + +typedef struct _sigreceive +{ + t_object x_obj; + t_symbol *x_sym; + t_sample *x_wherefrom; + int x_n; +} t_sigreceive; + +static void *sigreceive_new(t_symbol *s) +{ + t_sigreceive *x = (t_sigreceive *)pd_new(sigreceive_class); + x->x_n = DEFSENDVS; /* LATER find our vector size correctly */ + x->x_sym = s; + x->x_wherefrom = 0; + outlet_new(&x->x_obj, &s_signal); + return (x); +} + +static t_int *sigreceive_perform(t_int *w) +{ + t_sigreceive *x = (t_sigreceive *)(w[1]); + t_sample *out = (t_sample *)(w[2]); + int n = (int)(w[3]); + t_sample *in = x->x_wherefrom; + if (in) + { + while (n--) + *out++ = *in++; + } + else + { + while (n--) + *out++ = 0; + } + return (w+4); +} + +static void sigreceive_set(t_sigreceive *x, t_symbol *s) +{ + t_sigsend *sender = (t_sigsend *)pd_findbyclass((x->x_sym = s), + sigsend_class); + if (sender) + { + if (sender->x_n == x->x_n) + x->x_wherefrom = sender->x_vec; + else + { + pd_error(x, "receive~ %s: vector size mismatch", x->x_sym->s_name); + x->x_wherefrom = 0; + } + } + else + { + pd_error(x, "receive~ %s: no matching send", x->x_sym->s_name); + x->x_wherefrom = 0; + } +} + +static void sigreceive_dsp(t_sigreceive *x, t_signal **sp) +{ + if (sp[0]->s_n != x->x_n) + { + pd_error(x, "receive~ %s: vector size mismatch", x->x_sym->s_name); + } + else + { + sigreceive_set(x, x->x_sym); + dsp_add(sigreceive_perform, 3, + x, sp[0]->s_vec, sp[0]->s_n); + } +} + +static void sigreceive_setup(void) +{ + sigreceive_class = class_new(gensym("receive~"), + (t_newmethod)sigreceive_new, 0, + sizeof(t_sigreceive), 0, A_DEFSYM, 0); + class_addcreator((t_newmethod)sigreceive_new, gensym("r~"), A_DEFSYM, 0); + class_addmethod(sigreceive_class, (t_method)sigreceive_set, gensym("set"), + A_SYMBOL, 0); + class_addmethod(sigreceive_class, (t_method)sigreceive_dsp, gensym("dsp"), + 0); + class_sethelpsymbol(sigreceive_class, gensym("send~")); +} + +/* ----------------------------- catch~ ----------------------------- */ +static t_class *sigcatch_class; + +typedef struct _sigcatch +{ + t_object x_obj; + t_symbol *x_sym; + int x_n; + t_sample *x_vec; +} t_sigcatch; + +static void *sigcatch_new(t_symbol *s) +{ + t_sigcatch *x = (t_sigcatch *)pd_new(sigcatch_class); + pd_bind(&x->x_obj.ob_pd, s); + x->x_sym = s; + x->x_n = DEFSENDVS; + x->x_vec = (t_sample *)getbytes(DEFSENDVS * sizeof(t_sample)); + memset((char *)(x->x_vec), 0, DEFSENDVS * sizeof(t_sample)); + outlet_new(&x->x_obj, &s_signal); + return (x); +} + +static t_int *sigcatch_perform(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); + t_sample *out = (t_sample *)(w[2]); + int n = (int)(w[3]); + while (n--) *out++ = *in, *in++ = 0; + return (w+4); +} + +static void sigcatch_dsp(t_sigcatch *x, t_signal **sp) +{ + if (x->x_n == sp[0]->s_n) + dsp_add(sigcatch_perform, 3, x->x_vec, sp[0]->s_vec, sp[0]->s_n); + else error("sigcatch %s: unexpected vector size", x->x_sym->s_name); +} + +static void sigcatch_free(t_sigcatch *x) +{ + pd_unbind(&x->x_obj.ob_pd, x->x_sym); + freebytes(x->x_vec, x->x_n * sizeof(float)); +} + +static void sigcatch_setup(void) +{ + sigcatch_class = class_new(gensym("catch~"), (t_newmethod)sigcatch_new, + (t_method)sigcatch_free, sizeof(t_sigcatch), CLASS_NOINLET, A_DEFSYM, 0); + class_addmethod(sigcatch_class, (t_method)sigcatch_dsp, gensym("dsp"), 0); + class_sethelpsymbol(sigcatch_class, gensym("throw~")); +} + +/* ----------------------------- throw~ ----------------------------- */ +static t_class *sigthrow_class; + +typedef struct _sigthrow +{ + t_object x_obj; + t_symbol *x_sym; + t_sample *x_whereto; + int x_n; + t_float x_f; +} t_sigthrow; + +static void *sigthrow_new(t_symbol *s) +{ + t_sigthrow *x = (t_sigthrow *)pd_new(sigthrow_class); + x->x_sym = s; + x->x_whereto = 0; + x->x_n = DEFSENDVS; + x->x_f = 0; + return (x); +} + +static t_int *sigthrow_perform(t_int *w) +{ + t_sigthrow *x = (t_sigthrow *)(w[1]); + t_sample *in = (t_sample *)(w[2]); + int n = (int)(w[3]); + t_sample *out = x->x_whereto; + if (out) + { + while (n--) + { + *out += (PD_BIGORSMALL(*in) ? 0 : *in); + out++; + in++; + } + } + return (w+4); +} + +static void sigthrow_set(t_sigthrow *x, t_symbol *s) +{ + t_sigcatch *catcher = (t_sigcatch *)pd_findbyclass((x->x_sym = s), + sigcatch_class); + if (catcher) + { + if (catcher->x_n == x->x_n) + x->x_whereto = catcher->x_vec; + else + { + pd_error(x, "throw~ %s: vector size mismatch", x->x_sym->s_name); + x->x_whereto = 0; + } + } + else + { + pd_error(x, "throw~ %s: no matching catch", x->x_sym->s_name); + x->x_whereto = 0; + } +} + +static void sigthrow_dsp(t_sigthrow *x, t_signal **sp) +{ + if (sp[0]->s_n != x->x_n) + { + pd_error(x, "throw~ %s: vector size mismatch", x->x_sym->s_name); + } + else + { + sigthrow_set(x, x->x_sym); + dsp_add(sigthrow_perform, 3, + x, sp[0]->s_vec, sp[0]->s_n); + } +} + +static void sigthrow_setup(void) +{ + sigthrow_class = class_new(gensym("throw~"), (t_newmethod)sigthrow_new, 0, + sizeof(t_sigthrow), 0, A_DEFSYM, 0); + class_addcreator((t_newmethod)sigthrow_new, gensym("r~"), A_DEFSYM, 0); + class_addmethod(sigthrow_class, (t_method)sigthrow_set, gensym("set"), + A_SYMBOL, 0); + CLASS_MAINSIGNALIN(sigthrow_class, t_sigthrow, x_f); + class_addmethod(sigthrow_class, (t_method)sigthrow_dsp, gensym("dsp"), 0); +} + +/* ----------------------- global setup routine ---------------- */ + +void d_global_setup(void) +{ + sigsend_setup(); + sigreceive_setup(); + sigcatch_setup(); + sigthrow_setup(); +} + diff --git a/apps/plugins/pdbox/PDa/src/d_imayer_fft.c b/apps/plugins/pdbox/PDa/src/d_imayer_fft.c new file mode 100644 index 0000000..d8e9e9f --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_imayer_fft.c @@ -0,0 +1,1032 @@ +/* +** Algorithm: complex multiplication +** +** Performance: Bad accuracy, great speed. +** +** The simplest, way of generating trig values. Note, this method is +** not stable, and errors accumulate rapidly. +** +** Computation: 2 *, 1 + per value. +*/ + + +#include "m_fixed.h" + + +#define TRIG_FAST + +#ifdef TRIG_FAST +char mtrig_algorithm[] = "Simple"; +#define TRIG_VARS \ + t_fixed t_c,t_s; +#define TRIG_INIT(k,c,s) \ + { \ + t_c = fcostab[k]; \ + t_s = fsintab[k]; \ + c = itofix(1); \ + s = 0; \ + } +#define TRIG_NEXT(k,c,s) \ + { \ + t_fixed t = c; \ + c = mult(t,t_c) - mult(s,t_s); \ + s = mult(t,t_s) + mult(s,t_c); \ + } +#define TRIG_23(k,c1,s1,c2,s2,c3,s3) \ + { \ + c2 = mult(c1,c1) - mult(s1,s1); \ + s2 = (mult(c1,s1)<<2); \ + c3 = mult(c2,c1) - mult(s2,s1); \ + s3 = mult(c2,s1) + mult(s2,c1); \ + } +#endif +#define TRIG_RESET(k,c,s) + +/* +** Algorithm: O. Buneman's trig generator. +** +** Performance: Good accuracy, mediocre speed. +** +** Manipulates a log(n) table to stably create n evenly spaced trig +** values. The newly generated values lay halfway between two +** known values, and are calculated by appropriatelly scaling the +** average of the known trig values appropriatelly according to the +** angle between them. This process is inherently stable; and is +** about as accurate as most trig library functions. The errors +** caused by this code segment are primarily due to the less stable +** method to calculate values for 2t and s 3t. Note: I believe this +** algorithm is patented(!), see readme file for more details. +** +** Computation: 1 +, 1 *, much integer math, per trig value +** +*/ + +#ifdef TRIG_GOOD +#define TRIG_VARS \ + int t_lam=0; \ + double coswrk[TRIG_TABLE_SIZE],sinwrk[TRIG_TABLE_SIZE]; +#define TRIG_INIT(k,c,s) \ + { \ + int i; \ + for (i=0 ; i<=k ; i++) \ + {coswrk[i]=fcostab[i];sinwrk[i]=fsintab[i];} \ + t_lam = 0; \ + c = 1; \ + s = 0; \ + } +#define TRIG_NEXT(k,c,s) \ + { \ + int i,j; \ + (t_lam)++; \ + for (i=0 ; !((1<1) \ + { \ + for (j=k-i+2 ; (1<>1; (!((k2^=k)&k)); k>>=1); + if (k1>k2) + { + aa=fz[k1];fz[k1]=fz[k2];fz[k2]=aa; + } + } + for ( k=0 ; (1<> 1; + fi = fz; + gi = fi + kx; + fn = fz + n; + do + { + t_fixed g0,f0,f1,g1,f2,g2,f3,g3; + f1 = fi[0 ] - fi[k1]; + f0 = fi[0 ] + fi[k1]; + f3 = fi[k2] - fi[k3]; + f2 = fi[k2] + fi[k3]; + fi[k2] = f0 - f2; + fi[0 ] = f0 + f2; + fi[k3] = f1 - f3; + fi[k1] = f1 + f3; + g1 = gi[0 ] - gi[k1]; + g0 = gi[0 ] + gi[k1]; + g3 = FFTmult(SQRT2, gi[k3]); + g2 = FFTmult(SQRT2, gi[k2]); + gi[k2] = g0 - g2; + gi[0 ] = g0 + g2; + gi[k3] = g1 - g3; + gi[k1] = g1 + g3; + gi += k4; + fi += k4; + } while (fi>1; real[j] = (q-t)>>1; + imag[i] = (s-r)>>1; imag[j] = (s+r)>>1; + } + imayer_fht(real,n); + imayer_fht(imag,n); +} + +void imayer_ifft(int n, t_fixed *real, t_fixed *imag) +{ + t_fixed a,b,c,d; + t_fixed q,r,s,t; + int i,j,k; + imayer_fht(real,n); + imayer_fht(imag,n); + for (i=1,j=n-1,k=n/2;i>1; imag[j] = (s-r)>>1; + real[i] = (q-t)>>1; real[j] = (q+t)>>1; + } +} + +void imayer_realfft(int n, t_fixed *real) +{ + t_fixed a,b,c,d; + int i,j,k; + imayer_fht(real,n); + for (i=1,j=n-1,k=n/2;i>1; + real[i] = (a+b)>>1; + } +} + +void imayer_realifft(int n, t_fixed *real) +{ + t_fixed a,b,c,d; + int i,j,k; + for (i=1,j=n-1,k=n/2;i + + +int writetables() +{ + int i; + + printf("/* Tables for fixed point lookup with %d bit precision*/\n\n",fix1); + + printf("static int fsintab[TRIG_TAB_SIZE]= {\n"); + + for (i=0;i %f %f\n",fixtof(r[i]),fixtof(im[i]),\ + fr[i],fim[i]);\ + fprintf(stderr,"\n\n"); + + + +int main() +{ + int i; + t_fixed r[256]; + t_fixed im[256]; + float fr[256]; + float fim[256]; + + +#if 1 + writetables(); + exit(0); +#endif + + +#if 0 + t_fixed c1,s1,c2,s2,c3,s3; + int k; + int i; + + TRIG_VARS; + + for (k=2;k<10;k+=2) { + TRIG_INIT(k,c1,s1); + for (i=0;i<8;i++) { + TRIG_NEXT(k,c1,s1); + TRIG_23(k,c1,s1,c2,s2,c3,s3); + printf("TRIG value k=%d,%d val1 = %f %f\n",k,i,fixtof(c1),fixtof(s1)); + } + } +#endif + + + +#if 1 + + #define NP 16 + + for (i=0;i1) \ + { \ + for (j=k-i+2 ; (1<>1; (!((k2^=k)&k)); k>>=1); + if (k1>k2) + { + aa=fz[k1];fz[k1]=fz[k2];fz[k2]=aa; + } + } + for ( k=0 ; (1<> 1; + fi = fz; + gi = fi + kx; + fn = fz + n; + do + { + t_fixed g0,f0,f1,g1,f2,g2,f3,g3; + f1 = fi[0 ] - fi[k1]; + f0 = fi[0 ] + fi[k1]; + f3 = fi[k2] - fi[k3]; + f2 = fi[k2] + fi[k3]; + fi[k2] = f0 - f2; + fi[0 ] = f0 + f2; + fi[k3] = f1 - f3; + fi[k1] = f1 + f3; + g1 = gi[0 ] - gi[k1]; + g0 = gi[0 ] + gi[k1]; + g3 = FFTmult(SQRT2, gi[k3]); + g2 = FFTmult(SQRT2, gi[k2]); + gi[k2] = g0 - g2; + gi[0 ] = g0 + g2; + gi[k3] = g1 - g3; + gi[k1] = g1 + g3; + gi += k4; + fi += k4; + } while (fi>1; real[j] = (q-t)>>1; + imag[i] = (s-r)>>1; imag[j] = (s+r)>>1; + } + imayer_fht(real,n); + imayer_fht(imag,n); +} + +void imayer_ifft(int n, t_fixed *real, t_fixed *imag) +{ + t_fixed a,b,c,d; + t_fixed q,r,s,t; + int i,j,k; + imayer_fht(real,n); + imayer_fht(imag,n); + for (i=1,j=n-1,k=n/2;i>1; imag[j] = (s-r)>>1; + real[i] = (q-t)>>1; real[j] = (q+t)>>1; + } +} + +void imayer_realfft(int n, t_fixed *real) +{ + t_fixed a,b,c,d; + int i,j,k; + imayer_fht(real,n); + for (i=1,j=n-1,k=n/2;i>1; + real[i] = (a+b)>>1; + } +} + +void imayer_realifft(int n, t_fixed *real) +{ + t_fixed a,b,c,d; + int i,j,k; + for (i=1,j=n-1,k=n/2;i + + +int writetables() +{ + int i; + + printf("/* Tables for fixed point lookup with %d bit precision*/\n\n",fix1); + + printf("static int fsintab[TRIG_TAB_SIZE]= {\n"); + + for (i=0;i %f %f\n",fixtof(r[i]),fixtof(im[i]),\ + fr[i],fim[i]);\ + fprintf(stderr,"\n\n"); + + + +int main() +{ + int i; + t_fixed r[256]; + t_fixed im[256]; + float fr[256]; + float fim[256]; + + +#if 1 + writetables(); + exit(0); +#endif + + +#if 0 + t_fixed c1,s1,c2,s2,c3,s3; + int k; + int i; + + TRIG_VARS; + + for (k=2;k<10;k+=2) { + TRIG_INIT(k,c1,s1); + for (i=0;i<8;i++) { + TRIG_NEXT(k,c1,s1); + TRIG_23(k,c1,s1,c2,s2,c3,s3); + printf("TRIG value k=%d,%d val1 = %f %f\n",k,i,fixtof(c1),fixtof(s1)); + } + } +#endif + + + +#if 1 + + #define NP 16 + + for (i=0;i +#define LOGTEN 2.302585092994 + +/* ------------------------- clip~ -------------------------- */ +static t_class *clip_class; + +typedef struct _clip +{ + t_object x_obj; + float x_f; + t_sample x_lo; + t_sample x_hi; +} t_clip; + +static void *clip_new(t_floatarg lo, t_floatarg hi) +{ + t_clip *x = (t_clip *)pd_new(clip_class); + x->x_lo = lo; + x->x_hi = hi; + outlet_new(&x->x_obj, gensym("signal")); + floatinlet_new(&x->x_obj, &x->x_lo); + floatinlet_new(&x->x_obj, &x->x_hi); + x->x_f = 0; + return (x); +} + +static t_int *clip_perform(t_int *w) +{ + t_clip *x = (t_clip *)(w[1]); + t_float *in = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + float f = *in++; + if (f < x->x_lo) f = x->x_lo; + if (f > x->x_hi) f = x->x_hi; + *out++ = f; + } + return (w+5); +} + +static void clip_dsp(t_clip *x, t_signal **sp) +{ + dsp_add(clip_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +static void clip_setup(void) +{ + clip_class = class_new(gensym("clip~"), (t_newmethod)clip_new, 0, + sizeof(t_clip), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(clip_class, t_clip, x_f); + class_addmethod(clip_class, (t_method)clip_dsp, gensym("dsp"), 0); +} + +/* sigrsqrt - reciprocal square root good to 8 mantissa bits */ + +#define DUMTAB1SIZE 256 +#define DUMTAB2SIZE 1024 + +static float rsqrt_exptab[DUMTAB1SIZE], rsqrt_mantissatab[DUMTAB2SIZE]; + +static void init_rsqrt(void) +{ + int i; + for (i = 0; i < DUMTAB1SIZE; i++) + { + float f; + long l = (i ? (i == DUMTAB1SIZE-1 ? DUMTAB1SIZE-2 : i) : 1)<< 23; + *(long *)(&f) = l; + rsqrt_exptab[i] = 1./sqrt(f); + } + for (i = 0; i < DUMTAB2SIZE; i++) + { + float f = 1 + (1./DUMTAB2SIZE) * i; + rsqrt_mantissatab[i] = 1./sqrt(f); + } +} + + /* these are used in externs like "bonk" */ + +float q8_rsqrt(float f) +{ + long l = *(long *)(&f); + if (f < 0) return (0); + else return (rsqrt_exptab[(l >> 23) & 0xff] * + rsqrt_mantissatab[(l >> 13) & 0x3ff]); +} + +float q8_sqrt(float f) +{ + long l = *(long *)(&f); + if (f < 0) return (0); + else return (f * rsqrt_exptab[(l >> 23) & 0xff] * + rsqrt_mantissatab[(l >> 13) & 0x3ff]); +} + + /* the old names are OK unless we're in IRIX N32 */ + +#ifndef N32 +float qsqrt(float f) {return (q8_sqrt(f)); } +float qrsqrt(float f) {return (q8_rsqrt(f)); } +#endif + + + +typedef struct sigrsqrt +{ + t_object x_obj; + float x_f; +} t_sigrsqrt; + +static t_class *sigrsqrt_class; + +static void *sigrsqrt_new(void) +{ + t_sigrsqrt *x = (t_sigrsqrt *)pd_new(sigrsqrt_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *sigrsqrt_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + while (n--) + { + float f = *in; + long l = *(long *)(in++); + if (f < 0) *out++ = 0; + else + { + float g = rsqrt_exptab[(l >> 23) & 0xff] * + rsqrt_mantissatab[(l >> 13) & 0x3ff]; + *out++ = 1.5 * g - 0.5 * g * g * g * f; + } + } + return (w + 4); +} + +static void sigrsqrt_dsp(t_sigrsqrt *x, t_signal **sp) +{ + dsp_add(sigrsqrt_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void sigrsqrt_setup(void) +{ + init_rsqrt(); + sigrsqrt_class = class_new(gensym("rsqrt~"), (t_newmethod)sigrsqrt_new, 0, + sizeof(t_sigrsqrt), 0, 0); + /* an old name for it: */ + class_addcreator(sigrsqrt_new, gensym("q8_rsqrt~"), 0); + CLASS_MAINSIGNALIN(sigrsqrt_class, t_sigrsqrt, x_f); + class_addmethod(sigrsqrt_class, (t_method)sigrsqrt_dsp, gensym("dsp"), 0); +} + + +/* sigsqrt - square root good to 8 mantissa bits */ + +typedef struct sigsqrt +{ + t_object x_obj; + float x_f; +} t_sigsqrt; + +static t_class *sigsqrt_class; + +static void *sigsqrt_new(void) +{ + t_sigsqrt *x = (t_sigsqrt *)pd_new(sigsqrt_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +t_int *sigsqrt_perform(t_int *w) /* not static; also used in d_fft.c */ +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + while (n--) + { + float f = *in; + long l = *(long *)(in++); + if (f < 0) *out++ = 0; + else + { + float g = rsqrt_exptab[(l >> 23) & 0xff] * + rsqrt_mantissatab[(l >> 13) & 0x3ff]; + *out++ = f * (1.5 * g - 0.5 * g * g * g * f); + } + } + return (w + 4); +} + +static void sigsqrt_dsp(t_sigsqrt *x, t_signal **sp) +{ + dsp_add(sigsqrt_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void sigsqrt_setup(void) +{ + sigsqrt_class = class_new(gensym("sqrt~"), (t_newmethod)sigsqrt_new, 0, + sizeof(t_sigsqrt), 0, 0); + class_addcreator(sigsqrt_new, gensym("q8_sqrt~"), 0); /* old name */ + CLASS_MAINSIGNALIN(sigsqrt_class, t_sigsqrt, x_f); + class_addmethod(sigsqrt_class, (t_method)sigsqrt_dsp, gensym("dsp"), 0); +} + +/* ------------------------------ wrap~ -------------------------- */ + +typedef struct wrap +{ + t_object x_obj; + float x_f; +} t_sigwrap; + +t_class *sigwrap_class; + +static void *sigwrap_new(void) +{ + t_sigwrap *x = (t_sigwrap *)pd_new(sigwrap_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *sigwrap_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + while (n--) + { + float f = *in++; + int k = f; + if (f > 0) *out++ = f-k; + else *out++ = f - (k-1); + } + return (w + 4); +} + +static void sigwrap_dsp(t_sigwrap *x, t_signal **sp) +{ + dsp_add(sigwrap_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void sigwrap_setup(void) +{ + sigwrap_class = class_new(gensym("wrap~"), (t_newmethod)sigwrap_new, 0, + sizeof(t_sigwrap), 0, 0); + CLASS_MAINSIGNALIN(sigwrap_class, t_sigwrap, x_f); + class_addmethod(sigwrap_class, (t_method)sigwrap_dsp, gensym("dsp"), 0); +} + +/* ------------------------------ mtof_tilde~ -------------------------- */ + +typedef struct mtof_tilde +{ + t_object x_obj; + float x_f; +} t_mtof_tilde; + +t_class *mtof_tilde_class; + +static void *mtof_tilde_new(void) +{ + t_mtof_tilde *x = (t_mtof_tilde *)pd_new(mtof_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *mtof_tilde_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + for (; n--; in++, out++) + { + float f = *in; + if (f <= -1500) *out = 0; + else + { + if (f > 1499) f = 1499; + *out = 8.17579891564 * exp(.0577622650 * f); + } + } + return (w + 4); +} + +static void mtof_tilde_dsp(t_mtof_tilde *x, t_signal **sp) +{ + dsp_add(mtof_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void mtof_tilde_setup(void) +{ + mtof_tilde_class = class_new(gensym("mtof~"), (t_newmethod)mtof_tilde_new, 0, + sizeof(t_mtof_tilde), 0, 0); + CLASS_MAINSIGNALIN(mtof_tilde_class, t_mtof_tilde, x_f); + class_addmethod(mtof_tilde_class, (t_method)mtof_tilde_dsp, gensym("dsp"), 0); +} + +/* ------------------------------ ftom_tilde~ -------------------------- */ + +typedef struct ftom_tilde +{ + t_object x_obj; + float x_f; +} t_ftom_tilde; + +t_class *ftom_tilde_class; + +static void *ftom_tilde_new(void) +{ + t_ftom_tilde *x = (t_ftom_tilde *)pd_new(ftom_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *ftom_tilde_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + for (; n--; *in++, out++) + { + float f = *in; + *out = (f > 0 ? 17.3123405046 * log(.12231220585 * f) : -1500); + } + return (w + 4); +} + +static void ftom_tilde_dsp(t_ftom_tilde *x, t_signal **sp) +{ + dsp_add(ftom_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void ftom_tilde_setup(void) +{ + ftom_tilde_class = class_new(gensym("ftom~"), (t_newmethod)ftom_tilde_new, 0, + sizeof(t_ftom_tilde), 0, 0); + CLASS_MAINSIGNALIN(ftom_tilde_class, t_ftom_tilde, x_f); + class_addmethod(ftom_tilde_class, (t_method)ftom_tilde_dsp, gensym("dsp"), 0); +} + +/* ------------------------------ dbtorms~ -------------------------- */ + +typedef struct dbtorms_tilde +{ + t_object x_obj; + float x_f; +} t_dbtorms_tilde; + +t_class *dbtorms_tilde_class; + +static void *dbtorms_tilde_new(void) +{ + t_dbtorms_tilde *x = (t_dbtorms_tilde *)pd_new(dbtorms_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *dbtorms_tilde_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + for (; n--; in++, out++) + { + float f = *in; + if (f <= 0) *out = 0; + else + { + if (f > 485) + f = 485; + *out = exp((LOGTEN * 0.05) * (f-100.)); + } + } + return (w + 4); +} + +static void dbtorms_tilde_dsp(t_dbtorms_tilde *x, t_signal **sp) +{ + dsp_add(dbtorms_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void dbtorms_tilde_setup(void) +{ + dbtorms_tilde_class = class_new(gensym("dbtorms~"), (t_newmethod)dbtorms_tilde_new, 0, + sizeof(t_dbtorms_tilde), 0, 0); + CLASS_MAINSIGNALIN(dbtorms_tilde_class, t_dbtorms_tilde, x_f); + class_addmethod(dbtorms_tilde_class, (t_method)dbtorms_tilde_dsp, gensym("dsp"), 0); +} + +/* ------------------------------ rmstodb~ -------------------------- */ + +typedef struct rmstodb_tilde +{ + t_object x_obj; + float x_f; +} t_rmstodb_tilde; + +t_class *rmstodb_tilde_class; + +static void *rmstodb_tilde_new(void) +{ + t_rmstodb_tilde *x = (t_rmstodb_tilde *)pd_new(rmstodb_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *rmstodb_tilde_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + for (; n--; in++, out++) + { + float f = *in; + if (f <= 0) *out = 0; + else + { + float g = 100 + 20./LOGTEN * log(f); + *out = (g < 0 ? 0 : g); + } + } + return (w + 4); +} + +static void rmstodb_tilde_dsp(t_rmstodb_tilde *x, t_signal **sp) +{ + dsp_add(rmstodb_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void rmstodb_tilde_setup(void) +{ + rmstodb_tilde_class = class_new(gensym("rmstodb~"), (t_newmethod)rmstodb_tilde_new, 0, + sizeof(t_rmstodb_tilde), 0, 0); + CLASS_MAINSIGNALIN(rmstodb_tilde_class, t_rmstodb_tilde, x_f); + class_addmethod(rmstodb_tilde_class, (t_method)rmstodb_tilde_dsp, gensym("dsp"), 0); +} + +/* ------------------------------ dbtopow~ -------------------------- */ + +typedef struct dbtopow_tilde +{ + t_object x_obj; + float x_f; +} t_dbtopow_tilde; + +t_class *dbtopow_tilde_class; + +static void *dbtopow_tilde_new(void) +{ + t_dbtopow_tilde *x = (t_dbtopow_tilde *)pd_new(dbtopow_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *dbtopow_tilde_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + for (; n--; in++, out++) + { + float f = *in; + if (f <= 0) *out = 0; + else + { + if (f > 870) + f = 870; + *out = exp((LOGTEN * 0.1) * (f-100.)); + } + } + return (w + 4); +} + +static void dbtopow_tilde_dsp(t_dbtopow_tilde *x, t_signal **sp) +{ + dsp_add(dbtopow_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void dbtopow_tilde_setup(void) +{ + dbtopow_tilde_class = class_new(gensym("dbtopow~"), (t_newmethod)dbtopow_tilde_new, 0, + sizeof(t_dbtopow_tilde), 0, 0); + CLASS_MAINSIGNALIN(dbtopow_tilde_class, t_dbtopow_tilde, x_f); + class_addmethod(dbtopow_tilde_class, (t_method)dbtopow_tilde_dsp, gensym("dsp"), 0); +} + +/* ------------------------------ powtodb~ -------------------------- */ + +typedef struct powtodb_tilde +{ + t_object x_obj; + float x_f; +} t_powtodb_tilde; + +t_class *powtodb_tilde_class; + +static void *powtodb_tilde_new(void) +{ + t_powtodb_tilde *x = (t_powtodb_tilde *)pd_new(powtodb_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *powtodb_tilde_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + for (; n--; in++, out++) + { + float f = *in; + if (f <= 0) *out = 0; + else + { + float g = 100 + 10./LOGTEN * log(f); + *out = (g < 0 ? 0 : g); + } + } + return (w + 4); +} + +static void powtodb_tilde_dsp(t_powtodb_tilde *x, t_signal **sp) +{ + dsp_add(powtodb_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void powtodb_tilde_setup(void) +{ + powtodb_tilde_class = class_new(gensym("powtodb~"), (t_newmethod)powtodb_tilde_new, 0, + sizeof(t_powtodb_tilde), 0, 0); + CLASS_MAINSIGNALIN(powtodb_tilde_class, t_powtodb_tilde, x_f); + class_addmethod(powtodb_tilde_class, (t_method)powtodb_tilde_dsp, gensym("dsp"), 0); +} + + +/* ------------------------ global setup routine ------------------------- */ + +void d_math_setup(void) +{ + t_symbol *s = gensym("acoustics~.pd"); + clip_setup(); + sigrsqrt_setup(); + sigsqrt_setup(); + sigwrap_setup(); + mtof_tilde_setup(); + ftom_tilde_setup(); + dbtorms_tilde_setup(); + rmstodb_tilde_setup(); + dbtopow_tilde_setup(); + powtodb_tilde_setup(); + + class_sethelpsymbol(mtof_tilde_class, s); + class_sethelpsymbol(ftom_tilde_class, s); + class_sethelpsymbol(dbtorms_tilde_class, s); + class_sethelpsymbol(rmstodb_tilde_class, s); + class_sethelpsymbol(dbtopow_tilde_class, s); + class_sethelpsymbol(powtodb_tilde_class, s); +} + +/* Copyright (c) 1997-2001 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* mathematical functions and other transfer functions, including tilde + versions of stuff from x_acoustics.c. +*/ + +#include "m_pd.h" +#include +#define LOGTEN 2.302585092994 + +/* ------------------------- clip~ -------------------------- */ +static t_class *clip_class; + +typedef struct _clip +{ + t_object x_obj; + float x_f; + t_sample x_lo; + t_sample x_hi; +} t_clip; + +static void *clip_new(t_floatarg lo, t_floatarg hi) +{ + t_clip *x = (t_clip *)pd_new(clip_class); + x->x_lo = lo; + x->x_hi = hi; + outlet_new(&x->x_obj, gensym("signal")); + floatinlet_new(&x->x_obj, &x->x_lo); + floatinlet_new(&x->x_obj, &x->x_hi); + x->x_f = 0; + return (x); +} + +static t_int *clip_perform(t_int *w) +{ + t_clip *x = (t_clip *)(w[1]); + t_float *in = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + float f = *in++; + if (f < x->x_lo) f = x->x_lo; + if (f > x->x_hi) f = x->x_hi; + *out++ = f; + } + return (w+5); +} + +static void clip_dsp(t_clip *x, t_signal **sp) +{ + dsp_add(clip_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +static void clip_setup(void) +{ + clip_class = class_new(gensym("clip~"), (t_newmethod)clip_new, 0, + sizeof(t_clip), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(clip_class, t_clip, x_f); + class_addmethod(clip_class, (t_method)clip_dsp, gensym("dsp"), 0); +} + +/* sigrsqrt - reciprocal square root good to 8 mantissa bits */ + +#define DUMTAB1SIZE 256 +#define DUMTAB2SIZE 1024 + +static float rsqrt_exptab[DUMTAB1SIZE], rsqrt_mantissatab[DUMTAB2SIZE]; + +static void init_rsqrt(void) +{ + int i; + for (i = 0; i < DUMTAB1SIZE; i++) + { + float f; + long l = (i ? (i == DUMTAB1SIZE-1 ? DUMTAB1SIZE-2 : i) : 1)<< 23; + *(long *)(&f) = l; + rsqrt_exptab[i] = 1./sqrt(f); + } + for (i = 0; i < DUMTAB2SIZE; i++) + { + float f = 1 + (1./DUMTAB2SIZE) * i; + rsqrt_mantissatab[i] = 1./sqrt(f); + } +} + + /* these are used in externs like "bonk" */ + +float q8_rsqrt(float f) +{ + long l = *(long *)(&f); + if (f < 0) return (0); + else return (rsqrt_exptab[(l >> 23) & 0xff] * + rsqrt_mantissatab[(l >> 13) & 0x3ff]); +} + +float q8_sqrt(float f) +{ + long l = *(long *)(&f); + if (f < 0) return (0); + else return (f * rsqrt_exptab[(l >> 23) & 0xff] * + rsqrt_mantissatab[(l >> 13) & 0x3ff]); +} + + /* the old names are OK unless we're in IRIX N32 */ + +#ifndef N32 +float qsqrt(float f) {return (q8_sqrt(f)); } +float qrsqrt(float f) {return (q8_rsqrt(f)); } +#endif + + + +typedef struct sigrsqrt +{ + t_object x_obj; + float x_f; +} t_sigrsqrt; + +static t_class *sigrsqrt_class; + +static void *sigrsqrt_new(void) +{ + t_sigrsqrt *x = (t_sigrsqrt *)pd_new(sigrsqrt_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *sigrsqrt_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + while (n--) + { + float f = *in; + long l = *(long *)(in++); + if (f < 0) *out++ = 0; + else + { + float g = rsqrt_exptab[(l >> 23) & 0xff] * + rsqrt_mantissatab[(l >> 13) & 0x3ff]; + *out++ = 1.5 * g - 0.5 * g * g * g * f; + } + } + return (w + 4); +} + +static void sigrsqrt_dsp(t_sigrsqrt *x, t_signal **sp) +{ + dsp_add(sigrsqrt_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void sigrsqrt_setup(void) +{ + init_rsqrt(); + sigrsqrt_class = class_new(gensym("rsqrt~"), (t_newmethod)sigrsqrt_new, 0, + sizeof(t_sigrsqrt), 0, 0); + /* an old name for it: */ + class_addcreator(sigrsqrt_new, gensym("q8_rsqrt~"), 0); + CLASS_MAINSIGNALIN(sigrsqrt_class, t_sigrsqrt, x_f); + class_addmethod(sigrsqrt_class, (t_method)sigrsqrt_dsp, gensym("dsp"), 0); +} + + +/* sigsqrt - square root good to 8 mantissa bits */ + +typedef struct sigsqrt +{ + t_object x_obj; + float x_f; +} t_sigsqrt; + +static t_class *sigsqrt_class; + +static void *sigsqrt_new(void) +{ + t_sigsqrt *x = (t_sigsqrt *)pd_new(sigsqrt_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +t_int *sigsqrt_perform(t_int *w) /* not static; also used in d_fft.c */ +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + while (n--) + { + float f = *in; + long l = *(long *)(in++); + if (f < 0) *out++ = 0; + else + { + float g = rsqrt_exptab[(l >> 23) & 0xff] * + rsqrt_mantissatab[(l >> 13) & 0x3ff]; + *out++ = f * (1.5 * g - 0.5 * g * g * g * f); + } + } + return (w + 4); +} + +static void sigsqrt_dsp(t_sigsqrt *x, t_signal **sp) +{ + dsp_add(sigsqrt_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void sigsqrt_setup(void) +{ + sigsqrt_class = class_new(gensym("sqrt~"), (t_newmethod)sigsqrt_new, 0, + sizeof(t_sigsqrt), 0, 0); + class_addcreator(sigsqrt_new, gensym("q8_sqrt~"), 0); /* old name */ + CLASS_MAINSIGNALIN(sigsqrt_class, t_sigsqrt, x_f); + class_addmethod(sigsqrt_class, (t_method)sigsqrt_dsp, gensym("dsp"), 0); +} + +/* ------------------------------ wrap~ -------------------------- */ + +typedef struct wrap +{ + t_object x_obj; + float x_f; +} t_sigwrap; + +t_class *sigwrap_class; + +static void *sigwrap_new(void) +{ + t_sigwrap *x = (t_sigwrap *)pd_new(sigwrap_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *sigwrap_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + while (n--) + { + float f = *in++; + int k = f; + if (f > 0) *out++ = f-k; + else *out++ = f - (k-1); + } + return (w + 4); +} + +static void sigwrap_dsp(t_sigwrap *x, t_signal **sp) +{ + dsp_add(sigwrap_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void sigwrap_setup(void) +{ + sigwrap_class = class_new(gensym("wrap~"), (t_newmethod)sigwrap_new, 0, + sizeof(t_sigwrap), 0, 0); + CLASS_MAINSIGNALIN(sigwrap_class, t_sigwrap, x_f); + class_addmethod(sigwrap_class, (t_method)sigwrap_dsp, gensym("dsp"), 0); +} + +/* ------------------------------ mtof_tilde~ -------------------------- */ + +typedef struct mtof_tilde +{ + t_object x_obj; + float x_f; +} t_mtof_tilde; + +t_class *mtof_tilde_class; + +static void *mtof_tilde_new(void) +{ + t_mtof_tilde *x = (t_mtof_tilde *)pd_new(mtof_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *mtof_tilde_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + for (; n--; in++, out++) + { + float f = *in; + if (f <= -1500) *out = 0; + else + { + if (f > 1499) f = 1499; + *out = 8.17579891564 * exp(.0577622650 * f); + } + } + return (w + 4); +} + +static void mtof_tilde_dsp(t_mtof_tilde *x, t_signal **sp) +{ + dsp_add(mtof_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void mtof_tilde_setup(void) +{ + mtof_tilde_class = class_new(gensym("mtof~"), (t_newmethod)mtof_tilde_new, 0, + sizeof(t_mtof_tilde), 0, 0); + CLASS_MAINSIGNALIN(mtof_tilde_class, t_mtof_tilde, x_f); + class_addmethod(mtof_tilde_class, (t_method)mtof_tilde_dsp, gensym("dsp"), 0); +} + +/* ------------------------------ ftom_tilde~ -------------------------- */ + +typedef struct ftom_tilde +{ + t_object x_obj; + float x_f; +} t_ftom_tilde; + +t_class *ftom_tilde_class; + +static void *ftom_tilde_new(void) +{ + t_ftom_tilde *x = (t_ftom_tilde *)pd_new(ftom_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *ftom_tilde_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + for (; n--; *in++, out++) + { + float f = *in; + *out = (f > 0 ? 17.3123405046 * log(.12231220585 * f) : -1500); + } + return (w + 4); +} + +static void ftom_tilde_dsp(t_ftom_tilde *x, t_signal **sp) +{ + dsp_add(ftom_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void ftom_tilde_setup(void) +{ + ftom_tilde_class = class_new(gensym("ftom~"), (t_newmethod)ftom_tilde_new, 0, + sizeof(t_ftom_tilde), 0, 0); + CLASS_MAINSIGNALIN(ftom_tilde_class, t_ftom_tilde, x_f); + class_addmethod(ftom_tilde_class, (t_method)ftom_tilde_dsp, gensym("dsp"), 0); +} + +/* ------------------------------ dbtorms~ -------------------------- */ + +typedef struct dbtorms_tilde +{ + t_object x_obj; + float x_f; +} t_dbtorms_tilde; + +t_class *dbtorms_tilde_class; + +static void *dbtorms_tilde_new(void) +{ + t_dbtorms_tilde *x = (t_dbtorms_tilde *)pd_new(dbtorms_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *dbtorms_tilde_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + for (; n--; in++, out++) + { + float f = *in; + if (f <= 0) *out = 0; + else + { + if (f > 485) + f = 485; + *out = exp((LOGTEN * 0.05) * (f-100.)); + } + } + return (w + 4); +} + +static void dbtorms_tilde_dsp(t_dbtorms_tilde *x, t_signal **sp) +{ + dsp_add(dbtorms_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void dbtorms_tilde_setup(void) +{ + dbtorms_tilde_class = class_new(gensym("dbtorms~"), (t_newmethod)dbtorms_tilde_new, 0, + sizeof(t_dbtorms_tilde), 0, 0); + CLASS_MAINSIGNALIN(dbtorms_tilde_class, t_dbtorms_tilde, x_f); + class_addmethod(dbtorms_tilde_class, (t_method)dbtorms_tilde_dsp, gensym("dsp"), 0); +} + +/* ------------------------------ rmstodb~ -------------------------- */ + +typedef struct rmstodb_tilde +{ + t_object x_obj; + float x_f; +} t_rmstodb_tilde; + +t_class *rmstodb_tilde_class; + +static void *rmstodb_tilde_new(void) +{ + t_rmstodb_tilde *x = (t_rmstodb_tilde *)pd_new(rmstodb_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *rmstodb_tilde_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + for (; n--; in++, out++) + { + float f = *in; + if (f <= 0) *out = 0; + else + { + float g = 100 + 20./LOGTEN * log(f); + *out = (g < 0 ? 0 : g); + } + } + return (w + 4); +} + +static void rmstodb_tilde_dsp(t_rmstodb_tilde *x, t_signal **sp) +{ + dsp_add(rmstodb_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void rmstodb_tilde_setup(void) +{ + rmstodb_tilde_class = class_new(gensym("rmstodb~"), (t_newmethod)rmstodb_tilde_new, 0, + sizeof(t_rmstodb_tilde), 0, 0); + CLASS_MAINSIGNALIN(rmstodb_tilde_class, t_rmstodb_tilde, x_f); + class_addmethod(rmstodb_tilde_class, (t_method)rmstodb_tilde_dsp, gensym("dsp"), 0); +} + +/* ------------------------------ dbtopow~ -------------------------- */ + +typedef struct dbtopow_tilde +{ + t_object x_obj; + float x_f; +} t_dbtopow_tilde; + +t_class *dbtopow_tilde_class; + +static void *dbtopow_tilde_new(void) +{ + t_dbtopow_tilde *x = (t_dbtopow_tilde *)pd_new(dbtopow_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *dbtopow_tilde_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + for (; n--; in++, out++) + { + float f = *in; + if (f <= 0) *out = 0; + else + { + if (f > 870) + f = 870; + *out = exp((LOGTEN * 0.1) * (f-100.)); + } + } + return (w + 4); +} + +static void dbtopow_tilde_dsp(t_dbtopow_tilde *x, t_signal **sp) +{ + dsp_add(dbtopow_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void dbtopow_tilde_setup(void) +{ + dbtopow_tilde_class = class_new(gensym("dbtopow~"), (t_newmethod)dbtopow_tilde_new, 0, + sizeof(t_dbtopow_tilde), 0, 0); + CLASS_MAINSIGNALIN(dbtopow_tilde_class, t_dbtopow_tilde, x_f); + class_addmethod(dbtopow_tilde_class, (t_method)dbtopow_tilde_dsp, gensym("dsp"), 0); +} + +/* ------------------------------ powtodb~ -------------------------- */ + +typedef struct powtodb_tilde +{ + t_object x_obj; + float x_f; +} t_powtodb_tilde; + +t_class *powtodb_tilde_class; + +static void *powtodb_tilde_new(void) +{ + t_powtodb_tilde *x = (t_powtodb_tilde *)pd_new(powtodb_tilde_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *powtodb_tilde_perform(t_int *w) +{ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); + t_int n = *(t_int *)(w+3); + for (; n--; in++, out++) + { + float f = *in; + if (f <= 0) *out = 0; + else + { + float g = 100 + 10./LOGTEN * log(f); + *out = (g < 0 ? 0 : g); + } + } + return (w + 4); +} + +static void powtodb_tilde_dsp(t_powtodb_tilde *x, t_signal **sp) +{ + dsp_add(powtodb_tilde_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +void powtodb_tilde_setup(void) +{ + powtodb_tilde_class = class_new(gensym("powtodb~"), (t_newmethod)powtodb_tilde_new, 0, + sizeof(t_powtodb_tilde), 0, 0); + CLASS_MAINSIGNALIN(powtodb_tilde_class, t_powtodb_tilde, x_f); + class_addmethod(powtodb_tilde_class, (t_method)powtodb_tilde_dsp, gensym("dsp"), 0); +} + + +/* ------------------------ global setup routine ------------------------- */ + +void d_math_setup(void) +{ + t_symbol *s = gensym("acoustics~.pd"); + clip_setup(); + sigrsqrt_setup(); + sigsqrt_setup(); + sigwrap_setup(); + mtof_tilde_setup(); + ftom_tilde_setup(); + dbtorms_tilde_setup(); + rmstodb_tilde_setup(); + dbtopow_tilde_setup(); + powtodb_tilde_setup(); + + class_sethelpsymbol(mtof_tilde_class, s); + class_sethelpsymbol(ftom_tilde_class, s); + class_sethelpsymbol(dbtorms_tilde_class, s); + class_sethelpsymbol(rmstodb_tilde_class, s); + class_sethelpsymbol(dbtopow_tilde_class, s); + class_sethelpsymbol(powtodb_tilde_class, s); +} + diff --git a/apps/plugins/pdbox/PDa/src/d_mayer_fft.c b/apps/plugins/pdbox/PDa/src/d_mayer_fft.c new file mode 100644 index 0000000..7c29c6e --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_mayer_fft.c @@ -0,0 +1,838 @@ +/* +** FFT and FHT routines +** Copyright 1988, 1993; Ron Mayer +** +** mayer_fht(fz,n); +** Does a hartley transform of "n" points in the array "fz". +** mayer_fft(n,real,imag) +** Does a fourier transform of "n" points of the "real" and +** "imag" arrays. +** mayer_ifft(n,real,imag) +** Does an inverse fourier transform of "n" points of the "real" +** and "imag" arrays. +** mayer_realfft(n,real) +** Does a real-valued fourier transform of "n" points of the +** "real" array. The real part of the transform ends +** up in the first half of the array and the imaginary part of the +** transform ends up in the second half of the array. +** mayer_realifft(n,real) +** The inverse of the realfft() routine above. +** +** +** NOTE: This routine uses at least 2 patented algorithms, and may be +** under the restrictions of a bunch of different organizations. +** Although I wrote it completely myself, it is kind of a derivative +** of a routine I once authored and released under the GPL, so it +** may fall under the free software foundation's restrictions; +** it was worked on as a Stanford Univ project, so they claim +** some rights to it; it was further optimized at work here, so +** I think this company claims parts of it. The patents are +** held by R. Bracewell (the FHT algorithm) and O. Buneman (the +** trig generator), both at Stanford Univ. +** If it were up to me, I'd say go do whatever you want with it; +** but it would be polite to give credit to the following people +** if you use this anywhere: +** Euler - probable inventor of the fourier transform. +** Gauss - probable inventor of the FFT. +** Hartley - probable inventor of the hartley transform. +** Buneman - for a really cool trig generator +** Mayer(me) - for authoring this particular version and +** including all the optimizations in one package. +** Thanks, +** Ron Mayer; mayer@acuson.com +** +*/ + +/* This is a slightly modified version of Mayer's contribution; write +* msp@ucsd.edu for the original code. Kudos to Mayer for a fine piece +* of work. -msp +*/ + +#ifdef MSW +#pragma warning( disable : 4305 ) /* uncast const double to float */ +#pragma warning( disable : 4244 ) /* uncast double to float */ +#pragma warning( disable : 4101 ) /* unused local variables */ +#endif + +#define REAL float +#define GOOD_TRIG + +#ifdef GOOD_TRIG +#else +#define FAST_TRIG +#endif + +#if defined(GOOD_TRIG) +#define FHT_SWAP(a,b,t) {(t)=(a);(a)=(b);(b)=(t);} +#define TRIG_VARS \ + int t_lam=0; +#define TRIG_INIT(k,c,s) \ + { \ + int i; \ + for (i=2 ; i<=k ; i++) \ + {coswrk[i]=costab[i];sinwrk[i]=sintab[i];} \ + t_lam = 0; \ + c = 1; \ + s = 0; \ + } +#define TRIG_NEXT(k,c,s) \ + { \ + int i,j; \ + (t_lam)++; \ + for (i=0 ; !((1<1) \ + { \ + for (j=k-i+2 ; (1<>1; (!((k2^=k)&k)); k>>=1); + if (k1>k2) + { + aa=fz[k1];fz[k1]=fz[k2];fz[k2]=aa; + } + } + for ( k=0 ; (1<> 1; + fi = fz; + gi = fi + kx; + fn = fz + n; + do + { + REAL g0,f0,f1,g1,f2,g2,f3,g3; + f1 = fi[0 ] - fi[k1]; + f0 = fi[0 ] + fi[k1]; + f3 = fi[k2] - fi[k3]; + f2 = fi[k2] + fi[k3]; + fi[k2] = f0 - f2; + fi[0 ] = f0 + f2; + fi[k3] = f1 - f3; + fi[k1] = f1 + f3; + g1 = gi[0 ] - gi[k1]; + g0 = gi[0 ] + gi[k1]; + g3 = SQRT2 * gi[k3]; + g2 = SQRT2 * gi[k2]; + gi[k2] = g0 - g2; + gi[0 ] = g0 + g2; + gi[k3] = g1 - g3; + gi[k1] = g1 + g3; + gi += k4; + fi += k4; + } while (fi1) \ + { \ + for (j=k-i+2 ; (1<>1; (!((k2^=k)&k)); k>>=1); + if (k1>k2) + { + aa=fz[k1];fz[k1]=fz[k2];fz[k2]=aa; + } + } + for ( k=0 ; (1<> 1; + fi = fz; + gi = fi + kx; + fn = fz + n; + do + { + REAL g0,f0,f1,g1,f2,g2,f3,g3; + f1 = fi[0 ] - fi[k1]; + f0 = fi[0 ] + fi[k1]; + f3 = fi[k2] - fi[k3]; + f2 = fi[k2] + fi[k3]; + fi[k2] = f0 - f2; + fi[0 ] = f0 + f2; + fi[k3] = f1 - f3; + fi[k1] = f1 + f3; + g1 = gi[0 ] - gi[k1]; + g0 = gi[0 ] + gi[k1]; + g3 = SQRT2 * gi[k3]; + g2 = SQRT2 * gi[k2]; + gi[k2] = g0 - g2; + gi[0 ] = g0 + g2; + gi[k3] = g1 - g3; + gi[k1] = g1 + g3; + gi += k4; + fi += k4; + } while (fi +#include + +/* ------------------------- print~ -------------------------- */ +static t_class *print_class; + +typedef struct _print +{ + t_object x_obj; + float x_f; + t_symbol *x_sym; + int x_count; +} t_print; + +static t_int *print_perform(t_int *w) +{ + t_print *x = (t_print *)(w[1]); + t_float *in = (t_float *)(w[2]); + int n = (int)(w[3]); + if (x->x_count) + { + post("%s:", x->x_sym->s_name); + if (n == 1) post("%8g", in[0]); + else if (n == 2) post("%8g %8g", in[0], in[1]); + else if (n == 4) post("%8g %8g %8g %8g", + in[0], in[1], in[2], in[3]); + else while (n > 0) + { + post("%-8.5g %-8.5g %-8.5g %-8.5g %-8.5g %-8.5g %-8.5g %-8.5g", + in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7]); + n -= 8; + in += 8; + } + x->x_count--; + } + return (w+4); +} + +static void print_dsp(t_print *x, t_signal **sp) +{ + dsp_add(print_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); +} + +static void print_float(t_print *x, t_float f) +{ + if (f < 0) f = 0; + x->x_count = f; +} + +static void print_bang(t_print *x) +{ + x->x_count = 1; +} + +static void *print_new(t_symbol *s) +{ + t_print *x = (t_print *)pd_new(print_class); + x->x_sym = (s->s_name[0]? s : gensym("print~")); + x->x_count = 0; + x->x_f = 0; + return (x); +} + +static void print_setup(void) +{ + print_class = class_new(gensym("print~"), (t_newmethod)print_new, 0, + sizeof(t_print), 0, A_DEFSYM, 0); + CLASS_MAINSIGNALIN(print_class, t_print, x_f); + class_addmethod(print_class, (t_method)print_dsp, gensym("dsp"), 0); + class_addbang(print_class, print_bang); + class_addfloat(print_class, print_float); +} + +/* ------------------------- scope~ -------------------------- */ +/* this has been replaced by arrays; to be deleted later */ + +#include "g_canvas.h" + +static t_class *scope_class; + +#define SCOPESIZE 256 + +typedef struct _scope +{ + t_object x_obj; + t_sample x_samps[SCOPESIZE]; + int x_phase; + int x_drawn; + void *x_canvas; +} t_scope; + +static t_int *scope_perform(t_int *w) +{ + t_scope *x = (t_scope *)(w[1]); + t_float *in = (t_float *)(w[2]); + int n = (int)(w[3]), phase = x->x_phase; + while (n--) + { + x->x_samps[phase] = *in++; + phase = (phase + 1) & (SCOPESIZE-1); + } + x->x_phase = phase; + return (w+4); +} + +static void scope_dsp(t_scope *x, t_signal **sp) +{ + dsp_add(scope_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); +} + +static void scope_erase(t_scope *x) +{ + if (x->x_drawn) sys_vgui(".x%x.c delete gumbo\n", x->x_canvas); +} + +#define X1 10. +#define X2 20. +#define YC 5. +static void scope_bang(t_scope *x) +{ + int n, phase; + char hugebuf[10000], *s = hugebuf; + scope_erase(x); + sys_vgui(".x%x.c create line 10c 5c 20c 5c -tags gumbo\n", x->x_canvas); + sprintf(s, ".x%x.c create line ", (t_int)x->x_canvas); + s += strlen(s); + for (n = 0, phase = x->x_phase; + n < SCOPESIZE; phase = ((phase+1) & (SCOPESIZE-1)), n++) + { + sprintf(s, "%fc %fc ", X1 + (X2 - X1) * (float)n * (1./SCOPESIZE), + YC - 5 * x->x_samps[phase]); + s += strlen(s); + /* post("phase %d", phase); */ + } + sprintf(s, "-tags gumbo\n"); + sys_gui(hugebuf); + x->x_drawn = 1; +} + +static void scope_free(t_scope *x) +{ + scope_erase(x); +} + +static void *scope_new(t_symbol *s) +{ + t_scope *x = (t_scope *)pd_new(scope_class); + error("scope: this is now obsolete; use arrays and tabwrite~ instead"); + x->x_phase = 0; + x->x_drawn = 0; + x->x_canvas = canvas_getcurrent(); + return (x); +} + +static void scope_setup(void) +{ + scope_class = class_new(gensym("scope~"), (t_newmethod)scope_new, + (t_method)scope_free, sizeof(t_scope), 0, A_DEFSYM, 0); + class_addmethod(scope_class, nullfn, gensym("signal"), 0); + class_addmethod(scope_class, (t_method)scope_dsp, gensym("dsp"), 0); + class_addbang(scope_class, scope_bang); +} + +/* ------------------------ bang~ -------------------------- */ + +static t_class *bang_tilde_class; + +typedef struct _bang +{ + t_object x_obj; + t_clock *x_clock; +} t_bang; + +static t_int *bang_tilde_perform(t_int *w) +{ + t_bang *x = (t_bang *)(w[1]); + clock_delay(x->x_clock, 0); + return (w+2); +} + +static void bang_tilde_dsp(t_bang *x, t_signal **sp) +{ + dsp_add(bang_tilde_perform, 1, x); +} + +static void bang_tilde_tick(t_bang *x) +{ + outlet_bang(x->x_obj.ob_outlet); +} + +static void bang_tilde_free(t_bang *x) +{ + clock_free(x->x_clock); +} + +static void *bang_tilde_new(t_symbol *s) +{ + t_bang *x = (t_bang *)pd_new(bang_tilde_class); + x->x_clock = clock_new(x, (t_method)bang_tilde_tick); + outlet_new(&x->x_obj, &s_bang); + return (x); +} + +static void bang_tilde_setup(void) +{ + bang_tilde_class = class_new(gensym("bang~"), (t_newmethod)bang_tilde_new, + (t_method)bang_tilde_free, sizeof(t_bang), 0, 0); + class_addmethod(bang_tilde_class, (t_method)bang_tilde_dsp, + gensym("dsp"), 0); +} + +/* ------------------------ samplerate~~ -------------------------- */ + +static t_class *samplerate_tilde_class; + +typedef struct _samplerate +{ + t_object x_obj; +} t_samplerate; + +static void samplerate_tilde_bang(t_samplerate *x) +{ + outlet_float(x->x_obj.ob_outlet, sys_getsr()); +} + +static void *samplerate_tilde_new(t_symbol *s) +{ + t_samplerate *x = (t_samplerate *)pd_new(samplerate_tilde_class); + outlet_new(&x->x_obj, &s_float); + return (x); +} + +static void samplerate_tilde_setup(void) +{ + samplerate_tilde_class = class_new(gensym("samplerate~"), + (t_newmethod)samplerate_tilde_new, 0, sizeof(t_samplerate), 0, 0); + class_addbang(samplerate_tilde_class, samplerate_tilde_bang); +} + +/* ------------------------ global setup routine ------------------------- */ + +void d_misc_setup(void) +{ +#ifndef FIXEDPOINT + print_setup(); +#endif + scope_setup(); + bang_tilde_setup(); + samplerate_tilde_setup(); +} + + + + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* miscellaneous: print~; more to come. +*/ + +#include "m_pd.h" +#include +#include + +/* ------------------------- print~ -------------------------- */ +static t_class *print_class; + +typedef struct _print +{ + t_object x_obj; + float x_f; + t_symbol *x_sym; + int x_count; +} t_print; + +static t_int *print_perform(t_int *w) +{ + t_print *x = (t_print *)(w[1]); + t_float *in = (t_float *)(w[2]); + int n = (int)(w[3]); + if (x->x_count) + { + post("%s:", x->x_sym->s_name); + if (n == 1) post("%8g", in[0]); + else if (n == 2) post("%8g %8g", in[0], in[1]); + else if (n == 4) post("%8g %8g %8g %8g", + in[0], in[1], in[2], in[3]); + else while (n > 0) + { + post("%-8.5g %-8.5g %-8.5g %-8.5g %-8.5g %-8.5g %-8.5g %-8.5g", + in[0], in[1], in[2], in[3], in[4], in[5], in[6], in[7]); + n -= 8; + in += 8; + } + x->x_count--; + } + return (w+4); +} + +static void print_dsp(t_print *x, t_signal **sp) +{ + dsp_add(print_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); +} + +static void print_float(t_print *x, t_float f) +{ + if (f < 0) f = 0; + x->x_count = f; +} + +static void print_bang(t_print *x) +{ + x->x_count = 1; +} + +static void *print_new(t_symbol *s) +{ + t_print *x = (t_print *)pd_new(print_class); + x->x_sym = (s->s_name[0]? s : gensym("print~")); + x->x_count = 0; + x->x_f = 0; + return (x); +} + +static void print_setup(void) +{ + print_class = class_new(gensym("print~"), (t_newmethod)print_new, 0, + sizeof(t_print), 0, A_DEFSYM, 0); + CLASS_MAINSIGNALIN(print_class, t_print, x_f); + class_addmethod(print_class, (t_method)print_dsp, gensym("dsp"), 0); + class_addbang(print_class, print_bang); + class_addfloat(print_class, print_float); +} + +/* ------------------------- scope~ -------------------------- */ +/* this has been replaced by arrays; to be deleted later */ + +#include "g_canvas.h" + +static t_class *scope_class; + +#define SCOPESIZE 256 + +typedef struct _scope +{ + t_object x_obj; + t_sample x_samps[SCOPESIZE]; + int x_phase; + int x_drawn; + void *x_canvas; +} t_scope; + +static t_int *scope_perform(t_int *w) +{ + t_scope *x = (t_scope *)(w[1]); + t_float *in = (t_float *)(w[2]); + int n = (int)(w[3]), phase = x->x_phase; + while (n--) + { + x->x_samps[phase] = *in++; + phase = (phase + 1) & (SCOPESIZE-1); + } + x->x_phase = phase; + return (w+4); +} + +static void scope_dsp(t_scope *x, t_signal **sp) +{ + dsp_add(scope_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); +} + +static void scope_erase(t_scope *x) +{ + if (x->x_drawn) sys_vgui(".x%x.c delete gumbo\n", x->x_canvas); +} + +#define X1 10. +#define X2 20. +#define YC 5. +static void scope_bang(t_scope *x) +{ + int n, phase; + char hugebuf[10000], *s = hugebuf; + scope_erase(x); + sys_vgui(".x%x.c create line 10c 5c 20c 5c -tags gumbo\n", x->x_canvas); + sprintf(s, ".x%x.c create line ", (t_int)x->x_canvas); + s += strlen(s); + for (n = 0, phase = x->x_phase; + n < SCOPESIZE; phase = ((phase+1) & (SCOPESIZE-1)), n++) + { + sprintf(s, "%fc %fc ", X1 + (X2 - X1) * (float)n * (1./SCOPESIZE), + YC - 5 * x->x_samps[phase]); + s += strlen(s); + /* post("phase %d", phase); */ + } + sprintf(s, "-tags gumbo\n"); + sys_gui(hugebuf); + x->x_drawn = 1; +} + +static void scope_free(t_scope *x) +{ + scope_erase(x); +} + +static void *scope_new(t_symbol *s) +{ + t_scope *x = (t_scope *)pd_new(scope_class); + error("scope: this is now obsolete; use arrays and tabwrite~ instead"); + x->x_phase = 0; + x->x_drawn = 0; + x->x_canvas = canvas_getcurrent(); + return (x); +} + +static void scope_setup(void) +{ + scope_class = class_new(gensym("scope~"), (t_newmethod)scope_new, + (t_method)scope_free, sizeof(t_scope), 0, A_DEFSYM, 0); + class_addmethod(scope_class, nullfn, gensym("signal"), 0); + class_addmethod(scope_class, (t_method)scope_dsp, gensym("dsp"), 0); + class_addbang(scope_class, scope_bang); +} + +/* ------------------------ bang~ -------------------------- */ + +static t_class *bang_tilde_class; + +typedef struct _bang +{ + t_object x_obj; + t_clock *x_clock; +} t_bang; + +static t_int *bang_tilde_perform(t_int *w) +{ + t_bang *x = (t_bang *)(w[1]); + clock_delay(x->x_clock, 0); + return (w+2); +} + +static void bang_tilde_dsp(t_bang *x, t_signal **sp) +{ + dsp_add(bang_tilde_perform, 1, x); +} + +static void bang_tilde_tick(t_bang *x) +{ + outlet_bang(x->x_obj.ob_outlet); +} + +static void bang_tilde_free(t_bang *x) +{ + clock_free(x->x_clock); +} + +static void *bang_tilde_new(t_symbol *s) +{ + t_bang *x = (t_bang *)pd_new(bang_tilde_class); + x->x_clock = clock_new(x, (t_method)bang_tilde_tick); + outlet_new(&x->x_obj, &s_bang); + return (x); +} + +static void bang_tilde_setup(void) +{ + bang_tilde_class = class_new(gensym("bang~"), (t_newmethod)bang_tilde_new, + (t_method)bang_tilde_free, sizeof(t_bang), 0, 0); + class_addmethod(bang_tilde_class, (t_method)bang_tilde_dsp, + gensym("dsp"), 0); +} + +/* ------------------------ samplerate~~ -------------------------- */ + +static t_class *samplerate_tilde_class; + +typedef struct _samplerate +{ + t_object x_obj; +} t_samplerate; + +static void samplerate_tilde_bang(t_samplerate *x) +{ + outlet_float(x->x_obj.ob_outlet, sys_getsr()); +} + +static void *samplerate_tilde_new(t_symbol *s) +{ + t_samplerate *x = (t_samplerate *)pd_new(samplerate_tilde_class); + outlet_new(&x->x_obj, &s_float); + return (x); +} + +static void samplerate_tilde_setup(void) +{ + samplerate_tilde_class = class_new(gensym("samplerate~"), + (t_newmethod)samplerate_tilde_new, 0, sizeof(t_samplerate), 0, 0); + class_addbang(samplerate_tilde_class, samplerate_tilde_bang); +} + +/* ------------------------ global setup routine ------------------------- */ + +void d_misc_setup(void) +{ +#ifndef FIXEDPOINT + print_setup(); +#endif + scope_setup(); + bang_tilde_setup(); + samplerate_tilde_setup(); +} + + + + diff --git a/apps/plugins/pdbox/PDa/src/d_osc.c b/apps/plugins/pdbox/PDa/src/d_osc.c new file mode 100644 index 0000000..8a3486d --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_osc.c @@ -0,0 +1,1070 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* sinusoidal oscillator and table lookup; see also tabosc4~ in d_array.c. +*/ + +#include "m_pd.h" +#include "math.h" + +#define UNITBIT32 1572864. /* 3*2^19; bit 32 has place value 1 */ + + /* machine-dependent definitions. These ifdefs really + should have been by CPU type and not by operating system! */ +#ifdef IRIX + /* big-endian. Most significant byte is at low address in memory */ +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#define int32 long /* a data type that has 32 bits */ +#else +#ifdef MSW + /* little-endian; most significant byte is at highest address */ +#define HIOFFSET 1 +#define LOWOFFSET 0 +#define int32 long +#else +#ifdef __FreeBSD__ +#include +#if BYTE_ORDER == LITTLE_ENDIAN +#define HIOFFSET 1 +#define LOWOFFSET 0 +#else +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#endif /* BYTE_ORDER */ +#include +#define int32 int32_t +#endif +#ifdef __linux__ + +#include + +#if !defined(__BYTE_ORDER) || !defined(__LITTLE_ENDIAN) +#error No byte order defined +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define HIOFFSET 1 +#define LOWOFFSET 0 +#else +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#endif /* __BYTE_ORDER */ + +#include +#define int32 int32_t + +#else +#ifdef MACOSX +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#define int32 int /* a data type that has 32 bits */ + +#endif /* MACOSX */ +#endif /* __linux__ */ +#endif /* MSW */ +#endif /* SGI */ + +union tabfudge +{ + double tf_d; + int32 tf_i[2]; +}; + + +/* -------------------------- phasor~ ------------------------------ */ +static t_class *phasor_class, *scalarphasor_class; + +#if 1 /* in the style of R. Hoeldrich (ICMC 1995 Banff) */ + +typedef struct _phasor +{ + t_object x_obj; + double x_phase; + float x_conv; + float x_f; /* scalar frequency */ +} t_phasor; + +static void *phasor_new(t_floatarg f) +{ + t_phasor *x = (t_phasor *)pd_new(phasor_class); + x->x_f = f; + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1")); + x->x_phase = 0; + x->x_conv = 0; + outlet_new(&x->x_obj, gensym("signal")); + return (x); +} + +static t_int *phasor_perform(t_int *w) +{ + t_phasor *x = (t_phasor *)(w[1]); + t_float *in = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + double dphase = x->x_phase + UNITBIT32; + union tabfudge tf; + int normhipart; + float conv = x->x_conv; + + tf.tf_d = UNITBIT32; + normhipart = tf.tf_i[HIOFFSET]; + tf.tf_d = dphase; + + while (n--) + { + tf.tf_i[HIOFFSET] = normhipart; + dphase += *in++ * conv; + *out++ = tf.tf_d - UNITBIT32; + tf.tf_d = dphase; + } + tf.tf_i[HIOFFSET] = normhipart; + x->x_phase = tf.tf_d - UNITBIT32; + return (w+5); +} + +static void phasor_dsp(t_phasor *x, t_signal **sp) +{ + x->x_conv = 1./sp[0]->s_sr; + dsp_add(phasor_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +static void phasor_ft1(t_phasor *x, t_float f) +{ + x->x_phase = f; +} + +static void phasor_setup(void) +{ + phasor_class = class_new(gensym("phasor~"), (t_newmethod)phasor_new, 0, + sizeof(t_phasor), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(phasor_class, t_phasor, x_f); + class_addmethod(phasor_class, (t_method)phasor_dsp, gensym("dsp"), 0); + class_addmethod(phasor_class, (t_method)phasor_ft1, + gensym("ft1"), A_FLOAT, 0); +} + +#endif /* Hoeldrich version */ + +/* ------------------------ cos~ ----------------------------- */ + +float *cos_table; + +static t_class *cos_class; + +typedef struct _cos +{ + t_object x_obj; + float x_f; +} t_cos; + +static void *cos_new(void) +{ + t_cos *x = (t_cos *)pd_new(cos_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *cos_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + float *tab = cos_table, *addr, f1, f2, frac; + double dphase; + int normhipart; + union tabfudge tf; + + tf.tf_d = UNITBIT32; + normhipart = tf.tf_i[HIOFFSET]; + +#if 0 /* this is the readable version of the code. */ + while (n--) + { + dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32; + tf.tf_d = dphase; + addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); + tf.tf_i[HIOFFSET] = normhipart; + frac = tf.tf_d - UNITBIT32; + f1 = addr[0]; + f2 = addr[1]; + *out++ = f1 + frac * (f2 - f1); + } +#endif +#if 1 /* this is the same, unwrapped by hand. */ + dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32; + tf.tf_d = dphase; + addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); + tf.tf_i[HIOFFSET] = normhipart; + while (--n) + { + dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32; + frac = tf.tf_d - UNITBIT32; + tf.tf_d = dphase; + f1 = addr[0]; + f2 = addr[1]; + addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); + *out++ = f1 + frac * (f2 - f1); + tf.tf_i[HIOFFSET] = normhipart; + } + frac = tf.tf_d - UNITBIT32; + f1 = addr[0]; + f2 = addr[1]; + *out++ = f1 + frac * (f2 - f1); +#endif + return (w+4); +} + +static void cos_dsp(t_cos *x, t_signal **sp) +{ + dsp_add(cos_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +static void cos_maketable(void) +{ + int i; + float *fp, phase, phsinc = (2. * 3.14159) / COSTABSIZE; + union tabfudge tf; + + if (cos_table) return; + cos_table = (float *)getbytes(sizeof(float) * (COSTABSIZE+1)); + for (i = COSTABSIZE + 1, fp = cos_table, phase = 0; i--; + fp++, phase += phsinc) + *fp = cos(phase); + + /* here we check at startup whether the byte alignment + is as we declared it. If not, the code has to be + recompiled the other way. */ + tf.tf_d = UNITBIT32 + 0.5; + if ((unsigned)tf.tf_i[LOWOFFSET] != 0x80000000) + bug("cos~: unexpected machine alignment"); +} + +static void cos_setup(void) +{ + cos_class = class_new(gensym("cos~"), (t_newmethod)cos_new, 0, + sizeof(t_cos), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(cos_class, t_cos, x_f); + class_addmethod(cos_class, (t_method)cos_dsp, gensym("dsp"), 0); + cos_maketable(); +} + +/* ------------------------ osc~ ----------------------------- */ + +static t_class *osc_class, *scalarosc_class; + +typedef struct _osc +{ + t_object x_obj; + double x_phase; + float x_conv; + float x_f; /* frequency if scalar */ +} t_osc; + +static void *osc_new(t_floatarg f) +{ + t_osc *x = (t_osc *)pd_new(osc_class); + x->x_f = f; + outlet_new(&x->x_obj, gensym("signal")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1")); + x->x_phase = 0; + x->x_conv = 0; + return (x); +} + +static t_int *osc_perform(t_int *w) +{ + t_osc *x = (t_osc *)(w[1]); + t_float *in = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + float *tab = cos_table, *addr, f1, f2, frac; + double dphase = x->x_phase + UNITBIT32; + int normhipart; + union tabfudge tf; + float conv = x->x_conv; + + tf.tf_d = UNITBIT32; + normhipart = tf.tf_i[HIOFFSET]; +#if 0 + while (n--) + { + tf.tf_d = dphase; + dphase += *in++ * conv; + addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); + tf.tf_i[HIOFFSET] = normhipart; + frac = tf.tf_d - UNITBIT32; + f1 = addr[0]; + f2 = addr[1]; + *out++ = f1 + frac * (f2 - f1); + } +#endif +#if 1 + tf.tf_d = dphase; + dphase += *in++ * conv; + addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); + tf.tf_i[HIOFFSET] = normhipart; + frac = tf.tf_d - UNITBIT32; + while (--n) + { + tf.tf_d = dphase; + f1 = addr[0]; + dphase += *in++ * conv; + f2 = addr[1]; + addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); + tf.tf_i[HIOFFSET] = normhipart; + *out++ = f1 + frac * (f2 - f1); + frac = tf.tf_d - UNITBIT32; + } + f1 = addr[0]; + f2 = addr[1]; + *out++ = f1 + frac * (f2 - f1); +#endif + + tf.tf_d = UNITBIT32 * COSTABSIZE; + normhipart = tf.tf_i[HIOFFSET]; + tf.tf_d = dphase + (UNITBIT32 * COSTABSIZE - UNITBIT32); + tf.tf_i[HIOFFSET] = normhipart; + x->x_phase = tf.tf_d - UNITBIT32 * COSTABSIZE; + return (w+5); +} + +static void osc_dsp(t_osc *x, t_signal **sp) +{ + x->x_conv = COSTABSIZE/sp[0]->s_sr; + dsp_add(osc_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +static void osc_ft1(t_osc *x, t_float f) +{ + x->x_phase = COSTABSIZE * f; +} + +static void osc_setup(void) +{ + osc_class = class_new(gensym("osc~"), (t_newmethod)osc_new, 0, + sizeof(t_osc), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(osc_class, t_osc, x_f); + class_addmethod(osc_class, (t_method)osc_dsp, gensym("dsp"), 0); + class_addmethod(osc_class, (t_method)osc_ft1, gensym("ft1"), A_FLOAT, 0); + + cos_maketable(); +} + +/* ---------------- vcf~ - 2-pole bandpass filter. ----------------- */ + +typedef struct vcfctl +{ + float c_re; + float c_im; + float c_q; + float c_isr; +} t_vcfctl; + +typedef struct sigvcf +{ + t_object x_obj; + t_vcfctl x_cspace; + t_vcfctl *x_ctl; + float x_f; +} t_sigvcf; + +t_class *sigvcf_class; + +static void *sigvcf_new(t_floatarg q) +{ + t_sigvcf *x = (t_sigvcf *)pd_new(sigvcf_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); + outlet_new(&x->x_obj, gensym("signal")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_ctl = &x->x_cspace; + x->x_cspace.c_re = 0; + x->x_cspace.c_im = 0; + x->x_cspace.c_q = q; + x->x_cspace.c_isr = 0; + x->x_f = 0; + return (x); +} + +static void sigvcf_ft1(t_sigvcf *x, t_floatarg f) +{ + x->x_ctl->c_q = (f > 0 ? f : 0.f); +} + +static t_int *sigvcf_perform(t_int *w) +{ + float *in1 = (float *)(w[1]); + float *in2 = (float *)(w[2]); + float *out1 = (float *)(w[3]); + float *out2 = (float *)(w[4]); + t_vcfctl *c = (t_vcfctl *)(w[5]); + int n = (t_int)(w[6]); + int i; + float re = c->c_re, re2; + float im = c->c_im; + float q = c->c_q; + float qinv = (q > 0? 1.0f/q : 0); + float ampcorrect = 2.0f - 2.0f / (q + 2.0f); + float isr = c->c_isr; + float coefr, coefi; + float *tab = cos_table, *addr, f1, f2, frac; + double dphase; + int normhipart, tabindex; + union tabfudge tf; + + tf.tf_d = UNITBIT32; + normhipart = tf.tf_i[HIOFFSET]; + + for (i = 0; i < n; i++) + { + float cf, cfindx, r, oneminusr; + cf = *in2++ * isr; + if (cf < 0) cf = 0; + cfindx = cf * (float)(COSTABSIZE/6.28318f); + r = (qinv > 0 ? 1 - cf * qinv : 0); + if (r < 0) r = 0; + oneminusr = 1.0f - r; + dphase = ((double)(cfindx)) + UNITBIT32; + tf.tf_d = dphase; + tabindex = tf.tf_i[HIOFFSET] & (COSTABSIZE-1); + addr = tab + tabindex; + tf.tf_i[HIOFFSET] = normhipart; + frac = tf.tf_d - UNITBIT32; + f1 = addr[0]; + f2 = addr[1]; + coefr = r * (f1 + frac * (f2 - f1)); + + addr = tab + ((tabindex - (COSTABSIZE/4)) & (COSTABSIZE-1)); + f1 = addr[0]; + f2 = addr[1]; + coefi = r * (f1 + frac * (f2 - f1)); + + f1 = *in1++; + re2 = re; + *out1++ = re = ampcorrect * oneminusr * f1 + + coefr * re2 - coefi * im; + *out2++ = im = coefi * re2 + coefr * im; + } + if (PD_BIGORSMALL(re)) + re = 0; + if (PD_BIGORSMALL(im)) + im = 0; + c->c_re = re; + c->c_im = im; + return (w+7); +} + +static void sigvcf_dsp(t_sigvcf *x, t_signal **sp) +{ + x->x_ctl->c_isr = 6.28318f/sp[0]->s_sr; + dsp_add(sigvcf_perform, 6, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, + x->x_ctl, sp[0]->s_n); + +} + +void sigvcf_setup(void) +{ + sigvcf_class = class_new(gensym("vcf~"), (t_newmethod)sigvcf_new, 0, + sizeof(t_sigvcf), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(sigvcf_class, t_sigvcf, x_f); + class_addmethod(sigvcf_class, (t_method)sigvcf_dsp, gensym("dsp"), 0); + class_addmethod(sigvcf_class, (t_method)sigvcf_ft1, + gensym("ft1"), A_FLOAT, 0); +} + +/* -------------------------- noise~ ------------------------------ */ +static t_class *noise_class; + +typedef struct _noise +{ + t_object x_obj; + int x_val; +} t_noise; + +static void *noise_new(void) +{ + t_noise *x = (t_noise *)pd_new(noise_class); + static int init = 307; + x->x_val = (init *= 1319); + outlet_new(&x->x_obj, gensym("signal")); + return (x); +} + +static t_int *noise_perform(t_int *w) +{ + t_float *out = (t_float *)(w[1]); + int *vp = (int *)(w[2]); + int n = (int)(w[3]); + int val = *vp; + while (n--) + { + *out++ = ((float)((val & 0x7fffffff) - 0x40000000)) * + (float)(1.0 / 0x40000000); + val = val * 435898247 + 382842987; + } + *vp = val; + return (w+4); +} + +static void noise_dsp(t_noise *x, t_signal **sp) +{ + dsp_add(noise_perform, 3, sp[0]->s_vec, &x->x_val, sp[0]->s_n); +} + +static void noise_setup(void) +{ + noise_class = class_new(gensym("noise~"), (t_newmethod)noise_new, 0, + sizeof(t_noise), 0, 0); + class_addmethod(noise_class, (t_method)noise_dsp, gensym("dsp"), 0); +} + + +/* ----------------------- global setup routine ---------------- */ +void d_osc_setup(void) +{ + phasor_setup(); + cos_setup(); + osc_setup(); + sigvcf_setup(); + noise_setup(); +} + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* sinusoidal oscillator and table lookup; see also tabosc4~ in d_array.c. +*/ + +#include "m_pd.h" +#include "math.h" + +#define UNITBIT32 1572864. /* 3*2^19; bit 32 has place value 1 */ + + /* machine-dependent definitions. These ifdefs really + should have been by CPU type and not by operating system! */ +#ifdef IRIX + /* big-endian. Most significant byte is at low address in memory */ +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#define int32 long /* a data type that has 32 bits */ +#else +#ifdef MSW + /* little-endian; most significant byte is at highest address */ +#define HIOFFSET 1 +#define LOWOFFSET 0 +#define int32 long +#else +#ifdef __FreeBSD__ +#include +#if BYTE_ORDER == LITTLE_ENDIAN +#define HIOFFSET 1 +#define LOWOFFSET 0 +#else +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#endif /* BYTE_ORDER */ +#include +#define int32 int32_t +#endif +#ifdef __linux__ + +#include + +#if !defined(__BYTE_ORDER) || !defined(__LITTLE_ENDIAN) +#error No byte order defined +#endif + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define HIOFFSET 1 +#define LOWOFFSET 0 +#else +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#endif /* __BYTE_ORDER */ + +#include +#define int32 int32_t + +#else +#ifdef MACOSX +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#define int32 int /* a data type that has 32 bits */ + +#endif /* MACOSX */ +#endif /* __linux__ */ +#endif /* MSW */ +#endif /* SGI */ + +union tabfudge +{ + double tf_d; + int32 tf_i[2]; +}; + + +/* -------------------------- phasor~ ------------------------------ */ +static t_class *phasor_class, *scalarphasor_class; + +#if 1 /* in the style of R. Hoeldrich (ICMC 1995 Banff) */ + +typedef struct _phasor +{ + t_object x_obj; + double x_phase; + float x_conv; + float x_f; /* scalar frequency */ +} t_phasor; + +static void *phasor_new(t_floatarg f) +{ + t_phasor *x = (t_phasor *)pd_new(phasor_class); + x->x_f = f; + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1")); + x->x_phase = 0; + x->x_conv = 0; + outlet_new(&x->x_obj, gensym("signal")); + return (x); +} + +static t_int *phasor_perform(t_int *w) +{ + t_phasor *x = (t_phasor *)(w[1]); + t_float *in = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + double dphase = x->x_phase + UNITBIT32; + union tabfudge tf; + int normhipart; + float conv = x->x_conv; + + tf.tf_d = UNITBIT32; + normhipart = tf.tf_i[HIOFFSET]; + tf.tf_d = dphase; + + while (n--) + { + tf.tf_i[HIOFFSET] = normhipart; + dphase += *in++ * conv; + *out++ = tf.tf_d - UNITBIT32; + tf.tf_d = dphase; + } + tf.tf_i[HIOFFSET] = normhipart; + x->x_phase = tf.tf_d - UNITBIT32; + return (w+5); +} + +static void phasor_dsp(t_phasor *x, t_signal **sp) +{ + x->x_conv = 1./sp[0]->s_sr; + dsp_add(phasor_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +static void phasor_ft1(t_phasor *x, t_float f) +{ + x->x_phase = f; +} + +static void phasor_setup(void) +{ + phasor_class = class_new(gensym("phasor~"), (t_newmethod)phasor_new, 0, + sizeof(t_phasor), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(phasor_class, t_phasor, x_f); + class_addmethod(phasor_class, (t_method)phasor_dsp, gensym("dsp"), 0); + class_addmethod(phasor_class, (t_method)phasor_ft1, + gensym("ft1"), A_FLOAT, 0); +} + +#endif /* Hoeldrich version */ + +/* ------------------------ cos~ ----------------------------- */ + +float *cos_table; + +static t_class *cos_class; + +typedef struct _cos +{ + t_object x_obj; + float x_f; +} t_cos; + +static void *cos_new(void) +{ + t_cos *x = (t_cos *)pd_new(cos_class); + outlet_new(&x->x_obj, gensym("signal")); + x->x_f = 0; + return (x); +} + +static t_int *cos_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + float *tab = cos_table, *addr, f1, f2, frac; + double dphase; + int normhipart; + union tabfudge tf; + + tf.tf_d = UNITBIT32; + normhipart = tf.tf_i[HIOFFSET]; + +#if 0 /* this is the readable version of the code. */ + while (n--) + { + dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32; + tf.tf_d = dphase; + addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); + tf.tf_i[HIOFFSET] = normhipart; + frac = tf.tf_d - UNITBIT32; + f1 = addr[0]; + f2 = addr[1]; + *out++ = f1 + frac * (f2 - f1); + } +#endif +#if 1 /* this is the same, unwrapped by hand. */ + dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32; + tf.tf_d = dphase; + addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); + tf.tf_i[HIOFFSET] = normhipart; + while (--n) + { + dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32; + frac = tf.tf_d - UNITBIT32; + tf.tf_d = dphase; + f1 = addr[0]; + f2 = addr[1]; + addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); + *out++ = f1 + frac * (f2 - f1); + tf.tf_i[HIOFFSET] = normhipart; + } + frac = tf.tf_d - UNITBIT32; + f1 = addr[0]; + f2 = addr[1]; + *out++ = f1 + frac * (f2 - f1); +#endif + return (w+4); +} + +static void cos_dsp(t_cos *x, t_signal **sp) +{ + dsp_add(cos_perform, 3, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +static void cos_maketable(void) +{ + int i; + float *fp, phase, phsinc = (2. * 3.14159) / COSTABSIZE; + union tabfudge tf; + + if (cos_table) return; + cos_table = (float *)getbytes(sizeof(float) * (COSTABSIZE+1)); + for (i = COSTABSIZE + 1, fp = cos_table, phase = 0; i--; + fp++, phase += phsinc) + *fp = cos(phase); + + /* here we check at startup whether the byte alignment + is as we declared it. If not, the code has to be + recompiled the other way. */ + tf.tf_d = UNITBIT32 + 0.5; + if ((unsigned)tf.tf_i[LOWOFFSET] != 0x80000000) + bug("cos~: unexpected machine alignment"); +} + +static void cos_setup(void) +{ + cos_class = class_new(gensym("cos~"), (t_newmethod)cos_new, 0, + sizeof(t_cos), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(cos_class, t_cos, x_f); + class_addmethod(cos_class, (t_method)cos_dsp, gensym("dsp"), 0); + cos_maketable(); +} + +/* ------------------------ osc~ ----------------------------- */ + +static t_class *osc_class, *scalarosc_class; + +typedef struct _osc +{ + t_object x_obj; + double x_phase; + float x_conv; + float x_f; /* frequency if scalar */ +} t_osc; + +static void *osc_new(t_floatarg f) +{ + t_osc *x = (t_osc *)pd_new(osc_class); + x->x_f = f; + outlet_new(&x->x_obj, gensym("signal")); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_float, gensym("ft1")); + x->x_phase = 0; + x->x_conv = 0; + return (x); +} + +static t_int *osc_perform(t_int *w) +{ + t_osc *x = (t_osc *)(w[1]); + t_float *in = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + float *tab = cos_table, *addr, f1, f2, frac; + double dphase = x->x_phase + UNITBIT32; + int normhipart; + union tabfudge tf; + float conv = x->x_conv; + + tf.tf_d = UNITBIT32; + normhipart = tf.tf_i[HIOFFSET]; +#if 0 + while (n--) + { + tf.tf_d = dphase; + dphase += *in++ * conv; + addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); + tf.tf_i[HIOFFSET] = normhipart; + frac = tf.tf_d - UNITBIT32; + f1 = addr[0]; + f2 = addr[1]; + *out++ = f1 + frac * (f2 - f1); + } +#endif +#if 1 + tf.tf_d = dphase; + dphase += *in++ * conv; + addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); + tf.tf_i[HIOFFSET] = normhipart; + frac = tf.tf_d - UNITBIT32; + while (--n) + { + tf.tf_d = dphase; + f1 = addr[0]; + dphase += *in++ * conv; + f2 = addr[1]; + addr = tab + (tf.tf_i[HIOFFSET] & (COSTABSIZE-1)); + tf.tf_i[HIOFFSET] = normhipart; + *out++ = f1 + frac * (f2 - f1); + frac = tf.tf_d - UNITBIT32; + } + f1 = addr[0]; + f2 = addr[1]; + *out++ = f1 + frac * (f2 - f1); +#endif + + tf.tf_d = UNITBIT32 * COSTABSIZE; + normhipart = tf.tf_i[HIOFFSET]; + tf.tf_d = dphase + (UNITBIT32 * COSTABSIZE - UNITBIT32); + tf.tf_i[HIOFFSET] = normhipart; + x->x_phase = tf.tf_d - UNITBIT32 * COSTABSIZE; + return (w+5); +} + +static void osc_dsp(t_osc *x, t_signal **sp) +{ + x->x_conv = COSTABSIZE/sp[0]->s_sr; + dsp_add(osc_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n); +} + +static void osc_ft1(t_osc *x, t_float f) +{ + x->x_phase = COSTABSIZE * f; +} + +static void osc_setup(void) +{ + osc_class = class_new(gensym("osc~"), (t_newmethod)osc_new, 0, + sizeof(t_osc), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(osc_class, t_osc, x_f); + class_addmethod(osc_class, (t_method)osc_dsp, gensym("dsp"), 0); + class_addmethod(osc_class, (t_method)osc_ft1, gensym("ft1"), A_FLOAT, 0); + + cos_maketable(); +} + +/* ---------------- vcf~ - 2-pole bandpass filter. ----------------- */ + +typedef struct vcfctl +{ + float c_re; + float c_im; + float c_q; + float c_isr; +} t_vcfctl; + +typedef struct sigvcf +{ + t_object x_obj; + t_vcfctl x_cspace; + t_vcfctl *x_ctl; + float x_f; +} t_sigvcf; + +t_class *sigvcf_class; + +static void *sigvcf_new(t_floatarg q) +{ + t_sigvcf *x = (t_sigvcf *)pd_new(sigvcf_class); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("ft1")); + outlet_new(&x->x_obj, gensym("signal")); + outlet_new(&x->x_obj, gensym("signal")); + x->x_ctl = &x->x_cspace; + x->x_cspace.c_re = 0; + x->x_cspace.c_im = 0; + x->x_cspace.c_q = q; + x->x_cspace.c_isr = 0; + x->x_f = 0; + return (x); +} + +static void sigvcf_ft1(t_sigvcf *x, t_floatarg f) +{ + x->x_ctl->c_q = (f > 0 ? f : 0.f); +} + +static t_int *sigvcf_perform(t_int *w) +{ + float *in1 = (float *)(w[1]); + float *in2 = (float *)(w[2]); + float *out1 = (float *)(w[3]); + float *out2 = (float *)(w[4]); + t_vcfctl *c = (t_vcfctl *)(w[5]); + int n = (t_int)(w[6]); + int i; + float re = c->c_re, re2; + float im = c->c_im; + float q = c->c_q; + float qinv = (q > 0? 1.0f/q : 0); + float ampcorrect = 2.0f - 2.0f / (q + 2.0f); + float isr = c->c_isr; + float coefr, coefi; + float *tab = cos_table, *addr, f1, f2, frac; + double dphase; + int normhipart, tabindex; + union tabfudge tf; + + tf.tf_d = UNITBIT32; + normhipart = tf.tf_i[HIOFFSET]; + + for (i = 0; i < n; i++) + { + float cf, cfindx, r, oneminusr; + cf = *in2++ * isr; + if (cf < 0) cf = 0; + cfindx = cf * (float)(COSTABSIZE/6.28318f); + r = (qinv > 0 ? 1 - cf * qinv : 0); + if (r < 0) r = 0; + oneminusr = 1.0f - r; + dphase = ((double)(cfindx)) + UNITBIT32; + tf.tf_d = dphase; + tabindex = tf.tf_i[HIOFFSET] & (COSTABSIZE-1); + addr = tab + tabindex; + tf.tf_i[HIOFFSET] = normhipart; + frac = tf.tf_d - UNITBIT32; + f1 = addr[0]; + f2 = addr[1]; + coefr = r * (f1 + frac * (f2 - f1)); + + addr = tab + ((tabindex - (COSTABSIZE/4)) & (COSTABSIZE-1)); + f1 = addr[0]; + f2 = addr[1]; + coefi = r * (f1 + frac * (f2 - f1)); + + f1 = *in1++; + re2 = re; + *out1++ = re = ampcorrect * oneminusr * f1 + + coefr * re2 - coefi * im; + *out2++ = im = coefi * re2 + coefr * im; + } + if (PD_BIGORSMALL(re)) + re = 0; + if (PD_BIGORSMALL(im)) + im = 0; + c->c_re = re; + c->c_im = im; + return (w+7); +} + +static void sigvcf_dsp(t_sigvcf *x, t_signal **sp) +{ + x->x_ctl->c_isr = 6.28318f/sp[0]->s_sr; + dsp_add(sigvcf_perform, 6, + sp[0]->s_vec, sp[1]->s_vec, sp[2]->s_vec, sp[3]->s_vec, + x->x_ctl, sp[0]->s_n); + +} + +void sigvcf_setup(void) +{ + sigvcf_class = class_new(gensym("vcf~"), (t_newmethod)sigvcf_new, 0, + sizeof(t_sigvcf), 0, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(sigvcf_class, t_sigvcf, x_f); + class_addmethod(sigvcf_class, (t_method)sigvcf_dsp, gensym("dsp"), 0); + class_addmethod(sigvcf_class, (t_method)sigvcf_ft1, + gensym("ft1"), A_FLOAT, 0); +} + +/* -------------------------- noise~ ------------------------------ */ +static t_class *noise_class; + +typedef struct _noise +{ + t_object x_obj; + int x_val; +} t_noise; + +static void *noise_new(void) +{ + t_noise *x = (t_noise *)pd_new(noise_class); + static int init = 307; + x->x_val = (init *= 1319); + outlet_new(&x->x_obj, gensym("signal")); + return (x); +} + +static t_int *noise_perform(t_int *w) +{ + t_float *out = (t_float *)(w[1]); + int *vp = (int *)(w[2]); + int n = (int)(w[3]); + int val = *vp; + while (n--) + { + *out++ = ((float)((val & 0x7fffffff) - 0x40000000)) * + (float)(1.0 / 0x40000000); + val = val * 435898247 + 382842987; + } + *vp = val; + return (w+4); +} + +static void noise_dsp(t_noise *x, t_signal **sp) +{ + dsp_add(noise_perform, 3, sp[0]->s_vec, &x->x_val, sp[0]->s_n); +} + +static void noise_setup(void) +{ + noise_class = class_new(gensym("noise~"), (t_newmethod)noise_new, 0, + sizeof(t_noise), 0, 0); + class_addmethod(noise_class, (t_method)noise_dsp, gensym("dsp"), 0); +} + + +/* ----------------------- global setup routine ---------------- */ +void d_osc_setup(void) +{ + phasor_setup(); + cos_setup(); + osc_setup(); + sigvcf_setup(); + noise_setup(); +} + diff --git a/apps/plugins/pdbox/PDa/src/d_resample.c b/apps/plugins/pdbox/PDa/src/d_resample.c new file mode 100644 index 0000000..4e617ec --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_resample.c @@ -0,0 +1,450 @@ +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* upsampling/downsampling methods for inlet~/outlet~ + * + * mfg.gfd.uil + * IOhannes + * + * 2509:forum::für::umläute:2001 + */ + + + +#include "m_pd.h" + +/* --------------------- up/down-sampling --------------------- */ +t_int *downsampling_perform_0(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); /* original signal */ + t_sample *out = (t_sample *)(w[2]); /* downsampled signal */ + int down = (int)(w[3]); /* downsampling factor */ + int parent = (int)(w[4]); /* original vectorsize */ + + int n=parent/down; + + while(n--){ + *out++=*in; + in+=down; + } + + return (w+5); +} + +t_int *upsampling_perform_0(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); /* original signal */ + t_sample *out = (t_sample *)(w[2]); /* upsampled signal */ + int up = (int)(w[3]); /* upsampling factor */ + int parent = (int)(w[4]); /* original vectorsize */ + + int n=parent*up; + t_sample *dummy = out; + + while(n--)*out++=0; + + n = parent; + out = dummy; + while(n--){ + *out=*in++; + out+=up; + } + + return (w+5); +} + +t_int *upsampling_perform_hold(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); /* original signal */ + t_sample *out = (t_sample *)(w[2]); /* upsampled signal */ + int up = (int)(w[3]); /* upsampling factor */ + int parent = (int)(w[4]); /* original vectorsize */ + int i=up; + + int n=parent; + t_sample *dum_out = out; + t_sample *dum_in = in; + + while (i--) { + n = parent; + out = dum_out+i; + in = dum_in; + while(n--){ + *out=*in++; + out+=up; + } + } + return (w+5); +} + +t_int *upsampling_perform_linear(t_int *w) +{ + t_resample *x= (t_resample *)(w[1]); + t_sample *in = (t_sample *)(w[2]); /* original signal */ + t_sample *out = (t_sample *)(w[3]); /* upsampled signal */ + int up = (int)(w[4]); /* upsampling factor */ + int parent = (int)(w[5]); /* original vectorsize */ + int length = parent*up; + int n; + t_sample *fp; + t_sample a=*x->buffer, b=*in; + + + for (n=0; nbuffer = a; + return (w+6); +} + +/* ----------------------- public -------------------------------- */ + +/* utils */ + +void resample_init(t_resample *x) +{ + x->method=0; + + x->downsample=x->upsample=1; + + x->s_n = x->coefsize = x->bufsize = 0; + x->s_vec = x->coeffs = x->buffer = 0; +} + +void resample_free(t_resample *x) +{ + if (x->s_n) t_freebytes(x->s_vec, x->s_n*sizeof(*x->s_vec)); + if (x->coefsize) t_freebytes(x->coeffs, x->coefsize*sizeof(*x->coeffs)); + if (x->bufsize) t_freebytes(x->buffer, x->bufsize*sizeof(*x->buffer)); + + x->s_n = x->coefsize = x->bufsize = 0; + x->s_vec = x->coeffs = x->buffer = 0; +} + + +/* dsp-adding */ + +void resample_dsp(t_resample *x, + t_sample* in, int insize, + t_sample* out, int outsize, + int method) +{ + if (insize == outsize){ + bug("nothing to be done"); + return; + } + + if (insize > outsize) { /* downsampling */ + if (insize % outsize) { + error("bad downsampling factor"); + return; + } + switch (method) { + default: + dsp_add(downsampling_perform_0, 4, in, out, insize/outsize, insize); + } + + + } else { /* upsampling */ + if (outsize % insize) { + error("bad upsampling factor"); + return; + } + switch (method) { + case 1: + dsp_add(upsampling_perform_hold, 4, in, out, outsize/insize, insize); + break; + case 2: + if (x->bufsize != 1) { + t_freebytes(x->buffer, x->bufsize*sizeof(*x->buffer)); + x->bufsize = 1; + x->buffer = t_getbytes(x->bufsize*sizeof(*x->buffer)); + } + dsp_add(upsampling_perform_linear, 5, x, in, out, outsize/insize, insize); + break; + default: + dsp_add(upsampling_perform_0, 4, in, out, outsize/insize, insize); + } + } +} + +void resamplefrom_dsp(t_resample *x, + t_sample *in, + int insize, int outsize, int method) +{ + if (insize==outsize) { + t_freebytes(x->s_vec, x->s_n * sizeof(*x->s_vec)); + x->s_n = 0; + x->s_vec = in; + return; + } + + if (x->s_n != outsize) { + t_sample *buf=x->s_vec; + t_freebytes(buf, x->s_n * sizeof(*buf)); + buf = (t_sample *)t_getbytes(outsize * sizeof(*buf)); + x->s_vec = buf; + x->s_n = outsize; + } + + resample_dsp(x, in, insize, x->s_vec, x->s_n, method); + return; +} + +void resampleto_dsp(t_resample *x, + t_sample *out, + int insize, int outsize, int method) +{ + if (insize==outsize) { + if (x->s_n)t_freebytes(x->s_vec, x->s_n * sizeof(*x->s_vec)); + x->s_n = 0; + x->s_vec = out; + return; + } + + if (x->s_n != insize) { + t_sample *buf=x->s_vec; + t_freebytes(buf, x->s_n * sizeof(*buf)); + buf = (t_sample *)t_getbytes(insize * sizeof(*buf)); + x->s_vec = buf; + x->s_n = insize; + } + + resample_dsp(x, x->s_vec, x->s_n, out, outsize, method); + + return; +} +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* upsampling/downsampling methods for inlet~/outlet~ + * + * mfg.gfd.uil + * IOhannes + * + * 2509:forum::für::umläute:2001 + */ + + + +#include "m_pd.h" + +/* --------------------- up/down-sampling --------------------- */ +t_int *downsampling_perform_0(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); /* original signal */ + t_sample *out = (t_sample *)(w[2]); /* downsampled signal */ + int down = (int)(w[3]); /* downsampling factor */ + int parent = (int)(w[4]); /* original vectorsize */ + + int n=parent/down; + + while(n--){ + *out++=*in; + in+=down; + } + + return (w+5); +} + +t_int *upsampling_perform_0(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); /* original signal */ + t_sample *out = (t_sample *)(w[2]); /* upsampled signal */ + int up = (int)(w[3]); /* upsampling factor */ + int parent = (int)(w[4]); /* original vectorsize */ + + int n=parent*up; + t_sample *dummy = out; + + while(n--)*out++=0; + + n = parent; + out = dummy; + while(n--){ + *out=*in++; + out+=up; + } + + return (w+5); +} + +t_int *upsampling_perform_hold(t_int *w) +{ + t_sample *in = (t_sample *)(w[1]); /* original signal */ + t_sample *out = (t_sample *)(w[2]); /* upsampled signal */ + int up = (int)(w[3]); /* upsampling factor */ + int parent = (int)(w[4]); /* original vectorsize */ + int i=up; + + int n=parent; + t_sample *dum_out = out; + t_sample *dum_in = in; + + while (i--) { + n = parent; + out = dum_out+i; + in = dum_in; + while(n--){ + *out=*in++; + out+=up; + } + } + return (w+5); +} + +t_int *upsampling_perform_linear(t_int *w) +{ + t_resample *x= (t_resample *)(w[1]); + t_sample *in = (t_sample *)(w[2]); /* original signal */ + t_sample *out = (t_sample *)(w[3]); /* upsampled signal */ + int up = (int)(w[4]); /* upsampling factor */ + int parent = (int)(w[5]); /* original vectorsize */ + int length = parent*up; + int n; + t_sample *fp; + t_sample a=*x->buffer, b=*in; + + + for (n=0; nbuffer = a; + return (w+6); +} + +/* ----------------------- public -------------------------------- */ + +/* utils */ + +void resample_init(t_resample *x) +{ + x->method=0; + + x->downsample=x->upsample=1; + + x->s_n = x->coefsize = x->bufsize = 0; + x->s_vec = x->coeffs = x->buffer = 0; +} + +void resample_free(t_resample *x) +{ + if (x->s_n) t_freebytes(x->s_vec, x->s_n*sizeof(*x->s_vec)); + if (x->coefsize) t_freebytes(x->coeffs, x->coefsize*sizeof(*x->coeffs)); + if (x->bufsize) t_freebytes(x->buffer, x->bufsize*sizeof(*x->buffer)); + + x->s_n = x->coefsize = x->bufsize = 0; + x->s_vec = x->coeffs = x->buffer = 0; +} + + +/* dsp-adding */ + +void resample_dsp(t_resample *x, + t_sample* in, int insize, + t_sample* out, int outsize, + int method) +{ + if (insize == outsize){ + bug("nothing to be done"); + return; + } + + if (insize > outsize) { /* downsampling */ + if (insize % outsize) { + error("bad downsampling factor"); + return; + } + switch (method) { + default: + dsp_add(downsampling_perform_0, 4, in, out, insize/outsize, insize); + } + + + } else { /* upsampling */ + if (outsize % insize) { + error("bad upsampling factor"); + return; + } + switch (method) { + case 1: + dsp_add(upsampling_perform_hold, 4, in, out, outsize/insize, insize); + break; + case 2: + if (x->bufsize != 1) { + t_freebytes(x->buffer, x->bufsize*sizeof(*x->buffer)); + x->bufsize = 1; + x->buffer = t_getbytes(x->bufsize*sizeof(*x->buffer)); + } + dsp_add(upsampling_perform_linear, 5, x, in, out, outsize/insize, insize); + break; + default: + dsp_add(upsampling_perform_0, 4, in, out, outsize/insize, insize); + } + } +} + +void resamplefrom_dsp(t_resample *x, + t_sample *in, + int insize, int outsize, int method) +{ + if (insize==outsize) { + t_freebytes(x->s_vec, x->s_n * sizeof(*x->s_vec)); + x->s_n = 0; + x->s_vec = in; + return; + } + + if (x->s_n != outsize) { + t_sample *buf=x->s_vec; + t_freebytes(buf, x->s_n * sizeof(*buf)); + buf = (t_sample *)t_getbytes(outsize * sizeof(*buf)); + x->s_vec = buf; + x->s_n = outsize; + } + + resample_dsp(x, in, insize, x->s_vec, x->s_n, method); + return; +} + +void resampleto_dsp(t_resample *x, + t_sample *out, + int insize, int outsize, int method) +{ + if (insize==outsize) { + if (x->s_n)t_freebytes(x->s_vec, x->s_n * sizeof(*x->s_vec)); + x->s_n = 0; + x->s_vec = out; + return; + } + + if (x->s_n != insize) { + t_sample *buf=x->s_vec; + t_freebytes(buf, x->s_n * sizeof(*buf)); + buf = (t_sample *)t_getbytes(insize * sizeof(*buf)); + x->s_vec = buf; + x->s_n = insize; + } + + resample_dsp(x, x->s_vec, x->s_n, out, outsize, method); + + return; +} diff --git a/apps/plugins/pdbox/PDa/src/d_soundfile.c b/apps/plugins/pdbox/PDa/src/d_soundfile.c new file mode 100644 index 0000000..872a44a --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_soundfile.c @@ -0,0 +1,4734 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* this file contains, first, a collection of soundfile access routines, a +sort of soundfile library. Second, the "soundfiler" object is defined which +uses the routines to read or write soundfiles, synchronously, from garrays. +These operations are not to be done in "real time" as they may have to wait +for disk accesses (even the write routine.) Finally, the realtime objects +readsf~ and writesf~ are defined which confine disk operations to a separate +thread so that they can be used in real time. The readsf~ and writesf~ +objects use Posix-like threads. */ + +#ifdef UNIX +#include +#include +#endif +#include +#ifdef MSW +#include +#endif +#include +#include +#include + +#include "m_pd.h" + +#define MAXSFCHANS 64 + +/***************** soundfile header structures ************************/ + +typedef unsigned short uint16; +typedef unsigned long uint32; + +#define FORMAT_WAVE 0 +#define FORMAT_AIFF 1 +#define FORMAT_NEXT 2 + +/* the NeXTStep sound header structure; can be big or little endian */ + +typedef struct _nextstep +{ + char ns_fileid[4]; /* magic number '.snd' if file is big-endian */ + uint32 ns_onset; /* byte offset of first sample */ + uint32 ns_length; /* length of sound in bytes */ + uint32 ns_format; /* format; see below */ + uint32 ns_sr; /* sample rate */ + uint32 ns_nchans; /* number of channels */ + char ns_info[4]; /* comment */ +} t_nextstep; + +#define NS_FORMAT_LINEAR_16 3 +#define NS_FORMAT_LINEAR_24 4 +#define NS_FORMAT_FLOAT 6 +#define SCALE (1./(1024. * 1024. * 1024. * 2.)) + +/* the WAVE header. All Wave files are little endian. We assume + the "fmt" chunk comes first which is usually the case but perhaps not + always; same for AIFF and the "COMM" chunk. */ + +typedef unsigned word; +typedef unsigned long dword; + +typedef struct _wave +{ + char w_fileid[4]; /* chunk id 'RIFF' */ + uint32 w_chunksize; /* chunk size */ + char w_waveid[4]; /* wave chunk id 'WAVE' */ + char w_fmtid[4]; /* format chunk id 'fmt ' */ + uint32 w_fmtchunksize; /* format chunk size */ + uint16 w_fmttag; /* format tag (WAV_INT etc) */ + uint16 w_nchannels; /* number of channels */ + uint32 w_samplespersec; /* sample rate in hz */ + uint32 w_navgbytespersec; /* average bytes per second */ + uint16 w_nblockalign; /* number of bytes per frame */ + uint16 w_nbitspersample; /* number of bits in a sample */ + char w_datachunkid[4]; /* data chunk id 'data' */ + uint32 w_datachunksize; /* length of data chunk */ +} t_wave; + +typedef struct _fmt /* format chunk */ +{ + uint16 f_fmttag; /* format tag, 1 for PCM */ + uint16 f_nchannels; /* number of channels */ + uint32 f_samplespersec; /* sample rate in hz */ + uint32 f_navgbytespersec; /* average bytes per second */ + uint16 f_nblockalign; /* number of bytes per frame */ + uint16 f_nbitspersample; /* number of bits in a sample */ +} t_fmt; + +typedef struct _wavechunk /* ... and the last two items */ +{ + char wc_id[4]; /* data chunk id, e.g., 'data' or 'fmt ' */ + uint32 wc_size; /* length of data chunk */ +} t_wavechunk; + +#define WAV_INT 1 +#define WAV_FLOAT 3 + +/* the AIFF header. I'm assuming AIFC is compatible but don't really know + that. */ + +typedef struct _datachunk +{ + char dc_id[4]; /* data chunk id 'SSND' */ + uint32 dc_size; /* length of data chunk */ +} t_datachunk; + +typedef struct _comm +{ + uint16 c_nchannels; /* number of channels */ + uint16 c_nframeshi; /* # of sample frames (hi) */ + uint16 c_nframeslo; /* # of sample frames (lo) */ + uint16 c_bitspersamp; /* bits per sample */ + unsigned char c_samprate[10]; /* sample rate, 80-bit float! */ +} t_comm; + + /* this version is more convenient for writing them out: */ +typedef struct _aiff +{ + char a_fileid[4]; /* chunk id 'FORM' */ + uint32 a_chunksize; /* chunk size */ + char a_aiffid[4]; /* aiff chunk id 'AIFF' */ + char a_fmtid[4]; /* format chunk id 'COMM' */ + uint32 a_fmtchunksize; /* format chunk size, 18 */ + uint16 a_nchannels; /* number of channels */ + uint16 a_nframeshi; /* # of sample frames (hi) */ + uint16 a_nframeslo; /* # of sample frames (lo) */ + uint16 a_bitspersamp; /* bits per sample */ + unsigned char a_samprate[10]; /* sample rate, 80-bit float! */ +} t_aiff; + +#define AIFFHDRSIZE 38 /* probably not what sizeof() gives */ + + +#define AIFFPLUS (AIFFHDRSIZE + 8) /* header size including first chunk hdr */ + +#define WHDR1 sizeof(t_nextstep) +#define WHDR2 (sizeof(t_wave) > WHDR1 ? sizeof (t_wave) : WHDR1) +#define WRITEHDRSIZE (AIFFPLUS > WHDR2 ? AIFFPLUS : WHDR2) + +#define READHDRSIZE (16 > WHDR2 + 2 ? 16 : WHDR2 + 2) + +#define OBUFSIZE MAXPDSTRING /* assume MAXPDSTRING is bigger than headers */ + +#ifdef MSW +#include +#define BINCREATE _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY +#else +#define BINCREATE O_WRONLY | O_CREAT | O_TRUNC +#endif + +/* this routine returns 1 if the high order byte comes at the lower +address on our architecture (big-endianness.). It's 1 for Motorola, +0 for Intel: */ + +extern int garray_ambigendian(void); + +/* byte swappers */ + +static uint32 swap4(uint32 n, int doit) +{ + if (doit) + return (((n & 0xff) << 24) | ((n & 0xff00) << 8) | + ((n & 0xff0000) >> 8) | ((n & 0xff000000) >> 24)); + else return (n); +} + +static uint16 swap2(uint32 n, int doit) +{ + if (doit) + return (((n & 0xff) << 8) | ((n & 0xff00) >> 8)); + else return (n); +} + +static void swapstring(char *foo, int doit) +{ + if (doit) + { + char a = foo[0], b = foo[1], c = foo[2], d = foo[3]; + foo[0] = d; foo[1] = c; foo[2] = b; foo[3] = a; + } +} + +/******************** soundfile access routines **********************/ + +/* This routine opens a file, looks for either a nextstep or "wave" header, +* seeks to end of it, and fills in bytes per sample and number of channels. +* Only 2- and 3-byte fixed-point samples and 4-byte floating point samples +* are supported. If "headersize" is nonzero, the +* caller should supply the number of channels, endinanness, and bytes per +* sample; the header is ignored. Otherwise, the routine tries to read the +* header and fill in the properties. +*/ + +int open_soundfile(const char *dirname, const char *filename, int headersize, + int *p_bytespersamp, int *p_bigendian, int *p_nchannels, long *p_bytelimit, + long skipframes) +{ + char buf[OBUFSIZE], *bufptr; + int fd, format, nchannels, bigendian, bytespersamp, swap, sysrtn; + long bytelimit = 0x7fffffff; + errno = 0; + fd = open_via_path(dirname, filename, + "", buf, &bufptr, MAXPDSTRING, 1); + if (fd < 0) + return (-1); + if (headersize >= 0) /* header detection overridden */ + { + bigendian = *p_bigendian; + nchannels = *p_nchannels; + bytespersamp = *p_bytespersamp; + bytelimit = *p_bytelimit; + } + else + { + int bytesread = read(fd, buf, READHDRSIZE); + int format; + if (bytesread < 4) + goto badheader; + if (!strncmp(buf, ".snd", 4)) + format = FORMAT_NEXT, bigendian = 1; + else if (!strncmp(buf, "dns.", 4)) + format = FORMAT_NEXT, bigendian = 0; + else if (!strncmp(buf, "RIFF", 4)) + { + if (bytesread < 12 || strncmp(buf + 8, "WAVE", 4)) + goto badheader; + format = FORMAT_WAVE, bigendian = 0; + } + else if (!strncmp(buf, "FORM", 4)) + { + if (bytesread < 12 || strncmp(buf + 8, "AIFF", 4)) + goto badheader; + format = FORMAT_AIFF, bigendian = 1; + } + else + goto badheader; + swap = (bigendian != garray_ambigendian()); + if (format == FORMAT_NEXT) /* nextstep header */ + { + uint32 param; + if (bytesread < (int)sizeof(t_nextstep)) + goto badheader; + nchannels = swap4(((t_nextstep *)buf)->ns_nchans, swap); + format = swap4(((t_nextstep *)buf)->ns_format, swap); + headersize = swap4(((t_nextstep *)buf)->ns_onset, swap); + if (format == NS_FORMAT_LINEAR_16) + bytespersamp = 2; + else if (format == NS_FORMAT_LINEAR_24) + bytespersamp = 3; + else if (format == NS_FORMAT_FLOAT) + bytespersamp = 4; + else goto badheader; + bytelimit = 0x7fffffff; + } + else if (format == FORMAT_WAVE) /* wave header */ + { + /* This is awful. You have to skip over chunks, + except that if one happens to be a "fmt" chunk, you want to + find out the format from that one. The case where the + "fmt" chunk comes after the audio isn't handled. */ + headersize = 12; + if (bytesread < 20) + goto badheader; + /* First we guess a number of channels, etc., in case there's + no "fmt" chunk to follow. */ + nchannels = 1; + bytespersamp = 2; + /* copy the first chunk header to beginnning of buffer. */ + memcpy(buf, buf + headersize, sizeof(t_wavechunk)); + /* post("chunk %c %c %c %c", + ((t_wavechunk *)buf)->wc_id[0], + ((t_wavechunk *)buf)->wc_id[1], + ((t_wavechunk *)buf)->wc_id[2], + ((t_wavechunk *)buf)->wc_id[3]); */ + /* read chunks in loop until we get to the data chunk */ + while (strncmp(((t_wavechunk *)buf)->wc_id, "data", 4)) + { + long chunksize = swap4(((t_wavechunk *)buf)->wc_size, + swap), seekto = headersize + chunksize + 8, seekout; + + if (!strncmp(((t_wavechunk *)buf)->wc_id, "fmt ", 4)) + { + long commblockonset = headersize + 8; + seekout = lseek(fd, commblockonset, SEEK_SET); + if (seekout != commblockonset) + goto badheader; + if (read(fd, buf, sizeof(t_fmt)) < (int) sizeof(t_fmt)) + goto badheader; + nchannels = swap2(((t_fmt *)buf)->f_nchannels, swap); + format = swap2(((t_fmt *)buf)->f_nbitspersample, swap); + if (format == 16) + bytespersamp = 2; + else if (format == 24) + bytespersamp = 3; + else if (format == 32) + bytespersamp = 4; + else goto badheader; + } + seekout = lseek(fd, seekto, SEEK_SET); + if (seekout != seekto) + goto badheader; + if (read(fd, buf, sizeof(t_wavechunk)) < + (int) sizeof(t_wavechunk)) + goto badheader; + /* post("new chunk %c %c %c %c at %d", + ((t_wavechunk *)buf)->wc_id[0], + ((t_wavechunk *)buf)->wc_id[1], + ((t_wavechunk *)buf)->wc_id[2], + ((t_wavechunk *)buf)->wc_id[3], seekto); */ + headersize = seekto; + } + bytelimit = swap4(((t_wavechunk *)buf)->wc_size, swap); + headersize += 8; + } + else + { + /* AIFF. same as WAVE; actually predates it. Disgusting. */ + headersize = 12; + if (bytesread < 20) + goto badheader; + /* First we guess a number of channels, etc., in case there's + no COMM block to follow. */ + nchannels = 1; + bytespersamp = 2; + /* copy the first chunk header to beginnning of buffer. */ + memcpy(buf, buf + headersize, sizeof(t_datachunk)); + /* read chunks in loop until we get to the data chunk */ + while (strncmp(((t_datachunk *)buf)->dc_id, "SSND", 4)) + { + long chunksize = swap4(((t_datachunk *)buf)->dc_size, + swap), seekto = headersize + chunksize + 8, seekout; + /* post("chunk %c %c %c %c seek %d", + ((t_datachunk *)buf)->dc_id[0], + ((t_datachunk *)buf)->dc_id[1], + ((t_datachunk *)buf)->dc_id[2], + ((t_datachunk *)buf)->dc_id[3], seekto); */ + if (!strncmp(((t_datachunk *)buf)->dc_id, "COMM", 4)) + { + long commblockonset = headersize + 8; + seekout = lseek(fd, commblockonset, SEEK_SET); + if (seekout != commblockonset) + goto badheader; + if (read(fd, buf, sizeof(t_comm)) < + (int) sizeof(t_comm)) + goto badheader; + nchannels = swap2(((t_comm *)buf)->c_nchannels, swap); + format = swap2(((t_comm *)buf)->c_bitspersamp, swap); + if (format == 16) + bytespersamp = 2; + else if (format == 24) + bytespersamp = 3; + else goto badheader; + } + seekout = lseek(fd, seekto, SEEK_SET); + if (seekout != seekto) + goto badheader; + if (read(fd, buf, sizeof(t_datachunk)) < + (int) sizeof(t_datachunk)) + goto badheader; + headersize = seekto; + } + bytelimit = swap4(((t_datachunk *)buf)->dc_size, swap); + headersize += 8; + } + } + /* seek past header and any sample frames to skip */ + sysrtn = lseek(fd, nchannels * bytespersamp * skipframes + headersize, 0); + if (sysrtn != nchannels * bytespersamp * skipframes + headersize) + return (-1); + bytelimit -= nchannels * bytespersamp * skipframes; + if (bytelimit < 0) + bytelimit = 0; + /* copy sample format back to caller */ + *p_bigendian = bigendian; + *p_nchannels = nchannels; + *p_bytespersamp = bytespersamp; + *p_bytelimit = bytelimit; + return (fd); +badheader: + /* the header wasn't recognized. We're threadable here so let's not + print out the error... */ + errno = EIO; + return (-1); +} + +static void soundfile_xferin(int sfchannels, int nvecs, t_sample **vecs, + long itemsread, unsigned char *buf, int nitems, int bytespersamp, + int bigendian) +{ + int i, j; + unsigned char *sp, *sp2; + t_sample *fp; + int nchannels = (sfchannels < nvecs ? sfchannels : nvecs); + int bytesperframe = bytespersamp * sfchannels; + for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp) + { + if (bytespersamp == 2) + { + if (bigendian) + { + for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; + j < nitems; j++, sp2 += bytesperframe, fp++) + *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16)); + } + else + { + for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; + j < nitems; j++, sp2 += bytesperframe, fp++) + *fp = ((short*)sp2)[0]<<(fix1-16); + } + } + else if (bytespersamp == 3) + { + if (bigendian) + { + for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; + j < nitems; j++, sp2 += bytesperframe, fp++) + *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16) + | (sp2[2] << 8)); + } + else + { + for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; + j < nitems; j++, sp2 += bytesperframe, fp++) + *fp = SCALE * ((sp2[2] << 24) | (sp2[1] << 16) + | (sp2[0] << 8)); + } + } + else if (bytespersamp == 4) + { + if (bigendian) + { + for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; + j < nitems; j++, sp2 += bytesperframe, fp++) + *(long *)fp = ((sp2[0] << 24) | (sp2[1] << 16) + | (sp2[2] << 8) | sp2[3]); + } + else + { + for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; + j < nitems; j++, sp2 += bytesperframe, fp++) + *(long *)fp = ((sp2[3] << 24) | (sp2[2] << 16) + | (sp2[1] << 8) | sp2[0]); + } + } + } + /* zero out other outputs */ + for (i = sfchannels; i < nvecs; i++) + for (j = nitems, fp = vecs[i]; j--; ) + *fp++ = 0; + +} + + /* soundfiler_write ... + + usage: write [flags] filename table ... + flags: + -nframes + -skip + -bytes + -normalize + -nextstep + -wave + -big + -little + */ + + /* the routine which actually does the work should LATER also be called + from garray_write16. */ + + + /* Parse arguments for writing. The "obj" argument is only for flagging + errors. For streaming to a file the "normalize", "onset" and "nframes" + arguments shouldn't be set but the calling routine flags this. */ + +static int soundfiler_writeargparse(void *obj, int *p_argc, t_atom **p_argv, + t_symbol **p_filesym, + int *p_filetype, int *p_bytespersamp, int *p_swap, int *p_bigendian, + int *p_normalize, long *p_onset, long *p_nframes, float *p_rate) +{ + int argc = *p_argc; + t_atom *argv = *p_argv; + int bytespersamp = 2, bigendian = 0, + endianness = -1, swap, filetype = -1, normalize = 0; + long onset = 0, nframes = 0x7fffffff; + t_symbol *filesym; + float rate = -1; + + while (argc > 0 && argv->a_type == A_SYMBOL && + *argv->a_w.w_symbol->s_name == '-') + { + char *flag = argv->a_w.w_symbol->s_name + 1; + if (!strcmp(flag, "skip")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT || + ((onset = argv[1].a_w.w_float) < 0)) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(flag, "nframes")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT || + ((nframes = argv[1].a_w.w_float) < 0)) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(flag, "bytes")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT || + ((bytespersamp = argv[1].a_w.w_float) < 2) || + bytespersamp > 4) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(flag, "normalize")) + { + normalize = 1; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "wave")) + { + filetype = FORMAT_WAVE; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "nextstep")) + { + filetype = FORMAT_NEXT; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "aiff")) + { + filetype = FORMAT_AIFF; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "big")) + { + endianness = 1; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "little")) + { + endianness = 0; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "r") || !strcmp(flag, "rate")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT || + ((rate = argv[1].a_w.w_float) <= 0)) + goto usage; + argc -= 2; argv += 2; + } + else goto usage; + } + if (!argc || argv->a_type != A_SYMBOL) + goto usage; + filesym = argv->a_w.w_symbol; + + /* check if format not specified and fill in */ + if (filetype < 0) + { + if (strlen(filesym->s_name) >= 5 && + (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".aif") || + !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".AIF"))) + filetype = FORMAT_AIFF; + if (strlen(filesym->s_name) >= 6 && + (!strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".aiff") || + !strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".AIFF"))) + filetype = FORMAT_AIFF; + if (strlen(filesym->s_name) >= 5 && + (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".snd") || + !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".SND"))) + filetype = FORMAT_NEXT; + if (strlen(filesym->s_name) >= 4 && + (!strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".au") || + !strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".AU"))) + filetype = FORMAT_NEXT; + if (filetype < 0) + filetype = FORMAT_WAVE; + } + /* don't handle AIFF floating point samples */ + if (bytespersamp == 4) + { + if (filetype == FORMAT_AIFF) + { + pd_error(obj, "AIFF floating-point file format unavailable"); + goto usage; + } + } + /* for WAVE force little endian; for nextstep use machine native */ + if (filetype == FORMAT_WAVE) + { + bigendian = 0; + if (endianness == 1) + pd_error(obj, "WAVE file forced to little endian"); + } + else if (filetype == FORMAT_AIFF) + { + bigendian = 1; + if (endianness == 0) + pd_error(obj, "AIFF file forced to big endian"); + } + else if (endianness == -1) + { + bigendian = garray_ambigendian(); + } + else bigendian = endianness; + swap = (bigendian != garray_ambigendian()); + + argc--; argv++; + + *p_argc = argc; + *p_argv = argv; + *p_filesym = filesym; + *p_filetype = filetype; + *p_bytespersamp = bytespersamp; + *p_swap = swap; + *p_normalize = normalize; + *p_onset = onset; + *p_nframes = nframes; + *p_bigendian = bigendian; + *p_rate = rate; + return (0); +usage: + return (-1); +} + +static int create_soundfile(t_canvas *canvas, const char *filename, + int filetype, int nframes, int bytespersamp, + int bigendian, int nchannels, int swap, float samplerate) +{ + char filenamebuf[MAXPDSTRING], buf2[MAXPDSTRING]; + char headerbuf[WRITEHDRSIZE]; + t_wave *wavehdr = (t_wave *)headerbuf; + t_nextstep *nexthdr = (t_nextstep *)headerbuf; + t_aiff *aiffhdr = (t_aiff *)headerbuf; + int fd, headersize = 0; + + strncpy(filenamebuf, filename, MAXPDSTRING-10); + filenamebuf[MAXPDSTRING-10] = 0; + + if (filetype == FORMAT_NEXT) + { + if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".snd")) + strcat(filenamebuf, ".snd"); + if (bigendian) + strncpy(nexthdr->ns_fileid, ".snd", 4); + else strncpy(nexthdr->ns_fileid, "dns.", 4); + nexthdr->ns_onset = swap4(sizeof(*nexthdr), swap); + nexthdr->ns_length = 0; + nexthdr->ns_format = swap4((bytespersamp == 3 ? NS_FORMAT_LINEAR_24 : + (bytespersamp == 4 ? NS_FORMAT_FLOAT : NS_FORMAT_LINEAR_16)), swap); + nexthdr->ns_sr = swap4(samplerate, swap); + nexthdr->ns_nchans = swap4(nchannels, swap); + strcpy(nexthdr->ns_info, "Pd "); + swapstring(nexthdr->ns_info, swap); + headersize = sizeof(t_nextstep); + } + else if (filetype == FORMAT_AIFF) + { + long datasize = nframes * nchannels * bytespersamp; + long longtmp; + static unsigned char dogdoo[] = + {0x40, 0x0e, 0xac, 0x44, 0, 0, 0, 0, 0, 0, 'S', 'S', 'N', 'D'}; + if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".aif") && + strcmp(filenamebuf + strlen(filenamebuf)-5, ".aiff")) + strcat(filenamebuf, ".aif"); + strncpy(aiffhdr->a_fileid, "FORM", 4); + aiffhdr->a_chunksize = swap4(datasize + sizeof(*aiffhdr) + 4, swap); + strncpy(aiffhdr->a_aiffid, "AIFF", 4); + strncpy(aiffhdr->a_fmtid, "COMM", 4); + aiffhdr->a_fmtchunksize = swap4(18, swap); + aiffhdr->a_nchannels = swap2(nchannels, swap); + longtmp = swap4(nframes, swap); + memcpy(&aiffhdr->a_nframeshi, &longtmp, 4); + aiffhdr->a_bitspersamp = swap2(8 * bytespersamp, swap); + memcpy(aiffhdr->a_samprate, dogdoo, sizeof(dogdoo)); + longtmp = swap4(datasize, swap); + memcpy(aiffhdr->a_samprate + sizeof(dogdoo), &longtmp, 4); + headersize = AIFFPLUS; + } + else /* WAVE format */ + { + long datasize = nframes * nchannels * bytespersamp; + if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".wav")) + strcat(filenamebuf, ".wav"); + strncpy(wavehdr->w_fileid, "RIFF", 4); + wavehdr->w_chunksize = swap4(datasize + sizeof(*wavehdr) - 8, swap); + strncpy(wavehdr->w_waveid, "WAVE", 4); + strncpy(wavehdr->w_fmtid, "fmt ", 4); + wavehdr->w_fmtchunksize = swap4(16, swap); + wavehdr->w_fmttag = + swap2((bytespersamp == 4 ? WAV_FLOAT : WAV_INT), swap); + wavehdr->w_nchannels = swap2(nchannels, swap); + wavehdr->w_samplespersec = swap4(samplerate, swap); + wavehdr->w_navgbytespersec = + swap4((int)(samplerate * nchannels * bytespersamp), swap); + wavehdr->w_nblockalign = swap2(nchannels * bytespersamp, swap); + wavehdr->w_nbitspersample = swap2(8 * bytespersamp, swap); + strncpy(wavehdr->w_datachunkid, "data", 4); + wavehdr->w_datachunksize = swap4(datasize, swap); + headersize = sizeof(t_wave); + } + + canvas_makefilename(canvas, filenamebuf, buf2, MAXPDSTRING); + sys_bashfilename(buf2, buf2); + if ((fd = open(buf2, BINCREATE, 0666)) < 0) + return (-1); + + if (write(fd, headerbuf, headersize) < headersize) + { + close (fd); + return (-1); + } + return (fd); +} + +static void soundfile_finishwrite(void *obj, char *filename, int fd, + int filetype, long nframes, long itemswritten, int bytesperframe, int swap) +{ + if (itemswritten < nframes) + { + if (nframes < 0x7fffffff) + pd_error(obj, "soundfiler_write: %d out of %d bytes written", + itemswritten, nframes); + /* try to fix size fields in header */ + if (filetype == FORMAT_WAVE) + { + long datasize = itemswritten * bytesperframe, mofo; + + if (lseek(fd, + ((char *)(&((t_wave *)0)->w_chunksize)) - (char *)0, + SEEK_SET) == 0) + goto baddonewrite; + mofo = swap4(datasize + sizeof(t_wave) - 8, swap); + if (write(fd, (char *)(&mofo), 4) < 4) + goto baddonewrite; + if (lseek(fd, + ((char *)(&((t_wave *)0)->w_datachunksize)) - (char *)0, + SEEK_SET) == 0) + goto baddonewrite; + mofo = swap4(datasize, swap); + if (write(fd, (char *)(&mofo), 4) < 4) + goto baddonewrite; + } + if (filetype == FORMAT_AIFF) + { + long mofo; + if (lseek(fd, + ((char *)(&((t_aiff *)0)->a_nframeshi)) - (char *)0, + SEEK_SET) == 0) + goto baddonewrite; + mofo = swap4(nframes, swap); + if (write(fd, (char *)(&mofo), 4) < 4) + goto baddonewrite; + } + if (filetype == FORMAT_NEXT) + { + /* do it the lazy way: just set the size field to 'unknown size'*/ + uint32 nextsize = 0xffffffff; + if (lseek(fd, 8, SEEK_SET) == 0) + { + goto baddonewrite; + } + if (write(fd, &nextsize, 4) < 4) + { + goto baddonewrite; + } + } + } + return; +baddonewrite: + post("%s: %s", filename, strerror(errno)); +} + +static void soundfile_xferout(int nchannels, t_sample **vecs, + unsigned char *buf, int nitems, long onset, int bytespersamp, + int bigendian, float normalfactor) +{ + int i, j; + unsigned char *sp, *sp2; + t_sample *fp; + int bytesperframe = bytespersamp * nchannels; + long xx; + for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp) + { + if (bytespersamp == 2) + { + float ff = normalfactor * 32768.; + if (bigendian) + { + for (j = 0, sp2 = sp, fp = vecs[i] + onset; + j < nitems; j++, sp2 += bytesperframe, fp++) + { + int xx = 32768. + (*fp * ff); + xx -= 32768; + if (xx < -32767) + xx = -32767; + if (xx > 32767) + xx = 32767; + sp2[0] = (xx >> 8); + sp2[1] = xx; + } + } + else + { + for (j = 0, sp2 = sp, fp=vecs[i] + onset; + j < nitems; j++, sp2 += bytesperframe, fp++) + { + int xx = 32768. + (*fp * ff); + xx -= 32768; + if (xx < -32767) + xx = -32767; + if (xx > 32767) + xx = 32767; + sp2[1] = (xx >> 8); + sp2[0] = xx; + } + } + } + else if (bytespersamp == 3) + { + float ff = normalfactor * 8388608.; + if (bigendian) + { + for (j = 0, sp2 = sp, fp=vecs[i] + onset; + j < nitems; j++, sp2 += bytesperframe, fp++) + { + int xx = 8388608. + (*fp * ff); + xx -= 8388608; + if (xx < -8388607) + xx = -8388607; + if (xx > 8388607) + xx = 8388607; + sp2[0] = (xx >> 16); + sp2[1] = (xx >> 8); + sp2[2] = xx; + } + } + else + { + for (j = 0, sp2 = sp, fp=vecs[i] + onset; + j < nitems; j++, sp2 += bytesperframe, fp++) + { + int xx = 8388608. + (*fp * ff); + xx -= 8388608; + if (xx < -8388607) + xx = -8388607; + if (xx > 8388607) + xx = 8388607; + sp2[2] = (xx >> 16); + sp2[1] = (xx >> 8); + sp2[0] = xx; + } + } + } + else if (bytespersamp == 4) + { + if (bigendian) + { + for (j = 0, sp2 = sp, fp=vecs[i] + onset; + j < nitems; j++, sp2 += bytesperframe, fp++) + { + float f2 = *fp * normalfactor; + xx = *(long *)&f2; + sp2[0] = (xx >> 24); sp2[1] = (xx >> 16); + sp2[2] = (xx >> 8); sp2[3] = xx; + } + } + else + { + for (j = 0, sp2 = sp, fp=vecs[i] + onset; + j < nitems; j++, sp2 += bytesperframe, fp++) + { + float f2 = *fp * normalfactor; + xx = *(long *)&f2; + sp2[3] = (xx >> 24); sp2[2] = (xx >> 16); + sp2[1] = (xx >> 8); sp2[0] = xx; + } + } + } + } +} + + +/* ------- soundfiler - reads and writes soundfiles to/from "garrays" ---- */ +#define DEFMAXSIZE 4000000 /* default maximum 16 MB per channel */ +#define SAMPBUFSIZE 1024 + + +static t_class *soundfiler_class; + +typedef struct _soundfiler +{ + t_object x_obj; + t_canvas *x_canvas; +} t_soundfiler; + +static t_soundfiler *soundfiler_new(void) +{ + t_soundfiler *x = (t_soundfiler *)pd_new(soundfiler_class); + x->x_canvas = canvas_getcurrent(); + outlet_new(&x->x_obj, &s_float); + return (x); +} + + /* soundfiler_read ... + + usage: read [flags] filename table ... + flags: + -skip ... frames to skip in file + -nframes + -onset ... onset in table to read into (NOT DONE YET) + -raw + -resize + -maxsize + */ + +static void soundfiler_read(t_soundfiler *x, t_symbol *s, + int argc, t_atom *argv) +{ + int headersize = -1, channels = 0, bytespersamp = 0, bigendian = 0, + resize = 0, i, j; + long skipframes = 0, nframes = 0, finalsize = 0, itemsleft, + maxsize = DEFMAXSIZE, itemsread = 0, bytelimit = 0x7fffffff; + int fd = -1; + char endianness, *filename; + t_garray *garrays[MAXSFCHANS]; + t_sample *vecs[MAXSFCHANS]; + char sampbuf[SAMPBUFSIZE]; + int bufframes, nitems; + FILE *fp; + while (argc > 0 && argv->a_type == A_SYMBOL && + *argv->a_w.w_symbol->s_name == '-') + { + char *flag = argv->a_w.w_symbol->s_name + 1; + if (!strcmp(flag, "skip")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT || + ((skipframes = argv[1].a_w.w_float) < 0)) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(flag, "nframes")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT || + ((nframes = argv[1].a_w.w_float) < 0)) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(flag, "raw")) + { + if (argc < 5 || + argv[1].a_type != A_FLOAT || + ((headersize = argv[1].a_w.w_float) < 0) || + argv[2].a_type != A_FLOAT || + ((channels = argv[2].a_w.w_float) < 1) || + (channels > MAXSFCHANS) || + argv[3].a_type != A_FLOAT || + ((bytespersamp = argv[3].a_w.w_float) < 2) || + (bytespersamp > 4) || + argv[4].a_type != A_SYMBOL || + ((endianness = argv[4].a_w.w_symbol->s_name[0]) != 'b' + && endianness != 'l' && endianness != 'n')) + goto usage; + if (endianness == 'b') + bigendian = 1; + else if (endianness == 'l') + bigendian = 0; + else + bigendian = garray_ambigendian(); + argc -= 5; argv += 5; + } + else if (!strcmp(flag, "resize")) + { + resize = 1; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "maxsize")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT || + ((maxsize = argv[1].a_w.w_float) < 0)) + goto usage; + resize = 1; /* maxsize implies resize. */ + argc -= 2; argv += 2; + } + else goto usage; + } + if (argc < 2 || argc > MAXSFCHANS + 1 || argv[0].a_type != A_SYMBOL) + goto usage; + filename = argv[0].a_w.w_symbol->s_name; + argc--; argv++; + + for (i = 0; i < argc; i++) + { + int vecsize; + if (argv[i].a_type != A_SYMBOL) + goto usage; + if (!(garrays[i] = + (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class))) + { + pd_error(x, "%s: no such table", argv[i].a_w.w_symbol->s_name); + goto done; + } + else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i])) + error("%s: bad template for tabwrite", + argv[i].a_w.w_symbol->s_name); + if (finalsize && finalsize != vecsize && !resize) + { + post("soundfiler_read: arrays have different lengths; resizing..."); + resize = 1; + } + finalsize = vecsize; + } + fd = open_soundfile(canvas_getdir(x->x_canvas)->s_name, filename, + headersize, &bytespersamp, &bigendian, &channels, &bytelimit, + skipframes); + + if (fd < 0) + { + pd_error(x, "soundfiler_read: %s: %s", filename, (errno == EIO ? + "unknown or bad header format" : strerror(errno))); + goto done; + } + + if (resize) + { + /* figure out what to resize to */ + long poswas, eofis, framesinfile; + + poswas = lseek(fd, 0, SEEK_CUR); + eofis = lseek(fd, 0, SEEK_END); + if (poswas < 0 || eofis < 0) + { + pd_error(x, "lseek failed"); + goto done; + } + lseek(fd, poswas, SEEK_SET); + framesinfile = (eofis - poswas) / (channels * bytespersamp); + if (framesinfile > maxsize) + { + pd_error(x, "soundfiler_read: truncated to %d elements", maxsize); + framesinfile = maxsize; + } + if (framesinfile > bytelimit / (channels * bytespersamp)) + framesinfile = bytelimit / (channels * bytespersamp); + finalsize = framesinfile; + for (i = 0; i < argc; i++) + { + int vecsize; + + garray_resize(garrays[i], finalsize); + /* for sanity's sake let's clear the save-in-patch flag here */ + garray_setsaveit(garrays[i], 0); + garray_getfloatarray(garrays[i], &vecsize, &vecs[i]); + /* if the resize failed, garray_resize reported the error */ + if (vecsize != framesinfile) + { + pd_error(x, "resize failed"); + goto done; + } + } + } + if (!finalsize) finalsize = 0x7fffffff; + if (finalsize > bytelimit / (channels * bytespersamp)) + finalsize = bytelimit / (channels * bytespersamp); + fp = fdopen(fd, "rb"); + bufframes = SAMPBUFSIZE / (channels * bytespersamp); + + for (itemsread = 0; itemsread < finalsize; ) + { + int thisread = finalsize - itemsread; + thisread = (thisread > bufframes ? bufframes : thisread); + nitems = fread(sampbuf, channels * bytespersamp, thisread, fp); + if (nitems <= 0) break; + soundfile_xferin(channels, argc, vecs, itemsread, + (unsigned char *)sampbuf, nitems, bytespersamp, bigendian); + itemsread += nitems; + } + /* zero out remaining elements of vectors */ + + for (i = 0; i < argc; i++) + { + int nzero, vecsize; + garray_getfloatarray(garrays[i], &vecsize, &vecs[i]); + for (j = itemsread; j < vecsize; j++) + vecs[i][j] = 0; + } + /* zero out vectors in excess of number of channels */ + for (i = channels; i < argc; i++) + { + int vecsize; + t_sample *foo; + garray_getfloatarray(garrays[i], &vecsize, &foo); + for (j = 0; j < vecsize; j++) + foo[j] = 0; + } + /* do all graphics updates */ + for (i = 0; i < argc; i++) + garray_redraw(garrays[i]); + fclose(fp); + fd = -1; + goto done; +usage: + pd_error(x, "usage: read [flags] filename tablename..."); + post("flags: -skip -nframes -resize -maxsize ..."); + post("-raw ."); +done: + if (fd >= 0) + close (fd); + outlet_float(x->x_obj.ob_outlet, (float)itemsread); +} + + /* this is broken out from soundfiler_write below so garray_write can + call it too... not done yet though. */ + +long soundfiler_dowrite(void *obj, t_canvas *canvas, + int argc, t_atom *argv) +{ + int headersize, bytespersamp, bigendian, + endianness, swap, filetype, normalize, i, j, nchannels; + long onset, nframes, itemsleft, + maxsize = DEFMAXSIZE, itemswritten = 0; + t_garray *garrays[MAXSFCHANS]; + t_sample *vecs[MAXSFCHANS]; + char sampbuf[SAMPBUFSIZE]; + int bufframes, nitems; + int fd = -1; + float normfactor, biggest = 0, samplerate; + t_symbol *filesym; + + if (soundfiler_writeargparse(obj, &argc, &argv, &filesym, &filetype, + &bytespersamp, &swap, &bigendian, &normalize, &onset, &nframes, + &samplerate)) + goto usage; + nchannels = argc; + if (nchannels < 1 || nchannels > MAXSFCHANS) + goto usage; + if (samplerate < 0) + samplerate = sys_getsr(); + for (i = 0; i < nchannels; i++) + { + int vecsize; + if (argv[i].a_type != A_SYMBOL) + goto usage; + if (!(garrays[i] = + (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class))) + { + pd_error(obj, "%s: no such table", argv[i].a_w.w_symbol->s_name); + goto fail; + } + else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i])) + error("%s: bad template for tabwrite", + argv[i].a_w.w_symbol->s_name); + if (nframes > vecsize - onset) + nframes = vecsize - onset; + + for (j = 0; j < vecsize; j++) + { + if (vecs[i][j] > biggest) + biggest = vecs[i][j]; + else if (-vecs[i][j] > biggest) + biggest = -vecs[i][j]; + } + } + if (nframes <= 0) + { + pd_error(obj, "soundfiler_write: no samples at onset %ld", onset); + goto fail; + } + + if ((fd = create_soundfile(canvas, filesym->s_name, filetype, + nframes, bytespersamp, bigendian, nchannels, + swap, samplerate)) < 0) + { + post("%s: %s\n", filesym->s_name, strerror(errno)); + goto fail; + } + if (!normalize) + { + if ((bytespersamp != 4) && (biggest > 1)) + { + post("%s: normalizing max amplitude %f to 1", filesym->s_name, biggest); + normalize = 1; + } + else post("%s: biggest amplitude = %f", filesym->s_name, biggest); + } + if (normalize) + normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1); + else normfactor = 1; + + bufframes = SAMPBUFSIZE / (nchannels * bytespersamp); + + for (itemswritten = 0; itemswritten < nframes; ) + { + int thiswrite = nframes - itemswritten, nitems, nbytes; + thiswrite = (thiswrite > bufframes ? bufframes : thiswrite); + soundfile_xferout(argc, vecs, (unsigned char *)sampbuf, thiswrite, + onset, bytespersamp, bigendian, normfactor); + nbytes = write(fd, sampbuf, nchannels * bytespersamp * thiswrite); + if (nbytes < nchannels * bytespersamp * thiswrite) + { + post("%s: %s", filesym->s_name, strerror(errno)); + if (nbytes > 0) + itemswritten += nbytes / (nchannels * bytespersamp); + break; + } + itemswritten += thiswrite; + onset += thiswrite; + } + if (fd >= 0) + { + soundfile_finishwrite(obj, filesym->s_name, fd, + filetype, nframes, itemswritten, nchannels * bytespersamp, swap); + close (fd); + } + return ((float)itemswritten); +usage: + pd_error(obj, "usage: write [flags] filename tablename..."); + post("flags: -skip -nframes -bytes -wave -aiff -nextstep ..."); + post("-big -little -normalize"); + post("(defaults to a 16-bit wave file)."); +fail: + if (fd >= 0) + close (fd); + return (0); +} + +static void soundfiler_write(t_soundfiler *x, t_symbol *s, + int argc, t_atom *argv) +{ + long bozo = soundfiler_dowrite(x, x->x_canvas, + argc, argv); + outlet_float(x->x_obj.ob_outlet, (float)bozo); +} + +static void soundfiler_setup(void) +{ + soundfiler_class = class_new(gensym("soundfiler"), (t_newmethod)soundfiler_new, + 0, sizeof(t_soundfiler), 0, 0); + class_addmethod(soundfiler_class, (t_method)soundfiler_read, gensym("read"), + A_GIMME, 0); + class_addmethod(soundfiler_class, (t_method)soundfiler_write, + gensym("write"), A_GIMME, 0); +} + + +#ifndef FIXEDPOINT +/************************* readsf object ******************************/ + +/* READSF uses the Posix threads package; for the moment we're Linux +only although this should be portable to the other platforms. + +Each instance of readsf~ owns a "child" thread for doing the UNIX (MSW?) file +reading. The parent thread signals the child each time: + (1) a file wants opening or closing; + (2) we've eaten another 1/16 of the shared buffer (so that the + child thread should check if it's time to read some more.) +The child signals the parent whenever a read has completed. Signalling +is done by setting "conditions" and putting data in mutex-controlled common +areas. +*/ + +#define MAXBYTESPERSAMPLE 4 +#define MAXVECSIZE 128 + +#define READSIZE 65536 +#define WRITESIZE 65536 +#define DEFBUFPERCHAN 262144 +#define MINBUFSIZE (4 * READSIZE) +#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */ + +#define REQUEST_NOTHING 0 +#define REQUEST_OPEN 1 +#define REQUEST_CLOSE 2 +#define REQUEST_QUIT 3 +#define REQUEST_BUSY 4 + +#define STATE_IDLE 0 +#define STATE_STARTUP 1 +#define STATE_STREAM 2 + +static t_class *readsf_class; + +typedef struct _readsf +{ + t_object x_obj; + t_canvas *x_canvas; + t_clock *x_clock; + char *x_buf; /* soundfile buffer */ + int x_bufsize; /* buffer size in bytes */ + int x_noutlets; /* number of audio outlets */ + t_sample *(x_outvec[MAXSFCHANS]); /* audio vectors */ + int x_vecsize; /* vector size for transfers */ + t_outlet *x_bangout; /* bang-on-done outlet */ + int x_state; /* opened, running, or idle */ + float x_insamplerate; /* sample rate of input signal if known */ + /* parameters to communicate with subthread */ + int x_requestcode; /* pending request from parent to I/O thread */ + char *x_filename; /* file to open (string is permanently allocated) */ + int x_fileerror; /* slot for "errno" return */ + int x_skipheaderbytes; /* size of header we'll skip */ + int x_bytespersample; /* bytes per sample (2 or 3) */ + int x_bigendian; /* true if file is big-endian */ + int x_sfchannels; /* number of channels in soundfile */ + float x_samplerate; /* sample rate of soundfile */ + long x_onsetframes; /* number of sample frames to skip */ + long x_bytelimit; /* max number of data bytes to read */ + int x_fd; /* filedesc */ + int x_fifosize; /* buffer size appropriately rounded down */ + int x_fifohead; /* index of next byte to get from file */ + int x_fifotail; /* index of next byte the ugen will read */ + int x_eof; /* true if fifohead has stopped changing */ + int x_sigcountdown; /* counter for signalling child for more data */ + int x_sigperiod; /* number of ticks per signal */ + int x_filetype; /* writesf~ only; type of file to create */ + int x_itemswritten; /* writesf~ only; items writen */ + int x_swap; /* writesf~ only; true if byte swapping */ + float x_f; /* writesf~ only; scalar for signal inlet */ + pthread_mutex_t x_mutex; + pthread_cond_t x_requestcondition; + pthread_cond_t x_answercondition; + pthread_t x_childthread; +} t_readsf; + + +/************** the child thread which performs file I/O ***********/ + +#if 0 +static void pute(char *s) /* debug routine */ +{ + write(2, s, strlen(s)); +} +#define DEBUG_SOUNDFILE +#endif + +#if 1 +#define sfread_cond_wait pthread_cond_wait +#define sfread_cond_signal pthread_cond_signal +#else +#include /* debugging version... */ +#include +static void readsf_fakewait(pthread_mutex_t *b) +{ + struct timeval timout; + timout.tv_sec = 0; + timout.tv_usec = 1000000; + pthread_mutex_unlock(b); + select(0, 0, 0, 0, &timout); + pthread_mutex_lock(b); +} + +#define sfread_cond_wait(a,b) readsf_fakewait(b) +#define sfread_cond_signal(a) +#endif + +static void *readsf_child_main(void *zz) +{ + t_readsf *x = zz; +#ifdef DEBUG_SOUNDFILE + pute("1\n"); +#endif + pthread_mutex_lock(&x->x_mutex); + while (1) + { + int fd, fifohead; + char *buf; +#ifdef DEBUG_SOUNDFILE + pute("0\n"); +#endif + if (x->x_requestcode == REQUEST_NOTHING) + { +#ifdef DEBUG_SOUNDFILE + pute("wait 2\n"); +#endif + sfread_cond_signal(&x->x_answercondition); + sfread_cond_wait(&x->x_requestcondition, &x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("3\n"); +#endif + } + else if (x->x_requestcode == REQUEST_OPEN) + { + char boo[80]; + int sysrtn, wantbytes; + + /* copy file stuff out of the data structure so we can + relinquish the mutex while we're in open_soundfile(). */ + long onsetframes = x->x_onsetframes; + long bytelimit = 0x7fffffff; + int skipheaderbytes = x->x_skipheaderbytes; + int bytespersample = x->x_bytespersample; + int sfchannels = x->x_sfchannels; + int bigendian = x->x_bigendian; + char *filename = x->x_filename; + char *dirname = canvas_getdir(x->x_canvas)->s_name; + /* alter the request code so that an ensuing "open" will get + noticed. */ +#ifdef DEBUG_SOUNDFILE + pute("4\n"); +#endif + x->x_requestcode = REQUEST_BUSY; + x->x_fileerror = 0; + + /* if there's already a file open, close it */ + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + close (fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + if (x->x_requestcode != REQUEST_BUSY) + goto lost; + } + /* open the soundfile with the mutex unlocked */ + pthread_mutex_unlock(&x->x_mutex); + fd = open_soundfile(dirname, filename, + skipheaderbytes, &bytespersample, &bigendian, + &sfchannels, &bytelimit, onsetframes); + pthread_mutex_lock(&x->x_mutex); + +#ifdef DEBUG_SOUNDFILE + pute("5\n"); +#endif + /* copy back into the instance structure. */ + x->x_bytespersample = bytespersample; + x->x_sfchannels = sfchannels; + x->x_bigendian = bigendian; + x->x_fd = fd; + x->x_bytelimit = bytelimit; + if (fd < 0) + { + x->x_fileerror = errno; + x->x_eof = 1; +#ifdef DEBUG_SOUNDFILE + pute("open failed\n"); + pute(filename); + pute(dirname); +#endif + goto lost; + } + /* check if another request has been made; if so, field it */ + if (x->x_requestcode != REQUEST_BUSY) + goto lost; +#ifdef DEBUG_SOUNDFILE + pute("6\n"); +#endif + x->x_fifohead = 0; + /* set fifosize from bufsize. fifosize must be a + multiple of the number of bytes eaten for each DSP + tick. We pessimistically assume MAXVECSIZE samples + per tick since that could change. There could be a + problem here if the vector size increases while a + soundfile is being played... */ + x->x_fifosize = x->x_bufsize - (x->x_bufsize % + (x->x_bytespersample * x->x_sfchannels * MAXVECSIZE)); + /* arrange for the "request" condition to be signalled 16 + times per buffer */ +#ifdef DEBUG_SOUNDFILE + sprintf(boo, "fifosize %d\n", + x->x_fifosize); + pute(boo); +#endif + x->x_sigcountdown = x->x_sigperiod = + (x->x_fifosize / + (16 * x->x_bytespersample * x->x_sfchannels * + x->x_vecsize)); + /* in a loop, wait for the fifo to get hungry and feed it */ + + while (x->x_requestcode == REQUEST_BUSY) + { + int fifosize = x->x_fifosize; +#ifdef DEBUG_SOUNDFILE + pute("77\n"); +#endif + if (x->x_eof) + break; + if (x->x_fifohead >= x->x_fifotail) + { + /* if the head is >= the tail, we can immediately read + to the end of the fifo. Unless, that is, we would + read all the way to the end of the buffer and the + "tail" is zero; this would fill the buffer completely + which isn't allowed because you can't tell a completely + full buffer from an empty one. */ + if (x->x_fifotail || (fifosize - x->x_fifohead > READSIZE)) + { + wantbytes = fifosize - x->x_fifohead; + if (wantbytes > READSIZE) + wantbytes = READSIZE; + if (wantbytes > x->x_bytelimit) + wantbytes = x->x_bytelimit; +#ifdef DEBUG_SOUNDFILE + sprintf(boo, "head %d, tail %d, size %d\n", + x->x_fifohead, x->x_fifotail, wantbytes); + pute(boo); +#endif + } + else + { +#ifdef DEBUG_SOUNDFILE + pute("wait 7a ...\n"); +#endif + sfread_cond_signal(&x->x_answercondition); +#ifdef DEBUG_SOUNDFILE + pute("signalled\n"); +#endif + sfread_cond_wait(&x->x_requestcondition, + &x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("7a done\n"); +#endif + continue; + } + } + else + { + /* otherwise check if there are at least READSIZE + bytes to read. If not, wait and loop back. */ + wantbytes = x->x_fifotail - x->x_fifohead - 1; + if (wantbytes < READSIZE) + { +#ifdef DEBUG_SOUNDFILE + pute("wait 7...\n"); +#endif + sfread_cond_signal(&x->x_answercondition); + sfread_cond_wait(&x->x_requestcondition, + &x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("7 done\n"); +#endif + continue; + } + else wantbytes = READSIZE; + if (wantbytes > x->x_bytelimit) + wantbytes = x->x_bytelimit; + } +#ifdef DEBUG_SOUNDFILE + pute("8\n"); +#endif + fd = x->x_fd; + buf = x->x_buf; + fifohead = x->x_fifohead; + pthread_mutex_unlock(&x->x_mutex); + sysrtn = read(fd, buf + fifohead, wantbytes); + pthread_mutex_lock(&x->x_mutex); + if (x->x_requestcode != REQUEST_BUSY) + break; + if (sysrtn < 0) + { +#ifdef DEBUG_SOUNDFILE + pute("fileerror\n"); +#endif + x->x_fileerror = errno; + break; + } + else if (sysrtn == 0) + { + x->x_eof = 1; + break; + } + else + { + x->x_fifohead += sysrtn; + x->x_bytelimit -= sysrtn; + if (x->x_bytelimit <= 0) + { + x->x_eof = 1; + break; + } + if (x->x_fifohead == fifosize) + x->x_fifohead = 0; + } +#ifdef DEBUG_SOUNDFILE + sprintf(boo, "after: head %d, tail %d\n", + x->x_fifohead, x->x_fifotail); + pute(boo); +#endif + /* signal parent in case it's waiting for data */ + sfread_cond_signal(&x->x_answercondition); + } + lost: + + if (x->x_requestcode == REQUEST_BUSY) + x->x_requestcode = REQUEST_NOTHING; + /* fell out of read loop: close file if necessary, + set EOF and signal once more */ + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + close (fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + } + sfread_cond_signal(&x->x_answercondition); + + } + else if (x->x_requestcode == REQUEST_CLOSE) + { + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + close (fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + } + if (x->x_requestcode == REQUEST_CLOSE) + x->x_requestcode = REQUEST_NOTHING; + sfread_cond_signal(&x->x_answercondition); + } + else if (x->x_requestcode == REQUEST_QUIT) + { + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + close (fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + } + x->x_requestcode = REQUEST_NOTHING; + sfread_cond_signal(&x->x_answercondition); + break; + } + else + { +#ifdef DEBUG_SOUNDFILE + pute("13\n"); +#endif + } + } +#ifdef DEBUG_SOUNDFILE + pute("thread exit\n"); +#endif + pthread_mutex_unlock(&x->x_mutex); + return (0); +} + +/******** the object proper runs in the calling (parent) thread ****/ + +static void readsf_tick(t_readsf *x); + +static void *readsf_new(t_floatarg fnchannels, t_floatarg fbufsize) +{ + t_readsf *x; + int nchannels = fnchannels, bufsize = fbufsize, i; + char *buf; + + if (nchannels < 1) + nchannels = 1; + else if (nchannels > MAXSFCHANS) + nchannels = MAXSFCHANS; + if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels; + else if (bufsize < MINBUFSIZE) + bufsize = MINBUFSIZE; + else if (bufsize > MAXBUFSIZE) + bufsize = MAXBUFSIZE; + buf = getbytes(bufsize); + if (!buf) return (0); + + x = (t_readsf *)pd_new(readsf_class); + + for (i = 0; i < nchannels; i++) + outlet_new(&x->x_obj, gensym("signal")); + x->x_noutlets = nchannels; + x->x_bangout = outlet_new(&x->x_obj, &s_bang); + pthread_mutex_init(&x->x_mutex, 0); + pthread_cond_init(&x->x_requestcondition, 0); + pthread_cond_init(&x->x_answercondition, 0); + x->x_vecsize = MAXVECSIZE; + x->x_state = STATE_IDLE; + x->x_clock = clock_new(x, (t_method)readsf_tick); + x->x_canvas = canvas_getcurrent(); + x->x_bytespersample = 2; + x->x_sfchannels = 1; + x->x_fd = -1; + x->x_buf = buf; + x->x_bufsize = bufsize; + x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0; + pthread_create(&x->x_childthread, 0, readsf_child_main, x); + return (x); +} + +static void readsf_tick(t_readsf *x) +{ + outlet_bang(x->x_bangout); +} + +static t_int *readsf_perform(t_int *w) +{ + t_readsf *x = (t_readsf *)(w[1]); + int vecsize = x->x_vecsize, noutlets = x->x_noutlets, i, j, + bytespersample = x->x_bytespersample, + bigendian = x->x_bigendian; + float *fp; + if (x->x_state == STATE_STREAM) + { + int wantbytes, nchannels, sfchannels = x->x_sfchannels; + pthread_mutex_lock(&x->x_mutex); + wantbytes = sfchannels * vecsize * bytespersample; + while ( + !x->x_eof && x->x_fifohead >= x->x_fifotail && + x->x_fifohead < x->x_fifotail + wantbytes-1) + { +#ifdef DEBUG_SOUNDFILE + pute("wait...\n"); +#endif + sfread_cond_signal(&x->x_requestcondition); + sfread_cond_wait(&x->x_answercondition, &x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("done\n"); +#endif + } + if (x->x_eof && x->x_fifohead >= x->x_fifotail && + x->x_fifohead < x->x_fifotail + wantbytes-1) + { + int xfersize; + if (x->x_fileerror) + { + pd_error(x, "dsp: %s: %s", x->x_filename, + (x->x_fileerror == EIO ? + "unknown or bad header format" : + strerror(x->x_fileerror))); + } + clock_delay(x->x_clock, 0); + x->x_state = STATE_IDLE; + + /* if there's a partial buffer left, copy it out. */ + xfersize = (x->x_fifohead - x->x_fifotail + 1) / + (sfchannels * bytespersample); + if (xfersize) + { + soundfile_xferin(sfchannels, noutlets, x->x_outvec, 0, + (unsigned char *)(x->x_buf + x->x_fifotail), xfersize, + bytespersample, bigendian); + vecsize -= xfersize; + } + /* then zero out the (rest of the) output */ + for (i = 0; i < noutlets; i++) + for (j = vecsize, fp = x->x_outvec[i] + xfersize; j--; ) + *fp++ = 0; + + sfread_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); + return (w+2); + } + + soundfile_xferin(sfchannels, noutlets, x->x_outvec, 0, + (unsigned char *)(x->x_buf + x->x_fifotail), vecsize, + bytespersample, bigendian); + + x->x_fifotail += wantbytes; + if (x->x_fifotail >= x->x_fifosize) + x->x_fifotail = 0; + if ((--x->x_sigcountdown) <= 0) + { + sfread_cond_signal(&x->x_requestcondition); + x->x_sigcountdown = x->x_sigperiod; + } + pthread_mutex_unlock(&x->x_mutex); + } + else + { + idle: + for (i = 0; i < noutlets; i++) + for (j = vecsize, fp = x->x_outvec[i]; j--; ) + *fp++ = 0; + } + return (w+2); +} + +static void readsf_start(t_readsf *x) +{ + /* start making output. If we're in the "startup" state change + to the "running" state. */ + if (x->x_state == STATE_STARTUP) + x->x_state = STATE_STREAM; + else pd_error(x, "readsf: start requested with no prior 'open'"); +} + +static void readsf_stop(t_readsf *x) +{ + /* LATER rethink whether you need the mutex just to set a variable? */ + pthread_mutex_lock(&x->x_mutex); + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_CLOSE; + sfread_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); +} + +static void readsf_float(t_readsf *x, t_floatarg f) +{ + if (f != 0) + readsf_start(x); + else readsf_stop(x); +} + + /* open method. Called as: + open filename [skipframes headersize channels bytespersamp endianness] + (if headersize is zero, header is taken to be automatically + detected; thus, use the special "-1" to mean a truly headerless file.) + */ + +static void readsf_open(t_readsf *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *filesym = atom_getsymbolarg(0, argc, argv); + t_float onsetframes = atom_getfloatarg(1, argc, argv); + t_float headerbytes = atom_getfloatarg(2, argc, argv); + t_float channels = atom_getfloatarg(3, argc, argv); + t_float bytespersamp = atom_getfloatarg(4, argc, argv); + t_symbol *endian = atom_getsymbolarg(5, argc, argv); + if (!*filesym->s_name) + return; + pthread_mutex_lock(&x->x_mutex); + x->x_requestcode = REQUEST_OPEN; + x->x_filename = filesym->s_name; + x->x_fifotail = 0; + x->x_fifohead = 0; + if (*endian->s_name == 'b') + x->x_bigendian = 1; + else if (*endian->s_name == 'l') + x->x_bigendian = 0; + else if (*endian->s_name) + pd_error(x, "endianness neither 'b' nor 'l'"); + else x->x_bigendian = garray_ambigendian(); + x->x_onsetframes = (onsetframes > 0 ? onsetframes : 0); + x->x_skipheaderbytes = (headerbytes > 0 ? headerbytes : + (headerbytes == 0 ? -1 : 0)); + x->x_sfchannels = (channels >= 1 ? channels : 1); + x->x_bytespersample = (bytespersamp > 2 ? bytespersamp : 2); + x->x_eof = 0; + x->x_fileerror = 0; + x->x_state = STATE_STARTUP; + sfread_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); +} + +static void readsf_dsp(t_readsf *x, t_signal **sp) +{ + int i, noutlets = x->x_noutlets; + pthread_mutex_lock(&x->x_mutex); + x->x_vecsize = sp[0]->s_n; + + x->x_sigperiod = (x->x_fifosize / + (x->x_bytespersample * x->x_sfchannels * x->x_vecsize)); + for (i = 0; i < noutlets; i++) + x->x_outvec[i] = sp[i]->s_vec; + pthread_mutex_unlock(&x->x_mutex); + dsp_add(readsf_perform, 1, x); +} + +static void readsf_print(t_readsf *x) +{ + post("state %d", x->x_state); + post("fifo head %d", x->x_fifohead); + post("fifo tail %d", x->x_fifotail); + post("fifo size %d", x->x_fifosize); + post("fd %d", x->x_fd); + post("eof %d", x->x_eof); +} + +static void readsf_free(t_readsf *x) +{ + /* request QUIT and wait for acknowledge */ + void *threadrtn; + pthread_mutex_lock(&x->x_mutex); + x->x_requestcode = REQUEST_QUIT; + sfread_cond_signal(&x->x_requestcondition); + while (x->x_requestcode != REQUEST_NOTHING) + { + sfread_cond_signal(&x->x_requestcondition); + sfread_cond_wait(&x->x_answercondition, &x->x_mutex); + } + pthread_mutex_unlock(&x->x_mutex); + if (pthread_join(x->x_childthread, &threadrtn)) + error("readsf_free: join failed"); + + pthread_cond_destroy(&x->x_requestcondition); + pthread_cond_destroy(&x->x_answercondition); + pthread_mutex_destroy(&x->x_mutex); + freebytes(x->x_buf, x->x_bufsize); + clock_free(x->x_clock); +} + +static void readsf_setup(void) +{ + readsf_class = class_new(gensym("readsf~"), (t_newmethod)readsf_new, + (t_method)readsf_free, sizeof(t_readsf), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addfloat(readsf_class, (t_method)readsf_float); + class_addmethod(readsf_class, (t_method)readsf_start, gensym("start"), 0); + class_addmethod(readsf_class, (t_method)readsf_stop, gensym("stop"), 0); + class_addmethod(readsf_class, (t_method)readsf_dsp, gensym("dsp"), 0); + class_addmethod(readsf_class, (t_method)readsf_open, gensym("open"), + A_GIMME, 0); + class_addmethod(readsf_class, (t_method)readsf_print, gensym("print"), 0); +} + +/******************************* writesf *******************/ + +static t_class *writesf_class; + +#define t_writesf t_readsf /* just re-use the structure */ + +/************** the child thread which performs file I/O ***********/ + +static void *writesf_child_main(void *zz) +{ + t_writesf *x = zz; +#ifdef DEBUG_SOUNDFILE + pute("1\n"); +#endif + pthread_mutex_lock(&x->x_mutex); + while (1) + { +#ifdef DEBUG_SOUNDFILE + pute("0\n"); +#endif + if (x->x_requestcode == REQUEST_NOTHING) + { +#ifdef DEBUG_SOUNDFILE + pute("wait 2\n"); +#endif + sfread_cond_signal(&x->x_answercondition); + sfread_cond_wait(&x->x_requestcondition, &x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("3\n"); +#endif + } + else if (x->x_requestcode == REQUEST_OPEN) + { + char boo[80]; + int fd, sysrtn, writebytes; + + /* copy file stuff out of the data structure so we can + relinquish the mutex while we're in open_soundfile(). */ + long onsetframes = x->x_onsetframes; + long bytelimit = 0x7fffffff; + int skipheaderbytes = x->x_skipheaderbytes; + int bytespersample = x->x_bytespersample; + int sfchannels = x->x_sfchannels; + int bigendian = x->x_bigendian; + int filetype = x->x_filetype; + char *filename = x->x_filename; + t_canvas *canvas = x->x_canvas; + float samplerate = x->x_samplerate; + + /* alter the request code so that an ensuing "open" will get + noticed. */ +#ifdef DEBUG_SOUNDFILE + pute("4\n"); +#endif + x->x_requestcode = REQUEST_BUSY; + x->x_fileerror = 0; + + /* if there's already a file open, close it */ + if (x->x_fd >= 0) + { + pthread_mutex_unlock(&x->x_mutex); + close (x->x_fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + if (x->x_requestcode != REQUEST_BUSY) + continue; + } + /* open the soundfile with the mutex unlocked */ + pthread_mutex_unlock(&x->x_mutex); + fd = create_soundfile(canvas, filename, filetype, 0, + bytespersample, bigendian, sfchannels, + garray_ambigendian() != bigendian, samplerate); + pthread_mutex_lock(&x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("5\n"); +#endif + + if (fd < 0) + { + x->x_fd = -1; + x->x_eof = 1; + x->x_fileerror = errno; +#ifdef DEBUG_SOUNDFILE + pute("open failed\n"); + pute(filename); +#endif + x->x_requestcode = REQUEST_NOTHING; + continue; + } + /* check if another request has been made; if so, field it */ + if (x->x_requestcode != REQUEST_BUSY) + continue; +#ifdef DEBUG_SOUNDFILE + pute("6\n"); +#endif + x->x_fd = fd; + x->x_fifotail = 0; + x->x_itemswritten = 0; + x->x_swap = garray_ambigendian() != bigendian; + /* in a loop, wait for the fifo to have data and write it + to disk */ + while (x->x_requestcode == REQUEST_BUSY || + (x->x_requestcode == REQUEST_CLOSE && + x->x_fifohead != x->x_fifotail)) + { + int fifosize = x->x_fifosize, fifotail; + char *buf = x->x_buf; +#ifdef DEBUG_SOUNDFILE + pute("77\n"); +#endif + + /* if the head is < the tail, we can immediately write + from tail to end of fifo to disk; otherwise we hold off + writing until there are at least WRITESIZE bytes in the + buffer */ + if (x->x_fifohead < x->x_fifotail || + x->x_fifohead >= x->x_fifotail + WRITESIZE + || (x->x_requestcode == REQUEST_CLOSE && + x->x_fifohead != x->x_fifotail)) + { + writebytes = (x->x_fifohead < x->x_fifotail ? + fifosize : x->x_fifohead) - x->x_fifotail; + if (writebytes > READSIZE) + writebytes = READSIZE; + } + else + { +#ifdef DEBUG_SOUNDFILE + pute("wait 7a ...\n"); +#endif + sfread_cond_signal(&x->x_answercondition); +#ifdef DEBUG_SOUNDFILE + pute("signalled\n"); +#endif + sfread_cond_wait(&x->x_requestcondition, + &x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("7a done\n"); +#endif + continue; + } +#ifdef DEBUG_SOUNDFILE + pute("8\n"); +#endif + fifotail = x->x_fifotail; + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + sysrtn = write(fd, buf + fifotail, writebytes); + pthread_mutex_lock(&x->x_mutex); + if (x->x_requestcode != REQUEST_BUSY && + x->x_requestcode != REQUEST_CLOSE) + break; + if (sysrtn < writebytes) + { +#ifdef DEBUG_SOUNDFILE + pute("fileerror\n"); +#endif + x->x_fileerror = errno; + break; + } + else + { + x->x_fifotail += sysrtn; + if (x->x_fifotail == fifosize) + x->x_fifotail = 0; + } + x->x_itemswritten += + sysrtn / (x->x_bytespersample * x->x_sfchannels); + sprintf(boo, "after: head %d, tail %d\n", + x->x_fifohead, x->x_fifotail); +#ifdef DEBUG_SOUNDFILE + pute(boo); +#endif + /* signal parent in case it's waiting for data */ + sfread_cond_signal(&x->x_answercondition); + } + } + else if (x->x_requestcode == REQUEST_CLOSE || + x->x_requestcode == REQUEST_QUIT) + { + int quit = (x->x_requestcode == REQUEST_QUIT); + if (x->x_fd >= 0) + { + int bytesperframe = x->x_bytespersample * x->x_sfchannels; + int bigendian = x->x_bigendian; + char *filename = x->x_filename; + int fd = x->x_fd; + int filetype = x->x_filetype; + int itemswritten = x->x_itemswritten; + int swap = x->x_swap; + pthread_mutex_unlock(&x->x_mutex); + + soundfile_finishwrite(x, filename, fd, + filetype, 0x7fffffff, itemswritten, + bytesperframe, swap); + close (fd); + + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + } + x->x_requestcode = REQUEST_NOTHING; + sfread_cond_signal(&x->x_answercondition); + if (quit) + break; + } + else + { +#ifdef DEBUG_SOUNDFILE + pute("13\n"); +#endif + } + } +#ifdef DEBUG_SOUNDFILE + pute("thread exit\n"); +#endif + pthread_mutex_unlock(&x->x_mutex); + return (0); +} + +/******** the object proper runs in the calling (parent) thread ****/ + +static void writesf_tick(t_writesf *x); + +static void *writesf_new(t_floatarg fnchannels, t_floatarg fbufsize) +{ + t_writesf *x; + int nchannels = fnchannels, bufsize = fbufsize, i; + char *buf; + + if (nchannels < 1) + nchannels = 1; + else if (nchannels > MAXSFCHANS) + nchannels = MAXSFCHANS; + if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels; + else if (bufsize < MINBUFSIZE) + bufsize = MINBUFSIZE; + else if (bufsize > MAXBUFSIZE) + bufsize = MAXBUFSIZE; + buf = getbytes(bufsize); + if (!buf) return (0); + + x = (t_writesf *)pd_new(writesf_class); + + for (i = 1; i < nchannels; i++) + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + + x->x_f = 0; + x->x_sfchannels = nchannels; + pthread_mutex_init(&x->x_mutex, 0); + pthread_cond_init(&x->x_requestcondition, 0); + pthread_cond_init(&x->x_answercondition, 0); + x->x_vecsize = MAXVECSIZE; + x->x_insamplerate = x->x_samplerate = 0; + x->x_state = STATE_IDLE; + x->x_clock = 0; /* no callback needed here */ + x->x_canvas = canvas_getcurrent(); + x->x_bytespersample = 2; + x->x_fd = -1; + x->x_buf = buf; + x->x_bufsize = bufsize; + x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0; + pthread_create(&x->x_childthread, 0, writesf_child_main, x); + return (x); +} + +static t_int *writesf_perform(t_int *w) +{ + t_writesf *x = (t_writesf *)(w[1]); + int vecsize = x->x_vecsize, sfchannels = x->x_sfchannels, i, j, + bytespersample = x->x_bytespersample, + bigendian = x->x_bigendian; + float *fp; + if (x->x_state == STATE_STREAM) + { + int wantbytes; + pthread_mutex_lock(&x->x_mutex); + wantbytes = sfchannels * vecsize * bytespersample; + while (x->x_fifotail > x->x_fifohead && + x->x_fifotail < x->x_fifohead + wantbytes + 1) + { +#ifdef DEBUG_SOUNDFILE + pute("wait...\n"); +#endif + sfread_cond_signal(&x->x_requestcondition); + sfread_cond_wait(&x->x_answercondition, &x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("done\n"); +#endif + } + + soundfile_xferout(sfchannels, x->x_outvec, + (unsigned char *)(x->x_buf + x->x_fifohead), vecsize, 0, + bytespersample, bigendian, 1.); + + x->x_fifohead += wantbytes; + if (x->x_fifohead >= x->x_fifosize) + x->x_fifohead = 0; + if ((--x->x_sigcountdown) <= 0) + { +#ifdef DEBUG_SOUNDFILE + pute("signal 1\n"); +#endif + sfread_cond_signal(&x->x_requestcondition); + x->x_sigcountdown = x->x_sigperiod; + } + pthread_mutex_unlock(&x->x_mutex); + } + return (w+2); +} + +static void writesf_start(t_writesf *x) +{ + /* start making output. If we're in the "startup" state change + to the "running" state. */ + if (x->x_state == STATE_STARTUP) + x->x_state = STATE_STREAM; + else + pd_error(x, "writesf: start requested with no prior 'open'"); +} + +static void writesf_stop(t_writesf *x) +{ + /* LATER rethink whether you need the mutex just to set a Svariable? */ + pthread_mutex_lock(&x->x_mutex); + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_CLOSE; +#ifdef DEBUG_SOUNDFILE + pute("signal 2\n"); +#endif + sfread_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); +} + + + /* open method. Called as: open [args] filename with args as in + soundfiler_writeargparse(). + */ + +static void writesf_open(t_writesf *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *filesym; + int filetype, bytespersamp, swap, bigendian, normalize; + long onset, nframes; + float samplerate; + if (soundfiler_writeargparse(x, &argc, + &argv, &filesym, &filetype, &bytespersamp, &swap, &bigendian, + &normalize, &onset, &nframes, &samplerate)) + { + pd_error(x, + "writesf~: usage: open [-bytes [234]] [-wave,-nextstep,-aiff] ..."); + post("... [-big,-little] [-rate ####] filename"); + } + if (normalize || onset || (nframes != 0x7fffffff)) + pd_error(x, "normalize/onset/nframes argument to writesf~: ignored"); + if (argc) + pd_error(x, "extra argument(s) to writesf~: ignored"); + pthread_mutex_lock(&x->x_mutex); + x->x_bytespersample = bytespersamp; + x->x_swap = swap; + x->x_bigendian = bigendian; + x->x_filename = filesym->s_name; + x->x_filetype = filetype; + x->x_itemswritten = 0; + x->x_requestcode = REQUEST_OPEN; + x->x_fifotail = 0; + x->x_fifohead = 0; + x->x_eof = 0; + x->x_fileerror = 0; + x->x_state = STATE_STARTUP; + x->x_bytespersample = (bytespersamp > 2 ? bytespersamp : 2); + if (samplerate > 0) + x->x_samplerate = samplerate; + else if (x->x_insamplerate > 0) + x->x_samplerate = x->x_insamplerate; + else x->x_samplerate = sys_getsr(); + /* set fifosize from bufsize. fifosize must be a + multiple of the number of bytes eaten for each DSP + tick. */ + x->x_fifosize = x->x_bufsize - (x->x_bufsize % + (x->x_bytespersample * x->x_sfchannels * MAXVECSIZE)); + /* arrange for the "request" condition to be signalled 16 + times per buffer */ + x->x_sigcountdown = x->x_sigperiod = + (x->x_fifosize / + (16 * x->x_bytespersample * x->x_sfchannels * + x->x_vecsize)); + sfread_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); +} + +static void writesf_dsp(t_writesf *x, t_signal **sp) +{ + int i, ninlets = x->x_sfchannels; + pthread_mutex_lock(&x->x_mutex); + x->x_vecsize = sp[0]->s_n; + + x->x_sigperiod = (x->x_fifosize / + (x->x_bytespersample * ninlets * x->x_vecsize)); + for (i = 0; i < ninlets; i++) + x->x_outvec[i] = sp[i]->s_vec; + x->x_insamplerate = sp[0]->s_sr; + pthread_mutex_unlock(&x->x_mutex); + dsp_add(writesf_perform, 1, x); +} + +static void writesf_print(t_writesf *x) +{ + post("state %d", x->x_state); + post("fifo head %d", x->x_fifohead); + post("fifo tail %d", x->x_fifotail); + post("fifo size %d", x->x_fifosize); + post("fd %d", x->x_fd); + post("eof %d", x->x_eof); +} + +static void writesf_free(t_writesf *x) +{ + /* request QUIT and wait for acknowledge */ + void *threadrtn; + pthread_mutex_lock(&x->x_mutex); + x->x_requestcode = REQUEST_QUIT; + /* post("stopping writesf thread..."); */ + sfread_cond_signal(&x->x_requestcondition); + while (x->x_requestcode != REQUEST_NOTHING) + { + /* post("signalling..."); */ + sfread_cond_signal(&x->x_requestcondition); + sfread_cond_wait(&x->x_answercondition, &x->x_mutex); + } + pthread_mutex_unlock(&x->x_mutex); + if (pthread_join(x->x_childthread, &threadrtn)) + error("writesf_free: join failed"); + /* post("... done."); */ + + pthread_cond_destroy(&x->x_requestcondition); + pthread_cond_destroy(&x->x_answercondition); + pthread_mutex_destroy(&x->x_mutex); + freebytes(x->x_buf, x->x_bufsize); +} + +static void writesf_setup(void) +{ + writesf_class = class_new(gensym("writesf~"), (t_newmethod)writesf_new, + (t_method)writesf_free, sizeof(t_writesf), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addmethod(writesf_class, (t_method)writesf_start, gensym("start"), 0); + class_addmethod(writesf_class, (t_method)writesf_stop, gensym("stop"), 0); + class_addmethod(writesf_class, (t_method)writesf_dsp, gensym("dsp"), 0); + class_addmethod(writesf_class, (t_method)writesf_open, gensym("open"), + A_GIMME, 0); + class_addmethod(writesf_class, (t_method)writesf_print, gensym("print"), 0); + CLASS_MAINSIGNALIN(writesf_class, t_writesf, x_f); +} + +#endif + +/* ------------------------ global setup routine ------------------------- */ + +void d_soundfile_setup(void) +{ + soundfiler_setup(); +#ifndef FIXEDPOINT + readsf_setup(); + writesf_setup(); +#endif +} + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* this file contains, first, a collection of soundfile access routines, a +sort of soundfile library. Second, the "soundfiler" object is defined which +uses the routines to read or write soundfiles, synchronously, from garrays. +These operations are not to be done in "real time" as they may have to wait +for disk accesses (even the write routine.) Finally, the realtime objects +readsf~ and writesf~ are defined which confine disk operations to a separate +thread so that they can be used in real time. The readsf~ and writesf~ +objects use Posix-like threads. */ + +#ifdef UNIX +#include +#include +#endif +#include +#ifdef MSW +#include +#endif +#include +#include +#include + +#include "m_pd.h" + +#define MAXSFCHANS 64 + +/***************** soundfile header structures ************************/ + +typedef unsigned short uint16; +typedef unsigned long uint32; + +#define FORMAT_WAVE 0 +#define FORMAT_AIFF 1 +#define FORMAT_NEXT 2 + +/* the NeXTStep sound header structure; can be big or little endian */ + +typedef struct _nextstep +{ + char ns_fileid[4]; /* magic number '.snd' if file is big-endian */ + uint32 ns_onset; /* byte offset of first sample */ + uint32 ns_length; /* length of sound in bytes */ + uint32 ns_format; /* format; see below */ + uint32 ns_sr; /* sample rate */ + uint32 ns_nchans; /* number of channels */ + char ns_info[4]; /* comment */ +} t_nextstep; + +#define NS_FORMAT_LINEAR_16 3 +#define NS_FORMAT_LINEAR_24 4 +#define NS_FORMAT_FLOAT 6 +#define SCALE (1./(1024. * 1024. * 1024. * 2.)) + +/* the WAVE header. All Wave files are little endian. We assume + the "fmt" chunk comes first which is usually the case but perhaps not + always; same for AIFF and the "COMM" chunk. */ + +typedef unsigned word; +typedef unsigned long dword; + +typedef struct _wave +{ + char w_fileid[4]; /* chunk id 'RIFF' */ + uint32 w_chunksize; /* chunk size */ + char w_waveid[4]; /* wave chunk id 'WAVE' */ + char w_fmtid[4]; /* format chunk id 'fmt ' */ + uint32 w_fmtchunksize; /* format chunk size */ + uint16 w_fmttag; /* format tag (WAV_INT etc) */ + uint16 w_nchannels; /* number of channels */ + uint32 w_samplespersec; /* sample rate in hz */ + uint32 w_navgbytespersec; /* average bytes per second */ + uint16 w_nblockalign; /* number of bytes per frame */ + uint16 w_nbitspersample; /* number of bits in a sample */ + char w_datachunkid[4]; /* data chunk id 'data' */ + uint32 w_datachunksize; /* length of data chunk */ +} t_wave; + +typedef struct _fmt /* format chunk */ +{ + uint16 f_fmttag; /* format tag, 1 for PCM */ + uint16 f_nchannels; /* number of channels */ + uint32 f_samplespersec; /* sample rate in hz */ + uint32 f_navgbytespersec; /* average bytes per second */ + uint16 f_nblockalign; /* number of bytes per frame */ + uint16 f_nbitspersample; /* number of bits in a sample */ +} t_fmt; + +typedef struct _wavechunk /* ... and the last two items */ +{ + char wc_id[4]; /* data chunk id, e.g., 'data' or 'fmt ' */ + uint32 wc_size; /* length of data chunk */ +} t_wavechunk; + +#define WAV_INT 1 +#define WAV_FLOAT 3 + +/* the AIFF header. I'm assuming AIFC is compatible but don't really know + that. */ + +typedef struct _datachunk +{ + char dc_id[4]; /* data chunk id 'SSND' */ + uint32 dc_size; /* length of data chunk */ +} t_datachunk; + +typedef struct _comm +{ + uint16 c_nchannels; /* number of channels */ + uint16 c_nframeshi; /* # of sample frames (hi) */ + uint16 c_nframeslo; /* # of sample frames (lo) */ + uint16 c_bitspersamp; /* bits per sample */ + unsigned char c_samprate[10]; /* sample rate, 80-bit float! */ +} t_comm; + + /* this version is more convenient for writing them out: */ +typedef struct _aiff +{ + char a_fileid[4]; /* chunk id 'FORM' */ + uint32 a_chunksize; /* chunk size */ + char a_aiffid[4]; /* aiff chunk id 'AIFF' */ + char a_fmtid[4]; /* format chunk id 'COMM' */ + uint32 a_fmtchunksize; /* format chunk size, 18 */ + uint16 a_nchannels; /* number of channels */ + uint16 a_nframeshi; /* # of sample frames (hi) */ + uint16 a_nframeslo; /* # of sample frames (lo) */ + uint16 a_bitspersamp; /* bits per sample */ + unsigned char a_samprate[10]; /* sample rate, 80-bit float! */ +} t_aiff; + +#define AIFFHDRSIZE 38 /* probably not what sizeof() gives */ + + +#define AIFFPLUS (AIFFHDRSIZE + 8) /* header size including first chunk hdr */ + +#define WHDR1 sizeof(t_nextstep) +#define WHDR2 (sizeof(t_wave) > WHDR1 ? sizeof (t_wave) : WHDR1) +#define WRITEHDRSIZE (AIFFPLUS > WHDR2 ? AIFFPLUS : WHDR2) + +#define READHDRSIZE (16 > WHDR2 + 2 ? 16 : WHDR2 + 2) + +#define OBUFSIZE MAXPDSTRING /* assume MAXPDSTRING is bigger than headers */ + +#ifdef MSW +#include +#define BINCREATE _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY +#else +#define BINCREATE O_WRONLY | O_CREAT | O_TRUNC +#endif + +/* this routine returns 1 if the high order byte comes at the lower +address on our architecture (big-endianness.). It's 1 for Motorola, +0 for Intel: */ + +extern int garray_ambigendian(void); + +/* byte swappers */ + +static uint32 swap4(uint32 n, int doit) +{ + if (doit) + return (((n & 0xff) << 24) | ((n & 0xff00) << 8) | + ((n & 0xff0000) >> 8) | ((n & 0xff000000) >> 24)); + else return (n); +} + +static uint16 swap2(uint32 n, int doit) +{ + if (doit) + return (((n & 0xff) << 8) | ((n & 0xff00) >> 8)); + else return (n); +} + +static void swapstring(char *foo, int doit) +{ + if (doit) + { + char a = foo[0], b = foo[1], c = foo[2], d = foo[3]; + foo[0] = d; foo[1] = c; foo[2] = b; foo[3] = a; + } +} + +/******************** soundfile access routines **********************/ + +/* This routine opens a file, looks for either a nextstep or "wave" header, +* seeks to end of it, and fills in bytes per sample and number of channels. +* Only 2- and 3-byte fixed-point samples and 4-byte floating point samples +* are supported. If "headersize" is nonzero, the +* caller should supply the number of channels, endinanness, and bytes per +* sample; the header is ignored. Otherwise, the routine tries to read the +* header and fill in the properties. +*/ + +int open_soundfile(const char *dirname, const char *filename, int headersize, + int *p_bytespersamp, int *p_bigendian, int *p_nchannels, long *p_bytelimit, + long skipframes) +{ + char buf[OBUFSIZE], *bufptr; + int fd, format, nchannels, bigendian, bytespersamp, swap, sysrtn; + long bytelimit = 0x7fffffff; + errno = 0; + fd = open_via_path(dirname, filename, + "", buf, &bufptr, MAXPDSTRING, 1); + if (fd < 0) + return (-1); + if (headersize >= 0) /* header detection overridden */ + { + bigendian = *p_bigendian; + nchannels = *p_nchannels; + bytespersamp = *p_bytespersamp; + bytelimit = *p_bytelimit; + } + else + { + int bytesread = read(fd, buf, READHDRSIZE); + int format; + if (bytesread < 4) + goto badheader; + if (!strncmp(buf, ".snd", 4)) + format = FORMAT_NEXT, bigendian = 1; + else if (!strncmp(buf, "dns.", 4)) + format = FORMAT_NEXT, bigendian = 0; + else if (!strncmp(buf, "RIFF", 4)) + { + if (bytesread < 12 || strncmp(buf + 8, "WAVE", 4)) + goto badheader; + format = FORMAT_WAVE, bigendian = 0; + } + else if (!strncmp(buf, "FORM", 4)) + { + if (bytesread < 12 || strncmp(buf + 8, "AIFF", 4)) + goto badheader; + format = FORMAT_AIFF, bigendian = 1; + } + else + goto badheader; + swap = (bigendian != garray_ambigendian()); + if (format == FORMAT_NEXT) /* nextstep header */ + { + uint32 param; + if (bytesread < (int)sizeof(t_nextstep)) + goto badheader; + nchannels = swap4(((t_nextstep *)buf)->ns_nchans, swap); + format = swap4(((t_nextstep *)buf)->ns_format, swap); + headersize = swap4(((t_nextstep *)buf)->ns_onset, swap); + if (format == NS_FORMAT_LINEAR_16) + bytespersamp = 2; + else if (format == NS_FORMAT_LINEAR_24) + bytespersamp = 3; + else if (format == NS_FORMAT_FLOAT) + bytespersamp = 4; + else goto badheader; + bytelimit = 0x7fffffff; + } + else if (format == FORMAT_WAVE) /* wave header */ + { + /* This is awful. You have to skip over chunks, + except that if one happens to be a "fmt" chunk, you want to + find out the format from that one. The case where the + "fmt" chunk comes after the audio isn't handled. */ + headersize = 12; + if (bytesread < 20) + goto badheader; + /* First we guess a number of channels, etc., in case there's + no "fmt" chunk to follow. */ + nchannels = 1; + bytespersamp = 2; + /* copy the first chunk header to beginnning of buffer. */ + memcpy(buf, buf + headersize, sizeof(t_wavechunk)); + /* post("chunk %c %c %c %c", + ((t_wavechunk *)buf)->wc_id[0], + ((t_wavechunk *)buf)->wc_id[1], + ((t_wavechunk *)buf)->wc_id[2], + ((t_wavechunk *)buf)->wc_id[3]); */ + /* read chunks in loop until we get to the data chunk */ + while (strncmp(((t_wavechunk *)buf)->wc_id, "data", 4)) + { + long chunksize = swap4(((t_wavechunk *)buf)->wc_size, + swap), seekto = headersize + chunksize + 8, seekout; + + if (!strncmp(((t_wavechunk *)buf)->wc_id, "fmt ", 4)) + { + long commblockonset = headersize + 8; + seekout = lseek(fd, commblockonset, SEEK_SET); + if (seekout != commblockonset) + goto badheader; + if (read(fd, buf, sizeof(t_fmt)) < (int) sizeof(t_fmt)) + goto badheader; + nchannels = swap2(((t_fmt *)buf)->f_nchannels, swap); + format = swap2(((t_fmt *)buf)->f_nbitspersample, swap); + if (format == 16) + bytespersamp = 2; + else if (format == 24) + bytespersamp = 3; + else if (format == 32) + bytespersamp = 4; + else goto badheader; + } + seekout = lseek(fd, seekto, SEEK_SET); + if (seekout != seekto) + goto badheader; + if (read(fd, buf, sizeof(t_wavechunk)) < + (int) sizeof(t_wavechunk)) + goto badheader; + /* post("new chunk %c %c %c %c at %d", + ((t_wavechunk *)buf)->wc_id[0], + ((t_wavechunk *)buf)->wc_id[1], + ((t_wavechunk *)buf)->wc_id[2], + ((t_wavechunk *)buf)->wc_id[3], seekto); */ + headersize = seekto; + } + bytelimit = swap4(((t_wavechunk *)buf)->wc_size, swap); + headersize += 8; + } + else + { + /* AIFF. same as WAVE; actually predates it. Disgusting. */ + headersize = 12; + if (bytesread < 20) + goto badheader; + /* First we guess a number of channels, etc., in case there's + no COMM block to follow. */ + nchannels = 1; + bytespersamp = 2; + /* copy the first chunk header to beginnning of buffer. */ + memcpy(buf, buf + headersize, sizeof(t_datachunk)); + /* read chunks in loop until we get to the data chunk */ + while (strncmp(((t_datachunk *)buf)->dc_id, "SSND", 4)) + { + long chunksize = swap4(((t_datachunk *)buf)->dc_size, + swap), seekto = headersize + chunksize + 8, seekout; + /* post("chunk %c %c %c %c seek %d", + ((t_datachunk *)buf)->dc_id[0], + ((t_datachunk *)buf)->dc_id[1], + ((t_datachunk *)buf)->dc_id[2], + ((t_datachunk *)buf)->dc_id[3], seekto); */ + if (!strncmp(((t_datachunk *)buf)->dc_id, "COMM", 4)) + { + long commblockonset = headersize + 8; + seekout = lseek(fd, commblockonset, SEEK_SET); + if (seekout != commblockonset) + goto badheader; + if (read(fd, buf, sizeof(t_comm)) < + (int) sizeof(t_comm)) + goto badheader; + nchannels = swap2(((t_comm *)buf)->c_nchannels, swap); + format = swap2(((t_comm *)buf)->c_bitspersamp, swap); + if (format == 16) + bytespersamp = 2; + else if (format == 24) + bytespersamp = 3; + else goto badheader; + } + seekout = lseek(fd, seekto, SEEK_SET); + if (seekout != seekto) + goto badheader; + if (read(fd, buf, sizeof(t_datachunk)) < + (int) sizeof(t_datachunk)) + goto badheader; + headersize = seekto; + } + bytelimit = swap4(((t_datachunk *)buf)->dc_size, swap); + headersize += 8; + } + } + /* seek past header and any sample frames to skip */ + sysrtn = lseek(fd, nchannels * bytespersamp * skipframes + headersize, 0); + if (sysrtn != nchannels * bytespersamp * skipframes + headersize) + return (-1); + bytelimit -= nchannels * bytespersamp * skipframes; + if (bytelimit < 0) + bytelimit = 0; + /* copy sample format back to caller */ + *p_bigendian = bigendian; + *p_nchannels = nchannels; + *p_bytespersamp = bytespersamp; + *p_bytelimit = bytelimit; + return (fd); +badheader: + /* the header wasn't recognized. We're threadable here so let's not + print out the error... */ + errno = EIO; + return (-1); +} + +static void soundfile_xferin(int sfchannels, int nvecs, t_sample **vecs, + long itemsread, unsigned char *buf, int nitems, int bytespersamp, + int bigendian) +{ + int i, j; + unsigned char *sp, *sp2; + t_sample *fp; + int nchannels = (sfchannels < nvecs ? sfchannels : nvecs); + int bytesperframe = bytespersamp * sfchannels; + for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp) + { + if (bytespersamp == 2) + { + if (bigendian) + { + for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; + j < nitems; j++, sp2 += bytesperframe, fp++) + *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16)); + } + else + { + for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; + j < nitems; j++, sp2 += bytesperframe, fp++) + *fp = ((short*)sp2)[0]<<(fix1-16); + } + } + else if (bytespersamp == 3) + { + if (bigendian) + { + for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; + j < nitems; j++, sp2 += bytesperframe, fp++) + *fp = SCALE * ((sp2[0] << 24) | (sp2[1] << 16) + | (sp2[2] << 8)); + } + else + { + for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; + j < nitems; j++, sp2 += bytesperframe, fp++) + *fp = SCALE * ((sp2[2] << 24) | (sp2[1] << 16) + | (sp2[0] << 8)); + } + } + else if (bytespersamp == 4) + { + if (bigendian) + { + for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; + j < nitems; j++, sp2 += bytesperframe, fp++) + *(long *)fp = ((sp2[0] << 24) | (sp2[1] << 16) + | (sp2[2] << 8) | sp2[3]); + } + else + { + for (j = 0, sp2 = sp, fp=vecs[i] + itemsread; + j < nitems; j++, sp2 += bytesperframe, fp++) + *(long *)fp = ((sp2[3] << 24) | (sp2[2] << 16) + | (sp2[1] << 8) | sp2[0]); + } + } + } + /* zero out other outputs */ + for (i = sfchannels; i < nvecs; i++) + for (j = nitems, fp = vecs[i]; j--; ) + *fp++ = 0; + +} + + /* soundfiler_write ... + + usage: write [flags] filename table ... + flags: + -nframes + -skip + -bytes + -normalize + -nextstep + -wave + -big + -little + */ + + /* the routine which actually does the work should LATER also be called + from garray_write16. */ + + + /* Parse arguments for writing. The "obj" argument is only for flagging + errors. For streaming to a file the "normalize", "onset" and "nframes" + arguments shouldn't be set but the calling routine flags this. */ + +static int soundfiler_writeargparse(void *obj, int *p_argc, t_atom **p_argv, + t_symbol **p_filesym, + int *p_filetype, int *p_bytespersamp, int *p_swap, int *p_bigendian, + int *p_normalize, long *p_onset, long *p_nframes, float *p_rate) +{ + int argc = *p_argc; + t_atom *argv = *p_argv; + int bytespersamp = 2, bigendian = 0, + endianness = -1, swap, filetype = -1, normalize = 0; + long onset = 0, nframes = 0x7fffffff; + t_symbol *filesym; + float rate = -1; + + while (argc > 0 && argv->a_type == A_SYMBOL && + *argv->a_w.w_symbol->s_name == '-') + { + char *flag = argv->a_w.w_symbol->s_name + 1; + if (!strcmp(flag, "skip")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT || + ((onset = argv[1].a_w.w_float) < 0)) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(flag, "nframes")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT || + ((nframes = argv[1].a_w.w_float) < 0)) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(flag, "bytes")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT || + ((bytespersamp = argv[1].a_w.w_float) < 2) || + bytespersamp > 4) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(flag, "normalize")) + { + normalize = 1; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "wave")) + { + filetype = FORMAT_WAVE; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "nextstep")) + { + filetype = FORMAT_NEXT; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "aiff")) + { + filetype = FORMAT_AIFF; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "big")) + { + endianness = 1; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "little")) + { + endianness = 0; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "r") || !strcmp(flag, "rate")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT || + ((rate = argv[1].a_w.w_float) <= 0)) + goto usage; + argc -= 2; argv += 2; + } + else goto usage; + } + if (!argc || argv->a_type != A_SYMBOL) + goto usage; + filesym = argv->a_w.w_symbol; + + /* check if format not specified and fill in */ + if (filetype < 0) + { + if (strlen(filesym->s_name) >= 5 && + (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".aif") || + !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".AIF"))) + filetype = FORMAT_AIFF; + if (strlen(filesym->s_name) >= 6 && + (!strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".aiff") || + !strcmp(filesym->s_name + strlen(filesym->s_name) - 5, ".AIFF"))) + filetype = FORMAT_AIFF; + if (strlen(filesym->s_name) >= 5 && + (!strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".snd") || + !strcmp(filesym->s_name + strlen(filesym->s_name) - 4, ".SND"))) + filetype = FORMAT_NEXT; + if (strlen(filesym->s_name) >= 4 && + (!strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".au") || + !strcmp(filesym->s_name + strlen(filesym->s_name) - 3, ".AU"))) + filetype = FORMAT_NEXT; + if (filetype < 0) + filetype = FORMAT_WAVE; + } + /* don't handle AIFF floating point samples */ + if (bytespersamp == 4) + { + if (filetype == FORMAT_AIFF) + { + pd_error(obj, "AIFF floating-point file format unavailable"); + goto usage; + } + } + /* for WAVE force little endian; for nextstep use machine native */ + if (filetype == FORMAT_WAVE) + { + bigendian = 0; + if (endianness == 1) + pd_error(obj, "WAVE file forced to little endian"); + } + else if (filetype == FORMAT_AIFF) + { + bigendian = 1; + if (endianness == 0) + pd_error(obj, "AIFF file forced to big endian"); + } + else if (endianness == -1) + { + bigendian = garray_ambigendian(); + } + else bigendian = endianness; + swap = (bigendian != garray_ambigendian()); + + argc--; argv++; + + *p_argc = argc; + *p_argv = argv; + *p_filesym = filesym; + *p_filetype = filetype; + *p_bytespersamp = bytespersamp; + *p_swap = swap; + *p_normalize = normalize; + *p_onset = onset; + *p_nframes = nframes; + *p_bigendian = bigendian; + *p_rate = rate; + return (0); +usage: + return (-1); +} + +static int create_soundfile(t_canvas *canvas, const char *filename, + int filetype, int nframes, int bytespersamp, + int bigendian, int nchannels, int swap, float samplerate) +{ + char filenamebuf[MAXPDSTRING], buf2[MAXPDSTRING]; + char headerbuf[WRITEHDRSIZE]; + t_wave *wavehdr = (t_wave *)headerbuf; + t_nextstep *nexthdr = (t_nextstep *)headerbuf; + t_aiff *aiffhdr = (t_aiff *)headerbuf; + int fd, headersize = 0; + + strncpy(filenamebuf, filename, MAXPDSTRING-10); + filenamebuf[MAXPDSTRING-10] = 0; + + if (filetype == FORMAT_NEXT) + { + if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".snd")) + strcat(filenamebuf, ".snd"); + if (bigendian) + strncpy(nexthdr->ns_fileid, ".snd", 4); + else strncpy(nexthdr->ns_fileid, "dns.", 4); + nexthdr->ns_onset = swap4(sizeof(*nexthdr), swap); + nexthdr->ns_length = 0; + nexthdr->ns_format = swap4((bytespersamp == 3 ? NS_FORMAT_LINEAR_24 : + (bytespersamp == 4 ? NS_FORMAT_FLOAT : NS_FORMAT_LINEAR_16)), swap); + nexthdr->ns_sr = swap4(samplerate, swap); + nexthdr->ns_nchans = swap4(nchannels, swap); + strcpy(nexthdr->ns_info, "Pd "); + swapstring(nexthdr->ns_info, swap); + headersize = sizeof(t_nextstep); + } + else if (filetype == FORMAT_AIFF) + { + long datasize = nframes * nchannels * bytespersamp; + long longtmp; + static unsigned char dogdoo[] = + {0x40, 0x0e, 0xac, 0x44, 0, 0, 0, 0, 0, 0, 'S', 'S', 'N', 'D'}; + if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".aif") && + strcmp(filenamebuf + strlen(filenamebuf)-5, ".aiff")) + strcat(filenamebuf, ".aif"); + strncpy(aiffhdr->a_fileid, "FORM", 4); + aiffhdr->a_chunksize = swap4(datasize + sizeof(*aiffhdr) + 4, swap); + strncpy(aiffhdr->a_aiffid, "AIFF", 4); + strncpy(aiffhdr->a_fmtid, "COMM", 4); + aiffhdr->a_fmtchunksize = swap4(18, swap); + aiffhdr->a_nchannels = swap2(nchannels, swap); + longtmp = swap4(nframes, swap); + memcpy(&aiffhdr->a_nframeshi, &longtmp, 4); + aiffhdr->a_bitspersamp = swap2(8 * bytespersamp, swap); + memcpy(aiffhdr->a_samprate, dogdoo, sizeof(dogdoo)); + longtmp = swap4(datasize, swap); + memcpy(aiffhdr->a_samprate + sizeof(dogdoo), &longtmp, 4); + headersize = AIFFPLUS; + } + else /* WAVE format */ + { + long datasize = nframes * nchannels * bytespersamp; + if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".wav")) + strcat(filenamebuf, ".wav"); + strncpy(wavehdr->w_fileid, "RIFF", 4); + wavehdr->w_chunksize = swap4(datasize + sizeof(*wavehdr) - 8, swap); + strncpy(wavehdr->w_waveid, "WAVE", 4); + strncpy(wavehdr->w_fmtid, "fmt ", 4); + wavehdr->w_fmtchunksize = swap4(16, swap); + wavehdr->w_fmttag = + swap2((bytespersamp == 4 ? WAV_FLOAT : WAV_INT), swap); + wavehdr->w_nchannels = swap2(nchannels, swap); + wavehdr->w_samplespersec = swap4(samplerate, swap); + wavehdr->w_navgbytespersec = + swap4((int)(samplerate * nchannels * bytespersamp), swap); + wavehdr->w_nblockalign = swap2(nchannels * bytespersamp, swap); + wavehdr->w_nbitspersample = swap2(8 * bytespersamp, swap); + strncpy(wavehdr->w_datachunkid, "data", 4); + wavehdr->w_datachunksize = swap4(datasize, swap); + headersize = sizeof(t_wave); + } + + canvas_makefilename(canvas, filenamebuf, buf2, MAXPDSTRING); + sys_bashfilename(buf2, buf2); + if ((fd = open(buf2, BINCREATE, 0666)) < 0) + return (-1); + + if (write(fd, headerbuf, headersize) < headersize) + { + close (fd); + return (-1); + } + return (fd); +} + +static void soundfile_finishwrite(void *obj, char *filename, int fd, + int filetype, long nframes, long itemswritten, int bytesperframe, int swap) +{ + if (itemswritten < nframes) + { + if (nframes < 0x7fffffff) + pd_error(obj, "soundfiler_write: %d out of %d bytes written", + itemswritten, nframes); + /* try to fix size fields in header */ + if (filetype == FORMAT_WAVE) + { + long datasize = itemswritten * bytesperframe, mofo; + + if (lseek(fd, + ((char *)(&((t_wave *)0)->w_chunksize)) - (char *)0, + SEEK_SET) == 0) + goto baddonewrite; + mofo = swap4(datasize + sizeof(t_wave) - 8, swap); + if (write(fd, (char *)(&mofo), 4) < 4) + goto baddonewrite; + if (lseek(fd, + ((char *)(&((t_wave *)0)->w_datachunksize)) - (char *)0, + SEEK_SET) == 0) + goto baddonewrite; + mofo = swap4(datasize, swap); + if (write(fd, (char *)(&mofo), 4) < 4) + goto baddonewrite; + } + if (filetype == FORMAT_AIFF) + { + long mofo; + if (lseek(fd, + ((char *)(&((t_aiff *)0)->a_nframeshi)) - (char *)0, + SEEK_SET) == 0) + goto baddonewrite; + mofo = swap4(nframes, swap); + if (write(fd, (char *)(&mofo), 4) < 4) + goto baddonewrite; + } + if (filetype == FORMAT_NEXT) + { + /* do it the lazy way: just set the size field to 'unknown size'*/ + uint32 nextsize = 0xffffffff; + if (lseek(fd, 8, SEEK_SET) == 0) + { + goto baddonewrite; + } + if (write(fd, &nextsize, 4) < 4) + { + goto baddonewrite; + } + } + } + return; +baddonewrite: + post("%s: %s", filename, strerror(errno)); +} + +static void soundfile_xferout(int nchannels, t_sample **vecs, + unsigned char *buf, int nitems, long onset, int bytespersamp, + int bigendian, float normalfactor) +{ + int i, j; + unsigned char *sp, *sp2; + t_sample *fp; + int bytesperframe = bytespersamp * nchannels; + long xx; + for (i = 0, sp = buf; i < nchannels; i++, sp += bytespersamp) + { + if (bytespersamp == 2) + { + float ff = normalfactor * 32768.; + if (bigendian) + { + for (j = 0, sp2 = sp, fp = vecs[i] + onset; + j < nitems; j++, sp2 += bytesperframe, fp++) + { + int xx = 32768. + (*fp * ff); + xx -= 32768; + if (xx < -32767) + xx = -32767; + if (xx > 32767) + xx = 32767; + sp2[0] = (xx >> 8); + sp2[1] = xx; + } + } + else + { + for (j = 0, sp2 = sp, fp=vecs[i] + onset; + j < nitems; j++, sp2 += bytesperframe, fp++) + { + int xx = 32768. + (*fp * ff); + xx -= 32768; + if (xx < -32767) + xx = -32767; + if (xx > 32767) + xx = 32767; + sp2[1] = (xx >> 8); + sp2[0] = xx; + } + } + } + else if (bytespersamp == 3) + { + float ff = normalfactor * 8388608.; + if (bigendian) + { + for (j = 0, sp2 = sp, fp=vecs[i] + onset; + j < nitems; j++, sp2 += bytesperframe, fp++) + { + int xx = 8388608. + (*fp * ff); + xx -= 8388608; + if (xx < -8388607) + xx = -8388607; + if (xx > 8388607) + xx = 8388607; + sp2[0] = (xx >> 16); + sp2[1] = (xx >> 8); + sp2[2] = xx; + } + } + else + { + for (j = 0, sp2 = sp, fp=vecs[i] + onset; + j < nitems; j++, sp2 += bytesperframe, fp++) + { + int xx = 8388608. + (*fp * ff); + xx -= 8388608; + if (xx < -8388607) + xx = -8388607; + if (xx > 8388607) + xx = 8388607; + sp2[2] = (xx >> 16); + sp2[1] = (xx >> 8); + sp2[0] = xx; + } + } + } + else if (bytespersamp == 4) + { + if (bigendian) + { + for (j = 0, sp2 = sp, fp=vecs[i] + onset; + j < nitems; j++, sp2 += bytesperframe, fp++) + { + float f2 = *fp * normalfactor; + xx = *(long *)&f2; + sp2[0] = (xx >> 24); sp2[1] = (xx >> 16); + sp2[2] = (xx >> 8); sp2[3] = xx; + } + } + else + { + for (j = 0, sp2 = sp, fp=vecs[i] + onset; + j < nitems; j++, sp2 += bytesperframe, fp++) + { + float f2 = *fp * normalfactor; + xx = *(long *)&f2; + sp2[3] = (xx >> 24); sp2[2] = (xx >> 16); + sp2[1] = (xx >> 8); sp2[0] = xx; + } + } + } + } +} + + +/* ------- soundfiler - reads and writes soundfiles to/from "garrays" ---- */ +#define DEFMAXSIZE 4000000 /* default maximum 16 MB per channel */ +#define SAMPBUFSIZE 1024 + + +static t_class *soundfiler_class; + +typedef struct _soundfiler +{ + t_object x_obj; + t_canvas *x_canvas; +} t_soundfiler; + +static t_soundfiler *soundfiler_new(void) +{ + t_soundfiler *x = (t_soundfiler *)pd_new(soundfiler_class); + x->x_canvas = canvas_getcurrent(); + outlet_new(&x->x_obj, &s_float); + return (x); +} + + /* soundfiler_read ... + + usage: read [flags] filename table ... + flags: + -skip ... frames to skip in file + -nframes + -onset ... onset in table to read into (NOT DONE YET) + -raw + -resize + -maxsize + */ + +static void soundfiler_read(t_soundfiler *x, t_symbol *s, + int argc, t_atom *argv) +{ + int headersize = -1, channels = 0, bytespersamp = 0, bigendian = 0, + resize = 0, i, j; + long skipframes = 0, nframes = 0, finalsize = 0, itemsleft, + maxsize = DEFMAXSIZE, itemsread = 0, bytelimit = 0x7fffffff; + int fd = -1; + char endianness, *filename; + t_garray *garrays[MAXSFCHANS]; + t_sample *vecs[MAXSFCHANS]; + char sampbuf[SAMPBUFSIZE]; + int bufframes, nitems; + FILE *fp; + while (argc > 0 && argv->a_type == A_SYMBOL && + *argv->a_w.w_symbol->s_name == '-') + { + char *flag = argv->a_w.w_symbol->s_name + 1; + if (!strcmp(flag, "skip")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT || + ((skipframes = argv[1].a_w.w_float) < 0)) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(flag, "nframes")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT || + ((nframes = argv[1].a_w.w_float) < 0)) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(flag, "raw")) + { + if (argc < 5 || + argv[1].a_type != A_FLOAT || + ((headersize = argv[1].a_w.w_float) < 0) || + argv[2].a_type != A_FLOAT || + ((channels = argv[2].a_w.w_float) < 1) || + (channels > MAXSFCHANS) || + argv[3].a_type != A_FLOAT || + ((bytespersamp = argv[3].a_w.w_float) < 2) || + (bytespersamp > 4) || + argv[4].a_type != A_SYMBOL || + ((endianness = argv[4].a_w.w_symbol->s_name[0]) != 'b' + && endianness != 'l' && endianness != 'n')) + goto usage; + if (endianness == 'b') + bigendian = 1; + else if (endianness == 'l') + bigendian = 0; + else + bigendian = garray_ambigendian(); + argc -= 5; argv += 5; + } + else if (!strcmp(flag, "resize")) + { + resize = 1; + argc -= 1; argv += 1; + } + else if (!strcmp(flag, "maxsize")) + { + if (argc < 2 || argv[1].a_type != A_FLOAT || + ((maxsize = argv[1].a_w.w_float) < 0)) + goto usage; + resize = 1; /* maxsize implies resize. */ + argc -= 2; argv += 2; + } + else goto usage; + } + if (argc < 2 || argc > MAXSFCHANS + 1 || argv[0].a_type != A_SYMBOL) + goto usage; + filename = argv[0].a_w.w_symbol->s_name; + argc--; argv++; + + for (i = 0; i < argc; i++) + { + int vecsize; + if (argv[i].a_type != A_SYMBOL) + goto usage; + if (!(garrays[i] = + (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class))) + { + pd_error(x, "%s: no such table", argv[i].a_w.w_symbol->s_name); + goto done; + } + else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i])) + error("%s: bad template for tabwrite", + argv[i].a_w.w_symbol->s_name); + if (finalsize && finalsize != vecsize && !resize) + { + post("soundfiler_read: arrays have different lengths; resizing..."); + resize = 1; + } + finalsize = vecsize; + } + fd = open_soundfile(canvas_getdir(x->x_canvas)->s_name, filename, + headersize, &bytespersamp, &bigendian, &channels, &bytelimit, + skipframes); + + if (fd < 0) + { + pd_error(x, "soundfiler_read: %s: %s", filename, (errno == EIO ? + "unknown or bad header format" : strerror(errno))); + goto done; + } + + if (resize) + { + /* figure out what to resize to */ + long poswas, eofis, framesinfile; + + poswas = lseek(fd, 0, SEEK_CUR); + eofis = lseek(fd, 0, SEEK_END); + if (poswas < 0 || eofis < 0) + { + pd_error(x, "lseek failed"); + goto done; + } + lseek(fd, poswas, SEEK_SET); + framesinfile = (eofis - poswas) / (channels * bytespersamp); + if (framesinfile > maxsize) + { + pd_error(x, "soundfiler_read: truncated to %d elements", maxsize); + framesinfile = maxsize; + } + if (framesinfile > bytelimit / (channels * bytespersamp)) + framesinfile = bytelimit / (channels * bytespersamp); + finalsize = framesinfile; + for (i = 0; i < argc; i++) + { + int vecsize; + + garray_resize(garrays[i], finalsize); + /* for sanity's sake let's clear the save-in-patch flag here */ + garray_setsaveit(garrays[i], 0); + garray_getfloatarray(garrays[i], &vecsize, &vecs[i]); + /* if the resize failed, garray_resize reported the error */ + if (vecsize != framesinfile) + { + pd_error(x, "resize failed"); + goto done; + } + } + } + if (!finalsize) finalsize = 0x7fffffff; + if (finalsize > bytelimit / (channels * bytespersamp)) + finalsize = bytelimit / (channels * bytespersamp); + fp = fdopen(fd, "rb"); + bufframes = SAMPBUFSIZE / (channels * bytespersamp); + + for (itemsread = 0; itemsread < finalsize; ) + { + int thisread = finalsize - itemsread; + thisread = (thisread > bufframes ? bufframes : thisread); + nitems = fread(sampbuf, channels * bytespersamp, thisread, fp); + if (nitems <= 0) break; + soundfile_xferin(channels, argc, vecs, itemsread, + (unsigned char *)sampbuf, nitems, bytespersamp, bigendian); + itemsread += nitems; + } + /* zero out remaining elements of vectors */ + + for (i = 0; i < argc; i++) + { + int nzero, vecsize; + garray_getfloatarray(garrays[i], &vecsize, &vecs[i]); + for (j = itemsread; j < vecsize; j++) + vecs[i][j] = 0; + } + /* zero out vectors in excess of number of channels */ + for (i = channels; i < argc; i++) + { + int vecsize; + t_sample *foo; + garray_getfloatarray(garrays[i], &vecsize, &foo); + for (j = 0; j < vecsize; j++) + foo[j] = 0; + } + /* do all graphics updates */ + for (i = 0; i < argc; i++) + garray_redraw(garrays[i]); + fclose(fp); + fd = -1; + goto done; +usage: + pd_error(x, "usage: read [flags] filename tablename..."); + post("flags: -skip -nframes -resize -maxsize ..."); + post("-raw ."); +done: + if (fd >= 0) + close (fd); + outlet_float(x->x_obj.ob_outlet, (float)itemsread); +} + + /* this is broken out from soundfiler_write below so garray_write can + call it too... not done yet though. */ + +long soundfiler_dowrite(void *obj, t_canvas *canvas, + int argc, t_atom *argv) +{ + int headersize, bytespersamp, bigendian, + endianness, swap, filetype, normalize, i, j, nchannels; + long onset, nframes, itemsleft, + maxsize = DEFMAXSIZE, itemswritten = 0; + t_garray *garrays[MAXSFCHANS]; + t_sample *vecs[MAXSFCHANS]; + char sampbuf[SAMPBUFSIZE]; + int bufframes, nitems; + int fd = -1; + float normfactor, biggest = 0, samplerate; + t_symbol *filesym; + + if (soundfiler_writeargparse(obj, &argc, &argv, &filesym, &filetype, + &bytespersamp, &swap, &bigendian, &normalize, &onset, &nframes, + &samplerate)) + goto usage; + nchannels = argc; + if (nchannels < 1 || nchannels > MAXSFCHANS) + goto usage; + if (samplerate < 0) + samplerate = sys_getsr(); + for (i = 0; i < nchannels; i++) + { + int vecsize; + if (argv[i].a_type != A_SYMBOL) + goto usage; + if (!(garrays[i] = + (t_garray *)pd_findbyclass(argv[i].a_w.w_symbol, garray_class))) + { + pd_error(obj, "%s: no such table", argv[i].a_w.w_symbol->s_name); + goto fail; + } + else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i])) + error("%s: bad template for tabwrite", + argv[i].a_w.w_symbol->s_name); + if (nframes > vecsize - onset) + nframes = vecsize - onset; + + for (j = 0; j < vecsize; j++) + { + if (vecs[i][j] > biggest) + biggest = vecs[i][j]; + else if (-vecs[i][j] > biggest) + biggest = -vecs[i][j]; + } + } + if (nframes <= 0) + { + pd_error(obj, "soundfiler_write: no samples at onset %ld", onset); + goto fail; + } + + if ((fd = create_soundfile(canvas, filesym->s_name, filetype, + nframes, bytespersamp, bigendian, nchannels, + swap, samplerate)) < 0) + { + post("%s: %s\n", filesym->s_name, strerror(errno)); + goto fail; + } + if (!normalize) + { + if ((bytespersamp != 4) && (biggest > 1)) + { + post("%s: normalizing max amplitude %f to 1", filesym->s_name, biggest); + normalize = 1; + } + else post("%s: biggest amplitude = %f", filesym->s_name, biggest); + } + if (normalize) + normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1); + else normfactor = 1; + + bufframes = SAMPBUFSIZE / (nchannels * bytespersamp); + + for (itemswritten = 0; itemswritten < nframes; ) + { + int thiswrite = nframes - itemswritten, nitems, nbytes; + thiswrite = (thiswrite > bufframes ? bufframes : thiswrite); + soundfile_xferout(argc, vecs, (unsigned char *)sampbuf, thiswrite, + onset, bytespersamp, bigendian, normfactor); + nbytes = write(fd, sampbuf, nchannels * bytespersamp * thiswrite); + if (nbytes < nchannels * bytespersamp * thiswrite) + { + post("%s: %s", filesym->s_name, strerror(errno)); + if (nbytes > 0) + itemswritten += nbytes / (nchannels * bytespersamp); + break; + } + itemswritten += thiswrite; + onset += thiswrite; + } + if (fd >= 0) + { + soundfile_finishwrite(obj, filesym->s_name, fd, + filetype, nframes, itemswritten, nchannels * bytespersamp, swap); + close (fd); + } + return ((float)itemswritten); +usage: + pd_error(obj, "usage: write [flags] filename tablename..."); + post("flags: -skip -nframes -bytes -wave -aiff -nextstep ..."); + post("-big -little -normalize"); + post("(defaults to a 16-bit wave file)."); +fail: + if (fd >= 0) + close (fd); + return (0); +} + +static void soundfiler_write(t_soundfiler *x, t_symbol *s, + int argc, t_atom *argv) +{ + long bozo = soundfiler_dowrite(x, x->x_canvas, + argc, argv); + outlet_float(x->x_obj.ob_outlet, (float)bozo); +} + +static void soundfiler_setup(void) +{ + soundfiler_class = class_new(gensym("soundfiler"), (t_newmethod)soundfiler_new, + 0, sizeof(t_soundfiler), 0, 0); + class_addmethod(soundfiler_class, (t_method)soundfiler_read, gensym("read"), + A_GIMME, 0); + class_addmethod(soundfiler_class, (t_method)soundfiler_write, + gensym("write"), A_GIMME, 0); +} + + +#ifndef FIXEDPOINT +/************************* readsf object ******************************/ + +/* READSF uses the Posix threads package; for the moment we're Linux +only although this should be portable to the other platforms. + +Each instance of readsf~ owns a "child" thread for doing the UNIX (MSW?) file +reading. The parent thread signals the child each time: + (1) a file wants opening or closing; + (2) we've eaten another 1/16 of the shared buffer (so that the + child thread should check if it's time to read some more.) +The child signals the parent whenever a read has completed. Signalling +is done by setting "conditions" and putting data in mutex-controlled common +areas. +*/ + +#define MAXBYTESPERSAMPLE 4 +#define MAXVECSIZE 128 + +#define READSIZE 65536 +#define WRITESIZE 65536 +#define DEFBUFPERCHAN 262144 +#define MINBUFSIZE (4 * READSIZE) +#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */ + +#define REQUEST_NOTHING 0 +#define REQUEST_OPEN 1 +#define REQUEST_CLOSE 2 +#define REQUEST_QUIT 3 +#define REQUEST_BUSY 4 + +#define STATE_IDLE 0 +#define STATE_STARTUP 1 +#define STATE_STREAM 2 + +static t_class *readsf_class; + +typedef struct _readsf +{ + t_object x_obj; + t_canvas *x_canvas; + t_clock *x_clock; + char *x_buf; /* soundfile buffer */ + int x_bufsize; /* buffer size in bytes */ + int x_noutlets; /* number of audio outlets */ + t_sample *(x_outvec[MAXSFCHANS]); /* audio vectors */ + int x_vecsize; /* vector size for transfers */ + t_outlet *x_bangout; /* bang-on-done outlet */ + int x_state; /* opened, running, or idle */ + float x_insamplerate; /* sample rate of input signal if known */ + /* parameters to communicate with subthread */ + int x_requestcode; /* pending request from parent to I/O thread */ + char *x_filename; /* file to open (string is permanently allocated) */ + int x_fileerror; /* slot for "errno" return */ + int x_skipheaderbytes; /* size of header we'll skip */ + int x_bytespersample; /* bytes per sample (2 or 3) */ + int x_bigendian; /* true if file is big-endian */ + int x_sfchannels; /* number of channels in soundfile */ + float x_samplerate; /* sample rate of soundfile */ + long x_onsetframes; /* number of sample frames to skip */ + long x_bytelimit; /* max number of data bytes to read */ + int x_fd; /* filedesc */ + int x_fifosize; /* buffer size appropriately rounded down */ + int x_fifohead; /* index of next byte to get from file */ + int x_fifotail; /* index of next byte the ugen will read */ + int x_eof; /* true if fifohead has stopped changing */ + int x_sigcountdown; /* counter for signalling child for more data */ + int x_sigperiod; /* number of ticks per signal */ + int x_filetype; /* writesf~ only; type of file to create */ + int x_itemswritten; /* writesf~ only; items writen */ + int x_swap; /* writesf~ only; true if byte swapping */ + float x_f; /* writesf~ only; scalar for signal inlet */ + pthread_mutex_t x_mutex; + pthread_cond_t x_requestcondition; + pthread_cond_t x_answercondition; + pthread_t x_childthread; +} t_readsf; + + +/************** the child thread which performs file I/O ***********/ + +#if 0 +static void pute(char *s) /* debug routine */ +{ + write(2, s, strlen(s)); +} +#define DEBUG_SOUNDFILE +#endif + +#if 1 +#define sfread_cond_wait pthread_cond_wait +#define sfread_cond_signal pthread_cond_signal +#else +#include /* debugging version... */ +#include +static void readsf_fakewait(pthread_mutex_t *b) +{ + struct timeval timout; + timout.tv_sec = 0; + timout.tv_usec = 1000000; + pthread_mutex_unlock(b); + select(0, 0, 0, 0, &timout); + pthread_mutex_lock(b); +} + +#define sfread_cond_wait(a,b) readsf_fakewait(b) +#define sfread_cond_signal(a) +#endif + +static void *readsf_child_main(void *zz) +{ + t_readsf *x = zz; +#ifdef DEBUG_SOUNDFILE + pute("1\n"); +#endif + pthread_mutex_lock(&x->x_mutex); + while (1) + { + int fd, fifohead; + char *buf; +#ifdef DEBUG_SOUNDFILE + pute("0\n"); +#endif + if (x->x_requestcode == REQUEST_NOTHING) + { +#ifdef DEBUG_SOUNDFILE + pute("wait 2\n"); +#endif + sfread_cond_signal(&x->x_answercondition); + sfread_cond_wait(&x->x_requestcondition, &x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("3\n"); +#endif + } + else if (x->x_requestcode == REQUEST_OPEN) + { + char boo[80]; + int sysrtn, wantbytes; + + /* copy file stuff out of the data structure so we can + relinquish the mutex while we're in open_soundfile(). */ + long onsetframes = x->x_onsetframes; + long bytelimit = 0x7fffffff; + int skipheaderbytes = x->x_skipheaderbytes; + int bytespersample = x->x_bytespersample; + int sfchannels = x->x_sfchannels; + int bigendian = x->x_bigendian; + char *filename = x->x_filename; + char *dirname = canvas_getdir(x->x_canvas)->s_name; + /* alter the request code so that an ensuing "open" will get + noticed. */ +#ifdef DEBUG_SOUNDFILE + pute("4\n"); +#endif + x->x_requestcode = REQUEST_BUSY; + x->x_fileerror = 0; + + /* if there's already a file open, close it */ + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + close (fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + if (x->x_requestcode != REQUEST_BUSY) + goto lost; + } + /* open the soundfile with the mutex unlocked */ + pthread_mutex_unlock(&x->x_mutex); + fd = open_soundfile(dirname, filename, + skipheaderbytes, &bytespersample, &bigendian, + &sfchannels, &bytelimit, onsetframes); + pthread_mutex_lock(&x->x_mutex); + +#ifdef DEBUG_SOUNDFILE + pute("5\n"); +#endif + /* copy back into the instance structure. */ + x->x_bytespersample = bytespersample; + x->x_sfchannels = sfchannels; + x->x_bigendian = bigendian; + x->x_fd = fd; + x->x_bytelimit = bytelimit; + if (fd < 0) + { + x->x_fileerror = errno; + x->x_eof = 1; +#ifdef DEBUG_SOUNDFILE + pute("open failed\n"); + pute(filename); + pute(dirname); +#endif + goto lost; + } + /* check if another request has been made; if so, field it */ + if (x->x_requestcode != REQUEST_BUSY) + goto lost; +#ifdef DEBUG_SOUNDFILE + pute("6\n"); +#endif + x->x_fifohead = 0; + /* set fifosize from bufsize. fifosize must be a + multiple of the number of bytes eaten for each DSP + tick. We pessimistically assume MAXVECSIZE samples + per tick since that could change. There could be a + problem here if the vector size increases while a + soundfile is being played... */ + x->x_fifosize = x->x_bufsize - (x->x_bufsize % + (x->x_bytespersample * x->x_sfchannels * MAXVECSIZE)); + /* arrange for the "request" condition to be signalled 16 + times per buffer */ +#ifdef DEBUG_SOUNDFILE + sprintf(boo, "fifosize %d\n", + x->x_fifosize); + pute(boo); +#endif + x->x_sigcountdown = x->x_sigperiod = + (x->x_fifosize / + (16 * x->x_bytespersample * x->x_sfchannels * + x->x_vecsize)); + /* in a loop, wait for the fifo to get hungry and feed it */ + + while (x->x_requestcode == REQUEST_BUSY) + { + int fifosize = x->x_fifosize; +#ifdef DEBUG_SOUNDFILE + pute("77\n"); +#endif + if (x->x_eof) + break; + if (x->x_fifohead >= x->x_fifotail) + { + /* if the head is >= the tail, we can immediately read + to the end of the fifo. Unless, that is, we would + read all the way to the end of the buffer and the + "tail" is zero; this would fill the buffer completely + which isn't allowed because you can't tell a completely + full buffer from an empty one. */ + if (x->x_fifotail || (fifosize - x->x_fifohead > READSIZE)) + { + wantbytes = fifosize - x->x_fifohead; + if (wantbytes > READSIZE) + wantbytes = READSIZE; + if (wantbytes > x->x_bytelimit) + wantbytes = x->x_bytelimit; +#ifdef DEBUG_SOUNDFILE + sprintf(boo, "head %d, tail %d, size %d\n", + x->x_fifohead, x->x_fifotail, wantbytes); + pute(boo); +#endif + } + else + { +#ifdef DEBUG_SOUNDFILE + pute("wait 7a ...\n"); +#endif + sfread_cond_signal(&x->x_answercondition); +#ifdef DEBUG_SOUNDFILE + pute("signalled\n"); +#endif + sfread_cond_wait(&x->x_requestcondition, + &x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("7a done\n"); +#endif + continue; + } + } + else + { + /* otherwise check if there are at least READSIZE + bytes to read. If not, wait and loop back. */ + wantbytes = x->x_fifotail - x->x_fifohead - 1; + if (wantbytes < READSIZE) + { +#ifdef DEBUG_SOUNDFILE + pute("wait 7...\n"); +#endif + sfread_cond_signal(&x->x_answercondition); + sfread_cond_wait(&x->x_requestcondition, + &x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("7 done\n"); +#endif + continue; + } + else wantbytes = READSIZE; + if (wantbytes > x->x_bytelimit) + wantbytes = x->x_bytelimit; + } +#ifdef DEBUG_SOUNDFILE + pute("8\n"); +#endif + fd = x->x_fd; + buf = x->x_buf; + fifohead = x->x_fifohead; + pthread_mutex_unlock(&x->x_mutex); + sysrtn = read(fd, buf + fifohead, wantbytes); + pthread_mutex_lock(&x->x_mutex); + if (x->x_requestcode != REQUEST_BUSY) + break; + if (sysrtn < 0) + { +#ifdef DEBUG_SOUNDFILE + pute("fileerror\n"); +#endif + x->x_fileerror = errno; + break; + } + else if (sysrtn == 0) + { + x->x_eof = 1; + break; + } + else + { + x->x_fifohead += sysrtn; + x->x_bytelimit -= sysrtn; + if (x->x_bytelimit <= 0) + { + x->x_eof = 1; + break; + } + if (x->x_fifohead == fifosize) + x->x_fifohead = 0; + } +#ifdef DEBUG_SOUNDFILE + sprintf(boo, "after: head %d, tail %d\n", + x->x_fifohead, x->x_fifotail); + pute(boo); +#endif + /* signal parent in case it's waiting for data */ + sfread_cond_signal(&x->x_answercondition); + } + lost: + + if (x->x_requestcode == REQUEST_BUSY) + x->x_requestcode = REQUEST_NOTHING; + /* fell out of read loop: close file if necessary, + set EOF and signal once more */ + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + close (fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + } + sfread_cond_signal(&x->x_answercondition); + + } + else if (x->x_requestcode == REQUEST_CLOSE) + { + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + close (fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + } + if (x->x_requestcode == REQUEST_CLOSE) + x->x_requestcode = REQUEST_NOTHING; + sfread_cond_signal(&x->x_answercondition); + } + else if (x->x_requestcode == REQUEST_QUIT) + { + if (x->x_fd >= 0) + { + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + close (fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + } + x->x_requestcode = REQUEST_NOTHING; + sfread_cond_signal(&x->x_answercondition); + break; + } + else + { +#ifdef DEBUG_SOUNDFILE + pute("13\n"); +#endif + } + } +#ifdef DEBUG_SOUNDFILE + pute("thread exit\n"); +#endif + pthread_mutex_unlock(&x->x_mutex); + return (0); +} + +/******** the object proper runs in the calling (parent) thread ****/ + +static void readsf_tick(t_readsf *x); + +static void *readsf_new(t_floatarg fnchannels, t_floatarg fbufsize) +{ + t_readsf *x; + int nchannels = fnchannels, bufsize = fbufsize, i; + char *buf; + + if (nchannels < 1) + nchannels = 1; + else if (nchannels > MAXSFCHANS) + nchannels = MAXSFCHANS; + if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels; + else if (bufsize < MINBUFSIZE) + bufsize = MINBUFSIZE; + else if (bufsize > MAXBUFSIZE) + bufsize = MAXBUFSIZE; + buf = getbytes(bufsize); + if (!buf) return (0); + + x = (t_readsf *)pd_new(readsf_class); + + for (i = 0; i < nchannels; i++) + outlet_new(&x->x_obj, gensym("signal")); + x->x_noutlets = nchannels; + x->x_bangout = outlet_new(&x->x_obj, &s_bang); + pthread_mutex_init(&x->x_mutex, 0); + pthread_cond_init(&x->x_requestcondition, 0); + pthread_cond_init(&x->x_answercondition, 0); + x->x_vecsize = MAXVECSIZE; + x->x_state = STATE_IDLE; + x->x_clock = clock_new(x, (t_method)readsf_tick); + x->x_canvas = canvas_getcurrent(); + x->x_bytespersample = 2; + x->x_sfchannels = 1; + x->x_fd = -1; + x->x_buf = buf; + x->x_bufsize = bufsize; + x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0; + pthread_create(&x->x_childthread, 0, readsf_child_main, x); + return (x); +} + +static void readsf_tick(t_readsf *x) +{ + outlet_bang(x->x_bangout); +} + +static t_int *readsf_perform(t_int *w) +{ + t_readsf *x = (t_readsf *)(w[1]); + int vecsize = x->x_vecsize, noutlets = x->x_noutlets, i, j, + bytespersample = x->x_bytespersample, + bigendian = x->x_bigendian; + float *fp; + if (x->x_state == STATE_STREAM) + { + int wantbytes, nchannels, sfchannels = x->x_sfchannels; + pthread_mutex_lock(&x->x_mutex); + wantbytes = sfchannels * vecsize * bytespersample; + while ( + !x->x_eof && x->x_fifohead >= x->x_fifotail && + x->x_fifohead < x->x_fifotail + wantbytes-1) + { +#ifdef DEBUG_SOUNDFILE + pute("wait...\n"); +#endif + sfread_cond_signal(&x->x_requestcondition); + sfread_cond_wait(&x->x_answercondition, &x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("done\n"); +#endif + } + if (x->x_eof && x->x_fifohead >= x->x_fifotail && + x->x_fifohead < x->x_fifotail + wantbytes-1) + { + int xfersize; + if (x->x_fileerror) + { + pd_error(x, "dsp: %s: %s", x->x_filename, + (x->x_fileerror == EIO ? + "unknown or bad header format" : + strerror(x->x_fileerror))); + } + clock_delay(x->x_clock, 0); + x->x_state = STATE_IDLE; + + /* if there's a partial buffer left, copy it out. */ + xfersize = (x->x_fifohead - x->x_fifotail + 1) / + (sfchannels * bytespersample); + if (xfersize) + { + soundfile_xferin(sfchannels, noutlets, x->x_outvec, 0, + (unsigned char *)(x->x_buf + x->x_fifotail), xfersize, + bytespersample, bigendian); + vecsize -= xfersize; + } + /* then zero out the (rest of the) output */ + for (i = 0; i < noutlets; i++) + for (j = vecsize, fp = x->x_outvec[i] + xfersize; j--; ) + *fp++ = 0; + + sfread_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); + return (w+2); + } + + soundfile_xferin(sfchannels, noutlets, x->x_outvec, 0, + (unsigned char *)(x->x_buf + x->x_fifotail), vecsize, + bytespersample, bigendian); + + x->x_fifotail += wantbytes; + if (x->x_fifotail >= x->x_fifosize) + x->x_fifotail = 0; + if ((--x->x_sigcountdown) <= 0) + { + sfread_cond_signal(&x->x_requestcondition); + x->x_sigcountdown = x->x_sigperiod; + } + pthread_mutex_unlock(&x->x_mutex); + } + else + { + idle: + for (i = 0; i < noutlets; i++) + for (j = vecsize, fp = x->x_outvec[i]; j--; ) + *fp++ = 0; + } + return (w+2); +} + +static void readsf_start(t_readsf *x) +{ + /* start making output. If we're in the "startup" state change + to the "running" state. */ + if (x->x_state == STATE_STARTUP) + x->x_state = STATE_STREAM; + else pd_error(x, "readsf: start requested with no prior 'open'"); +} + +static void readsf_stop(t_readsf *x) +{ + /* LATER rethink whether you need the mutex just to set a variable? */ + pthread_mutex_lock(&x->x_mutex); + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_CLOSE; + sfread_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); +} + +static void readsf_float(t_readsf *x, t_floatarg f) +{ + if (f != 0) + readsf_start(x); + else readsf_stop(x); +} + + /* open method. Called as: + open filename [skipframes headersize channels bytespersamp endianness] + (if headersize is zero, header is taken to be automatically + detected; thus, use the special "-1" to mean a truly headerless file.) + */ + +static void readsf_open(t_readsf *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *filesym = atom_getsymbolarg(0, argc, argv); + t_float onsetframes = atom_getfloatarg(1, argc, argv); + t_float headerbytes = atom_getfloatarg(2, argc, argv); + t_float channels = atom_getfloatarg(3, argc, argv); + t_float bytespersamp = atom_getfloatarg(4, argc, argv); + t_symbol *endian = atom_getsymbolarg(5, argc, argv); + if (!*filesym->s_name) + return; + pthread_mutex_lock(&x->x_mutex); + x->x_requestcode = REQUEST_OPEN; + x->x_filename = filesym->s_name; + x->x_fifotail = 0; + x->x_fifohead = 0; + if (*endian->s_name == 'b') + x->x_bigendian = 1; + else if (*endian->s_name == 'l') + x->x_bigendian = 0; + else if (*endian->s_name) + pd_error(x, "endianness neither 'b' nor 'l'"); + else x->x_bigendian = garray_ambigendian(); + x->x_onsetframes = (onsetframes > 0 ? onsetframes : 0); + x->x_skipheaderbytes = (headerbytes > 0 ? headerbytes : + (headerbytes == 0 ? -1 : 0)); + x->x_sfchannels = (channels >= 1 ? channels : 1); + x->x_bytespersample = (bytespersamp > 2 ? bytespersamp : 2); + x->x_eof = 0; + x->x_fileerror = 0; + x->x_state = STATE_STARTUP; + sfread_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); +} + +static void readsf_dsp(t_readsf *x, t_signal **sp) +{ + int i, noutlets = x->x_noutlets; + pthread_mutex_lock(&x->x_mutex); + x->x_vecsize = sp[0]->s_n; + + x->x_sigperiod = (x->x_fifosize / + (x->x_bytespersample * x->x_sfchannels * x->x_vecsize)); + for (i = 0; i < noutlets; i++) + x->x_outvec[i] = sp[i]->s_vec; + pthread_mutex_unlock(&x->x_mutex); + dsp_add(readsf_perform, 1, x); +} + +static void readsf_print(t_readsf *x) +{ + post("state %d", x->x_state); + post("fifo head %d", x->x_fifohead); + post("fifo tail %d", x->x_fifotail); + post("fifo size %d", x->x_fifosize); + post("fd %d", x->x_fd); + post("eof %d", x->x_eof); +} + +static void readsf_free(t_readsf *x) +{ + /* request QUIT and wait for acknowledge */ + void *threadrtn; + pthread_mutex_lock(&x->x_mutex); + x->x_requestcode = REQUEST_QUIT; + sfread_cond_signal(&x->x_requestcondition); + while (x->x_requestcode != REQUEST_NOTHING) + { + sfread_cond_signal(&x->x_requestcondition); + sfread_cond_wait(&x->x_answercondition, &x->x_mutex); + } + pthread_mutex_unlock(&x->x_mutex); + if (pthread_join(x->x_childthread, &threadrtn)) + error("readsf_free: join failed"); + + pthread_cond_destroy(&x->x_requestcondition); + pthread_cond_destroy(&x->x_answercondition); + pthread_mutex_destroy(&x->x_mutex); + freebytes(x->x_buf, x->x_bufsize); + clock_free(x->x_clock); +} + +static void readsf_setup(void) +{ + readsf_class = class_new(gensym("readsf~"), (t_newmethod)readsf_new, + (t_method)readsf_free, sizeof(t_readsf), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addfloat(readsf_class, (t_method)readsf_float); + class_addmethod(readsf_class, (t_method)readsf_start, gensym("start"), 0); + class_addmethod(readsf_class, (t_method)readsf_stop, gensym("stop"), 0); + class_addmethod(readsf_class, (t_method)readsf_dsp, gensym("dsp"), 0); + class_addmethod(readsf_class, (t_method)readsf_open, gensym("open"), + A_GIMME, 0); + class_addmethod(readsf_class, (t_method)readsf_print, gensym("print"), 0); +} + +/******************************* writesf *******************/ + +static t_class *writesf_class; + +#define t_writesf t_readsf /* just re-use the structure */ + +/************** the child thread which performs file I/O ***********/ + +static void *writesf_child_main(void *zz) +{ + t_writesf *x = zz; +#ifdef DEBUG_SOUNDFILE + pute("1\n"); +#endif + pthread_mutex_lock(&x->x_mutex); + while (1) + { +#ifdef DEBUG_SOUNDFILE + pute("0\n"); +#endif + if (x->x_requestcode == REQUEST_NOTHING) + { +#ifdef DEBUG_SOUNDFILE + pute("wait 2\n"); +#endif + sfread_cond_signal(&x->x_answercondition); + sfread_cond_wait(&x->x_requestcondition, &x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("3\n"); +#endif + } + else if (x->x_requestcode == REQUEST_OPEN) + { + char boo[80]; + int fd, sysrtn, writebytes; + + /* copy file stuff out of the data structure so we can + relinquish the mutex while we're in open_soundfile(). */ + long onsetframes = x->x_onsetframes; + long bytelimit = 0x7fffffff; + int skipheaderbytes = x->x_skipheaderbytes; + int bytespersample = x->x_bytespersample; + int sfchannels = x->x_sfchannels; + int bigendian = x->x_bigendian; + int filetype = x->x_filetype; + char *filename = x->x_filename; + t_canvas *canvas = x->x_canvas; + float samplerate = x->x_samplerate; + + /* alter the request code so that an ensuing "open" will get + noticed. */ +#ifdef DEBUG_SOUNDFILE + pute("4\n"); +#endif + x->x_requestcode = REQUEST_BUSY; + x->x_fileerror = 0; + + /* if there's already a file open, close it */ + if (x->x_fd >= 0) + { + pthread_mutex_unlock(&x->x_mutex); + close (x->x_fd); + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + if (x->x_requestcode != REQUEST_BUSY) + continue; + } + /* open the soundfile with the mutex unlocked */ + pthread_mutex_unlock(&x->x_mutex); + fd = create_soundfile(canvas, filename, filetype, 0, + bytespersample, bigendian, sfchannels, + garray_ambigendian() != bigendian, samplerate); + pthread_mutex_lock(&x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("5\n"); +#endif + + if (fd < 0) + { + x->x_fd = -1; + x->x_eof = 1; + x->x_fileerror = errno; +#ifdef DEBUG_SOUNDFILE + pute("open failed\n"); + pute(filename); +#endif + x->x_requestcode = REQUEST_NOTHING; + continue; + } + /* check if another request has been made; if so, field it */ + if (x->x_requestcode != REQUEST_BUSY) + continue; +#ifdef DEBUG_SOUNDFILE + pute("6\n"); +#endif + x->x_fd = fd; + x->x_fifotail = 0; + x->x_itemswritten = 0; + x->x_swap = garray_ambigendian() != bigendian; + /* in a loop, wait for the fifo to have data and write it + to disk */ + while (x->x_requestcode == REQUEST_BUSY || + (x->x_requestcode == REQUEST_CLOSE && + x->x_fifohead != x->x_fifotail)) + { + int fifosize = x->x_fifosize, fifotail; + char *buf = x->x_buf; +#ifdef DEBUG_SOUNDFILE + pute("77\n"); +#endif + + /* if the head is < the tail, we can immediately write + from tail to end of fifo to disk; otherwise we hold off + writing until there are at least WRITESIZE bytes in the + buffer */ + if (x->x_fifohead < x->x_fifotail || + x->x_fifohead >= x->x_fifotail + WRITESIZE + || (x->x_requestcode == REQUEST_CLOSE && + x->x_fifohead != x->x_fifotail)) + { + writebytes = (x->x_fifohead < x->x_fifotail ? + fifosize : x->x_fifohead) - x->x_fifotail; + if (writebytes > READSIZE) + writebytes = READSIZE; + } + else + { +#ifdef DEBUG_SOUNDFILE + pute("wait 7a ...\n"); +#endif + sfread_cond_signal(&x->x_answercondition); +#ifdef DEBUG_SOUNDFILE + pute("signalled\n"); +#endif + sfread_cond_wait(&x->x_requestcondition, + &x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("7a done\n"); +#endif + continue; + } +#ifdef DEBUG_SOUNDFILE + pute("8\n"); +#endif + fifotail = x->x_fifotail; + fd = x->x_fd; + pthread_mutex_unlock(&x->x_mutex); + sysrtn = write(fd, buf + fifotail, writebytes); + pthread_mutex_lock(&x->x_mutex); + if (x->x_requestcode != REQUEST_BUSY && + x->x_requestcode != REQUEST_CLOSE) + break; + if (sysrtn < writebytes) + { +#ifdef DEBUG_SOUNDFILE + pute("fileerror\n"); +#endif + x->x_fileerror = errno; + break; + } + else + { + x->x_fifotail += sysrtn; + if (x->x_fifotail == fifosize) + x->x_fifotail = 0; + } + x->x_itemswritten += + sysrtn / (x->x_bytespersample * x->x_sfchannels); + sprintf(boo, "after: head %d, tail %d\n", + x->x_fifohead, x->x_fifotail); +#ifdef DEBUG_SOUNDFILE + pute(boo); +#endif + /* signal parent in case it's waiting for data */ + sfread_cond_signal(&x->x_answercondition); + } + } + else if (x->x_requestcode == REQUEST_CLOSE || + x->x_requestcode == REQUEST_QUIT) + { + int quit = (x->x_requestcode == REQUEST_QUIT); + if (x->x_fd >= 0) + { + int bytesperframe = x->x_bytespersample * x->x_sfchannels; + int bigendian = x->x_bigendian; + char *filename = x->x_filename; + int fd = x->x_fd; + int filetype = x->x_filetype; + int itemswritten = x->x_itemswritten; + int swap = x->x_swap; + pthread_mutex_unlock(&x->x_mutex); + + soundfile_finishwrite(x, filename, fd, + filetype, 0x7fffffff, itemswritten, + bytesperframe, swap); + close (fd); + + pthread_mutex_lock(&x->x_mutex); + x->x_fd = -1; + } + x->x_requestcode = REQUEST_NOTHING; + sfread_cond_signal(&x->x_answercondition); + if (quit) + break; + } + else + { +#ifdef DEBUG_SOUNDFILE + pute("13\n"); +#endif + } + } +#ifdef DEBUG_SOUNDFILE + pute("thread exit\n"); +#endif + pthread_mutex_unlock(&x->x_mutex); + return (0); +} + +/******** the object proper runs in the calling (parent) thread ****/ + +static void writesf_tick(t_writesf *x); + +static void *writesf_new(t_floatarg fnchannels, t_floatarg fbufsize) +{ + t_writesf *x; + int nchannels = fnchannels, bufsize = fbufsize, i; + char *buf; + + if (nchannels < 1) + nchannels = 1; + else if (nchannels > MAXSFCHANS) + nchannels = MAXSFCHANS; + if (bufsize <= 0) bufsize = DEFBUFPERCHAN * nchannels; + else if (bufsize < MINBUFSIZE) + bufsize = MINBUFSIZE; + else if (bufsize > MAXBUFSIZE) + bufsize = MAXBUFSIZE; + buf = getbytes(bufsize); + if (!buf) return (0); + + x = (t_writesf *)pd_new(writesf_class); + + for (i = 1; i < nchannels; i++) + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + + x->x_f = 0; + x->x_sfchannels = nchannels; + pthread_mutex_init(&x->x_mutex, 0); + pthread_cond_init(&x->x_requestcondition, 0); + pthread_cond_init(&x->x_answercondition, 0); + x->x_vecsize = MAXVECSIZE; + x->x_insamplerate = x->x_samplerate = 0; + x->x_state = STATE_IDLE; + x->x_clock = 0; /* no callback needed here */ + x->x_canvas = canvas_getcurrent(); + x->x_bytespersample = 2; + x->x_fd = -1; + x->x_buf = buf; + x->x_bufsize = bufsize; + x->x_fifosize = x->x_fifohead = x->x_fifotail = x->x_requestcode = 0; + pthread_create(&x->x_childthread, 0, writesf_child_main, x); + return (x); +} + +static t_int *writesf_perform(t_int *w) +{ + t_writesf *x = (t_writesf *)(w[1]); + int vecsize = x->x_vecsize, sfchannels = x->x_sfchannels, i, j, + bytespersample = x->x_bytespersample, + bigendian = x->x_bigendian; + float *fp; + if (x->x_state == STATE_STREAM) + { + int wantbytes; + pthread_mutex_lock(&x->x_mutex); + wantbytes = sfchannels * vecsize * bytespersample; + while (x->x_fifotail > x->x_fifohead && + x->x_fifotail < x->x_fifohead + wantbytes + 1) + { +#ifdef DEBUG_SOUNDFILE + pute("wait...\n"); +#endif + sfread_cond_signal(&x->x_requestcondition); + sfread_cond_wait(&x->x_answercondition, &x->x_mutex); +#ifdef DEBUG_SOUNDFILE + pute("done\n"); +#endif + } + + soundfile_xferout(sfchannels, x->x_outvec, + (unsigned char *)(x->x_buf + x->x_fifohead), vecsize, 0, + bytespersample, bigendian, 1.); + + x->x_fifohead += wantbytes; + if (x->x_fifohead >= x->x_fifosize) + x->x_fifohead = 0; + if ((--x->x_sigcountdown) <= 0) + { +#ifdef DEBUG_SOUNDFILE + pute("signal 1\n"); +#endif + sfread_cond_signal(&x->x_requestcondition); + x->x_sigcountdown = x->x_sigperiod; + } + pthread_mutex_unlock(&x->x_mutex); + } + return (w+2); +} + +static void writesf_start(t_writesf *x) +{ + /* start making output. If we're in the "startup" state change + to the "running" state. */ + if (x->x_state == STATE_STARTUP) + x->x_state = STATE_STREAM; + else + pd_error(x, "writesf: start requested with no prior 'open'"); +} + +static void writesf_stop(t_writesf *x) +{ + /* LATER rethink whether you need the mutex just to set a Svariable? */ + pthread_mutex_lock(&x->x_mutex); + x->x_state = STATE_IDLE; + x->x_requestcode = REQUEST_CLOSE; +#ifdef DEBUG_SOUNDFILE + pute("signal 2\n"); +#endif + sfread_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); +} + + + /* open method. Called as: open [args] filename with args as in + soundfiler_writeargparse(). + */ + +static void writesf_open(t_writesf *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *filesym; + int filetype, bytespersamp, swap, bigendian, normalize; + long onset, nframes; + float samplerate; + if (soundfiler_writeargparse(x, &argc, + &argv, &filesym, &filetype, &bytespersamp, &swap, &bigendian, + &normalize, &onset, &nframes, &samplerate)) + { + pd_error(x, + "writesf~: usage: open [-bytes [234]] [-wave,-nextstep,-aiff] ..."); + post("... [-big,-little] [-rate ####] filename"); + } + if (normalize || onset || (nframes != 0x7fffffff)) + pd_error(x, "normalize/onset/nframes argument to writesf~: ignored"); + if (argc) + pd_error(x, "extra argument(s) to writesf~: ignored"); + pthread_mutex_lock(&x->x_mutex); + x->x_bytespersample = bytespersamp; + x->x_swap = swap; + x->x_bigendian = bigendian; + x->x_filename = filesym->s_name; + x->x_filetype = filetype; + x->x_itemswritten = 0; + x->x_requestcode = REQUEST_OPEN; + x->x_fifotail = 0; + x->x_fifohead = 0; + x->x_eof = 0; + x->x_fileerror = 0; + x->x_state = STATE_STARTUP; + x->x_bytespersample = (bytespersamp > 2 ? bytespersamp : 2); + if (samplerate > 0) + x->x_samplerate = samplerate; + else if (x->x_insamplerate > 0) + x->x_samplerate = x->x_insamplerate; + else x->x_samplerate = sys_getsr(); + /* set fifosize from bufsize. fifosize must be a + multiple of the number of bytes eaten for each DSP + tick. */ + x->x_fifosize = x->x_bufsize - (x->x_bufsize % + (x->x_bytespersample * x->x_sfchannels * MAXVECSIZE)); + /* arrange for the "request" condition to be signalled 16 + times per buffer */ + x->x_sigcountdown = x->x_sigperiod = + (x->x_fifosize / + (16 * x->x_bytespersample * x->x_sfchannels * + x->x_vecsize)); + sfread_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); +} + +static void writesf_dsp(t_writesf *x, t_signal **sp) +{ + int i, ninlets = x->x_sfchannels; + pthread_mutex_lock(&x->x_mutex); + x->x_vecsize = sp[0]->s_n; + + x->x_sigperiod = (x->x_fifosize / + (x->x_bytespersample * ninlets * x->x_vecsize)); + for (i = 0; i < ninlets; i++) + x->x_outvec[i] = sp[i]->s_vec; + x->x_insamplerate = sp[0]->s_sr; + pthread_mutex_unlock(&x->x_mutex); + dsp_add(writesf_perform, 1, x); +} + +static void writesf_print(t_writesf *x) +{ + post("state %d", x->x_state); + post("fifo head %d", x->x_fifohead); + post("fifo tail %d", x->x_fifotail); + post("fifo size %d", x->x_fifosize); + post("fd %d", x->x_fd); + post("eof %d", x->x_eof); +} + +static void writesf_free(t_writesf *x) +{ + /* request QUIT and wait for acknowledge */ + void *threadrtn; + pthread_mutex_lock(&x->x_mutex); + x->x_requestcode = REQUEST_QUIT; + /* post("stopping writesf thread..."); */ + sfread_cond_signal(&x->x_requestcondition); + while (x->x_requestcode != REQUEST_NOTHING) + { + /* post("signalling..."); */ + sfread_cond_signal(&x->x_requestcondition); + sfread_cond_wait(&x->x_answercondition, &x->x_mutex); + } + pthread_mutex_unlock(&x->x_mutex); + if (pthread_join(x->x_childthread, &threadrtn)) + error("writesf_free: join failed"); + /* post("... done."); */ + + pthread_cond_destroy(&x->x_requestcondition); + pthread_cond_destroy(&x->x_answercondition); + pthread_mutex_destroy(&x->x_mutex); + freebytes(x->x_buf, x->x_bufsize); +} + +static void writesf_setup(void) +{ + writesf_class = class_new(gensym("writesf~"), (t_newmethod)writesf_new, + (t_method)writesf_free, sizeof(t_writesf), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addmethod(writesf_class, (t_method)writesf_start, gensym("start"), 0); + class_addmethod(writesf_class, (t_method)writesf_stop, gensym("stop"), 0); + class_addmethod(writesf_class, (t_method)writesf_dsp, gensym("dsp"), 0); + class_addmethod(writesf_class, (t_method)writesf_open, gensym("open"), + A_GIMME, 0); + class_addmethod(writesf_class, (t_method)writesf_print, gensym("print"), 0); + CLASS_MAINSIGNALIN(writesf_class, t_writesf, x_f); +} + +#endif + +/* ------------------------ global setup routine ------------------------- */ + +void d_soundfile_setup(void) +{ + soundfiler_setup(); +#ifndef FIXEDPOINT + readsf_setup(); + writesf_setup(); +#endif +} + diff --git a/apps/plugins/pdbox/PDa/src/d_ugen.c b/apps/plugins/pdbox/PDa/src/d_ugen.c new file mode 100644 index 0000000..9820f19 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/d_ugen.c @@ -0,0 +1,2252 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* These routines build a copy of the DSP portion of a graph, which is + then sorted into a linear list of DSP operations which are added to + the DSP duty cycle called by the scheduler. Once that's been done, + we delete the copy. The DSP objects are represented by "ugenbox" + structures which are parallel to the DSP objects in the graph and + have vectors of siginlets and sigoutlets which record their + interconnections. +*/ + +/* hacked to run subpatches with different samplerates + * only samplerates that are a power_of_2-multiple of the + * + * mfg.gfd.uil + * IOhannes + * + * edited lines are marked with "IOhannes" + * + */ + + +#include "m_pd.h" +#include "m_imp.h" +#include +#include + +extern t_class *vinlet_class, *voutlet_class, *canvas_class; +t_sample *obj_findsignalscalar(t_object *x, int m); +static int ugen_loud; +EXTERN_STRUCT _vinlet; +EXTERN_STRUCT _voutlet; + +void vinlet_dspprolog(struct _vinlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, + int switched); +void voutlet_dspprolog(struct _voutlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, + int switched); +void voutlet_dspepilog(struct _voutlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, + int switched); + +t_int *zero_perform(t_int *w) /* zero out a vector */ +{ + t_float *out = (t_float *)(w[1]); + int n = (int)(w[2]); + while (n--) *out++ = 0; + return (w+3); +} + +t_int *zero_perf8(t_int *w) +{ + t_float *out = (t_float *)(w[1]); + int n = (int)(w[2]); + + for (; n; n -= 8, out += 8) + { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 0; + out[6] = 0; + out[7] = 0; + } + return (w+3); +} + +void dsp_add_zero(t_sample *out, int n) +{ + if (n&7) + dsp_add(zero_perform, 2, out, n); + else + dsp_add(zero_perf8, 2, out, n); +} + +/* ---------------------------- block~ ----------------------------- */ + +/* The "block~ object maintains the containing canvas's DSP computation, +calling it at a super- or sub-multiple of the containing canvas's +calling frequency. The block~'s creation arguments specify block size +and overlap. Block~ does no "dsp" computation in its own right, but it +adds prolog and epilog code before and after the canvas's unit generators. + +A subcanvas need not have a block~ at all; if there's none, its +ugens are simply put on the list without any prolog or epilog code. + +Block~ may be invoked as switch~, in which case it also acts to switch the +subcanvas on and off. The overall order of scheduling for a subcanvas +is thus, + + inlet and outlet prologue code (1) + block prologue (2) + the objects in the subcanvas, including inlets and outlets + block epilogue (2) + outlet epilogue code (2) + +where (1) means, "if reblocked" and (2) means, "if reblocked or switched". + +If we're reblocked, the inlet prolog and outlet epilog code takes care of +overlapping and buffering to deal with vector size changes. If we're switched +but not reblocked, the inlet prolog is not needed, and the output epilog is +ONLY run when the block is switched off; in this case the epilog code simply +copies zeros to all signal outlets. +*/ + +static int dsp_phase; +static t_class *block_class; + +typedef struct _block +{ + t_object x_obj; + int x_vecsize; + int x_overlap; + int x_phase; /* from 0 to period-1; when zero we run the block */ + int x_period; /* submultiple of containing canvas */ + int x_frequency; /* supermultiple of comtaining canvas */ + int x_count; + int x_blocklength; /* length of dspchain for this block */ + int x_epiloglength; /* length of epilog */ + char x_switched; /* true if we're acting as a a switch */ + char x_switchon; /* true if we're switched on */ + char x_reblock; /* true if inlets and outlets are reblocking */ + int x_upsample; /* IOhannes: upsampling-factor */ + int x_downsample; /* IOhannes: downsampling-factor */ + +} t_block; + +static void block_set(t_block *x, t_floatarg fvecsize, t_floatarg foverlap, + t_floatarg fupsample); + +static void *block_new(t_floatarg fvecsize, t_floatarg foverlap, + t_floatarg fupsample) /* IOhannes */ +{ + t_block *x = (t_block *)pd_new(block_class); + x->x_phase = 0; + x->x_period = 1; + x->x_frequency = 1; + x->x_switched = 0; + x->x_switchon = 1; + block_set(x, fvecsize, foverlap, fupsample); + return (x); +} + +static void block_set(t_block *x, t_floatarg fvecsize, t_floatarg foverlap, + t_floatarg fupsample) +{ + int upsample, downsample; /* IOhannes */ + int vecsize = fvecsize; + int overlap = foverlap; + int dspstate = canvas_suspend_dsp(); + if (overlap < 1) + overlap = 1; + if (vecsize < 0) + vecsize = 0; /* this means we'll get it from parent later. */ + + /* IOhannes { */ + if (fupsample <= 0) upsample = downsample = 1; + else if (fupsample >= 1) { + upsample = fupsample; + downsample = 1; + } else { + downsample = 1.0 / fupsample; + upsample = 1; + } + /* } IOhannes */ + + if (vecsize && (vecsize != (1 << ilog2(vecsize)))) + { + pd_error(x, "block~: vector size not a power of 2"); + vecsize = 64; + } + if (overlap != (1 << ilog2(overlap))) + { + pd_error(x, "block~: overlap not a power of 2"); + overlap = 1; + } + /* IOhannes { */ + if (downsample != (1 << ilog2(downsample))) + { + pd_error(x, "block~: downsampling not a power of 2"); + downsample = 1; + } + if (upsample != (1 << ilog2(upsample))) + { + pd_error(x, "block~: upsampling not a power of 2"); + upsample = 1; + } + /* } IOhannes */ + + + x->x_vecsize = vecsize; + x->x_overlap = overlap; + /* IOhannes { */ + x->x_upsample = upsample; + x->x_downsample = downsample; + /* } IOhannes */ + canvas_resume_dsp(dspstate); +} + +static void *switch_new(t_floatarg fvecsize, t_floatarg foverlap, + t_floatarg fupsample) /* IOhannes */ +{ + t_block *x = (t_block *)(block_new(fvecsize, foverlap, fupsample)); /* IOhannes */ + x->x_switched = 1; + x->x_switchon = 0; + return (x); +} + +static void block_float(t_block *x, t_floatarg f) +{ + if (x->x_switched) + x->x_switchon = (f != 0); +} +#define PROLOGCALL 2 +#define EPILOGCALL 2 + +static t_int *block_prolog(t_int *w) +{ + t_block *x = (t_block *)w[1]; + int phase = x->x_phase; + /* if we're switched off, jump past the epilog code */ + if (!x->x_switchon) + return (w + x->x_blocklength); + if (phase) + { + phase++; + if (phase == x->x_period) phase = 0; + x->x_phase = phase; + return (w + x->x_blocklength); /* skip block; jump past epilog */ + } + else + { + x->x_count = x->x_frequency; + x->x_phase = (x->x_period > 1 ? 1 : 0); + return (w + PROLOGCALL); /* beginning of block is next ugen */ + } +} + +static t_int *block_epilog(t_int *w) +{ + t_block *x = (t_block *)w[1]; + int count = x->x_count - 1; + if (!x->x_reblock) + return (w + x->x_epiloglength + EPILOGCALL); + if (count) + { + x->x_count = count; + return (w - (x->x_blocklength - + (PROLOGCALL + EPILOGCALL))); /* go to ugen after prolog */ + } + else return (w + EPILOGCALL); +} + +static void block_dsp(t_block *x, t_signal **sp) +{ + /* do nothing here */ +} + +/* ------------------ DSP call list ----------------------- */ + +static t_int *dsp_chain; +static int dsp_chainsize; + +void dsp_add(t_perfroutine f, int n, ...) +{ + int newsize = dsp_chainsize + n+1, i; + va_list ap; + + dsp_chain = t_resizebytes(dsp_chain, dsp_chainsize * sizeof (t_int), + newsize * sizeof (t_int)); + dsp_chain[dsp_chainsize-1] = (t_int)f; + va_start(ap, n); + for (i = 0; i < n; i++) + dsp_chain[dsp_chainsize + i] = va_arg(ap, t_int); + va_end(ap); + dsp_chain[newsize-1] = 0; + dsp_chainsize = newsize; +} + + /* at Guenter's suggestion, here's a vectorized version */ +void dsp_addv(t_perfroutine f, int n, t_int *vec) +{ + int newsize = dsp_chainsize + n+1, i; + + dsp_chain = t_resizebytes(dsp_chain, dsp_chainsize * sizeof (t_int), + newsize * sizeof (t_int)); + dsp_chain[dsp_chainsize-1] = (t_int)f; + for (i = 0; i < n; i++) + dsp_chain[dsp_chainsize + i] = vec[i]; + dsp_chain[newsize-1] = 0; + dsp_chainsize = newsize; +} + +void dsp_tick(void) +{ + if (dsp_chain) + { + t_int *ip; + for (ip = dsp_chain; *ip; ) ip = (*(t_perfroutine)(*ip))(ip); + dsp_phase++; + } +} + +/* ---------------- signals ---------------------------- */ + +int ilog2(int n) +{ + int r = -1; + if (n <= 0) return(0); + while (n) + { + r++; + n >>= 1; + } + return (r); +} + + /* list of signals which can be reused, sorted by buffer size */ +static t_signal *signal_freelist[MAXLOGSIG+1]; + /* list of reusable "borrowed" signals (which don't own sample buffers) */ +static t_signal *signal_freeborrowed; + /* list of all signals allocated (not including "borrowed" ones) */ +static t_signal *signal_usedlist; + + /* call this when DSP is stopped to free all the signals */ +void signal_cleanup(void) +{ + t_signal **svec, *sig, *sig2; + int i; + while (sig = signal_usedlist) + { + signal_usedlist = sig->s_nextused; + if (!sig->s_isborrowed) + t_freebytes(sig->s_vec, sig->s_n * sizeof (*sig->s_vec)); + t_freebytes(sig, sizeof *sig); + } + for (i = 0; i <= MAXLOGSIG; i++) + signal_freelist[i] = 0; + signal_freeborrowed = 0; +} + + /* mark the signal "reusable." */ +void signal_makereusable(t_signal *sig) +{ + int logn = ilog2(sig->s_n); +#if 1 + t_signal *s5; + for (s5 = signal_freeborrowed; s5; s5 = s5->s_nextfree) + { + if (s5 == sig) + { + bug("signal_free 3"); + return; + } + } + for (s5 = signal_freelist[logn]; s5; s5 = s5->s_nextfree) + { + if (s5 == sig) + { + bug("signal_free 4"); + return; + } + } +#endif + if (ugen_loud) post("free %x: %d", sig, sig->s_isborrowed); + if (sig->s_isborrowed) + { + /* if the signal is borrowed, decrement the borrowed-from signal's + reference count, possibly marking it reusable too */ + t_signal *s2 = sig->s_borrowedfrom; + if ((s2 == sig) || !s2) + bug("signal_free"); + s2->s_refcount--; + if (!s2->s_refcount) + signal_makereusable(s2); + sig->s_nextfree = signal_freeborrowed; + signal_freeborrowed = sig; + } + else + { + /* if it's a real signal (not borrowed), put it on the free list + so we can reuse it. */ + if (signal_freelist[logn] == sig) bug("signal_free 2"); + sig->s_nextfree = signal_freelist[logn]; + signal_freelist[logn] = sig; + } +} + + /* reclaim or make an audio signal. If n is zero, return a "borrowed" + signal whose buffer and size will be obtained later via + signal_setborrowed(). */ + +t_signal *signal_new(int n, float sr) +{ + int logn, n2; + t_signal *ret, **whichlist; + t_sample *fp; + logn = ilog2(n); + if (n) + { + if (n != (1 << logn)) + bug("signal buffer not a power of 2"); + if (logn > MAXLOGSIG) + bug("signal buffer too large"); + whichlist = signal_freelist + logn; + } + else + whichlist = &signal_freeborrowed; + + /* first try to reclaim one from the free list */ + if (ret = *whichlist) + *whichlist = ret->s_nextfree; + else + { + /* LATER figure out what to do for out-of-space here! */ + ret = (t_signal *)t_getbytes(sizeof *ret); + if (n) + { + ret->s_vec = (t_sample *)getbytes(n * sizeof (*ret->s_vec)); + ret->s_isborrowed = 0; + } + else + { + ret->s_vec = 0; + ret->s_isborrowed = 1; + } + ret->s_nextused = signal_usedlist; + signal_usedlist = ret; + } + ret->s_n = n; + ret->s_sr = sr; + ret->s_refcount = 0; + ret->s_borrowedfrom = 0; + if (ugen_loud) post("new %x: %d", ret, ret->s_isborrowed); + return (ret); +} + +static t_signal *signal_newlike(const t_signal *sig) +{ + return (signal_new(sig->s_n, sig->s_sr)); +} + +void signal_setborrowed(t_signal *sig, t_signal *sig2) +{ + if (!sig->s_isborrowed || sig->s_borrowedfrom) + bug("signal_setborrowed"); + if (sig == sig2) + bug("signal_setborrowed 2"); + sig->s_borrowedfrom = sig2; + sig->s_vec = sig2->s_vec; + sig->s_n = sig2->s_n; +} + +int signal_compatible(t_signal *s1, t_signal *s2) +{ + return (s1->s_n == s2->s_n && s1->s_sr == s2->s_sr); +} + +/* ------------------ ugen ("unit generator") sorting ----------------- */ + +typedef struct _ugenbox +{ + struct _siginlet *u_in; + int u_nin; + struct _sigoutlet *u_out; + int u_nout; + int u_phase; + struct _ugenbox *u_next; + t_object *u_obj; + int u_done; +} t_ugenbox; + +typedef struct _siginlet +{ + int i_nconnect; + int i_ngot; + t_signal *i_signal; +} t_siginlet; + +typedef struct _sigoutconnect +{ + t_ugenbox *oc_who; + int oc_inno; + struct _sigoutconnect *oc_next; +} t_sigoutconnect; + +typedef struct _sigoutlet +{ + int o_nconnect; + int o_nsent; + t_signal *o_signal; + t_sigoutconnect *o_connections; +} t_sigoutlet; + + +struct _dspcontext +{ + struct _ugenbox *dc_ugenlist; + struct _dspcontext *dc_parentcontext; + int dc_ninlets; + int dc_noutlets; + t_signal **dc_iosigs; + float dc_srate; + int dc_vecsize; + char dc_toplevel; /* true if "iosigs" is invalid. */ + char dc_reblock; /* true if we have to reblock inlets/outlets */ + char dc_switched; /* true if we're switched */ + +}; + +#define t_dspcontext struct _dspcontext + +static int ugen_sortno = 0; +static t_dspcontext *ugen_currentcontext; + +void ugen_stop(void) +{ + t_signal *s; + int i; + if (dsp_chain) + { + freebytes(dsp_chain, dsp_chainsize * sizeof (t_int)); + dsp_chain = 0; + } + signal_cleanup(); + +} + +void ugen_start(void) +{ + ugen_stop(); + ugen_sortno++; + dsp_chain = (t_int *)getbytes(sizeof(*dsp_chain)); + dsp_chain[0] = 0; + dsp_chainsize = 1; + if (ugen_currentcontext) bug("ugen_start"); +} + +int ugen_getsortno(void) +{ + return (ugen_sortno); +} + +#if 0 +void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) +{ + int i, count; + t_signal *sig; + for (count = 0, sig = signal_usedlist; sig; + count++, sig = sig->s_nextused) + ; + post("used signals %d", count); + for (i = 0; i < MAXLOGSIG; i++) + { + for (count = 0, sig = signal_freelist[i]; sig; + count++, sig = sig->s_nextfree) + ; + if (count) + post("size %d: free %d", (1 << i), count); + } + for (count = 0, sig = signal_freeborrowed; sig; + count++, sig = sig->s_nextfree) + ; + post("free borrowed %d", count); + + ugen_loud = argc; +} +#endif + + /* start building the graph for a canvas */ +t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp, + int ninlets, int noutlets) +{ + t_dspcontext *dc = (t_dspcontext *)getbytes(sizeof(*dc)); + float parent_srate, srate; + int parent_vecsize, vecsize; + + if (ugen_loud) post("ugen_start_graph..."); + + dc->dc_ugenlist = 0; + dc->dc_toplevel = toplevel; + dc->dc_iosigs = sp; + dc->dc_ninlets = ninlets; + dc->dc_noutlets = noutlets; + dc->dc_parentcontext = ugen_currentcontext; + ugen_currentcontext = dc; + return (dc); +} + + /* first the canvas calls this to create all the boxes... */ +void ugen_add(t_dspcontext *dc, t_object *obj) +{ + t_ugenbox *x = (t_ugenbox *)getbytes(sizeof *x); + int i; + t_sigoutlet *uout; + t_siginlet *uin; + + x->u_next = dc->dc_ugenlist; + dc->dc_ugenlist = x; + x->u_obj = obj; + x->u_nin = obj_nsiginlets(obj); + x->u_in = getbytes(x->u_nin * sizeof (*x->u_in)); + for (uin = x->u_in, i = x->u_nin; i--; uin++) + uin->i_nconnect = 0; + x->u_nout = obj_nsigoutlets(obj); + x->u_out = getbytes(x->u_nout * sizeof (*x->u_out)); + for (uout = x->u_out, i = x->u_nout; i--; uout++) + uout->o_connections = 0, uout->o_nconnect = 0; +} + + /* and then this to make all the connections. */ +void ugen_connect(t_dspcontext *dc, t_object *x1, int outno, t_object *x2, + int inno) +{ + t_ugenbox *u1, *u2; + t_sigoutlet *uout; + t_siginlet *uin; + t_sigoutconnect *oc; + int sigoutno = obj_sigoutletindex(x1, outno); + int siginno = obj_siginletindex(x2, inno); + if (ugen_loud) + post("%s -> %s: %d->%d", + class_getname(x1->ob_pd), + class_getname(x2->ob_pd), outno, inno); + for (u1 = dc->dc_ugenlist; u1 && u1->u_obj != x1; u1 = u1->u_next); + for (u2 = dc->dc_ugenlist; u2 && u2->u_obj != x2; u2 = u2->u_next); + if (!u1 || !u2 || siginno < 0) + { + pd_error(u1->u_obj, + "signal outlet connect to nonsignal inlet (ignored)"); + return; + } + if (sigoutno < 0 || sigoutno >= u1->u_nout || siginno >= u2->u_nin) + { + bug("ugen_connect %s %s %d %d (%d %d)", + class_getname(x1->ob_pd), + class_getname(x2->ob_pd), sigoutno, siginno, u1->u_nout, + u2->u_nin); + } + uout = u1->u_out + sigoutno; + uin = u2->u_in + siginno; + + /* add a new connection to the outlet's list */ + oc = (t_sigoutconnect *)getbytes(sizeof *oc); + oc->oc_next = uout->o_connections; + uout->o_connections = oc; + oc->oc_who = u2; + oc->oc_inno = siginno; + /* update inlet and outlet counts */ + uout->o_nconnect++; + uin->i_nconnect++; +} + + /* get the index of a ugenbox or -1 if it's not on the list */ +static int ugen_index(t_dspcontext *dc, t_ugenbox *x) +{ + int ret; + t_ugenbox *u; + for (u = dc->dc_ugenlist, ret = 0; u; u = u->u_next, ret++) + if (u == x) return (ret); + return (-1); +} + + /* put a ugenbox on the chain, recursively putting any others on that + this one might uncover. */ +static void ugen_doit(t_dspcontext *dc, t_ugenbox *u) +{ + t_sigoutlet *uout; + t_siginlet *uin; + t_sigoutconnect *oc, *oc2; + t_class *class = pd_class(&u->u_obj->ob_pd); + int i, n; + /* suppress creating new signals for the outputs of signal + inlets and subpatchs; except in the case we're an inlet and "blocking" + is set. We don't yet know if a subcanvas will be "blocking" so there + we delay new signal creation, which will be handled by calling + signal_setborrowed in the ugen_done_graph routine below. */ + int nonewsigs = (class == canvas_class || + (class == vinlet_class) && !(dc->dc_reblock)); + /* when we encounter a subcanvas or a signal outlet, suppress freeing + the input signals as they may be "borrowed" for the super or sub + patch; same exception as above, but also if we're "switched" we + have to do a copy rather than a borrow. */ + int nofreesigs = (class == canvas_class || + (class == voutlet_class) && !(dc->dc_reblock || dc->dc_switched)); + t_signal **insig, **outsig, **sig, *s1, *s2, *s3; + t_ugenbox *u2; + + if (ugen_loud) post("doit %s %d %d", class_getname(class), nofreesigs, + nonewsigs); + for (i = 0, uin = u->u_in; i < u->u_nin; i++, uin++) + { + if (!uin->i_nconnect) + { + t_sample *scalar; + s3 = signal_new(dc->dc_vecsize, dc->dc_srate); + /* post("%s: unconnected signal inlet set to zero", + class_getname(u->u_obj->ob_pd)); */ + if (scalar = obj_findsignalscalar(u->u_obj, i)) + dsp_add_scalarcopy(scalar, s3->s_vec, s3->s_n); + else + dsp_add_zero(s3->s_vec, s3->s_n); + uin->i_signal = s3; + s3->s_refcount = 1; + } + } + insig = (t_signal **)getbytes((u->u_nin + u->u_nout) * sizeof(t_signal *)); + outsig = insig + u->u_nin; + for (sig = insig, uin = u->u_in, i = u->u_nin; i--; sig++, uin++) + { + int newrefcount; + *sig = uin->i_signal; + newrefcount = --(*sig)->s_refcount; + /* if the reference count went to zero, we free the signal now, + unless it's a subcanvas or outlet; these might keep the + signal around to send to objects connected to them. In this + case we increment the reference count; the corresponding decrement + is in sig_makereusable(). */ + if (nofreesigs) + (*sig)->s_refcount++; + else if (!newrefcount) + signal_makereusable(*sig); + } + for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++) + { + /* similarly, for outlets of subcanvases we delay creating + them; instead we create "borrowed" ones so that the refcount + is known. The subcanvas replaces the fake signal with one showing + where the output data actually is, to avoid having to copy it. + For any other object, we just allocate a new output vector; + since we've already freed the inputs the objects might get called + "in place." */ + if (nonewsigs) + { + *sig = uout->o_signal = + signal_new(0, dc->dc_srate); + } + else + *sig = uout->o_signal = signal_new(dc->dc_vecsize, dc->dc_srate); + (*sig)->s_refcount = uout->o_nconnect; + } + /* now call the DSP scheduling routine for the ugen. This + routine must fill in "borrowed" signal outputs in case it's either + a subcanvas or a signal inlet. */ + mess1(&u->u_obj->ob_pd, gensym("dsp"), insig); + + /* if any output signals aren't connected to anyone, free them + now; otherwise they'll either get freed when the reference count + goes back to zero, or even later as explained above. */ + + for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++) + { + if (!(*sig)->s_refcount) + signal_makereusable(*sig); + } + if (ugen_loud) + { + if (u->u_nin + u->u_nout == 0) post("put %s %d", + class_getname(u->u_obj->ob_pd), ugen_index(dc, u)); + else if (u->u_nin + u->u_nout == 1) post("put %s %d (%x)", + class_getname(u->u_obj->ob_pd), ugen_index(dc, u), sig[0]); + else if (u->u_nin + u->u_nout == 2) post("put %s %d (%x %x)", + class_getname(u->u_obj->ob_pd), ugen_index(dc, u), + sig[0], sig[1]); + else post("put %s %d (%x %x %x ...)", + class_getname(u->u_obj->ob_pd), ugen_index(dc, u), + sig[0], sig[1], sig[2]); + } + + /* pass it on and trip anyone whose last inlet was filled */ + for (uout = u->u_out, i = u->u_nout; i--; uout++) + { + s1 = uout->o_signal; + for (oc = uout->o_connections; oc; oc = oc->oc_next) + { + u2 = oc->oc_who; + uin = &u2->u_in[oc->oc_inno]; + /* if there's already someone here, sum the two */ + if (s2 = uin->i_signal) + { + s1->s_refcount--; + s2->s_refcount--; + if (!signal_compatible(s1, s2)) + { + pd_error(u->u_obj, "%s: incompatible signal inputs", + class_getname(u->u_obj->ob_pd)); + return; + } + s3 = signal_newlike(s1); + dsp_add_plus(s1->s_vec, s2->s_vec, s3->s_vec, s1->s_n); + uin->i_signal = s3; + s3->s_refcount = 1; + if (!s1->s_refcount) signal_makereusable(s1); + if (!s2->s_refcount) signal_makereusable(s2); + } + else uin->i_signal = s1; + uin->i_ngot++; + /* if we didn't fill this inlet don't bother yet */ + if (uin->i_ngot < uin->i_nconnect) + goto notyet; + /* if there's more than one, check them all */ + if (u2->u_nin > 1) + { + for (uin = u2->u_in, n = u2->u_nin; n--; uin++) + if (uin->i_ngot < uin->i_nconnect) goto notyet; + } + /* so now we can schedule the ugen. */ + ugen_doit(dc, u2); + notyet: ; + } + } + t_freebytes(insig,(u->u_nin + u->u_nout) * sizeof(t_signal *)); + u->u_done = 1; +} + + /* once the DSP graph is built, we call this routine to sort it. + This routine also deletes the graph; later we might want to leave the + graph around, in case the user is editing the DSP network, to save having + to recreate it all the time. But not today. */ + +void ugen_done_graph(t_dspcontext *dc) +{ + t_ugenbox *u, *u2; + t_sigoutlet *uout; + t_siginlet *uin; + t_sigoutconnect *oc, *oc2; + int i, n; + t_block *blk; + t_dspcontext *parent_context = dc->dc_parentcontext; + float parent_srate; + int parent_vecsize; + int period, frequency, phase, vecsize; + float srate; + int chainblockbegin; /* DSP chain onset before block prolog code */ + int chainblockend; /* and after block epilog code */ + int chainafterall; /* and after signal outlet epilog */ + int reblock = 0, switched; + int downsample = 1, upsample = 1; /* IOhannes */ + /* debugging printout */ + + if (ugen_loud) + { + post("ugen_done_graph..."); + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + post("ugen: %s", class_getname(u->u_obj->ob_pd)); + for (uout = u->u_out, i = 0; i < u->u_nout; uout++, i++) + for (oc = uout->o_connections; oc; oc = oc->oc_next) + { + post("... out %d to %s, index %d, inlet %d", i, + class_getname(oc->oc_who->u_obj->ob_pd), + ugen_index(dc, oc->oc_who), oc->oc_inno); + } + } + } + + /* search for an object of class "block~" */ + for (u = dc->dc_ugenlist, blk = 0; u; u = u->u_next) + { + t_pd *zz = &u->u_obj->ob_pd; + if (pd_class(zz) == block_class) + { + if (blk) + pd_error(blk, "conflicting block~ objects in same page"); + else blk = (t_block *)zz; + } + } + + /* figure out block size, calling frequency, sample rate */ + if (parent_context) + { + parent_srate = parent_context->dc_srate; + parent_vecsize = parent_context->dc_vecsize; + } + else + { + parent_srate = sys_getsr(); + parent_vecsize = sys_getblksize(); + } + if (blk) + { + int realoverlap; + vecsize = blk->x_vecsize; + if (vecsize == 0) + vecsize = parent_vecsize; + realoverlap = blk->x_overlap; + if (realoverlap > vecsize) realoverlap = vecsize; + /* IOhannes { */ + downsample = blk->x_downsample; + upsample = blk->x_upsample; + if (downsample > parent_vecsize) downsample=parent_vecsize; + period = (vecsize * downsample)/ + (parent_vecsize * realoverlap * upsample); + frequency = (parent_vecsize * realoverlap * upsample)/ + (vecsize * downsample); + /* } IOhannes*/ + phase = blk->x_phase; + srate = parent_srate * realoverlap * upsample / downsample; + /* IOhannes */ + if (period < 1) period = 1; + if (frequency < 1) frequency = 1; + blk->x_frequency = frequency; + blk->x_period = period; + blk->x_phase = dsp_phase & (period - 1); + if (! parent_context || (realoverlap != 1) || + (vecsize != parent_vecsize) || + (downsample != 1) || (upsample != 1)) /* IOhannes */ + reblock = 1; + switched = blk->x_switched; + } + else + { + srate = parent_srate; + vecsize = parent_vecsize; + downsample = upsample = 1;/* IOhannes */ + period = frequency = 1; + phase = 0; + if (!parent_context) reblock = 1; + switched = 0; + } + dc->dc_reblock = reblock; + dc->dc_switched = switched; + dc->dc_srate = srate; + dc->dc_vecsize = vecsize; + + /* if we're reblocking or switched, we now have to create output + signals to fill in for the "borrowed" ones we have now. This + is also possibly true even if we're not blocked/switched, in + the case that there was a signal loop. But we don't know this + yet. */ + + if (dc->dc_iosigs && (switched || reblock)) + { + t_signal **sigp; + for (i = 0, sigp = dc->dc_iosigs + dc->dc_ninlets; i < dc->dc_noutlets; + i++, sigp++) + { + if ((*sigp)->s_isborrowed && !(*sigp)->s_borrowedfrom) + { + signal_setborrowed(*sigp, + signal_new(parent_vecsize, parent_srate)); + (*sigp)->s_refcount++; + + if (ugen_loud) post("set %x->%x", *sigp, + (*sigp)->s_borrowedfrom); + } + } + } + + if (ugen_loud) + post("reblock %d, switched %d", reblock, switched); + + /* schedule prologs for inlets and outlets. If the "reblock" flag + is set, an inlet will put code on the DSP chain to copy its input + into an internal buffer here, before any unit generators' DSP code + gets scheduled. If we don't "reblock", inlets will need to get + pointers to their corresponding inlets/outlets on the box we're inside, + if any. Outlets will also need pointers, unless we're switched, in + which case outlet epilog code will kick in. */ + + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + t_pd *zz = &u->u_obj->ob_pd; + t_signal **insigs = dc->dc_iosigs, **outsigs = dc->dc_iosigs; + if (outsigs) outsigs += dc->dc_ninlets; + + if (pd_class(zz) == vinlet_class) + vinlet_dspprolog((struct _vinlet *)zz, + dc->dc_iosigs, vecsize, dsp_phase, period, frequency, + downsample, upsample, /* IOhannes */ + reblock, switched); + else if (pd_class(zz) == voutlet_class) + voutlet_dspprolog((struct _voutlet *)zz, + outsigs, vecsize, dsp_phase, period, frequency, + downsample, upsample, /* IOhannes */ + reblock, switched); + } + chainblockbegin = dsp_chainsize; + + if (blk && (reblock || switched)) /* add the block DSP prolog */ + dsp_add(block_prolog, 1, blk); + + /* Initialize for sorting */ + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + u->u_done = 0; + for (uout = u->u_out, i = u->u_nout; i--; uout++) + uout->o_nsent = 0; + for (uin = u->u_in, i = u->u_nin; i--; uin++) + uin->i_ngot = 0, uin->i_signal = 0; + } + + /* Do the sort */ + + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + /* check that we have no connected signal inlets */ + if (u->u_done) continue; + for (uin = u->u_in, i = u->u_nin; i--; uin++) + if (uin->i_nconnect) goto next; + + ugen_doit(dc, u); + next: ; + } + + /* check for a DSP loop, which is evidenced here by the presence + of ugens not yet scheduled. */ + + for (u = dc->dc_ugenlist; u; u = u->u_next) + if (!u->u_done) + { + t_signal **sigp; + pd_error(u->u_obj, + "DSP loop detected (some tilde objects not scheduled)"); + /* this might imply that we have unfilled "borrowed" outputs + which we'd better fill in now. */ + for (i = 0, sigp = dc->dc_iosigs + dc->dc_ninlets; i < dc->dc_noutlets; + i++, sigp++) + { + if ((*sigp)->s_isborrowed && !(*sigp)->s_borrowedfrom) + { + t_signal *s3 = signal_new(parent_vecsize, parent_srate); + signal_setborrowed(*sigp, s3); + (*sigp)->s_refcount++; + dsp_add_zero(s3->s_vec, s3->s_n); + if (ugen_loud) + post("oops, belatedly set %x->%x", *sigp, + (*sigp)->s_borrowedfrom); + } + } + break; /* don't need to keep looking. */ + } + + if (blk && (reblock || switched)) /* add block DSP epilog */ + dsp_add(block_epilog, 1, blk); + chainblockend = dsp_chainsize; + + /* add epilogs for outlets. */ + + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + t_pd *zz = &u->u_obj->ob_pd; + if (pd_class(zz) == voutlet_class) + { + t_signal **iosigs = dc->dc_iosigs; + if (iosigs) iosigs += dc->dc_ninlets; + voutlet_dspepilog((struct _voutlet *)zz, + iosigs, vecsize, dsp_phase, period, frequency, + downsample, upsample, /* IOhannes */ + reblock, switched); + } + } + + chainafterall = dsp_chainsize; + if (blk) + { + blk->x_blocklength = chainblockend - chainblockbegin; + blk->x_epiloglength = chainafterall - chainblockend; + blk->x_reblock = reblock; + } + + if (ugen_loud) + { + t_int *ip; + if (!dc->dc_parentcontext) + for (i = dsp_chainsize, ip = dsp_chain; i--; ip++) + post("chain %x", *ip); + post("... ugen_done_graph done."); + } + /* now delete everything. */ + while (dc->dc_ugenlist) + { + for (uout = dc->dc_ugenlist->u_out, n = dc->dc_ugenlist->u_nout; + n--; uout++) + { + oc = uout->o_connections; + while (oc) + { + oc2 = oc->oc_next; + freebytes(oc, sizeof *oc); + oc = oc2; + } + } + freebytes(dc->dc_ugenlist->u_out, dc->dc_ugenlist->u_nout * + sizeof (*dc->dc_ugenlist->u_out)); + freebytes(dc->dc_ugenlist->u_in, dc->dc_ugenlist->u_nin * + sizeof(*dc->dc_ugenlist->u_in)); + u = dc->dc_ugenlist; + dc->dc_ugenlist = u->u_next; + freebytes(u, sizeof *u); + } + if (ugen_currentcontext == dc) + ugen_currentcontext = dc->dc_parentcontext; + else bug("ugen_currentcontext"); + freebytes(dc, sizeof(*dc)); + +} + +t_signal *ugen_getiosig(int index, int inout) +{ + if (!ugen_currentcontext) bug("ugen_getiosig"); + if (ugen_currentcontext->dc_toplevel) return (0); + if (inout) index += ugen_currentcontext->dc_ninlets; + return (ugen_currentcontext->dc_iosigs[index]); +} + + +/* -------------------- setup routine -------------------------- */ + +void d_ugen_setup(void) /* really just block_setup */ +{ + block_class = class_new(gensym("block~"), (t_newmethod)block_new, 0, + sizeof(t_block), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT/*IOhannes*/, 0); + class_addcreator((t_newmethod)switch_new, gensym("switch~"), + A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT/*IOhannes*/, 0); + class_addmethod(block_class, (t_method)block_set, gensym("set"), + A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addmethod(block_class, (t_method)block_dsp, gensym("dsp"), 0); + class_addfloat(block_class, block_float); +} + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* These routines build a copy of the DSP portion of a graph, which is + then sorted into a linear list of DSP operations which are added to + the DSP duty cycle called by the scheduler. Once that's been done, + we delete the copy. The DSP objects are represented by "ugenbox" + structures which are parallel to the DSP objects in the graph and + have vectors of siginlets and sigoutlets which record their + interconnections. +*/ + +/* hacked to run subpatches with different samplerates + * only samplerates that are a power_of_2-multiple of the + * + * mfg.gfd.uil + * IOhannes + * + * edited lines are marked with "IOhannes" + * + */ + + +#include "m_pd.h" +#include "m_imp.h" +#include +#include + +extern t_class *vinlet_class, *voutlet_class, *canvas_class; +t_sample *obj_findsignalscalar(t_object *x, int m); +static int ugen_loud; +EXTERN_STRUCT _vinlet; +EXTERN_STRUCT _voutlet; + +void vinlet_dspprolog(struct _vinlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, + int switched); +void voutlet_dspprolog(struct _voutlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, + int switched); +void voutlet_dspepilog(struct _voutlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, + int switched); + +t_int *zero_perform(t_int *w) /* zero out a vector */ +{ + t_float *out = (t_float *)(w[1]); + int n = (int)(w[2]); + while (n--) *out++ = 0; + return (w+3); +} + +t_int *zero_perf8(t_int *w) +{ + t_float *out = (t_float *)(w[1]); + int n = (int)(w[2]); + + for (; n; n -= 8, out += 8) + { + out[0] = 0; + out[1] = 0; + out[2] = 0; + out[3] = 0; + out[4] = 0; + out[5] = 0; + out[6] = 0; + out[7] = 0; + } + return (w+3); +} + +void dsp_add_zero(t_sample *out, int n) +{ + if (n&7) + dsp_add(zero_perform, 2, out, n); + else + dsp_add(zero_perf8, 2, out, n); +} + +/* ---------------------------- block~ ----------------------------- */ + +/* The "block~ object maintains the containing canvas's DSP computation, +calling it at a super- or sub-multiple of the containing canvas's +calling frequency. The block~'s creation arguments specify block size +and overlap. Block~ does no "dsp" computation in its own right, but it +adds prolog and epilog code before and after the canvas's unit generators. + +A subcanvas need not have a block~ at all; if there's none, its +ugens are simply put on the list without any prolog or epilog code. + +Block~ may be invoked as switch~, in which case it also acts to switch the +subcanvas on and off. The overall order of scheduling for a subcanvas +is thus, + + inlet and outlet prologue code (1) + block prologue (2) + the objects in the subcanvas, including inlets and outlets + block epilogue (2) + outlet epilogue code (2) + +where (1) means, "if reblocked" and (2) means, "if reblocked or switched". + +If we're reblocked, the inlet prolog and outlet epilog code takes care of +overlapping and buffering to deal with vector size changes. If we're switched +but not reblocked, the inlet prolog is not needed, and the output epilog is +ONLY run when the block is switched off; in this case the epilog code simply +copies zeros to all signal outlets. +*/ + +static int dsp_phase; +static t_class *block_class; + +typedef struct _block +{ + t_object x_obj; + int x_vecsize; + int x_overlap; + int x_phase; /* from 0 to period-1; when zero we run the block */ + int x_period; /* submultiple of containing canvas */ + int x_frequency; /* supermultiple of comtaining canvas */ + int x_count; + int x_blocklength; /* length of dspchain for this block */ + int x_epiloglength; /* length of epilog */ + char x_switched; /* true if we're acting as a a switch */ + char x_switchon; /* true if we're switched on */ + char x_reblock; /* true if inlets and outlets are reblocking */ + int x_upsample; /* IOhannes: upsampling-factor */ + int x_downsample; /* IOhannes: downsampling-factor */ + +} t_block; + +static void block_set(t_block *x, t_floatarg fvecsize, t_floatarg foverlap, + t_floatarg fupsample); + +static void *block_new(t_floatarg fvecsize, t_floatarg foverlap, + t_floatarg fupsample) /* IOhannes */ +{ + t_block *x = (t_block *)pd_new(block_class); + x->x_phase = 0; + x->x_period = 1; + x->x_frequency = 1; + x->x_switched = 0; + x->x_switchon = 1; + block_set(x, fvecsize, foverlap, fupsample); + return (x); +} + +static void block_set(t_block *x, t_floatarg fvecsize, t_floatarg foverlap, + t_floatarg fupsample) +{ + int upsample, downsample; /* IOhannes */ + int vecsize = fvecsize; + int overlap = foverlap; + int dspstate = canvas_suspend_dsp(); + if (overlap < 1) + overlap = 1; + if (vecsize < 0) + vecsize = 0; /* this means we'll get it from parent later. */ + + /* IOhannes { */ + if (fupsample <= 0) upsample = downsample = 1; + else if (fupsample >= 1) { + upsample = fupsample; + downsample = 1; + } else { + downsample = 1.0 / fupsample; + upsample = 1; + } + /* } IOhannes */ + + if (vecsize && (vecsize != (1 << ilog2(vecsize)))) + { + pd_error(x, "block~: vector size not a power of 2"); + vecsize = 64; + } + if (overlap != (1 << ilog2(overlap))) + { + pd_error(x, "block~: overlap not a power of 2"); + overlap = 1; + } + /* IOhannes { */ + if (downsample != (1 << ilog2(downsample))) + { + pd_error(x, "block~: downsampling not a power of 2"); + downsample = 1; + } + if (upsample != (1 << ilog2(upsample))) + { + pd_error(x, "block~: upsampling not a power of 2"); + upsample = 1; + } + /* } IOhannes */ + + + x->x_vecsize = vecsize; + x->x_overlap = overlap; + /* IOhannes { */ + x->x_upsample = upsample; + x->x_downsample = downsample; + /* } IOhannes */ + canvas_resume_dsp(dspstate); +} + +static void *switch_new(t_floatarg fvecsize, t_floatarg foverlap, + t_floatarg fupsample) /* IOhannes */ +{ + t_block *x = (t_block *)(block_new(fvecsize, foverlap, fupsample)); /* IOhannes */ + x->x_switched = 1; + x->x_switchon = 0; + return (x); +} + +static void block_float(t_block *x, t_floatarg f) +{ + if (x->x_switched) + x->x_switchon = (f != 0); +} +#define PROLOGCALL 2 +#define EPILOGCALL 2 + +static t_int *block_prolog(t_int *w) +{ + t_block *x = (t_block *)w[1]; + int phase = x->x_phase; + /* if we're switched off, jump past the epilog code */ + if (!x->x_switchon) + return (w + x->x_blocklength); + if (phase) + { + phase++; + if (phase == x->x_period) phase = 0; + x->x_phase = phase; + return (w + x->x_blocklength); /* skip block; jump past epilog */ + } + else + { + x->x_count = x->x_frequency; + x->x_phase = (x->x_period > 1 ? 1 : 0); + return (w + PROLOGCALL); /* beginning of block is next ugen */ + } +} + +static t_int *block_epilog(t_int *w) +{ + t_block *x = (t_block *)w[1]; + int count = x->x_count - 1; + if (!x->x_reblock) + return (w + x->x_epiloglength + EPILOGCALL); + if (count) + { + x->x_count = count; + return (w - (x->x_blocklength - + (PROLOGCALL + EPILOGCALL))); /* go to ugen after prolog */ + } + else return (w + EPILOGCALL); +} + +static void block_dsp(t_block *x, t_signal **sp) +{ + /* do nothing here */ +} + +/* ------------------ DSP call list ----------------------- */ + +static t_int *dsp_chain; +static int dsp_chainsize; + +void dsp_add(t_perfroutine f, int n, ...) +{ + int newsize = dsp_chainsize + n+1, i; + va_list ap; + + dsp_chain = t_resizebytes(dsp_chain, dsp_chainsize * sizeof (t_int), + newsize * sizeof (t_int)); + dsp_chain[dsp_chainsize-1] = (t_int)f; + va_start(ap, n); + for (i = 0; i < n; i++) + dsp_chain[dsp_chainsize + i] = va_arg(ap, t_int); + va_end(ap); + dsp_chain[newsize-1] = 0; + dsp_chainsize = newsize; +} + + /* at Guenter's suggestion, here's a vectorized version */ +void dsp_addv(t_perfroutine f, int n, t_int *vec) +{ + int newsize = dsp_chainsize + n+1, i; + + dsp_chain = t_resizebytes(dsp_chain, dsp_chainsize * sizeof (t_int), + newsize * sizeof (t_int)); + dsp_chain[dsp_chainsize-1] = (t_int)f; + for (i = 0; i < n; i++) + dsp_chain[dsp_chainsize + i] = vec[i]; + dsp_chain[newsize-1] = 0; + dsp_chainsize = newsize; +} + +void dsp_tick(void) +{ + if (dsp_chain) + { + t_int *ip; + for (ip = dsp_chain; *ip; ) ip = (*(t_perfroutine)(*ip))(ip); + dsp_phase++; + } +} + +/* ---------------- signals ---------------------------- */ + +int ilog2(int n) +{ + int r = -1; + if (n <= 0) return(0); + while (n) + { + r++; + n >>= 1; + } + return (r); +} + + /* list of signals which can be reused, sorted by buffer size */ +static t_signal *signal_freelist[MAXLOGSIG+1]; + /* list of reusable "borrowed" signals (which don't own sample buffers) */ +static t_signal *signal_freeborrowed; + /* list of all signals allocated (not including "borrowed" ones) */ +static t_signal *signal_usedlist; + + /* call this when DSP is stopped to free all the signals */ +void signal_cleanup(void) +{ + t_signal **svec, *sig, *sig2; + int i; + while (sig = signal_usedlist) + { + signal_usedlist = sig->s_nextused; + if (!sig->s_isborrowed) + t_freebytes(sig->s_vec, sig->s_n * sizeof (*sig->s_vec)); + t_freebytes(sig, sizeof *sig); + } + for (i = 0; i <= MAXLOGSIG; i++) + signal_freelist[i] = 0; + signal_freeborrowed = 0; +} + + /* mark the signal "reusable." */ +void signal_makereusable(t_signal *sig) +{ + int logn = ilog2(sig->s_n); +#if 1 + t_signal *s5; + for (s5 = signal_freeborrowed; s5; s5 = s5->s_nextfree) + { + if (s5 == sig) + { + bug("signal_free 3"); + return; + } + } + for (s5 = signal_freelist[logn]; s5; s5 = s5->s_nextfree) + { + if (s5 == sig) + { + bug("signal_free 4"); + return; + } + } +#endif + if (ugen_loud) post("free %x: %d", sig, sig->s_isborrowed); + if (sig->s_isborrowed) + { + /* if the signal is borrowed, decrement the borrowed-from signal's + reference count, possibly marking it reusable too */ + t_signal *s2 = sig->s_borrowedfrom; + if ((s2 == sig) || !s2) + bug("signal_free"); + s2->s_refcount--; + if (!s2->s_refcount) + signal_makereusable(s2); + sig->s_nextfree = signal_freeborrowed; + signal_freeborrowed = sig; + } + else + { + /* if it's a real signal (not borrowed), put it on the free list + so we can reuse it. */ + if (signal_freelist[logn] == sig) bug("signal_free 2"); + sig->s_nextfree = signal_freelist[logn]; + signal_freelist[logn] = sig; + } +} + + /* reclaim or make an audio signal. If n is zero, return a "borrowed" + signal whose buffer and size will be obtained later via + signal_setborrowed(). */ + +t_signal *signal_new(int n, float sr) +{ + int logn, n2; + t_signal *ret, **whichlist; + t_sample *fp; + logn = ilog2(n); + if (n) + { + if (n != (1 << logn)) + bug("signal buffer not a power of 2"); + if (logn > MAXLOGSIG) + bug("signal buffer too large"); + whichlist = signal_freelist + logn; + } + else + whichlist = &signal_freeborrowed; + + /* first try to reclaim one from the free list */ + if (ret = *whichlist) + *whichlist = ret->s_nextfree; + else + { + /* LATER figure out what to do for out-of-space here! */ + ret = (t_signal *)t_getbytes(sizeof *ret); + if (n) + { + ret->s_vec = (t_sample *)getbytes(n * sizeof (*ret->s_vec)); + ret->s_isborrowed = 0; + } + else + { + ret->s_vec = 0; + ret->s_isborrowed = 1; + } + ret->s_nextused = signal_usedlist; + signal_usedlist = ret; + } + ret->s_n = n; + ret->s_sr = sr; + ret->s_refcount = 0; + ret->s_borrowedfrom = 0; + if (ugen_loud) post("new %x: %d", ret, ret->s_isborrowed); + return (ret); +} + +static t_signal *signal_newlike(const t_signal *sig) +{ + return (signal_new(sig->s_n, sig->s_sr)); +} + +void signal_setborrowed(t_signal *sig, t_signal *sig2) +{ + if (!sig->s_isborrowed || sig->s_borrowedfrom) + bug("signal_setborrowed"); + if (sig == sig2) + bug("signal_setborrowed 2"); + sig->s_borrowedfrom = sig2; + sig->s_vec = sig2->s_vec; + sig->s_n = sig2->s_n; +} + +int signal_compatible(t_signal *s1, t_signal *s2) +{ + return (s1->s_n == s2->s_n && s1->s_sr == s2->s_sr); +} + +/* ------------------ ugen ("unit generator") sorting ----------------- */ + +typedef struct _ugenbox +{ + struct _siginlet *u_in; + int u_nin; + struct _sigoutlet *u_out; + int u_nout; + int u_phase; + struct _ugenbox *u_next; + t_object *u_obj; + int u_done; +} t_ugenbox; + +typedef struct _siginlet +{ + int i_nconnect; + int i_ngot; + t_signal *i_signal; +} t_siginlet; + +typedef struct _sigoutconnect +{ + t_ugenbox *oc_who; + int oc_inno; + struct _sigoutconnect *oc_next; +} t_sigoutconnect; + +typedef struct _sigoutlet +{ + int o_nconnect; + int o_nsent; + t_signal *o_signal; + t_sigoutconnect *o_connections; +} t_sigoutlet; + + +struct _dspcontext +{ + struct _ugenbox *dc_ugenlist; + struct _dspcontext *dc_parentcontext; + int dc_ninlets; + int dc_noutlets; + t_signal **dc_iosigs; + float dc_srate; + int dc_vecsize; + char dc_toplevel; /* true if "iosigs" is invalid. */ + char dc_reblock; /* true if we have to reblock inlets/outlets */ + char dc_switched; /* true if we're switched */ + +}; + +#define t_dspcontext struct _dspcontext + +static int ugen_sortno = 0; +static t_dspcontext *ugen_currentcontext; + +void ugen_stop(void) +{ + t_signal *s; + int i; + if (dsp_chain) + { + freebytes(dsp_chain, dsp_chainsize * sizeof (t_int)); + dsp_chain = 0; + } + signal_cleanup(); + +} + +void ugen_start(void) +{ + ugen_stop(); + ugen_sortno++; + dsp_chain = (t_int *)getbytes(sizeof(*dsp_chain)); + dsp_chain[0] = 0; + dsp_chainsize = 1; + if (ugen_currentcontext) bug("ugen_start"); +} + +int ugen_getsortno(void) +{ + return (ugen_sortno); +} + +#if 0 +void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) +{ + int i, count; + t_signal *sig; + for (count = 0, sig = signal_usedlist; sig; + count++, sig = sig->s_nextused) + ; + post("used signals %d", count); + for (i = 0; i < MAXLOGSIG; i++) + { + for (count = 0, sig = signal_freelist[i]; sig; + count++, sig = sig->s_nextfree) + ; + if (count) + post("size %d: free %d", (1 << i), count); + } + for (count = 0, sig = signal_freeborrowed; sig; + count++, sig = sig->s_nextfree) + ; + post("free borrowed %d", count); + + ugen_loud = argc; +} +#endif + + /* start building the graph for a canvas */ +t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp, + int ninlets, int noutlets) +{ + t_dspcontext *dc = (t_dspcontext *)getbytes(sizeof(*dc)); + float parent_srate, srate; + int parent_vecsize, vecsize; + + if (ugen_loud) post("ugen_start_graph..."); + + dc->dc_ugenlist = 0; + dc->dc_toplevel = toplevel; + dc->dc_iosigs = sp; + dc->dc_ninlets = ninlets; + dc->dc_noutlets = noutlets; + dc->dc_parentcontext = ugen_currentcontext; + ugen_currentcontext = dc; + return (dc); +} + + /* first the canvas calls this to create all the boxes... */ +void ugen_add(t_dspcontext *dc, t_object *obj) +{ + t_ugenbox *x = (t_ugenbox *)getbytes(sizeof *x); + int i; + t_sigoutlet *uout; + t_siginlet *uin; + + x->u_next = dc->dc_ugenlist; + dc->dc_ugenlist = x; + x->u_obj = obj; + x->u_nin = obj_nsiginlets(obj); + x->u_in = getbytes(x->u_nin * sizeof (*x->u_in)); + for (uin = x->u_in, i = x->u_nin; i--; uin++) + uin->i_nconnect = 0; + x->u_nout = obj_nsigoutlets(obj); + x->u_out = getbytes(x->u_nout * sizeof (*x->u_out)); + for (uout = x->u_out, i = x->u_nout; i--; uout++) + uout->o_connections = 0, uout->o_nconnect = 0; +} + + /* and then this to make all the connections. */ +void ugen_connect(t_dspcontext *dc, t_object *x1, int outno, t_object *x2, + int inno) +{ + t_ugenbox *u1, *u2; + t_sigoutlet *uout; + t_siginlet *uin; + t_sigoutconnect *oc; + int sigoutno = obj_sigoutletindex(x1, outno); + int siginno = obj_siginletindex(x2, inno); + if (ugen_loud) + post("%s -> %s: %d->%d", + class_getname(x1->ob_pd), + class_getname(x2->ob_pd), outno, inno); + for (u1 = dc->dc_ugenlist; u1 && u1->u_obj != x1; u1 = u1->u_next); + for (u2 = dc->dc_ugenlist; u2 && u2->u_obj != x2; u2 = u2->u_next); + if (!u1 || !u2 || siginno < 0) + { + pd_error(u1->u_obj, + "signal outlet connect to nonsignal inlet (ignored)"); + return; + } + if (sigoutno < 0 || sigoutno >= u1->u_nout || siginno >= u2->u_nin) + { + bug("ugen_connect %s %s %d %d (%d %d)", + class_getname(x1->ob_pd), + class_getname(x2->ob_pd), sigoutno, siginno, u1->u_nout, + u2->u_nin); + } + uout = u1->u_out + sigoutno; + uin = u2->u_in + siginno; + + /* add a new connection to the outlet's list */ + oc = (t_sigoutconnect *)getbytes(sizeof *oc); + oc->oc_next = uout->o_connections; + uout->o_connections = oc; + oc->oc_who = u2; + oc->oc_inno = siginno; + /* update inlet and outlet counts */ + uout->o_nconnect++; + uin->i_nconnect++; +} + + /* get the index of a ugenbox or -1 if it's not on the list */ +static int ugen_index(t_dspcontext *dc, t_ugenbox *x) +{ + int ret; + t_ugenbox *u; + for (u = dc->dc_ugenlist, ret = 0; u; u = u->u_next, ret++) + if (u == x) return (ret); + return (-1); +} + + /* put a ugenbox on the chain, recursively putting any others on that + this one might uncover. */ +static void ugen_doit(t_dspcontext *dc, t_ugenbox *u) +{ + t_sigoutlet *uout; + t_siginlet *uin; + t_sigoutconnect *oc, *oc2; + t_class *class = pd_class(&u->u_obj->ob_pd); + int i, n; + /* suppress creating new signals for the outputs of signal + inlets and subpatchs; except in the case we're an inlet and "blocking" + is set. We don't yet know if a subcanvas will be "blocking" so there + we delay new signal creation, which will be handled by calling + signal_setborrowed in the ugen_done_graph routine below. */ + int nonewsigs = (class == canvas_class || + (class == vinlet_class) && !(dc->dc_reblock)); + /* when we encounter a subcanvas or a signal outlet, suppress freeing + the input signals as they may be "borrowed" for the super or sub + patch; same exception as above, but also if we're "switched" we + have to do a copy rather than a borrow. */ + int nofreesigs = (class == canvas_class || + (class == voutlet_class) && !(dc->dc_reblock || dc->dc_switched)); + t_signal **insig, **outsig, **sig, *s1, *s2, *s3; + t_ugenbox *u2; + + if (ugen_loud) post("doit %s %d %d", class_getname(class), nofreesigs, + nonewsigs); + for (i = 0, uin = u->u_in; i < u->u_nin; i++, uin++) + { + if (!uin->i_nconnect) + { + t_sample *scalar; + s3 = signal_new(dc->dc_vecsize, dc->dc_srate); + /* post("%s: unconnected signal inlet set to zero", + class_getname(u->u_obj->ob_pd)); */ + if (scalar = obj_findsignalscalar(u->u_obj, i)) + dsp_add_scalarcopy(scalar, s3->s_vec, s3->s_n); + else + dsp_add_zero(s3->s_vec, s3->s_n); + uin->i_signal = s3; + s3->s_refcount = 1; + } + } + insig = (t_signal **)getbytes((u->u_nin + u->u_nout) * sizeof(t_signal *)); + outsig = insig + u->u_nin; + for (sig = insig, uin = u->u_in, i = u->u_nin; i--; sig++, uin++) + { + int newrefcount; + *sig = uin->i_signal; + newrefcount = --(*sig)->s_refcount; + /* if the reference count went to zero, we free the signal now, + unless it's a subcanvas or outlet; these might keep the + signal around to send to objects connected to them. In this + case we increment the reference count; the corresponding decrement + is in sig_makereusable(). */ + if (nofreesigs) + (*sig)->s_refcount++; + else if (!newrefcount) + signal_makereusable(*sig); + } + for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++) + { + /* similarly, for outlets of subcanvases we delay creating + them; instead we create "borrowed" ones so that the refcount + is known. The subcanvas replaces the fake signal with one showing + where the output data actually is, to avoid having to copy it. + For any other object, we just allocate a new output vector; + since we've already freed the inputs the objects might get called + "in place." */ + if (nonewsigs) + { + *sig = uout->o_signal = + signal_new(0, dc->dc_srate); + } + else + *sig = uout->o_signal = signal_new(dc->dc_vecsize, dc->dc_srate); + (*sig)->s_refcount = uout->o_nconnect; + } + /* now call the DSP scheduling routine for the ugen. This + routine must fill in "borrowed" signal outputs in case it's either + a subcanvas or a signal inlet. */ + mess1(&u->u_obj->ob_pd, gensym("dsp"), insig); + + /* if any output signals aren't connected to anyone, free them + now; otherwise they'll either get freed when the reference count + goes back to zero, or even later as explained above. */ + + for (sig = outsig, uout = u->u_out, i = u->u_nout; i--; sig++, uout++) + { + if (!(*sig)->s_refcount) + signal_makereusable(*sig); + } + if (ugen_loud) + { + if (u->u_nin + u->u_nout == 0) post("put %s %d", + class_getname(u->u_obj->ob_pd), ugen_index(dc, u)); + else if (u->u_nin + u->u_nout == 1) post("put %s %d (%x)", + class_getname(u->u_obj->ob_pd), ugen_index(dc, u), sig[0]); + else if (u->u_nin + u->u_nout == 2) post("put %s %d (%x %x)", + class_getname(u->u_obj->ob_pd), ugen_index(dc, u), + sig[0], sig[1]); + else post("put %s %d (%x %x %x ...)", + class_getname(u->u_obj->ob_pd), ugen_index(dc, u), + sig[0], sig[1], sig[2]); + } + + /* pass it on and trip anyone whose last inlet was filled */ + for (uout = u->u_out, i = u->u_nout; i--; uout++) + { + s1 = uout->o_signal; + for (oc = uout->o_connections; oc; oc = oc->oc_next) + { + u2 = oc->oc_who; + uin = &u2->u_in[oc->oc_inno]; + /* if there's already someone here, sum the two */ + if (s2 = uin->i_signal) + { + s1->s_refcount--; + s2->s_refcount--; + if (!signal_compatible(s1, s2)) + { + pd_error(u->u_obj, "%s: incompatible signal inputs", + class_getname(u->u_obj->ob_pd)); + return; + } + s3 = signal_newlike(s1); + dsp_add_plus(s1->s_vec, s2->s_vec, s3->s_vec, s1->s_n); + uin->i_signal = s3; + s3->s_refcount = 1; + if (!s1->s_refcount) signal_makereusable(s1); + if (!s2->s_refcount) signal_makereusable(s2); + } + else uin->i_signal = s1; + uin->i_ngot++; + /* if we didn't fill this inlet don't bother yet */ + if (uin->i_ngot < uin->i_nconnect) + goto notyet; + /* if there's more than one, check them all */ + if (u2->u_nin > 1) + { + for (uin = u2->u_in, n = u2->u_nin; n--; uin++) + if (uin->i_ngot < uin->i_nconnect) goto notyet; + } + /* so now we can schedule the ugen. */ + ugen_doit(dc, u2); + notyet: ; + } + } + t_freebytes(insig,(u->u_nin + u->u_nout) * sizeof(t_signal *)); + u->u_done = 1; +} + + /* once the DSP graph is built, we call this routine to sort it. + This routine also deletes the graph; later we might want to leave the + graph around, in case the user is editing the DSP network, to save having + to recreate it all the time. But not today. */ + +void ugen_done_graph(t_dspcontext *dc) +{ + t_ugenbox *u, *u2; + t_sigoutlet *uout; + t_siginlet *uin; + t_sigoutconnect *oc, *oc2; + int i, n; + t_block *blk; + t_dspcontext *parent_context = dc->dc_parentcontext; + float parent_srate; + int parent_vecsize; + int period, frequency, phase, vecsize; + float srate; + int chainblockbegin; /* DSP chain onset before block prolog code */ + int chainblockend; /* and after block epilog code */ + int chainafterall; /* and after signal outlet epilog */ + int reblock = 0, switched; + int downsample = 1, upsample = 1; /* IOhannes */ + /* debugging printout */ + + if (ugen_loud) + { + post("ugen_done_graph..."); + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + post("ugen: %s", class_getname(u->u_obj->ob_pd)); + for (uout = u->u_out, i = 0; i < u->u_nout; uout++, i++) + for (oc = uout->o_connections; oc; oc = oc->oc_next) + { + post("... out %d to %s, index %d, inlet %d", i, + class_getname(oc->oc_who->u_obj->ob_pd), + ugen_index(dc, oc->oc_who), oc->oc_inno); + } + } + } + + /* search for an object of class "block~" */ + for (u = dc->dc_ugenlist, blk = 0; u; u = u->u_next) + { + t_pd *zz = &u->u_obj->ob_pd; + if (pd_class(zz) == block_class) + { + if (blk) + pd_error(blk, "conflicting block~ objects in same page"); + else blk = (t_block *)zz; + } + } + + /* figure out block size, calling frequency, sample rate */ + if (parent_context) + { + parent_srate = parent_context->dc_srate; + parent_vecsize = parent_context->dc_vecsize; + } + else + { + parent_srate = sys_getsr(); + parent_vecsize = sys_getblksize(); + } + if (blk) + { + int realoverlap; + vecsize = blk->x_vecsize; + if (vecsize == 0) + vecsize = parent_vecsize; + realoverlap = blk->x_overlap; + if (realoverlap > vecsize) realoverlap = vecsize; + /* IOhannes { */ + downsample = blk->x_downsample; + upsample = blk->x_upsample; + if (downsample > parent_vecsize) downsample=parent_vecsize; + period = (vecsize * downsample)/ + (parent_vecsize * realoverlap * upsample); + frequency = (parent_vecsize * realoverlap * upsample)/ + (vecsize * downsample); + /* } IOhannes*/ + phase = blk->x_phase; + srate = parent_srate * realoverlap * upsample / downsample; + /* IOhannes */ + if (period < 1) period = 1; + if (frequency < 1) frequency = 1; + blk->x_frequency = frequency; + blk->x_period = period; + blk->x_phase = dsp_phase & (period - 1); + if (! parent_context || (realoverlap != 1) || + (vecsize != parent_vecsize) || + (downsample != 1) || (upsample != 1)) /* IOhannes */ + reblock = 1; + switched = blk->x_switched; + } + else + { + srate = parent_srate; + vecsize = parent_vecsize; + downsample = upsample = 1;/* IOhannes */ + period = frequency = 1; + phase = 0; + if (!parent_context) reblock = 1; + switched = 0; + } + dc->dc_reblock = reblock; + dc->dc_switched = switched; + dc->dc_srate = srate; + dc->dc_vecsize = vecsize; + + /* if we're reblocking or switched, we now have to create output + signals to fill in for the "borrowed" ones we have now. This + is also possibly true even if we're not blocked/switched, in + the case that there was a signal loop. But we don't know this + yet. */ + + if (dc->dc_iosigs && (switched || reblock)) + { + t_signal **sigp; + for (i = 0, sigp = dc->dc_iosigs + dc->dc_ninlets; i < dc->dc_noutlets; + i++, sigp++) + { + if ((*sigp)->s_isborrowed && !(*sigp)->s_borrowedfrom) + { + signal_setborrowed(*sigp, + signal_new(parent_vecsize, parent_srate)); + (*sigp)->s_refcount++; + + if (ugen_loud) post("set %x->%x", *sigp, + (*sigp)->s_borrowedfrom); + } + } + } + + if (ugen_loud) + post("reblock %d, switched %d", reblock, switched); + + /* schedule prologs for inlets and outlets. If the "reblock" flag + is set, an inlet will put code on the DSP chain to copy its input + into an internal buffer here, before any unit generators' DSP code + gets scheduled. If we don't "reblock", inlets will need to get + pointers to their corresponding inlets/outlets on the box we're inside, + if any. Outlets will also need pointers, unless we're switched, in + which case outlet epilog code will kick in. */ + + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + t_pd *zz = &u->u_obj->ob_pd; + t_signal **insigs = dc->dc_iosigs, **outsigs = dc->dc_iosigs; + if (outsigs) outsigs += dc->dc_ninlets; + + if (pd_class(zz) == vinlet_class) + vinlet_dspprolog((struct _vinlet *)zz, + dc->dc_iosigs, vecsize, dsp_phase, period, frequency, + downsample, upsample, /* IOhannes */ + reblock, switched); + else if (pd_class(zz) == voutlet_class) + voutlet_dspprolog((struct _voutlet *)zz, + outsigs, vecsize, dsp_phase, period, frequency, + downsample, upsample, /* IOhannes */ + reblock, switched); + } + chainblockbegin = dsp_chainsize; + + if (blk && (reblock || switched)) /* add the block DSP prolog */ + dsp_add(block_prolog, 1, blk); + + /* Initialize for sorting */ + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + u->u_done = 0; + for (uout = u->u_out, i = u->u_nout; i--; uout++) + uout->o_nsent = 0; + for (uin = u->u_in, i = u->u_nin; i--; uin++) + uin->i_ngot = 0, uin->i_signal = 0; + } + + /* Do the sort */ + + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + /* check that we have no connected signal inlets */ + if (u->u_done) continue; + for (uin = u->u_in, i = u->u_nin; i--; uin++) + if (uin->i_nconnect) goto next; + + ugen_doit(dc, u); + next: ; + } + + /* check for a DSP loop, which is evidenced here by the presence + of ugens not yet scheduled. */ + + for (u = dc->dc_ugenlist; u; u = u->u_next) + if (!u->u_done) + { + t_signal **sigp; + pd_error(u->u_obj, + "DSP loop detected (some tilde objects not scheduled)"); + /* this might imply that we have unfilled "borrowed" outputs + which we'd better fill in now. */ + for (i = 0, sigp = dc->dc_iosigs + dc->dc_ninlets; i < dc->dc_noutlets; + i++, sigp++) + { + if ((*sigp)->s_isborrowed && !(*sigp)->s_borrowedfrom) + { + t_signal *s3 = signal_new(parent_vecsize, parent_srate); + signal_setborrowed(*sigp, s3); + (*sigp)->s_refcount++; + dsp_add_zero(s3->s_vec, s3->s_n); + if (ugen_loud) + post("oops, belatedly set %x->%x", *sigp, + (*sigp)->s_borrowedfrom); + } + } + break; /* don't need to keep looking. */ + } + + if (blk && (reblock || switched)) /* add block DSP epilog */ + dsp_add(block_epilog, 1, blk); + chainblockend = dsp_chainsize; + + /* add epilogs for outlets. */ + + for (u = dc->dc_ugenlist; u; u = u->u_next) + { + t_pd *zz = &u->u_obj->ob_pd; + if (pd_class(zz) == voutlet_class) + { + t_signal **iosigs = dc->dc_iosigs; + if (iosigs) iosigs += dc->dc_ninlets; + voutlet_dspepilog((struct _voutlet *)zz, + iosigs, vecsize, dsp_phase, period, frequency, + downsample, upsample, /* IOhannes */ + reblock, switched); + } + } + + chainafterall = dsp_chainsize; + if (blk) + { + blk->x_blocklength = chainblockend - chainblockbegin; + blk->x_epiloglength = chainafterall - chainblockend; + blk->x_reblock = reblock; + } + + if (ugen_loud) + { + t_int *ip; + if (!dc->dc_parentcontext) + for (i = dsp_chainsize, ip = dsp_chain; i--; ip++) + post("chain %x", *ip); + post("... ugen_done_graph done."); + } + /* now delete everything. */ + while (dc->dc_ugenlist) + { + for (uout = dc->dc_ugenlist->u_out, n = dc->dc_ugenlist->u_nout; + n--; uout++) + { + oc = uout->o_connections; + while (oc) + { + oc2 = oc->oc_next; + freebytes(oc, sizeof *oc); + oc = oc2; + } + } + freebytes(dc->dc_ugenlist->u_out, dc->dc_ugenlist->u_nout * + sizeof (*dc->dc_ugenlist->u_out)); + freebytes(dc->dc_ugenlist->u_in, dc->dc_ugenlist->u_nin * + sizeof(*dc->dc_ugenlist->u_in)); + u = dc->dc_ugenlist; + dc->dc_ugenlist = u->u_next; + freebytes(u, sizeof *u); + } + if (ugen_currentcontext == dc) + ugen_currentcontext = dc->dc_parentcontext; + else bug("ugen_currentcontext"); + freebytes(dc, sizeof(*dc)); + +} + +t_signal *ugen_getiosig(int index, int inout) +{ + if (!ugen_currentcontext) bug("ugen_getiosig"); + if (ugen_currentcontext->dc_toplevel) return (0); + if (inout) index += ugen_currentcontext->dc_ninlets; + return (ugen_currentcontext->dc_iosigs[index]); +} + + +/* -------------------- setup routine -------------------------- */ + +void d_ugen_setup(void) /* really just block_setup */ +{ + block_class = class_new(gensym("block~"), (t_newmethod)block_new, 0, + sizeof(t_block), 0, A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT/*IOhannes*/, 0); + class_addcreator((t_newmethod)switch_new, gensym("switch~"), + A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT/*IOhannes*/, 0); + class_addmethod(block_class, (t_method)block_set, gensym("set"), + A_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); + class_addmethod(block_class, (t_method)block_dsp, gensym("dsp"), 0); + class_addfloat(block_class, block_float); +} + diff --git a/apps/plugins/pdbox/PDa/src/delme.pd b/apps/plugins/pdbox/PDa/src/delme.pd new file mode 100644 index 0000000..8e2c4f2 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/delme.pd @@ -0,0 +1,16 @@ +#N canvas 0 0 236 296 10; +#X obj 79 77 gcanvas 80 80 0 0; +#X floatatom 42 209 5 0 0 0 - - -; +#X floatatom 107 205 5 0 0 0 - - -; +#X floatatom 149 210 5 0 0 0 - - -; +#X connect 0 0 1 0; +#X connect 0 1 2 0; +#X connect 0 2 3 0; +#N canvas 0 0 236 296 10; +#X obj 79 77 gcanvas 80 80 0 0; +#X floatatom 42 209 5 0 0 0 - - -; +#X floatatom 107 205 5 0 0 0 - - -; +#X floatatom 149 210 5 0 0 0 - - -; +#X connect 0 0 1 0; +#X connect 0 1 2 0; +#X connect 0 2 3 0; diff --git a/apps/plugins/pdbox/PDa/src/g_all_guis.c b/apps/plugins/pdbox/PDa/src/g_all_guis.c new file mode 100644 index 0000000..9254baf --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_all_guis.c @@ -0,0 +1,1324 @@ +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + +/* #define GGEE_HSLIDER_COMPATIBLE */ + +/*------------------ global varaibles -------------------------*/ + +int iemgui_color_hex[]= +{ + 16579836, 10526880, 4210752, 16572640, 16572608, + 16579784, 14220504, 14220540, 14476540, 16308476, + 14737632, 8158332, 2105376, 16525352, 16559172, + 15263784, 1370132, 2684148, 3952892, 16003312, + 12369084, 6316128, 0, 9177096, 5779456, + 7874580, 2641940, 17488, 5256, 5767248 +}; + +int iemgui_vu_db2i[]= +{ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9,10,10,10,10,10, + 11,11,11,11,11,12,12,12,12,12, + 13,13,13,13,14,14,14,14,15,15, + 15,15,16,16,16,16,17,17,17,18, + 18,18,19,19,19,20,20,20,21,21, + 22,22,23,23,24,24,25,26,27,28, + 29,30,31,32,33,33,34,34,35,35, + 36,36,37,37,37,38,38,38,39,39, + 39,39,39,39,40,40 +}; + +int iemgui_vu_col[]= +{ + 0,17,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 15,15,15,15,15,15,15,15,15,15,14,14,13,13,13,13,13,13,13,13,13,13,13,19,19,19 +}; + +char *iemgui_vu_scale_str[]= +{ + "", + "<-99", + "", + "", + "", + "-50", + "", + "", + "", + "-30", + "", + "", + "", + "-20", + "", + "", + "", + "-12", + "", + "", + "", + "-6", + "", + "", + "", + "-2", + "", + "", + "", + "-0dB", + "", + "", + "", + "+2", + "", + "", + "", + "+6", + "", + "", + "", + ">+12", + "", + "", + "", + "", + "", +}; + + +/*------------------ global functions -------------------------*/ + + +int iemgui_clip_size(int size) +{ + if(size < IEM_GUI_MINSIZE) + size = IEM_GUI_MINSIZE; + return(size); +} + +int iemgui_clip_font(int size) +{ + if(size < IEM_FONT_MINSIZE) + size = IEM_FONT_MINSIZE; + return(size); +} + +int iemgui_modulo_color(int col) +{ + while(col >= IEM_GUI_MAX_COLOR) + col -= IEM_GUI_MAX_COLOR; + while(col < 0) + col += IEM_GUI_MAX_COLOR; + return(col); +} + +t_symbol *iemgui_raute2dollar(t_symbol *s) +{ + if (s->s_name[0] == '#') + { + char buf[MAXPDSTRING]; + strncpy(buf, s->s_name, MAXPDSTRING); + buf[MAXPDSTRING-1] = 0; + buf[0] = '$'; + return (gensym(buf)); + } + else return (s); +} + +t_symbol *iemgui_dollar2raute(t_symbol *s) +{ + if (s->s_name[0] == '$') + { + char buf[MAXPDSTRING]; + strncpy(buf, s->s_name, MAXPDSTRING); + buf[MAXPDSTRING-1] = 0; + buf[0] = '#'; + return (gensym(buf)); + } + else return (s); +} + +void iemgui_verify_snd_ne_rcv(t_iemgui *iemgui) +{ + iemgui->x_fsf.x_put_in2out = 1; + if(iemgui->x_fsf.x_snd_able && iemgui->x_fsf.x_rcv_able) + { + if(!strcmp(iemgui->x_snd->s_name, iemgui->x_rcv->s_name)) + iemgui->x_fsf.x_put_in2out = 0; + } +} + +t_symbol *iemgui_new_dogetname(t_iemgui *iemgui, int indx, t_atom *argv) +{ + if (IS_A_SYMBOL(argv, indx)) + return (atom_getsymbolarg(indx, 100000, argv)); + else if (IS_A_FLOAT(argv, indx)) + { + char str[80]; + sprintf(str, "%d", (int)atom_getintarg(indx, 100000, argv)); + return (gensym(str)); + } + else return (gensym("empty")); +} + +void iemgui_new_getnames(t_iemgui *iemgui, int indx, t_atom *argv) +{ + if (argv) + { + iemgui->x_snd = iemgui_new_dogetname(iemgui, indx, argv); + iemgui->x_rcv = iemgui_new_dogetname(iemgui, indx+1, argv); + iemgui->x_lab = iemgui_new_dogetname(iemgui, indx+2, argv); + } + else iemgui->x_snd = iemgui->x_rcv = iemgui->x_lab = gensym("empty"); + iemgui->x_snd_unexpanded = iemgui->x_rcv_unexpanded = + iemgui->x_lab_unexpanded = 0; + iemgui->x_binbufindex = indx; + iemgui->x_labelbindex = indx + 3; +} + + /* convert symbols in "$" form to the expanded symbols */ +void iemgui_all_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym) +{ + /* save unexpanded ones for later */ + iemgui->x_snd_unexpanded = srlsym[0]; + iemgui->x_rcv_unexpanded = srlsym[1]; + iemgui->x_lab_unexpanded = srlsym[2]; + srlsym[0] = canvas_realizedollar(iemgui->x_glist, srlsym[0]); + srlsym[1] = canvas_realizedollar(iemgui->x_glist, srlsym[1]); + srlsym[2] = canvas_realizedollar(iemgui->x_glist, srlsym[2]); +} + + /* initialize a single symbol in unexpanded form. We reach into the + binbuf to grab them; if there's nothing there, set it to the + fallback; if still nothing, set to "empty". */ +static void iemgui_init_sym2dollararg(t_iemgui *iemgui, t_symbol **symp, + int indx, t_symbol *fallback) +{ + if (!*symp) + { + t_binbuf *b = iemgui->x_obj.ob_binbuf; + if (binbuf_getnatom(b) > indx) + { + char buf[80]; + atom_string(binbuf_getvec(b) + indx, buf, 80); + *symp = gensym(buf); + } + else if (fallback) + *symp = fallback; + else *symp = gensym("empty"); + } +} + + /* get the unexpanded versions of the symbols; initialize them if + necessary. */ +void iemgui_all_sym2dollararg(t_iemgui *iemgui, t_symbol **srlsym) +{ + iemgui_init_sym2dollararg(iemgui, &iemgui->x_snd_unexpanded, + iemgui->x_binbufindex+1, iemgui->x_snd); + iemgui_init_sym2dollararg(iemgui, &iemgui->x_rcv_unexpanded, + iemgui->x_binbufindex+2, iemgui->x_rcv); + iemgui_init_sym2dollararg(iemgui, &iemgui->x_lab_unexpanded, + iemgui->x_labelbindex, iemgui->x_lab); + srlsym[0] = iemgui->x_snd_unexpanded; + srlsym[1] = iemgui->x_rcv_unexpanded; + srlsym[2] = iemgui->x_lab_unexpanded; +} + +void iemgui_first_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym) +{ + /* delete this function */ +} + +void iemgui_all_col2save(t_iemgui *iemgui, int *bflcol) +{ + bflcol[0] = -1 - (((0xfc0000 & iemgui->x_bcol) >> 6)| + ((0xfc00 & iemgui->x_bcol) >> 4)|((0xfc & iemgui->x_bcol) >> 2)); + bflcol[1] = -1 - (((0xfc0000 & iemgui->x_fcol) >> 6)| + ((0xfc00 & iemgui->x_fcol) >> 4)|((0xfc & iemgui->x_fcol) >> 2)); + bflcol[2] = -1 - (((0xfc0000 & iemgui->x_lcol) >> 6)| + ((0xfc00 & iemgui->x_lcol) >> 4)|((0xfc & iemgui->x_lcol) >> 2)); +} + +void iemgui_all_colfromload(t_iemgui *iemgui, int *bflcol) +{ + if(bflcol[0] < 0) + { + bflcol[0] = -1 - bflcol[0]; + iemgui->x_bcol = ((bflcol[0] & 0x3f000) << 6)|((bflcol[0] & 0xfc0) << 4)| + ((bflcol[0] & 0x3f) << 2); + } + else + { + bflcol[0] = iemgui_modulo_color(bflcol[0]); + iemgui->x_bcol = iemgui_color_hex[bflcol[0]]; + } + if(bflcol[1] < 0) + { + bflcol[1] = -1 - bflcol[1]; + iemgui->x_fcol = ((bflcol[1] & 0x3f000) << 6)|((bflcol[1] & 0xfc0) << 4)| + ((bflcol[1] & 0x3f) << 2); + } + else + { + bflcol[1] = iemgui_modulo_color(bflcol[1]); + iemgui->x_fcol = iemgui_color_hex[bflcol[1]]; + } + if(bflcol[2] < 0) + { + bflcol[2] = -1 - bflcol[2]; + iemgui->x_lcol = ((bflcol[2] & 0x3f000) << 6)|((bflcol[2] & 0xfc0) << 4)| + ((bflcol[2] & 0x3f) << 2); + } + else + { + bflcol[2] = iemgui_modulo_color(bflcol[2]); + iemgui->x_lcol = iemgui_color_hex[bflcol[2]]; + } +} + +int iemgui_compatible_col(int i) +{ + int j; + + if(i >= 0) + { + j = iemgui_modulo_color(i); + return(iemgui_color_hex[(j)]); + } + else + return((-1 -i)&0xffffff); +} + +void iemgui_all_dollar2raute(t_symbol **srlsym) +{ + srlsym[0] = iemgui_dollar2raute(srlsym[0]); + srlsym[1] = iemgui_dollar2raute(srlsym[1]); + srlsym[2] = iemgui_dollar2raute(srlsym[2]); +} + +void iemgui_all_raute2dollar(t_symbol **srlsym) +{ + srlsym[0] = iemgui_raute2dollar(srlsym[0]); + srlsym[1] = iemgui_raute2dollar(srlsym[1]); + srlsym[2] = iemgui_raute2dollar(srlsym[2]); +} + +void iemgui_send(void *x, t_iemgui *iemgui, t_symbol *s) +{ + t_symbol *snd; + int pargc, tail_len, nth_arg, sndable=1, oldsndrcvable=0; + t_atom *pargv; + + if(iemgui->x_fsf.x_rcv_able) + oldsndrcvable += IEM_GUI_OLD_RCV_FLAG; + if(iemgui->x_fsf.x_snd_able) + oldsndrcvable += IEM_GUI_OLD_SND_FLAG; + + if(!strcmp(s->s_name, "empty")) sndable = 0; + snd = iemgui_raute2dollar(s); + iemgui->x_snd_unexpanded = snd; + iemgui->x_snd = snd = canvas_realizedollar(iemgui->x_glist, snd); + post("send: before %s, after %s", iemgui->x_snd_unexpanded->s_name, + iemgui->x_snd->s_name); + + iemgui->x_fsf.x_snd_able = sndable; + iemgui_verify_snd_ne_rcv(iemgui); + (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_IO + oldsndrcvable); +} + +void iemgui_receive(void *x, t_iemgui *iemgui, t_symbol *s) +{ + t_symbol *rcv; + int pargc, tail_len, nth_arg, rcvable=1, oldsndrcvable=0; + t_atom *pargv; + + if(iemgui->x_fsf.x_rcv_able) + oldsndrcvable += IEM_GUI_OLD_RCV_FLAG; + if(iemgui->x_fsf.x_snd_able) + oldsndrcvable += IEM_GUI_OLD_SND_FLAG; + + if(!strcmp(s->s_name, "empty")) rcvable = 0; + rcv = iemgui_raute2dollar(s); + iemgui->x_rcv_unexpanded = rcv; + iemgui->x_rcv = rcv = canvas_realizedollar(iemgui->x_glist, rcv); + if(rcvable) + { + if(strcmp(rcv->s_name, iemgui->x_rcv->s_name)) + { + if(iemgui->x_fsf.x_rcv_able) + pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); + iemgui->x_rcv = rcv; + pd_bind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); + } + } + else if(!rcvable && iemgui->x_fsf.x_rcv_able) + { + pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); + iemgui->x_rcv = rcv; + } + iemgui->x_fsf.x_rcv_able = rcvable; + iemgui_verify_snd_ne_rcv(iemgui); + (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_IO + oldsndrcvable); +} + +void iemgui_label(void *x, t_iemgui *iemgui, t_symbol *s) +{ + t_symbol *lab; + int pargc, tail_len, nth_arg; + t_atom *pargv; + + lab = iemgui_raute2dollar(s); + iemgui->x_lab_unexpanded = lab; + iemgui->x_lab = lab = canvas_realizedollar(iemgui->x_glist, lab); + + if(glist_isvisible(iemgui->x_glist)) + sys_vgui(".x%x.c itemconfigure %xLABEL -text {%s} \n", + glist_getcanvas(iemgui->x_glist), x, + strcmp(s->s_name, "empty")?iemgui->x_lab->s_name:""); +} + +void iemgui_label_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) +{ + iemgui->x_ldx = (int)atom_getintarg(0, ac, av); + iemgui->x_ldy = (int)atom_getintarg(1, ac, av); + if(glist_isvisible(iemgui->x_glist)) + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + glist_getcanvas(iemgui->x_glist), x, + iemgui->x_obj.te_xpix+iemgui->x_ldx, + iemgui->x_obj.te_ypix+iemgui->x_ldy); +} + +void iemgui_label_font(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) +{ + int f = (int)atom_getintarg(0, ac, av); + + if(f == 1) strcpy(iemgui->x_font, "helvetica"); + else if(f == 2) strcpy(iemgui->x_font, "times"); + else + { + f = 0; + strcpy(iemgui->x_font, "courier"); + } + iemgui->x_fsf.x_font_style = f; + f = (int)atom_getintarg(1, ac, av); + if(f < 4) + f = 4; + iemgui->x_fontsize = f; + if(glist_isvisible(iemgui->x_glist)) + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold}\n", + glist_getcanvas(iemgui->x_glist), x, iemgui->x_font, iemgui->x_fontsize); +} + +void iemgui_size(void *x, t_iemgui *iemgui) +{ + if(glist_isvisible(iemgui->x_glist)) + { + (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(iemgui->x_glist), (t_text*)x); + } +} + +void iemgui_delta(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) +{ + iemgui->x_obj.te_xpix += (int)atom_getintarg(0, ac, av); + iemgui->x_obj.te_ypix += (int)atom_getintarg(1, ac, av); + if(glist_isvisible(iemgui->x_glist)) + { + (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(iemgui->x_glist), (t_text*)x); + } +} + +void iemgui_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) +{ + iemgui->x_obj.te_xpix = (int)atom_getintarg(0, ac, av); + iemgui->x_obj.te_ypix = (int)atom_getintarg(1, ac, av); + if(glist_isvisible(iemgui->x_glist)) + { + (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(iemgui->x_glist), (t_text*)x); + } +} + +void iemgui_color(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) +{ + iemgui->x_bcol = iemgui_compatible_col(atom_getintarg(0, ac, av)); + if(ac > 2) + { + iemgui->x_fcol = iemgui_compatible_col(atom_getintarg(1, ac, av)); + iemgui->x_lcol = iemgui_compatible_col(atom_getintarg(2, ac, av)); + } + else + iemgui->x_lcol = iemgui_compatible_col(atom_getintarg(1, ac, av)); + if(glist_isvisible(iemgui->x_glist)) + (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_CONFIG); +} + +void iemgui_displace(t_gobj *z, t_glist *glist, int dx, int dy) +{ + t_iemguidummy *x = (t_iemguidummy *)z; + + x->x_gui.x_obj.te_xpix += dx; + x->x_gui.x_obj.te_ypix += dy; + (*x->x_gui.x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(glist), (t_text *)z); +} + +void iemgui_select(t_gobj *z, t_glist *glist, int selected) +{ + t_iemguidummy *x = (t_iemguidummy *)z; + + x->x_gui.x_fsf.x_selected = selected; + (*x->x_gui.x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_SELECT); +} + +void iemgui_delete(t_gobj *z, t_glist *glist) +{ + canvas_deletelinesfor(glist, (t_text *)z); +} + +void iemgui_vis(t_gobj *z, t_glist *glist, int vis) +{ + t_iemguidummy *x = (t_iemguidummy *)z; + + if (vis) + (*x->x_gui.x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_NEW); + else + (*x->x_gui.x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_ERASE); +} + +void iemgui_save(t_iemgui *iemgui, t_symbol **srl, int *bflcol) +{ + srl[0] = iemgui->x_snd; + srl[1] = iemgui->x_rcv; + srl[2] = iemgui->x_lab; + iemgui_all_sym2dollararg(iemgui, srl); + iemgui_all_col2save(iemgui, bflcol); +} + +void iemgui_properties(t_iemgui *iemgui, t_symbol **srl) +{ + srl[0] = iemgui->x_snd; + srl[1] = iemgui->x_rcv; + srl[2] = iemgui->x_lab; + iemgui_all_sym2dollararg(iemgui, srl); + iemgui_all_dollar2raute(srl); +} + +int iemgui_dialog(t_iemgui *iemgui, t_symbol **srl, int argc, t_atom *argv) +{ + char str[144]; + int init = (int)atom_getintarg(5, argc, argv); + int ldx = (int)atom_getintarg(10, argc, argv); + int ldy = (int)atom_getintarg(11, argc, argv); + int f = (int)atom_getintarg(12, argc, argv); + int fs = (int)atom_getintarg(13, argc, argv); + int bcol = (int)atom_getintarg(14, argc, argv); + int fcol = (int)atom_getintarg(15, argc, argv); + int lcol = (int)atom_getintarg(16, argc, argv); + int sndable=1, rcvable=1, oldsndrcvable=0; + + if(iemgui->x_fsf.x_rcv_able) + oldsndrcvable += IEM_GUI_OLD_RCV_FLAG; + if(iemgui->x_fsf.x_snd_able) + oldsndrcvable += IEM_GUI_OLD_SND_FLAG; + if(IS_A_SYMBOL(argv,7)) + srl[0] = atom_getsymbolarg(7, argc, argv); + else if(IS_A_FLOAT(argv,7)) + { + sprintf(str, "%d", (int)atom_getintarg(7, argc, argv)); + srl[0] = gensym(str); + } + if(IS_A_SYMBOL(argv,8)) + srl[1] = atom_getsymbolarg(8, argc, argv); + else if(IS_A_FLOAT(argv,8)) + { + sprintf(str, "%d", (int)atom_getintarg(8, argc, argv)); + srl[1] = gensym(str); + } + if(IS_A_SYMBOL(argv,9)) + srl[2] = atom_getsymbolarg(9, argc, argv); + else if(IS_A_FLOAT(argv,9)) + { + sprintf(str, "%d", (int)atom_getintarg(9, argc, argv)); + srl[2] = gensym(str); + } + if(init != 0) init = 1; + iemgui->x_isa.x_loadinit = init; + if(!strcmp(srl[0]->s_name, "empty")) sndable = 0; + if(!strcmp(srl[1]->s_name, "empty")) rcvable = 0; + iemgui_all_raute2dollar(srl); + iemgui_all_dollararg2sym(iemgui, srl); + if(rcvable) + { + if(strcmp(srl[1]->s_name, iemgui->x_rcv->s_name)) + { + if(iemgui->x_fsf.x_rcv_able) + pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); + iemgui->x_rcv = srl[1]; + pd_bind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); + } + } + else if(!rcvable && iemgui->x_fsf.x_rcv_able) + { + pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); + iemgui->x_rcv = srl[1]; + } + iemgui->x_snd = srl[0]; + iemgui->x_fsf.x_snd_able = sndable; + iemgui->x_fsf.x_rcv_able = rcvable; + iemgui->x_lcol = lcol & 0xffffff; + iemgui->x_fcol = fcol & 0xffffff; + iemgui->x_bcol = bcol & 0xffffff; + iemgui->x_lab = srl[2]; + iemgui->x_ldx = ldx; + iemgui->x_ldy = ldy; + if(f == 1) strcpy(iemgui->x_font, "helvetica"); + else if(f == 2) strcpy(iemgui->x_font, "times"); + else + { + f = 0; + strcpy(iemgui->x_font, "courier"); + } + iemgui->x_fsf.x_font_style = f; + if(fs < 4) + fs = 4; + iemgui->x_fontsize = fs; + iemgui_verify_snd_ne_rcv(iemgui); + return(oldsndrcvable); +} + +void iem_inttosymargs(t_iem_init_symargs *symargp, int n) +{ + memset(symargp, 0, sizeof(*symargp)); + symargp->x_loadinit = (n >> 0); + symargp->x_scale = (n >> 20); + symargp->x_flashed = 0; + symargp->x_locked = 0; + symargp->x_reverse = 0; + symargp->dummy = 0; +} + +int iem_symargstoint(t_iem_init_symargs *symargp) +{ + return ( + (((symargp->x_loadinit & 1) << 0) | + ((symargp->x_scale & 1) << 20))); +} + +void iem_inttofstyle(t_iem_fstyle_flags *fstylep, int n) +{ + memset(fstylep, 0, sizeof(*fstylep)); + fstylep->x_font_style = (n >> 0); + fstylep->x_shiftdown = 0; + fstylep->x_selected = 0; + fstylep->x_finemoved = 0; + fstylep->x_put_in2out = 0; + fstylep->x_change = 0; + fstylep->x_thick = 0; + fstylep->x_lin0_log1 = 0; + fstylep->x_steady = 0; + fstylep->dummy = 0; +} + +int iem_fstyletoint(t_iem_fstyle_flags *fstylep) +{ + return ((fstylep->x_font_style << 0) & 63); +} +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + +/* #define GGEE_HSLIDER_COMPATIBLE */ + +/*------------------ global varaibles -------------------------*/ + +int iemgui_color_hex[]= +{ + 16579836, 10526880, 4210752, 16572640, 16572608, + 16579784, 14220504, 14220540, 14476540, 16308476, + 14737632, 8158332, 2105376, 16525352, 16559172, + 15263784, 1370132, 2684148, 3952892, 16003312, + 12369084, 6316128, 0, 9177096, 5779456, + 7874580, 2641940, 17488, 5256, 5767248 +}; + +int iemgui_vu_db2i[]= +{ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9,10,10,10,10,10, + 11,11,11,11,11,12,12,12,12,12, + 13,13,13,13,14,14,14,14,15,15, + 15,15,16,16,16,16,17,17,17,18, + 18,18,19,19,19,20,20,20,21,21, + 22,22,23,23,24,24,25,26,27,28, + 29,30,31,32,33,33,34,34,35,35, + 36,36,37,37,37,38,38,38,39,39, + 39,39,39,39,40,40 +}; + +int iemgui_vu_col[]= +{ + 0,17,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 15,15,15,15,15,15,15,15,15,15,14,14,13,13,13,13,13,13,13,13,13,13,13,19,19,19 +}; + +char *iemgui_vu_scale_str[]= +{ + "", + "<-99", + "", + "", + "", + "-50", + "", + "", + "", + "-30", + "", + "", + "", + "-20", + "", + "", + "", + "-12", + "", + "", + "", + "-6", + "", + "", + "", + "-2", + "", + "", + "", + "-0dB", + "", + "", + "", + "+2", + "", + "", + "", + "+6", + "", + "", + "", + ">+12", + "", + "", + "", + "", + "", +}; + + +/*------------------ global functions -------------------------*/ + + +int iemgui_clip_size(int size) +{ + if(size < IEM_GUI_MINSIZE) + size = IEM_GUI_MINSIZE; + return(size); +} + +int iemgui_clip_font(int size) +{ + if(size < IEM_FONT_MINSIZE) + size = IEM_FONT_MINSIZE; + return(size); +} + +int iemgui_modulo_color(int col) +{ + while(col >= IEM_GUI_MAX_COLOR) + col -= IEM_GUI_MAX_COLOR; + while(col < 0) + col += IEM_GUI_MAX_COLOR; + return(col); +} + +t_symbol *iemgui_raute2dollar(t_symbol *s) +{ + if (s->s_name[0] == '#') + { + char buf[MAXPDSTRING]; + strncpy(buf, s->s_name, MAXPDSTRING); + buf[MAXPDSTRING-1] = 0; + buf[0] = '$'; + return (gensym(buf)); + } + else return (s); +} + +t_symbol *iemgui_dollar2raute(t_symbol *s) +{ + if (s->s_name[0] == '$') + { + char buf[MAXPDSTRING]; + strncpy(buf, s->s_name, MAXPDSTRING); + buf[MAXPDSTRING-1] = 0; + buf[0] = '#'; + return (gensym(buf)); + } + else return (s); +} + +void iemgui_verify_snd_ne_rcv(t_iemgui *iemgui) +{ + iemgui->x_fsf.x_put_in2out = 1; + if(iemgui->x_fsf.x_snd_able && iemgui->x_fsf.x_rcv_able) + { + if(!strcmp(iemgui->x_snd->s_name, iemgui->x_rcv->s_name)) + iemgui->x_fsf.x_put_in2out = 0; + } +} + +t_symbol *iemgui_new_dogetname(t_iemgui *iemgui, int indx, t_atom *argv) +{ + if (IS_A_SYMBOL(argv, indx)) + return (atom_getsymbolarg(indx, 100000, argv)); + else if (IS_A_FLOAT(argv, indx)) + { + char str[80]; + sprintf(str, "%d", (int)atom_getintarg(indx, 100000, argv)); + return (gensym(str)); + } + else return (gensym("empty")); +} + +void iemgui_new_getnames(t_iemgui *iemgui, int indx, t_atom *argv) +{ + if (argv) + { + iemgui->x_snd = iemgui_new_dogetname(iemgui, indx, argv); + iemgui->x_rcv = iemgui_new_dogetname(iemgui, indx+1, argv); + iemgui->x_lab = iemgui_new_dogetname(iemgui, indx+2, argv); + } + else iemgui->x_snd = iemgui->x_rcv = iemgui->x_lab = gensym("empty"); + iemgui->x_snd_unexpanded = iemgui->x_rcv_unexpanded = + iemgui->x_lab_unexpanded = 0; + iemgui->x_binbufindex = indx; + iemgui->x_labelbindex = indx + 3; +} + + /* convert symbols in "$" form to the expanded symbols */ +void iemgui_all_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym) +{ + /* save unexpanded ones for later */ + iemgui->x_snd_unexpanded = srlsym[0]; + iemgui->x_rcv_unexpanded = srlsym[1]; + iemgui->x_lab_unexpanded = srlsym[2]; + srlsym[0] = canvas_realizedollar(iemgui->x_glist, srlsym[0]); + srlsym[1] = canvas_realizedollar(iemgui->x_glist, srlsym[1]); + srlsym[2] = canvas_realizedollar(iemgui->x_glist, srlsym[2]); +} + + /* initialize a single symbol in unexpanded form. We reach into the + binbuf to grab them; if there's nothing there, set it to the + fallback; if still nothing, set to "empty". */ +static void iemgui_init_sym2dollararg(t_iemgui *iemgui, t_symbol **symp, + int indx, t_symbol *fallback) +{ + if (!*symp) + { + t_binbuf *b = iemgui->x_obj.ob_binbuf; + if (binbuf_getnatom(b) > indx) + { + char buf[80]; + atom_string(binbuf_getvec(b) + indx, buf, 80); + *symp = gensym(buf); + } + else if (fallback) + *symp = fallback; + else *symp = gensym("empty"); + } +} + + /* get the unexpanded versions of the symbols; initialize them if + necessary. */ +void iemgui_all_sym2dollararg(t_iemgui *iemgui, t_symbol **srlsym) +{ + iemgui_init_sym2dollararg(iemgui, &iemgui->x_snd_unexpanded, + iemgui->x_binbufindex+1, iemgui->x_snd); + iemgui_init_sym2dollararg(iemgui, &iemgui->x_rcv_unexpanded, + iemgui->x_binbufindex+2, iemgui->x_rcv); + iemgui_init_sym2dollararg(iemgui, &iemgui->x_lab_unexpanded, + iemgui->x_labelbindex, iemgui->x_lab); + srlsym[0] = iemgui->x_snd_unexpanded; + srlsym[1] = iemgui->x_rcv_unexpanded; + srlsym[2] = iemgui->x_lab_unexpanded; +} + +void iemgui_first_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym) +{ + /* delete this function */ +} + +void iemgui_all_col2save(t_iemgui *iemgui, int *bflcol) +{ + bflcol[0] = -1 - (((0xfc0000 & iemgui->x_bcol) >> 6)| + ((0xfc00 & iemgui->x_bcol) >> 4)|((0xfc & iemgui->x_bcol) >> 2)); + bflcol[1] = -1 - (((0xfc0000 & iemgui->x_fcol) >> 6)| + ((0xfc00 & iemgui->x_fcol) >> 4)|((0xfc & iemgui->x_fcol) >> 2)); + bflcol[2] = -1 - (((0xfc0000 & iemgui->x_lcol) >> 6)| + ((0xfc00 & iemgui->x_lcol) >> 4)|((0xfc & iemgui->x_lcol) >> 2)); +} + +void iemgui_all_colfromload(t_iemgui *iemgui, int *bflcol) +{ + if(bflcol[0] < 0) + { + bflcol[0] = -1 - bflcol[0]; + iemgui->x_bcol = ((bflcol[0] & 0x3f000) << 6)|((bflcol[0] & 0xfc0) << 4)| + ((bflcol[0] & 0x3f) << 2); + } + else + { + bflcol[0] = iemgui_modulo_color(bflcol[0]); + iemgui->x_bcol = iemgui_color_hex[bflcol[0]]; + } + if(bflcol[1] < 0) + { + bflcol[1] = -1 - bflcol[1]; + iemgui->x_fcol = ((bflcol[1] & 0x3f000) << 6)|((bflcol[1] & 0xfc0) << 4)| + ((bflcol[1] & 0x3f) << 2); + } + else + { + bflcol[1] = iemgui_modulo_color(bflcol[1]); + iemgui->x_fcol = iemgui_color_hex[bflcol[1]]; + } + if(bflcol[2] < 0) + { + bflcol[2] = -1 - bflcol[2]; + iemgui->x_lcol = ((bflcol[2] & 0x3f000) << 6)|((bflcol[2] & 0xfc0) << 4)| + ((bflcol[2] & 0x3f) << 2); + } + else + { + bflcol[2] = iemgui_modulo_color(bflcol[2]); + iemgui->x_lcol = iemgui_color_hex[bflcol[2]]; + } +} + +int iemgui_compatible_col(int i) +{ + int j; + + if(i >= 0) + { + j = iemgui_modulo_color(i); + return(iemgui_color_hex[(j)]); + } + else + return((-1 -i)&0xffffff); +} + +void iemgui_all_dollar2raute(t_symbol **srlsym) +{ + srlsym[0] = iemgui_dollar2raute(srlsym[0]); + srlsym[1] = iemgui_dollar2raute(srlsym[1]); + srlsym[2] = iemgui_dollar2raute(srlsym[2]); +} + +void iemgui_all_raute2dollar(t_symbol **srlsym) +{ + srlsym[0] = iemgui_raute2dollar(srlsym[0]); + srlsym[1] = iemgui_raute2dollar(srlsym[1]); + srlsym[2] = iemgui_raute2dollar(srlsym[2]); +} + +void iemgui_send(void *x, t_iemgui *iemgui, t_symbol *s) +{ + t_symbol *snd; + int pargc, tail_len, nth_arg, sndable=1, oldsndrcvable=0; + t_atom *pargv; + + if(iemgui->x_fsf.x_rcv_able) + oldsndrcvable += IEM_GUI_OLD_RCV_FLAG; + if(iemgui->x_fsf.x_snd_able) + oldsndrcvable += IEM_GUI_OLD_SND_FLAG; + + if(!strcmp(s->s_name, "empty")) sndable = 0; + snd = iemgui_raute2dollar(s); + iemgui->x_snd_unexpanded = snd; + iemgui->x_snd = snd = canvas_realizedollar(iemgui->x_glist, snd); + post("send: before %s, after %s", iemgui->x_snd_unexpanded->s_name, + iemgui->x_snd->s_name); + + iemgui->x_fsf.x_snd_able = sndable; + iemgui_verify_snd_ne_rcv(iemgui); + (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_IO + oldsndrcvable); +} + +void iemgui_receive(void *x, t_iemgui *iemgui, t_symbol *s) +{ + t_symbol *rcv; + int pargc, tail_len, nth_arg, rcvable=1, oldsndrcvable=0; + t_atom *pargv; + + if(iemgui->x_fsf.x_rcv_able) + oldsndrcvable += IEM_GUI_OLD_RCV_FLAG; + if(iemgui->x_fsf.x_snd_able) + oldsndrcvable += IEM_GUI_OLD_SND_FLAG; + + if(!strcmp(s->s_name, "empty")) rcvable = 0; + rcv = iemgui_raute2dollar(s); + iemgui->x_rcv_unexpanded = rcv; + iemgui->x_rcv = rcv = canvas_realizedollar(iemgui->x_glist, rcv); + if(rcvable) + { + if(strcmp(rcv->s_name, iemgui->x_rcv->s_name)) + { + if(iemgui->x_fsf.x_rcv_able) + pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); + iemgui->x_rcv = rcv; + pd_bind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); + } + } + else if(!rcvable && iemgui->x_fsf.x_rcv_able) + { + pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); + iemgui->x_rcv = rcv; + } + iemgui->x_fsf.x_rcv_able = rcvable; + iemgui_verify_snd_ne_rcv(iemgui); + (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_IO + oldsndrcvable); +} + +void iemgui_label(void *x, t_iemgui *iemgui, t_symbol *s) +{ + t_symbol *lab; + int pargc, tail_len, nth_arg; + t_atom *pargv; + + lab = iemgui_raute2dollar(s); + iemgui->x_lab_unexpanded = lab; + iemgui->x_lab = lab = canvas_realizedollar(iemgui->x_glist, lab); + + if(glist_isvisible(iemgui->x_glist)) + sys_vgui(".x%x.c itemconfigure %xLABEL -text {%s} \n", + glist_getcanvas(iemgui->x_glist), x, + strcmp(s->s_name, "empty")?iemgui->x_lab->s_name:""); +} + +void iemgui_label_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) +{ + iemgui->x_ldx = (int)atom_getintarg(0, ac, av); + iemgui->x_ldy = (int)atom_getintarg(1, ac, av); + if(glist_isvisible(iemgui->x_glist)) + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + glist_getcanvas(iemgui->x_glist), x, + iemgui->x_obj.te_xpix+iemgui->x_ldx, + iemgui->x_obj.te_ypix+iemgui->x_ldy); +} + +void iemgui_label_font(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) +{ + int f = (int)atom_getintarg(0, ac, av); + + if(f == 1) strcpy(iemgui->x_font, "helvetica"); + else if(f == 2) strcpy(iemgui->x_font, "times"); + else + { + f = 0; + strcpy(iemgui->x_font, "courier"); + } + iemgui->x_fsf.x_font_style = f; + f = (int)atom_getintarg(1, ac, av); + if(f < 4) + f = 4; + iemgui->x_fontsize = f; + if(glist_isvisible(iemgui->x_glist)) + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold}\n", + glist_getcanvas(iemgui->x_glist), x, iemgui->x_font, iemgui->x_fontsize); +} + +void iemgui_size(void *x, t_iemgui *iemgui) +{ + if(glist_isvisible(iemgui->x_glist)) + { + (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(iemgui->x_glist), (t_text*)x); + } +} + +void iemgui_delta(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) +{ + iemgui->x_obj.te_xpix += (int)atom_getintarg(0, ac, av); + iemgui->x_obj.te_ypix += (int)atom_getintarg(1, ac, av); + if(glist_isvisible(iemgui->x_glist)) + { + (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(iemgui->x_glist), (t_text*)x); + } +} + +void iemgui_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) +{ + iemgui->x_obj.te_xpix = (int)atom_getintarg(0, ac, av); + iemgui->x_obj.te_ypix = (int)atom_getintarg(1, ac, av); + if(glist_isvisible(iemgui->x_glist)) + { + (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(iemgui->x_glist), (t_text*)x); + } +} + +void iemgui_color(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) +{ + iemgui->x_bcol = iemgui_compatible_col(atom_getintarg(0, ac, av)); + if(ac > 2) + { + iemgui->x_fcol = iemgui_compatible_col(atom_getintarg(1, ac, av)); + iemgui->x_lcol = iemgui_compatible_col(atom_getintarg(2, ac, av)); + } + else + iemgui->x_lcol = iemgui_compatible_col(atom_getintarg(1, ac, av)); + if(glist_isvisible(iemgui->x_glist)) + (*iemgui->x_draw)(x, iemgui->x_glist, IEM_GUI_DRAW_MODE_CONFIG); +} + +void iemgui_displace(t_gobj *z, t_glist *glist, int dx, int dy) +{ + t_iemguidummy *x = (t_iemguidummy *)z; + + x->x_gui.x_obj.te_xpix += dx; + x->x_gui.x_obj.te_ypix += dy; + (*x->x_gui.x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(glist), (t_text *)z); +} + +void iemgui_select(t_gobj *z, t_glist *glist, int selected) +{ + t_iemguidummy *x = (t_iemguidummy *)z; + + x->x_gui.x_fsf.x_selected = selected; + (*x->x_gui.x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_SELECT); +} + +void iemgui_delete(t_gobj *z, t_glist *glist) +{ + canvas_deletelinesfor(glist, (t_text *)z); +} + +void iemgui_vis(t_gobj *z, t_glist *glist, int vis) +{ + t_iemguidummy *x = (t_iemguidummy *)z; + + if (vis) + (*x->x_gui.x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_NEW); + else + (*x->x_gui.x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_ERASE); +} + +void iemgui_save(t_iemgui *iemgui, t_symbol **srl, int *bflcol) +{ + srl[0] = iemgui->x_snd; + srl[1] = iemgui->x_rcv; + srl[2] = iemgui->x_lab; + iemgui_all_sym2dollararg(iemgui, srl); + iemgui_all_col2save(iemgui, bflcol); +} + +void iemgui_properties(t_iemgui *iemgui, t_symbol **srl) +{ + srl[0] = iemgui->x_snd; + srl[1] = iemgui->x_rcv; + srl[2] = iemgui->x_lab; + iemgui_all_sym2dollararg(iemgui, srl); + iemgui_all_dollar2raute(srl); +} + +int iemgui_dialog(t_iemgui *iemgui, t_symbol **srl, int argc, t_atom *argv) +{ + char str[144]; + int init = (int)atom_getintarg(5, argc, argv); + int ldx = (int)atom_getintarg(10, argc, argv); + int ldy = (int)atom_getintarg(11, argc, argv); + int f = (int)atom_getintarg(12, argc, argv); + int fs = (int)atom_getintarg(13, argc, argv); + int bcol = (int)atom_getintarg(14, argc, argv); + int fcol = (int)atom_getintarg(15, argc, argv); + int lcol = (int)atom_getintarg(16, argc, argv); + int sndable=1, rcvable=1, oldsndrcvable=0; + + if(iemgui->x_fsf.x_rcv_able) + oldsndrcvable += IEM_GUI_OLD_RCV_FLAG; + if(iemgui->x_fsf.x_snd_able) + oldsndrcvable += IEM_GUI_OLD_SND_FLAG; + if(IS_A_SYMBOL(argv,7)) + srl[0] = atom_getsymbolarg(7, argc, argv); + else if(IS_A_FLOAT(argv,7)) + { + sprintf(str, "%d", (int)atom_getintarg(7, argc, argv)); + srl[0] = gensym(str); + } + if(IS_A_SYMBOL(argv,8)) + srl[1] = atom_getsymbolarg(8, argc, argv); + else if(IS_A_FLOAT(argv,8)) + { + sprintf(str, "%d", (int)atom_getintarg(8, argc, argv)); + srl[1] = gensym(str); + } + if(IS_A_SYMBOL(argv,9)) + srl[2] = atom_getsymbolarg(9, argc, argv); + else if(IS_A_FLOAT(argv,9)) + { + sprintf(str, "%d", (int)atom_getintarg(9, argc, argv)); + srl[2] = gensym(str); + } + if(init != 0) init = 1; + iemgui->x_isa.x_loadinit = init; + if(!strcmp(srl[0]->s_name, "empty")) sndable = 0; + if(!strcmp(srl[1]->s_name, "empty")) rcvable = 0; + iemgui_all_raute2dollar(srl); + iemgui_all_dollararg2sym(iemgui, srl); + if(rcvable) + { + if(strcmp(srl[1]->s_name, iemgui->x_rcv->s_name)) + { + if(iemgui->x_fsf.x_rcv_able) + pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); + iemgui->x_rcv = srl[1]; + pd_bind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); + } + } + else if(!rcvable && iemgui->x_fsf.x_rcv_able) + { + pd_unbind(&iemgui->x_obj.ob_pd, iemgui->x_rcv); + iemgui->x_rcv = srl[1]; + } + iemgui->x_snd = srl[0]; + iemgui->x_fsf.x_snd_able = sndable; + iemgui->x_fsf.x_rcv_able = rcvable; + iemgui->x_lcol = lcol & 0xffffff; + iemgui->x_fcol = fcol & 0xffffff; + iemgui->x_bcol = bcol & 0xffffff; + iemgui->x_lab = srl[2]; + iemgui->x_ldx = ldx; + iemgui->x_ldy = ldy; + if(f == 1) strcpy(iemgui->x_font, "helvetica"); + else if(f == 2) strcpy(iemgui->x_font, "times"); + else + { + f = 0; + strcpy(iemgui->x_font, "courier"); + } + iemgui->x_fsf.x_font_style = f; + if(fs < 4) + fs = 4; + iemgui->x_fontsize = fs; + iemgui_verify_snd_ne_rcv(iemgui); + return(oldsndrcvable); +} + +void iem_inttosymargs(t_iem_init_symargs *symargp, int n) +{ + memset(symargp, 0, sizeof(*symargp)); + symargp->x_loadinit = (n >> 0); + symargp->x_scale = (n >> 20); + symargp->x_flashed = 0; + symargp->x_locked = 0; + symargp->x_reverse = 0; + symargp->dummy = 0; +} + +int iem_symargstoint(t_iem_init_symargs *symargp) +{ + return ( + (((symargp->x_loadinit & 1) << 0) | + ((symargp->x_scale & 1) << 20))); +} + +void iem_inttofstyle(t_iem_fstyle_flags *fstylep, int n) +{ + memset(fstylep, 0, sizeof(*fstylep)); + fstylep->x_font_style = (n >> 0); + fstylep->x_shiftdown = 0; + fstylep->x_selected = 0; + fstylep->x_finemoved = 0; + fstylep->x_put_in2out = 0; + fstylep->x_change = 0; + fstylep->x_thick = 0; + fstylep->x_lin0_log1 = 0; + fstylep->x_steady = 0; + fstylep->dummy = 0; +} + +int iem_fstyletoint(t_iem_fstyle_flags *fstylep) +{ + return ((fstylep->x_font_style << 0) & 63); +} diff --git a/apps/plugins/pdbox/PDa/src/g_all_guis.h b/apps/plugins/pdbox/PDa/src/g_all_guis.h new file mode 100644 index 0000000..6d03ee9 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_all_guis.h @@ -0,0 +1,658 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ +/* g_7_guis.h written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ + + +#define IEM_GUI_COLNR_WHITE 0 +#define IEM_GUI_COLNR_ML_GREY 1 +#define IEM_GUI_COLNR_D_GREY 2 +#define IEM_GUI_COLNR_L_RED 3 +#define IEM_GUI_COLNR_L_ORANGE 4 +#define IEM_GUI_COLNR_L_YELLOW 5 +#define IEM_GUI_COLNR_L_GREEN 6 +#define IEM_GUI_COLNR_L_CYAN 7 +#define IEM_GUI_COLNR_L_BLUE 8 +#define IEM_GUI_COLNR_L_MAGENTA 9 + +#define IEM_GUI_COLNR_LL_GREY 10 +#define IEM_GUI_COLNR_M_GREY 11 +#define IEM_GUI_COLNR_DD_GREY 12 +#define IEM_GUI_COLNR_RED 13 +#define IEM_GUI_COLNR_ORANGE 14 +#define IEM_GUI_COLNR_YELLOW 15 +#define IEM_GUI_COLNR_GREEN 16 +#define IEM_GUI_COLNR_CYAN 17 +#define IEM_GUI_COLNR_BLUE 18 +#define IEM_GUI_COLNR_MAGENTA 19 + +#define IEM_GUI_COLNR_L_GREY 20 +#define IEM_GUI_COLNR_MD_GREY 21 +#define IEM_GUI_COLNR_BLACK 22 +#define IEM_GUI_COLNR_D_RED 23 +#define IEM_GUI_COLNR_D_ORANGE 24 +#define IEM_GUI_COLNR_D_YELLOW 25 +#define IEM_GUI_COLNR_D_GREEN 26 +#define IEM_GUI_COLNR_D_CYAN 27 +#define IEM_GUI_COLNR_D_BLUE 28 +#define IEM_GUI_COLNR_D_MAGENTA 29 + +#define IEM_GUI_COLOR_SELECTED 255 +#define IEM_GUI_COLOR_NORMAL 0 + +#define IEM_GUI_MAX_COLOR 30 + +#define IEM_GUI_DEFAULTSIZE 15 +#define IEM_GUI_MINSIZE 8 +#define IEM_GUI_MAXSIZE 1000 +#define IEM_SL_DEFAULTSIZE 128 +#define IEM_SL_MINSIZE 2 +#define IEM_FONT_MINSIZE 4 + +#define IEM_BNG_DEFAULTHOLDFLASHTIME 250 +#define IEM_BNG_DEFAULTBREAKFLASHTIME 50 +#define IEM_BNG_MINHOLDFLASHTIME 50 +#define IEM_BNG_MINBREAKFLASHTIME 10 + +#define IEM_VU_DEFAULTSIZE 3 +#define IEM_VU_LARGESMALL 2 +#define IEM_VU_MINSIZE 2 +#define IEM_VU_MAXSIZE 25 +#define IEM_VU_STEPS 40 + +#define IEM_VU_MINDB -99.9 +#define IEM_VU_MAXDB 12.0 +#define IEM_VU_OFFSET 100.0 + +#define IEM_RADIO_MAX 128 + +#define IEM_SYM_UNIQUE_SND 256 +#define IEM_SYM_UNIQUE_RCV 512 +#define IEM_SYM_UNIQUE_LAB 1024 +#define IEM_SYM_UNIQUE_ALL 1792 +#define IEM_FONT_STYLE_ALL 255 + +#define IEM_MAX_SYM_LEN 127 + +#define IEM_GUI_DRAW_MODE_UPDATE 0 +#define IEM_GUI_DRAW_MODE_MOVE 1 +#define IEM_GUI_DRAW_MODE_NEW 2 +#define IEM_GUI_DRAW_MODE_SELECT 3 +#define IEM_GUI_DRAW_MODE_ERASE 4 +#define IEM_GUI_DRAW_MODE_CONFIG 5 +#define IEM_GUI_DRAW_MODE_IO 6 + + +#define IS_A_POINTER(atom,index) ((atom+index)->a_type == A_POINTER) +#define IS_A_FLOAT(atom,index) ((atom+index)->a_type == A_FLOAT) +#define IS_A_SYMBOL(atom,index) ((atom+index)->a_type == A_SYMBOL) +#define IS_A_DOLLAR(atom,index) ((atom+index)->a_type == A_DOLLAR) +#define IS_A_DOLLSYM(atom,index) ((atom+index)->a_type == A_DOLLSYM) + +#define IEM_FSTYLE_FLAGS_ALL 0x007fffff +#define IEM_INIT_ARGS_ALL 0x01ffffff + +#define IEM_GUI_OLD_SND_FLAG 1 +#define IEM_GUI_OLD_RCV_FLAG 2 + +#define IEM_GUI_COLOR_EDITED 16711680 +#define IEMGUI_MAX_NUM_LEN 32 + +typedef struct _iem_fstyle_flags +{ + unsigned int x_font_style:6; + unsigned int x_rcv_able:1; + unsigned int x_snd_able:1; + unsigned int x_lab_is_unique:1; + unsigned int x_rcv_is_unique:1; + unsigned int x_snd_is_unique:1; + unsigned int x_lab_arg_tail_len:6; + unsigned int x_lab_is_arg_num:6; + unsigned int x_shiftdown:1; + unsigned int x_selected:1; + unsigned int x_finemoved:1; + unsigned int x_put_in2out:1; + unsigned int x_change:1; + unsigned int x_thick:1; + unsigned int x_lin0_log1:1; + unsigned int x_steady:1; + unsigned int dummy:1; +} t_iem_fstyle_flags; + +typedef struct _iem_init_symargs +{ + unsigned int x_loadinit:1; + unsigned int x_rcv_arg_tail_len:6; + unsigned int x_snd_arg_tail_len:6; + unsigned int x_rcv_is_arg_num:6; + unsigned int x_snd_is_arg_num:6; + unsigned int x_scale:1; + unsigned int x_flashed:1; + unsigned int x_locked:1; + unsigned int x_reverse:1; /* bugfix */ + unsigned int dummy:3; +} t_iem_init_symargs; + +typedef void (*t_iemfunptr)(void *x, t_glist *glist, int mode); + +typedef struct _iemgui +{ + t_object x_obj; + t_glist *x_glist; + t_iemfunptr x_draw; + int x_h; + int x_w; + int x_ldx; + int x_ldy; + char x_font[16]; + t_iem_fstyle_flags x_fsf; + int x_fontsize; + t_iem_init_symargs x_isa; + int x_fcol; + int x_bcol; + int x_lcol; + t_symbol *x_snd; /* send symbol */ + t_symbol *x_rcv; /* receive */ + t_symbol *x_lab; /* label */ + t_symbol *x_snd_unexpanded; /* same 3, with '$' unexpanded */ + t_symbol *x_rcv_unexpanded; + t_symbol *x_lab_unexpanded; + int x_binbufindex; /* where in binbuf to find these */ + int x_labelbindex; /* where in binbuf to find label */ +} t_iemgui; + +typedef struct _iemguidummy +{ + t_iemgui x_gui; + int x_dum1; + int x_dum2; + int x_dum3; +} t_iemguidummy; + +typedef struct _bng +{ + t_iemgui x_gui; + int x_flashed; + int x_flashtime_break; + int x_flashtime_hold; + t_clock *x_clock_hld; + t_clock *x_clock_brk; + t_clock *x_clock_lck; +} t_bng; + +typedef struct _hslider +{ + t_iemgui x_gui; + int x_pos; + int x_val; + int x_center; + int x_thick; + int x_lin0_log1; + int x_steady; + double x_min; + double x_max; + double x_k; +} t_hslider; + +typedef struct _hdial +{ + t_iemgui x_gui; + int x_on; + int x_on_old; + int x_change; + int x_number; + t_atom x_at[2]; +} t_hdial; + +typedef struct _toggle +{ + t_iemgui x_gui; + float x_on; + float x_nonzero; +} t_toggle; + +typedef struct _my_canvas +{ + t_iemgui x_gui; + t_atom x_at[3]; + int x_vis_w; + int x_vis_h; +} t_my_canvas; + +typedef struct _vslider +{ + t_iemgui x_gui; + int x_pos; + int x_val; + int x_lin0_log1; + int x_steady; + double x_min; + double x_max; + double x_k; +} t_vslider; + +typedef struct _vu +{ + t_iemgui x_gui; + int x_led_size; + int x_peak; + int x_rms; + float x_fp; + float x_fr; + int x_scale; + void *x_out_rms; + void *x_out_peak; +} t_vu; + +typedef struct _my_numbox +{ + t_iemgui x_gui; + t_clock *x_clock_reset; + t_clock *x_clock_wait; + double x_val; + double x_min; + double x_max; + double x_k; + int x_lin0_log1; + char x_buf[IEMGUI_MAX_NUM_LEN]; + int x_numwidth; + int x_log_height; +} t_my_numbox; + +typedef struct _vdial +{ + t_iemgui x_gui; + int x_on; + int x_on_old; + int x_change; + int x_number; + t_atom x_at[2]; +} t_vdial; + +#define t_vradio t_vdial +#define t_hradio t_hdial + +extern int sys_noloadbang; +extern int iemgui_color_hex[]; +extern int iemgui_vu_db2i[]; +extern int iemgui_vu_col[]; +extern char *iemgui_vu_scale_str[]; + +EXTERN int iemgui_clip_size(int size); +EXTERN int iemgui_clip_font(int size); +EXTERN int iemgui_modulo_color(int col); +EXTERN t_symbol *iemgui_unique2dollarzero(t_symbol *s, int unique_num, int and_unique_flag); +EXTERN t_symbol *iemgui_sym2dollararg(t_symbol *s, int nth_arg, int tail_len); +EXTERN t_symbol *iemgui_dollarzero2unique(t_symbol *s, int unique_num); +EXTERN t_symbol *iemgui_dollararg2sym(t_symbol *s, int nth_arg, int tail_len, int pargc, t_atom *pargv); +EXTERN int iemgui_is_dollarzero(t_symbol *s); +EXTERN int iemgui_is_dollararg(t_symbol *s, int *tail_len); +EXTERN void iemgui_fetch_unique(t_iemgui *iemgui); +EXTERN void iemgui_fetch_parent_args(t_iemgui *iemgui, int *pargc, t_atom **pargv); +EXTERN void iemgui_verify_snd_ne_rcv(t_iemgui *iemgui); +EXTERN void iemgui_all_unique2dollarzero(t_iemgui *iemgui, t_symbol **srlsym); +EXTERN void iemgui_all_sym2dollararg(t_iemgui *iemgui, t_symbol **srlsym); +EXTERN void iemgui_all_dollarzero2unique(t_iemgui *iemgui, t_symbol **srlsym); +EXTERN t_symbol *iemgui_new_dogetname(t_iemgui *iemgui, int indx, t_atom *argv); +EXTERN void iemgui_new_getnames(t_iemgui *iemgui, int indx, t_atom *argv); +EXTERN void iemgui_all_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym); +EXTERN void iemgui_first_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym); +EXTERN void iemgui_all_col2save(t_iemgui *iemgui, int *bflcol); +EXTERN void iemgui_all_colfromload(t_iemgui *iemgui, int *bflcol); +EXTERN int iemgui_compatible_col(int i); +EXTERN void iemgui_all_dollar2raute(t_symbol **srlsym); +EXTERN void iemgui_all_raute2dollar(t_symbol **srlsym); +EXTERN void iemgui_send(void *x, t_iemgui *iemgui, t_symbol *s); +EXTERN void iemgui_receive(void *x, t_iemgui *iemgui, t_symbol *s); +EXTERN void iemgui_label(void *x, t_iemgui *iemgui, t_symbol *s); +EXTERN void iemgui_label_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); +EXTERN void iemgui_label_font(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); +EXTERN void iemgui_size(void *x, t_iemgui *iemgui); +EXTERN void iemgui_delta(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); +EXTERN void iemgui_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); +EXTERN void iemgui_color(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); +EXTERN int iemgui_list(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); +EXTERN void iemgui_displace(t_gobj *z, t_glist *glist, int dx, int dy); +EXTERN void iemgui_select(t_gobj *z, t_glist *glist, int selected); +EXTERN void iemgui_delete(t_gobj *z, t_glist *glist); +EXTERN void iemgui_vis(t_gobj *z, t_glist *glist, int vis); +EXTERN void iemgui_save(t_iemgui *iemgui, t_symbol **srl, int *bflcol); +EXTERN void iemgui_properties(t_iemgui *iemgui, t_symbol **srl); +EXTERN int iemgui_dialog(t_iemgui *iemgui, t_symbol **srl, int argc, t_atom *argv); + +EXTERN int canvas_getdollarzero(void); +EXTERN void canvas_getargs(int *argcp, t_atom **argvp); + +EXTERN void iem_inttosymargs(t_iem_init_symargs *symargp, int n); +EXTERN int iem_symargstoint(t_iem_init_symargs *symargp); +EXTERN void iem_inttofstyle(t_iem_fstyle_flags *fstylep, int n); +EXTERN int iem_fstyletoint(t_iem_fstyle_flags *fstylep); +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ +/* g_7_guis.h written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ + + +#define IEM_GUI_COLNR_WHITE 0 +#define IEM_GUI_COLNR_ML_GREY 1 +#define IEM_GUI_COLNR_D_GREY 2 +#define IEM_GUI_COLNR_L_RED 3 +#define IEM_GUI_COLNR_L_ORANGE 4 +#define IEM_GUI_COLNR_L_YELLOW 5 +#define IEM_GUI_COLNR_L_GREEN 6 +#define IEM_GUI_COLNR_L_CYAN 7 +#define IEM_GUI_COLNR_L_BLUE 8 +#define IEM_GUI_COLNR_L_MAGENTA 9 + +#define IEM_GUI_COLNR_LL_GREY 10 +#define IEM_GUI_COLNR_M_GREY 11 +#define IEM_GUI_COLNR_DD_GREY 12 +#define IEM_GUI_COLNR_RED 13 +#define IEM_GUI_COLNR_ORANGE 14 +#define IEM_GUI_COLNR_YELLOW 15 +#define IEM_GUI_COLNR_GREEN 16 +#define IEM_GUI_COLNR_CYAN 17 +#define IEM_GUI_COLNR_BLUE 18 +#define IEM_GUI_COLNR_MAGENTA 19 + +#define IEM_GUI_COLNR_L_GREY 20 +#define IEM_GUI_COLNR_MD_GREY 21 +#define IEM_GUI_COLNR_BLACK 22 +#define IEM_GUI_COLNR_D_RED 23 +#define IEM_GUI_COLNR_D_ORANGE 24 +#define IEM_GUI_COLNR_D_YELLOW 25 +#define IEM_GUI_COLNR_D_GREEN 26 +#define IEM_GUI_COLNR_D_CYAN 27 +#define IEM_GUI_COLNR_D_BLUE 28 +#define IEM_GUI_COLNR_D_MAGENTA 29 + +#define IEM_GUI_COLOR_SELECTED 255 +#define IEM_GUI_COLOR_NORMAL 0 + +#define IEM_GUI_MAX_COLOR 30 + +#define IEM_GUI_DEFAULTSIZE 15 +#define IEM_GUI_MINSIZE 8 +#define IEM_GUI_MAXSIZE 1000 +#define IEM_SL_DEFAULTSIZE 128 +#define IEM_SL_MINSIZE 2 +#define IEM_FONT_MINSIZE 4 + +#define IEM_BNG_DEFAULTHOLDFLASHTIME 250 +#define IEM_BNG_DEFAULTBREAKFLASHTIME 50 +#define IEM_BNG_MINHOLDFLASHTIME 50 +#define IEM_BNG_MINBREAKFLASHTIME 10 + +#define IEM_VU_DEFAULTSIZE 3 +#define IEM_VU_LARGESMALL 2 +#define IEM_VU_MINSIZE 2 +#define IEM_VU_MAXSIZE 25 +#define IEM_VU_STEPS 40 + +#define IEM_VU_MINDB -99.9 +#define IEM_VU_MAXDB 12.0 +#define IEM_VU_OFFSET 100.0 + +#define IEM_RADIO_MAX 128 + +#define IEM_SYM_UNIQUE_SND 256 +#define IEM_SYM_UNIQUE_RCV 512 +#define IEM_SYM_UNIQUE_LAB 1024 +#define IEM_SYM_UNIQUE_ALL 1792 +#define IEM_FONT_STYLE_ALL 255 + +#define IEM_MAX_SYM_LEN 127 + +#define IEM_GUI_DRAW_MODE_UPDATE 0 +#define IEM_GUI_DRAW_MODE_MOVE 1 +#define IEM_GUI_DRAW_MODE_NEW 2 +#define IEM_GUI_DRAW_MODE_SELECT 3 +#define IEM_GUI_DRAW_MODE_ERASE 4 +#define IEM_GUI_DRAW_MODE_CONFIG 5 +#define IEM_GUI_DRAW_MODE_IO 6 + + +#define IS_A_POINTER(atom,index) ((atom+index)->a_type == A_POINTER) +#define IS_A_FLOAT(atom,index) ((atom+index)->a_type == A_FLOAT) +#define IS_A_SYMBOL(atom,index) ((atom+index)->a_type == A_SYMBOL) +#define IS_A_DOLLAR(atom,index) ((atom+index)->a_type == A_DOLLAR) +#define IS_A_DOLLSYM(atom,index) ((atom+index)->a_type == A_DOLLSYM) + +#define IEM_FSTYLE_FLAGS_ALL 0x007fffff +#define IEM_INIT_ARGS_ALL 0x01ffffff + +#define IEM_GUI_OLD_SND_FLAG 1 +#define IEM_GUI_OLD_RCV_FLAG 2 + +#define IEM_GUI_COLOR_EDITED 16711680 +#define IEMGUI_MAX_NUM_LEN 32 + +typedef struct _iem_fstyle_flags +{ + unsigned int x_font_style:6; + unsigned int x_rcv_able:1; + unsigned int x_snd_able:1; + unsigned int x_lab_is_unique:1; + unsigned int x_rcv_is_unique:1; + unsigned int x_snd_is_unique:1; + unsigned int x_lab_arg_tail_len:6; + unsigned int x_lab_is_arg_num:6; + unsigned int x_shiftdown:1; + unsigned int x_selected:1; + unsigned int x_finemoved:1; + unsigned int x_put_in2out:1; + unsigned int x_change:1; + unsigned int x_thick:1; + unsigned int x_lin0_log1:1; + unsigned int x_steady:1; + unsigned int dummy:1; +} t_iem_fstyle_flags; + +typedef struct _iem_init_symargs +{ + unsigned int x_loadinit:1; + unsigned int x_rcv_arg_tail_len:6; + unsigned int x_snd_arg_tail_len:6; + unsigned int x_rcv_is_arg_num:6; + unsigned int x_snd_is_arg_num:6; + unsigned int x_scale:1; + unsigned int x_flashed:1; + unsigned int x_locked:1; + unsigned int x_reverse:1; /* bugfix */ + unsigned int dummy:3; +} t_iem_init_symargs; + +typedef void (*t_iemfunptr)(void *x, t_glist *glist, int mode); + +typedef struct _iemgui +{ + t_object x_obj; + t_glist *x_glist; + t_iemfunptr x_draw; + int x_h; + int x_w; + int x_ldx; + int x_ldy; + char x_font[16]; + t_iem_fstyle_flags x_fsf; + int x_fontsize; + t_iem_init_symargs x_isa; + int x_fcol; + int x_bcol; + int x_lcol; + t_symbol *x_snd; /* send symbol */ + t_symbol *x_rcv; /* receive */ + t_symbol *x_lab; /* label */ + t_symbol *x_snd_unexpanded; /* same 3, with '$' unexpanded */ + t_symbol *x_rcv_unexpanded; + t_symbol *x_lab_unexpanded; + int x_binbufindex; /* where in binbuf to find these */ + int x_labelbindex; /* where in binbuf to find label */ +} t_iemgui; + +typedef struct _iemguidummy +{ + t_iemgui x_gui; + int x_dum1; + int x_dum2; + int x_dum3; +} t_iemguidummy; + +typedef struct _bng +{ + t_iemgui x_gui; + int x_flashed; + int x_flashtime_break; + int x_flashtime_hold; + t_clock *x_clock_hld; + t_clock *x_clock_brk; + t_clock *x_clock_lck; +} t_bng; + +typedef struct _hslider +{ + t_iemgui x_gui; + int x_pos; + int x_val; + int x_center; + int x_thick; + int x_lin0_log1; + int x_steady; + double x_min; + double x_max; + double x_k; +} t_hslider; + +typedef struct _hdial +{ + t_iemgui x_gui; + int x_on; + int x_on_old; + int x_change; + int x_number; + t_atom x_at[2]; +} t_hdial; + +typedef struct _toggle +{ + t_iemgui x_gui; + float x_on; + float x_nonzero; +} t_toggle; + +typedef struct _my_canvas +{ + t_iemgui x_gui; + t_atom x_at[3]; + int x_vis_w; + int x_vis_h; +} t_my_canvas; + +typedef struct _vslider +{ + t_iemgui x_gui; + int x_pos; + int x_val; + int x_lin0_log1; + int x_steady; + double x_min; + double x_max; + double x_k; +} t_vslider; + +typedef struct _vu +{ + t_iemgui x_gui; + int x_led_size; + int x_peak; + int x_rms; + float x_fp; + float x_fr; + int x_scale; + void *x_out_rms; + void *x_out_peak; +} t_vu; + +typedef struct _my_numbox +{ + t_iemgui x_gui; + t_clock *x_clock_reset; + t_clock *x_clock_wait; + double x_val; + double x_min; + double x_max; + double x_k; + int x_lin0_log1; + char x_buf[IEMGUI_MAX_NUM_LEN]; + int x_numwidth; + int x_log_height; +} t_my_numbox; + +typedef struct _vdial +{ + t_iemgui x_gui; + int x_on; + int x_on_old; + int x_change; + int x_number; + t_atom x_at[2]; +} t_vdial; + +#define t_vradio t_vdial +#define t_hradio t_hdial + +extern int sys_noloadbang; +extern int iemgui_color_hex[]; +extern int iemgui_vu_db2i[]; +extern int iemgui_vu_col[]; +extern char *iemgui_vu_scale_str[]; + +EXTERN int iemgui_clip_size(int size); +EXTERN int iemgui_clip_font(int size); +EXTERN int iemgui_modulo_color(int col); +EXTERN t_symbol *iemgui_unique2dollarzero(t_symbol *s, int unique_num, int and_unique_flag); +EXTERN t_symbol *iemgui_sym2dollararg(t_symbol *s, int nth_arg, int tail_len); +EXTERN t_symbol *iemgui_dollarzero2unique(t_symbol *s, int unique_num); +EXTERN t_symbol *iemgui_dollararg2sym(t_symbol *s, int nth_arg, int tail_len, int pargc, t_atom *pargv); +EXTERN int iemgui_is_dollarzero(t_symbol *s); +EXTERN int iemgui_is_dollararg(t_symbol *s, int *tail_len); +EXTERN void iemgui_fetch_unique(t_iemgui *iemgui); +EXTERN void iemgui_fetch_parent_args(t_iemgui *iemgui, int *pargc, t_atom **pargv); +EXTERN void iemgui_verify_snd_ne_rcv(t_iemgui *iemgui); +EXTERN void iemgui_all_unique2dollarzero(t_iemgui *iemgui, t_symbol **srlsym); +EXTERN void iemgui_all_sym2dollararg(t_iemgui *iemgui, t_symbol **srlsym); +EXTERN void iemgui_all_dollarzero2unique(t_iemgui *iemgui, t_symbol **srlsym); +EXTERN t_symbol *iemgui_new_dogetname(t_iemgui *iemgui, int indx, t_atom *argv); +EXTERN void iemgui_new_getnames(t_iemgui *iemgui, int indx, t_atom *argv); +EXTERN void iemgui_all_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym); +EXTERN void iemgui_first_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym); +EXTERN void iemgui_all_col2save(t_iemgui *iemgui, int *bflcol); +EXTERN void iemgui_all_colfromload(t_iemgui *iemgui, int *bflcol); +EXTERN int iemgui_compatible_col(int i); +EXTERN void iemgui_all_dollar2raute(t_symbol **srlsym); +EXTERN void iemgui_all_raute2dollar(t_symbol **srlsym); +EXTERN void iemgui_send(void *x, t_iemgui *iemgui, t_symbol *s); +EXTERN void iemgui_receive(void *x, t_iemgui *iemgui, t_symbol *s); +EXTERN void iemgui_label(void *x, t_iemgui *iemgui, t_symbol *s); +EXTERN void iemgui_label_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); +EXTERN void iemgui_label_font(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); +EXTERN void iemgui_size(void *x, t_iemgui *iemgui); +EXTERN void iemgui_delta(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); +EXTERN void iemgui_pos(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); +EXTERN void iemgui_color(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); +EXTERN int iemgui_list(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av); +EXTERN void iemgui_displace(t_gobj *z, t_glist *glist, int dx, int dy); +EXTERN void iemgui_select(t_gobj *z, t_glist *glist, int selected); +EXTERN void iemgui_delete(t_gobj *z, t_glist *glist); +EXTERN void iemgui_vis(t_gobj *z, t_glist *glist, int vis); +EXTERN void iemgui_save(t_iemgui *iemgui, t_symbol **srl, int *bflcol); +EXTERN void iemgui_properties(t_iemgui *iemgui, t_symbol **srl); +EXTERN int iemgui_dialog(t_iemgui *iemgui, t_symbol **srl, int argc, t_atom *argv); + +EXTERN int canvas_getdollarzero(void); +EXTERN void canvas_getargs(int *argcp, t_atom **argvp); + +EXTERN void iem_inttosymargs(t_iem_init_symargs *symargp, int n); +EXTERN int iem_symargstoint(t_iem_init_symargs *symargp); +EXTERN void iem_inttofstyle(t_iem_fstyle_flags *fstylep, int n); +EXTERN int iem_fstyletoint(t_iem_fstyle_flags *fstylep); diff --git a/apps/plugins/pdbox/PDa/src/g_array.c b/apps/plugins/pdbox/PDa/src/g_array.c new file mode 100644 index 0000000..f259ac8 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_array.c @@ -0,0 +1,2734 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include +#include +#include /* for read/write to files */ +#include "m_pd.h" +#include "g_canvas.h" +#include + +/* see also the "plot" object in g_scalar.c which deals with graphing +arrays which are fields in scalars. Someday we should unify the +two, but how? */ + + /* aux routine to bash leading '#' to '$' for dialogs in u_main.tk + which can't send symbols starting with '$' (because the Pd message + interpreter would change them!) */ + +static t_symbol *sharptodollar(t_symbol *s) +{ + if (*s->s_name == '#') + { + char buf[MAXPDSTRING]; + strncpy(buf, s->s_name, MAXPDSTRING); + buf[MAXPDSTRING-1] = 0; + buf[0] = '$'; + return (gensym(buf)); + } + else return (s); +} + +/* --------- "pure" arrays with scalars for elements. --------------- */ + +/* Pure arrays have no a priori graphical capabilities. +They are instantiated by "garrays" below or can be elements of other +scalars (g_scalar.c); their graphical behavior is defined accordingly. */ + +t_array *array_new(t_symbol *templatesym, t_gpointer *parent) +{ + t_array *x = (t_array *)getbytes(sizeof (*x)); + t_template *template; + t_gpointer *gp; + template = template_findbyname(templatesym); + x->a_templatesym = templatesym; + x->a_n = 1; + x->a_elemsize = sizeof(t_word) * template->t_n; + x->a_vec = (char *)getbytes(x->a_elemsize); + /* note here we blithely copy a gpointer instead of "setting" a + new one; this gpointer isn't accounted for and needn't be since + we'll be deleted before the thing pointed to gets deleted anyway; + see array_free. */ + x->a_gp = *parent; + x->a_stub = gstub_new(0, x); + word_init((t_word *)(x->a_vec), template, parent); + return (x); +} + +void array_resize(t_array *x, t_template *template, int n) +{ + int elemsize, oldn; + t_gpointer *gp; + + if (n < 1) + n = 1; + oldn = x->a_n; + elemsize = sizeof(t_word) * template->t_n; + + x->a_vec = (char *)resizebytes(x->a_vec, oldn * elemsize, + n * elemsize); + x->a_n = n; + if (n > oldn) + { + char *cp = x->a_vec + elemsize * oldn; + int i = n - oldn; + for (; i--; cp += elemsize) + { + t_word *wp = (t_word *)cp; + word_init(wp, template, &x->a_gp); + } + } +} + +void word_free(t_word *wp, t_template *template); + +void array_free(t_array *x) +{ + int i; + t_template *scalartemplate = template_findbyname(x->a_templatesym); + /* we don't unset our gpointer here since it was never "set." */ + /* gpointer_unset(&x->a_gp); */ + gstub_cutoff(x->a_stub); + for (i = 0; i < x->a_n; i++) + { + t_word *wp = (t_word *)(x->a_vec + x->a_elemsize * i); + word_free(wp, scalartemplate); + } + freebytes(x->a_vec, x->a_elemsize * x->a_n); + freebytes(x, sizeof *x); +} + +/* --------------------- graphical arrays (garrays) ------------------- */ + +t_class *garray_class; +static int gcount = 0; + +struct _garray +{ + t_gobj x_gobj; + t_glist *x_glist; + t_array x_array; /* actual array; note only 4 fields used as below */ + t_symbol *x_name; + t_symbol *x_realname; /* name with "$" expanded */ + t_float x_firstx; /* X value of first item */ + t_float x_xinc; /* X increment */ + char x_usedindsp; /* true if some DSP routine is using this */ + char x_saveit; /* true if we should save this with parent */ +}; + + /* macros to get into the "array" structure */ +#define x_n x_array.a_n +#define x_elemsize x_array.a_elemsize +#define x_vec x_array.a_vec +#define x_templatesym x_array.a_templatesym + +t_garray *graph_array(t_glist *gl, t_symbol *s, t_symbol *templatesym, + t_floatarg f, t_floatarg saveit) +{ + int n = f, i; + int zz, nwords; + t_garray *x; + t_pd *x2; + t_template *template; + char *str; + if (s == &s_) + { + char buf[40]; + sprintf(buf, "array%d", ++gcount); + s = gensym(buf); + templatesym = &s_float; + n = 100; + } + else if (!strncmp((str = s->s_name), "array", 5) + && (zz = atoi(str + 5)) > gcount) gcount = zz; + template = template_findbyname(templatesym); + if (!template) + { + error("array: couldn't find template %s", templatesym->s_name); + return (0); + } + nwords = template->t_n; + for (i = 0; i < nwords; i++) + { + /* we can't have array or list elements yet because what scalar + can act as their "parent"??? */ + if (template->t_vec[i].ds_type == DT_ARRAY + || template->t_vec[i].ds_type == DT_LIST) + { + error("array: template %s can't have sublists or arrays", + templatesym->s_name); + return (0); + } + } + x = (t_garray *)pd_new(garray_class); + + if (n <= 0) n = 100; + x->x_n = n; + x->x_elemsize = nwords * sizeof(t_word); + x->x_vec = getbytes(x->x_n * x->x_elemsize); + memset(x->x_vec, 0, x->x_n * x->x_elemsize); + /* LATER should check that malloc */ + x->x_name = s; + x->x_realname = canvas_realizedollar(gl, s); + pd_bind(&x->x_gobj.g_pd, x->x_realname); + x->x_templatesym = templatesym; + x->x_firstx = 0; + x->x_xinc = 1; /* LATER make methods to set this... */ + glist_add(gl, &x->x_gobj); + x->x_glist = gl; + x->x_usedindsp = 0; + x->x_saveit = (saveit != 0); + if (x2 = pd_findbyclass(gensym("#A"), garray_class)) + pd_unbind(x2, gensym("#A")); + + pd_bind(&x->x_gobj.g_pd, gensym("#A")); + + return (x); +} + + /* called from array menu item to create a new one */ +void canvas_menuarray(t_glist *canvas) +{ + t_glist *x = (t_glist *)canvas; + char cmdbuf[200]; + sprintf(cmdbuf, "pdtk_array_dialog %%s array%d 100 1 1\n", + ++gcount); + gfxstub_new(&x->gl_pd, x, cmdbuf); +} + + /* called from graph_dialog to set properties */ +void garray_properties(t_garray *x) +{ + char cmdbuf[200]; + gfxstub_deleteforkey(x); + /* create dialog window. LATER fix this to escape '$' + properly; right now we just detect a leading '$' and escape + it. There should be a systematic way of doing this. */ + if (x->x_name->s_name[0] == '$') + sprintf(cmdbuf, "pdtk_array_dialog %%s \\%s %d %d 0\n", + x->x_name->s_name, x->x_n, x->x_saveit); + else sprintf(cmdbuf, "pdtk_array_dialog %%s %s %d %d 0\n", + x->x_name->s_name, x->x_n, x->x_saveit); + gfxstub_new(&x->x_gobj.g_pd, x, cmdbuf); +} + + /* this is called back from the dialog window to create a garray. + The otherflag requests that we find an existing graph to put it in. */ +void glist_arraydialog(t_glist *parent, t_symbol *name, t_floatarg size, + t_floatarg saveit, t_floatarg otherflag) +{ + t_glist *gl; + t_garray *a; + if (size < 1) + size = 1; + if (otherflag == 0 || (!(gl = glist_findgraph(parent)))) + gl = glist_addglist(parent, &s_, 0, 1, + (size > 1 ? size-1 : size), -1, 0, 0, 0, 0); + a = graph_array(gl, sharptodollar(name), &s_float, size, saveit); +} + + /* this is called from the properties dialog window for an existing array */ +void garray_arraydialog(t_garray *x, t_symbol *name, t_floatarg fsize, + t_floatarg saveit, t_floatarg deleteit) +{ + if (deleteit != 0) + { + glist_delete(x->x_glist, &x->x_gobj); + } + else + { + int size; + t_symbol *argname = sharptodollar(name); + if (argname != x->x_name) + { + x->x_name = argname; + pd_unbind(&x->x_gobj.g_pd, x->x_realname); + x->x_realname = canvas_realizedollar(x->x_glist, argname); + pd_bind(&x->x_gobj.g_pd, x->x_realname); + } + size = fsize; + if (size < 1) + size = 1; + if (size != x->x_n) + garray_resize(x, size); + garray_setsaveit(x, (saveit != 0)); + garray_redraw(x); + } +} + +static void garray_free(t_garray *x) +{ + t_pd *x2; + gfxstub_deleteforkey(x); + pd_unbind(&x->x_gobj.g_pd, x->x_realname); + /* LATER find a way to get #A unbound earlier (at end of load?) */ + while (x2 = pd_findbyclass(gensym("#A"), garray_class)) + pd_unbind(x2, gensym("#A")); + freebytes(x->x_vec, x->x_n * x->x_elemsize); +} + +/* ------------- code used by both array and plot widget functions ---- */ + + /* routine to get screen coordinates of a point in an array */ +void array_getcoordinate(t_glist *glist, + char *elem, int xonset, int yonset, int wonset, int indx, + float basex, float basey, float xinc, + float *xp, float *yp, float *wp) +{ + float xval, yval, ypix, wpix; + if (xonset >= 0) + xval = fixtof(*(t_sample *)(elem + xonset)); + else xval = indx * xinc; + if (yonset >= 0) + yval = fixtof(*(t_sample *)(elem + yonset)); + else yval = 0; + ypix = glist_ytopixels(glist, basey + yval); + if (wonset >= 0) + { + /* found "w" field which controls linewidth. */ + float wval = *(float *)(elem + wonset); + wpix = glist_ytopixels(glist, basey + yval + wval) - ypix; + if (wpix < 0) + wpix = -wpix; + } + else wpix = 1; + *xp = glist_xtopixels(glist, basex + xval); + *yp = ypix; + *wp = wpix; +} + +static float array_motion_xcumulative; +static float array_motion_ycumulative; +static t_symbol *array_motion_xfield; +static t_symbol *array_motion_yfield; +static t_glist *array_motion_glist; +static t_gobj *array_motion_gobj; +static t_word *array_motion_wp; +static t_template *array_motion_template; +static int array_motion_npoints; +static int array_motion_elemsize; +static int array_motion_altkey; +static float array_motion_initx; +static float array_motion_xperpix; +static float array_motion_yperpix; +static int array_motion_lastx; +static int array_motion_fatten; + + /* LATER protect against the template changing or the scalar disappearing + probably by attaching a gpointer here ... */ + +static void array_motion(void *z, t_floatarg dx, t_floatarg dy) +{ + array_motion_xcumulative += dx * array_motion_xperpix; + array_motion_ycumulative += dy * array_motion_yperpix; + if (*array_motion_xfield->s_name) + { + /* it's an x, y plot; can drag many points at once */ + int i; + char *charword = (char *)array_motion_wp; + for (i = 0; i < array_motion_npoints; i++) + { + t_word *thisword = (t_word *)(charword + i * array_motion_elemsize); + if (*array_motion_xfield->s_name) + { + float xwas = template_getfloat(array_motion_template, + array_motion_xfield, thisword, 1); + template_setfloat(array_motion_template, + array_motion_xfield, thisword, xwas + dx, 1); + } + if (*array_motion_yfield->s_name) + { + float ywas = template_getfloat(array_motion_template, + array_motion_yfield, thisword, 1); + if (array_motion_fatten) + { + if (i == 0) + { + float newy = ywas + dy * array_motion_yperpix; + if (newy < 0) + newy = 0; + template_setfloat(array_motion_template, + array_motion_yfield, thisword, newy, 1); + } + } + else + { + template_setfloat(array_motion_template, + array_motion_yfield, thisword, + ywas + dy * array_motion_yperpix, 1); + } + } + } + } + else + { + /* a y-only plot. */ + int thisx = array_motion_initx + + array_motion_xcumulative, x2; + int increment, i, nchange; + char *charword = (char *)array_motion_wp; + float newy = array_motion_ycumulative, + oldy = template_getfloat( + array_motion_template, array_motion_yfield, + (t_word *)(charword + array_motion_elemsize * array_motion_lastx), 1); + float ydiff = newy - oldy; + if (thisx < 0) thisx = 0; + else if (thisx >= array_motion_npoints) + thisx = array_motion_npoints - 1; + increment = (thisx > array_motion_lastx ? -1 : 1); + nchange = 1 + increment * (array_motion_lastx - thisx); + + for (i = 0, x2 = thisx; i < nchange; i++, x2 += increment) + { + template_setfloat(array_motion_template, + array_motion_yfield, + (t_word *)(charword + array_motion_elemsize * x2), + newy, 1); + if (nchange > 1) + newy -= ydiff * (1./(nchange - 1)); + } + array_motion_lastx = thisx; + } + glist_redrawitem(array_motion_glist, array_motion_gobj); +} + +int array_doclick(t_array *array, t_glist *glist, t_gobj *gobj, + t_symbol *elemtemplatesym, + float linewidth, float xloc, float xinc, float yloc, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_canvas *elemtemplatecanvas; + t_template *elemtemplate; + int elemsize, yonset, wonset, xonset, i; + + if (!array_getfields(elemtemplatesym, &elemtemplatecanvas, + &elemtemplate, &elemsize, &xonset, &yonset, &wonset)) + { + float best = 100; + int incr; + /* if it has more than 2000 points, just check 300 of them. */ + if (array->a_n < 2000) + incr = 1; + else incr = array->a_n / 300; + for (i = 0; i < array->a_n; i += incr) + { + float pxpix, pypix, pwpix, dx, dy; + array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, + xonset, yonset, wonset, i, xloc, yloc, xinc, + &pxpix, &pypix, &pwpix); + if (pwpix < 4) + pwpix = 4; + dx = pxpix - xpix; + if (dx < 0) dx = -dx; + if (dx > 8) + continue; + dy = pypix - ypix; + if (dy < 0) dy = -dy; + if (dx + dy < best) + best = dx + dy; + if (wonset >= 0) + { + dy = (pypix + pwpix) - ypix; + if (dy < 0) dy = -dy; + if (dx + dy < best) + best = dx + dy; + dy = (pypix - pwpix) - ypix; + if (dy < 0) dy = -dy; + if (dx + dy < best) + best = dx + dy; + } + } + if (best > 8) + return (0); + best += 0.001; /* add truncation error margin */ + for (i = 0; i < array->a_n; i += incr) + { + float pxpix, pypix, pwpix, dx, dy, dy2, dy3; + array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, + xonset, yonset, wonset, i, xloc, yloc, xinc, + &pxpix, &pypix, &pwpix); + if (pwpix < 4) + pwpix = 4; + dx = pxpix - xpix; + if (dx < 0) dx = -dx; + dy = pypix - ypix; + if (dy < 0) dy = -dy; + if (wonset >= 0) + { + dy2 = (pypix + pwpix) - ypix; + if (dy2 < 0) dy2 = -dy2; + dy3 = (pypix - pwpix) - ypix; + if (dy3 < 0) dy3 = -dy3; + if (yonset <= 0) + dy = 100; + } + else dy2 = dy3 = 100; + if (dx + dy <= best || dx + dy2 <= best || dx + dy3 <= best) + { + if (dy < dy2 && dy < dy3) + array_motion_fatten = 0; + else if (dy2 < dy3) + array_motion_fatten = -1; + else array_motion_fatten = 1; + if (doit) + { + char *elem = (char *)array->a_vec; + array_motion_elemsize = elemsize; + array_motion_glist = glist; + array_motion_gobj = gobj; + array_motion_template = elemtemplate; + array_motion_xperpix = glist_dpixtodx(glist, 1); + array_motion_yperpix = glist_dpixtody(glist, 1); + if (alt && xpix < pxpix) /* delete a point */ + { + if (array->a_n <= 1) + return (0); + memmove((char *)(array->a_vec) + elemsize * i, + (char *)(array->a_vec) + elemsize * (i+1), + (array->a_n - 1 - i) * elemsize); + array_resize(array, elemtemplate, array->a_n - 1); + glist_redrawitem(array_motion_glist, array_motion_gobj); + return (0); + } + else if (alt) + { + /* add a point (after the clicked-on one) */ + array_resize(array, elemtemplate, array->a_n + 1); + elem = (char *)array->a_vec; + memmove(elem + elemsize * (i+1), + elem + elemsize * i, + (array->a_n - i - 1) * elemsize); + i++; + } + if (xonset >= 0) + { + array_motion_xfield = gensym("x"); + array_motion_xcumulative = + *(float *)((elem + elemsize * i) + xonset); + array_motion_wp = (t_word *)(elem + i * elemsize); + array_motion_npoints = array->a_n - i; + } + else + { + array_motion_xfield = &s_; + array_motion_xcumulative = 0; + array_motion_wp = (t_word *)elem; + array_motion_npoints = array->a_n; + + array_motion_initx = i; + array_motion_lastx = i; + array_motion_xperpix *= (xinc == 0 ? 1 : 1./xinc); + } + if (array_motion_fatten) + { + array_motion_yfield = gensym("w"); + array_motion_ycumulative = + *(float *)((elem + elemsize * i) + wonset); + array_motion_yperpix *= array_motion_fatten; + } + else if (yonset >= 0) + { + array_motion_yfield = gensym("y"); + array_motion_ycumulative = + *(float *)((elem + elemsize * i) + yonset); + } + else + { + array_motion_yfield = &s_; + array_motion_ycumulative = 0; + } + glist_grab(glist, 0, array_motion, 0, xpix, ypix); + } + if (alt) + { + if (xpix < pxpix) + return (CURSOR_EDITMODE_DISCONNECT); + else return (CURSOR_RUNMODE_ADDPOINT); + } + else return (array_motion_fatten ? + CURSOR_RUNMODE_THICKEN : CURSOR_RUNMODE_CLICKME); + } + } + } + return (0); +} + +/* -------------------- widget behavior for garray ------------ */ + +static void garray_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_garray *x = (t_garray *)z; + float x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff; + t_canvas *elemtemplatecanvas; + t_template *elemtemplate; + int elemsize, yonset, wonset, xonset, i; + + if (!array_getfields(x->x_templatesym, &elemtemplatecanvas, + &elemtemplate, &elemsize, &xonset, &yonset, &wonset)) + { + int incr; + /* if it has more than 2000 points, just check 300 of them. */ + if (x->x_array.a_n < 2000) + incr = 1; + else incr = x->x_array.a_n / 300; + for (i = 0; i < x->x_array.a_n; i += incr) + { + float pxpix, pypix, pwpix, dx, dy; + array_getcoordinate(glist, (char *)(x->x_array.a_vec) + + i * elemsize, + xonset, yonset, wonset, i, 0, 0, 1, + &pxpix, &pypix, &pwpix); + if (pwpix < 2) + pwpix = 2; + if (pxpix < x1) + x1 = pxpix; + if (pxpix > x2) + x2 = pxpix; + if (pypix - pwpix < y1) + y1 = pypix - pwpix; + if (pypix + pwpix > y2) + y2 = pypix + pwpix; + } + } + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void garray_displace(t_gobj *z, t_glist *glist, int dx, int dy) +{ + /* refuse */ +} + +static void garray_select(t_gobj *z, t_glist *glist, int state) +{ + t_garray *x = (t_garray *)z; + /* fill in later */ +} + +static void garray_activate(t_gobj *z, t_glist *glist, int state) +{ +} + +static void garray_delete(t_gobj *z, t_glist *glist) +{ + /* nothing to do */ +} + +static void garray_vis(t_gobj *z, t_glist *glist, int vis) +{ + t_garray *x = (t_garray *)z; + if (vis) + { + int i, xonset, yonset, type; + t_symbol *arraytype; + t_template *template = template_findbyname(x->x_templatesym); + if (!template) + return; + if (!template_find_field(template, gensym("y"), &yonset, &type, + &arraytype) || type != DT_FLOAT) + { + error("%s: needs floating-point 'y' field", + x->x_templatesym->s_name); + sys_vgui(".x%x.c create text 50 50 -text foo\ + -tags .x%x.a%x\n", + glist_getcanvas(glist), glist_getcanvas(glist), x); + } + else if (!template_find_field(template, gensym("x"), &xonset, &type, + &arraytype) || type != DT_FLOAT) + { + float firsty, xcum = x->x_firstx; + int lastpixel = -1, ndrawn = 0; + float yval = 0, xpix; + int ixpix = 0; + sys_vgui(".x%x.c create line \\\n", glist_getcanvas(glist)); + for (i = 0; i < x->x_n; i++) + { + yval = fixtof(*(t_sample *)(x->x_vec + + template->t_n * i * sizeof (t_word) + yonset)); + xpix = glist_xtopixels(glist, xcum); + ixpix = xpix + 0.5; + if (ixpix != lastpixel) + { + sys_vgui("%d %f \\\n", ixpix, + glist_ytopixels(glist, yval)); + ndrawn++; + } + lastpixel = ixpix; + if (ndrawn >= 1000) break; + xcum += x->x_xinc; + } + /* TK will complain if there aren't at least 2 points... */ + if (ndrawn == 0) sys_vgui("0 0 0 0 \\\n"); + else if (ndrawn == 1) sys_vgui("%d %f \\\n", ixpix, + glist_ytopixels(glist, yval)); + sys_vgui("-tags .x%x.a%x\n", glist_getcanvas(glist), x); + firsty = fixtof(*(t_sample *)(x->x_vec + yonset)); + sys_vgui(".x%x.c create text %f %f -text {%s} -anchor e\ + -font -*-courier-bold--normal--%d-* -tags .x%x.a%x\n", + glist_getcanvas(glist), + glist_xtopixels(glist, x->x_firstx) - 5., + glist_ytopixels(glist, firsty), + x->x_name->s_name, glist_getfont(glist), + glist_getcanvas(glist), x); + } + else + { + post("x, y arrays not yet supported"); + } + } + else + { + sys_vgui(".x%x.c delete .x%x.a%x\n", + glist_getcanvas(glist), glist_getcanvas(glist), x); + } +} + +static int garray_click(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_garray *x = (t_garray *)z; + return (array_doclick(&x->x_array, glist, z, x->x_templatesym, 1, 0, 1, 0, + xpix, ypix, shift, alt, dbl, doit)); +} + +#define ARRAYWRITECHUNKSIZE 1000 + +static void garray_save(t_gobj *z, t_binbuf *b) +{ + t_garray *x = (t_garray *)z; + binbuf_addv(b, "sssisi;", gensym("#X"), gensym("array"), + x->x_name, x->x_n, x->x_templatesym, x->x_saveit); + fprintf(stderr,"array save\n"); + if (x->x_saveit) + { + int n = x->x_n, n2 = 0; + if (x->x_templatesym != &s_float) + { + pd_error(x, "sorry, you can only save 'float' arrays now"); + return; + } + if (n > 200000) + post("warning: I'm saving an array with %d points!\n", n); + while (n2 < n) + { + int chunk = n - n2, i; + if (chunk > ARRAYWRITECHUNKSIZE) + chunk = ARRAYWRITECHUNKSIZE; + binbuf_addv(b, "si", gensym("#A"), n2); + for (i = 0; i < chunk; i++) + binbuf_addv(b, "f", fixtof(((t_sample *)(x->x_vec))[n2+i])); + binbuf_addv(b, ";"); + n2 += chunk; + } + } +} + +t_widgetbehavior garray_widgetbehavior = +{ + garray_getrect, + garray_displace, + garray_select, + garray_activate, + garray_delete, + garray_vis, + garray_click +}; + +/* ----------------------- public functions -------------------- */ + +void garray_usedindsp(t_garray *x) +{ + x->x_usedindsp = 1; +} + +void garray_redraw(t_garray *x) +{ + if (glist_isvisible(x->x_glist)) + { + garray_vis(&x->x_gobj, x->x_glist, 0); + garray_vis(&x->x_gobj, x->x_glist, 1); + } +} + + /* This functiopn gets the template of an array; if we can't figure + out what template an array's elements belong to we're in grave trouble + when it's time to free or resize it. */ +t_template *garray_template(t_garray *x) +{ + t_template *template = template_findbyname(x->x_templatesym); + if (!template) + bug("garray_template"); + return (template); +} + +int garray_npoints(t_garray *x) /* get the length */ +{ + return (x->x_n); +} + +char *garray_vec(t_garray *x) /* get the contents */ +{ + return ((char *)(x->x_vec)); +} + + /* routine that checks if we're just an array of floats and if + so returns the goods */ + +int garray_getfloatarray(t_garray *x, int *size, t_sample **vec) +{ + t_template *template = garray_template(x); + int yonset, type; + t_symbol *arraytype; + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + error("%s: needs floating-point 'y' field", + x->x_templatesym->s_name); + else if (template->t_n != 1) + error("%s: has more than one field", x->x_templatesym->s_name); + else + { + *size = garray_npoints(x); + *vec = (t_sample *)garray_vec(x); + return (1); + } + return (0); +} + + /* get any floating-point field of any element of an array */ +float garray_get(t_garray *x, t_symbol *s, t_int indx) +{ + t_template *template = garray_template(x); + int yonset, type; + t_symbol *arraytype; + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + { + error("%s: needs floating-point '%s' field", x->x_templatesym->s_name, + s->s_name); + return (0); + } + if (indx < 0) indx = 0; + else if (indx >= x->x_n) indx = x->x_n - 1; + return (*(float *)((x->x_vec + sizeof(t_word) * indx) + yonset)); +} + + /* set the "saveit" flag */ +void garray_setsaveit(t_garray *x, int saveit) +{ + if (x->x_saveit && !saveit) + post("warning: array %s: clearing save-in-patch flag", + x->x_name->s_name); + x->x_saveit = saveit; +} + +/*------------------- Pd messages ------------------------ */ +static void garray_const(t_garray *x, t_floatarg g) +{ + t_template *template = garray_template(x); + int yonset, type, i; + t_symbol *arraytype; + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + error("%s: needs floating-point 'y' field", + x->x_templatesym->s_name); + else for (i = 0; i < x->x_n; i++) + *(float *)(((char *)x->x_vec + sizeof(t_word) * i) + yonset) = g; + garray_redraw(x); +} + + /* sum of Fourier components; called from routines below */ +static void garray_dofo(t_garray *x, int npoints, float dcval, + int nsin, t_float *vsin, int sineflag) +{ + t_template *template = garray_template(x); + int yonset, type, i, j; + t_symbol *arraytype; + double phase, phaseincr, fj; + if (npoints == 0) + npoints = 512; /* dunno what a good default would be... */ + if (npoints != (1 << ilog2(npoints))) + post("%s: rounnding to %d points", x->x_templatesym->s_name, + (npoints = (1<x_templatesym->s_name); + return; + } + for (i = 0, phase = -phaseincr; i < x->x_n; i++, phase += phaseincr ) + { + double sum = dcval; + if (sineflag) + for (j = 0, fj = phase; j < nsin; j++, fj += phase) + sum += vsin[j] * sin(fj); + else + for (j = 0, fj = 0; j < nsin; j++, fj += phase) + sum += vsin[j] * cos(fj); + *(float *)((x->x_vec + sizeof(t_word) * i) + yonset) = sum; + } + garray_redraw(x); +} + +static void garray_sinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv) +{ + t_template *template = garray_template(x); + + t_float *svec = (t_float *)t_getbytes(sizeof(t_float) * argc); + int npoints, i; + if (argc < 2) + { + error("sinesum: %s: need number of points and partial strengths", + x->x_templatesym->s_name); + return; + } + + npoints = atom_getfloatarg(0, argc, argv); + argv++, argc--; + + svec = (t_float *)t_getbytes(sizeof(t_float) * argc); + if (!svec) return; + + for (i = 0; i < argc; i++) + svec[i] = atom_getfloatarg(i, argc, argv); + garray_dofo(x, npoints, 0, argc, svec, 1); + t_freebytes(svec, sizeof(t_float) * argc); +} + +static void garray_cosinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv) +{ + t_template *template = garray_template(x); + + t_float *svec = (t_float *)t_getbytes(sizeof(t_float) * argc); + int npoints, i; + if (argc < 2) + { + error("sinesum: %s: need number of points and partial strengths", + x->x_templatesym->s_name); + return; + } + + npoints = atom_getfloatarg(0, argc, argv); + argv++, argc--; + + svec = (t_float *)t_getbytes(sizeof(t_float) * argc); + if (!svec) return; + + for (i = 0; i < argc; i++) + svec[i] = atom_getfloatarg(i, argc, argv); + garray_dofo(x, npoints, 0, argc, svec, 0); + t_freebytes(svec, sizeof(t_float) * argc); +} + +static void garray_normalize(t_garray *x, t_float f) +{ + t_template *template = garray_template(x); + int yonset, type, npoints, i; + double maxv, renormer; + t_symbol *arraytype; + + if (f <= 0) + f = 1; + + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + { + error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); + return; + } + for (i = 0, maxv = 0; i < x->x_n; i++) + { + double v = *(float *)((x->x_vec + sizeof(t_word) * i) + yonset); + if (v > maxv) + maxv = v; + if (-v > maxv) + maxv = -v; + } + if (maxv >= 0) + { + renormer = f / maxv; + for (i = 0; i < x->x_n; i++) + { + *(float *)((x->x_vec + sizeof(t_word) * i) + yonset) + *= renormer; + } + } + garray_redraw(x); +} + + /* list -- the first value is an index; subsequent values are put in + the "y" slot of the array. This generalizes Max's "table", sort of. */ +static void garray_list(t_garray *x, t_symbol *s, int argc, t_atom *argv) +{ + t_template *template = garray_template(x); + int yonset, type, i; + t_symbol *arraytype; + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + error("%s: needs floating-point 'y' field", + x->x_templatesym->s_name); + else if (argc < 2) return; + else + { + int firstindex = atom_getfloat(argv); + argc--; + argv++; + /* drop negative x values */ + if (firstindex < 0) + { + argc += firstindex; + argv -= firstindex; + firstindex = 0; + if (argc <= 0) return; + } + if (argc + firstindex > x->x_n) + { + argc = x->x_n - firstindex; + if (argc <= 0) return; + } + for (i = 0; i < argc; i++) + *(t_sample *)((x->x_vec + sizeof(t_word) * (i + firstindex)) + yonset) = + ftofix(atom_getfloat(argv + i)); + } + garray_redraw(x); +} + + /* forward a "bounds" message to the owning graph */ +static void garray_bounds(t_garray *x, t_floatarg x1, t_floatarg y1, + t_floatarg x2, t_floatarg y2) +{ + vmess(&x->x_glist->gl_pd, gensym("bounds"), "ffff", x1, y1, x2, y2); +} + + /* same for "xticks", etc */ +static void garray_xticks(t_garray *x, + t_floatarg point, t_floatarg inc, t_floatarg f) +{ + vmess(&x->x_glist->gl_pd, gensym("xticks"), "fff", point, inc, f); +} + +static void garray_yticks(t_garray *x, + t_floatarg point, t_floatarg inc, t_floatarg f) +{ + vmess(&x->x_glist->gl_pd, gensym("yticks"), "fff", point, inc, f); +} + +static void garray_xlabel(t_garray *x, t_symbol *s, int argc, t_atom *argv) +{ + typedmess(&x->x_glist->gl_pd, s, argc, argv); +} + +static void garray_ylabel(t_garray *x, t_symbol *s, int argc, t_atom *argv) +{ + typedmess(&x->x_glist->gl_pd, s, argc, argv); +} + /* change the name of a garray. */ +static void garray_rename(t_garray *x, t_symbol *s) +{ + pd_unbind(&x->x_gobj.g_pd, x->x_realname); + pd_bind(&x->x_gobj.g_pd, x->x_realname = x->x_name = s); + garray_redraw(x); +} + +static void garray_read(t_garray *x, t_symbol *filename) +{ + int nelem = x->x_n, filedesc; + FILE *fd; + char buf[MAXPDSTRING], *bufptr; + t_template *template = garray_template(x); + int yonset, type, i; + t_symbol *arraytype; + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + { + error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); + return; + } + if ((filedesc = open_via_path( + canvas_getdir(glist_getcanvas(x->x_glist))->s_name, + filename->s_name, "", buf, &bufptr, MAXPDSTRING, 0)) < 0 + || !(fd = fdopen(filedesc, "r"))) + { + error("%s: can't open", filename->s_name); + return; + } + for (i = 0; i < nelem; i++) + { + if (!fscanf(fd, "%f", (float *)((x->x_vec + sizeof(t_word) * i) + + yonset))) + { + post("%s: read %d elements into table of size %d", + filename->s_name, i, nelem); + break; + } + } + while (i < nelem) + *(float *)((x->x_vec + sizeof(t_word) * i) + yonset) = 0, i++; + fclose(fd); + garray_redraw(x); +} + + /* this should be renamed and moved... */ +int garray_ambigendian(void) +{ + unsigned short s = 1; + unsigned char c = *(char *)(&s); + return (c==0); +} + +#define BINREADMODE "rb" +#define BINWRITEMODE "wb" + +static void garray_read16(t_garray *x, t_symbol *filename, + t_symbol *endian, t_floatarg fskip) +{ + int skip = fskip, filedesc; + int i, nelem; + t_sample *vec; + FILE *fd; + char buf[MAXPDSTRING], *bufptr; + short s; + int cpubig = garray_ambigendian(), swap = 0; + char c = endian->s_name[0]; + if (c == 'b') + { + if (!cpubig) swap = 1; + } + else if (c == 'l') + { + if (cpubig) swap = 1; + } + else if (c) + { + error("array_read16: endianness is 'l' (low byte first ala INTEL)"); + post("... or 'b' (high byte first ala MIPS,DEC,PPC)"); + } + if (!garray_getfloatarray(x, &nelem, &vec)) + { + error("%s: not a float array", x->x_templatesym->s_name); + return; + } + if ((filedesc = open_via_path( + canvas_getdir(glist_getcanvas(x->x_glist))->s_name, + filename->s_name, "", buf, &bufptr, MAXPDSTRING, 1)) < 0 + || !(fd = fdopen(filedesc, BINREADMODE))) + { + error("%s: can't open", filename->s_name); + return; + } + if (skip) + { + long pos = fseek(fd, (long)skip, SEEK_SET); + if (pos < 0) + { + error("%s: can't seek to byte %d", buf, skip); + fclose(fd); + return; + } + } + + for (i = 0; i < nelem; i++) + { + if (fread(&s, sizeof(s), 1, fd) < 1) + { + post("%s: read %d elements into table of size %d", + filename->s_name, i, nelem); + break; + } + if (swap) s = ((s & 0xff) << 8) | ((s & 0xff00) >> 8); + vec[i] = s * (1./32768.); + } + while (i < nelem) vec[i++] = 0; + fclose(fd); + garray_redraw(x); +} + +static void garray_write(t_garray *x, t_symbol *filename) +{ + FILE *fd; + char buf[MAXPDSTRING]; + t_template *template = garray_template(x); + int yonset, type, i; + t_symbol *arraytype; + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + { + error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); + return; + } + canvas_makefilename(glist_getcanvas(x->x_glist), filename->s_name, + buf, MAXPDSTRING); + sys_bashfilename(buf, buf); + if (!(fd = fopen(buf, "w"))) + { + error("%s: can't create", buf); + return; + } + for (i = 0; i < x->x_n; i++) + { + if (fprintf(fd, "%g\n", + *(float *)((x->x_vec + sizeof(t_word) * i) + yonset)) < 1) + { + post("%s: write error", filename->s_name); + break; + } + } + fclose(fd); +} + +static unsigned char waveheader[] = { +0x52, 0x49, 0x46, 0x46, +0x00, 0x00, 0x00, 0x00, +0x57, 0x41, 0x56, 0x45, +0x66, 0x6d, 0x74, 0x20, + +0x10, 0x00, 0x00, 0x00, +0x01, 0x00, 0x01, 0x00, +0x44, 0xac, 0x00, 0x00, +0x88, 0x58, 0x01, 0x00, + +0x02, 0x00, 0x10, 0x00, +0x64, 0x61, 0x74, 0x61, +0x00, 0x00, 0x00, 0x00, +}; + + /* wave format only so far */ +static void garray_write16(t_garray *x, t_symbol *filename, t_symbol *format) +{ + t_template *template = garray_template(x); + int yonset, type, i; + t_symbol *arraytype; + FILE *fd; + int aiff = (format == gensym("aiff")); + char filenamebuf[MAXPDSTRING], buf2[MAXPDSTRING]; + int swap = garray_ambigendian(); /* wave is only little endian */ + int intbuf; + strncpy(filenamebuf, filename->s_name, MAXPDSTRING-10); + filenamebuf[MAXPDSTRING-10] = 0; + if (sizeof(int) != 4) post("write16: only works on 32-bit machines"); + if (aiff) + { + if (strcmp(filenamebuf + strlen(filenamebuf)-5, ".aiff")) + strcat(filenamebuf, ".aiff"); + } + else + { + if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".wav")) + strcat(filenamebuf, ".wav"); + } + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + { + error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); + return; + } + canvas_makefilename(glist_getcanvas(x->x_glist), filenamebuf, + buf2, MAXPDSTRING); + sys_bashfilename(buf2, buf2); + if (!(fd = fopen(buf2, BINWRITEMODE))) + { + error("%s: can't create", buf2); + return; + } + intbuf = 2 * x->x_n + 36; + if (swap) + { + unsigned char *foo = (unsigned char *)&intbuf, xxx; + xxx = foo[0]; foo[0] = foo[3]; foo[3] = xxx; + xxx = foo[1]; foo[1] = foo[2]; foo[2] = xxx; + } + memcpy((void *)(waveheader + 4), (void *)(&intbuf), 4); + intbuf = 2 * x->x_n; + if (swap) + { + unsigned char *foo = (unsigned char *)&intbuf, xxx; + xxx = foo[0]; foo[0] = foo[3]; foo[3] = xxx; + xxx = foo[1]; foo[1] = foo[2]; foo[2] = xxx; + } + memcpy((void *)(waveheader + 40), (void *)(&intbuf), 4); + if (fwrite(waveheader, sizeof(waveheader), 1, fd) < 1) + { + post("%s: write error", buf2); + goto closeit; + } + for (i = 0; i < x->x_n; i++) + { + float f = 32767. * *(float *)((x->x_vec + sizeof(t_word) * i) + yonset); + short sh; + if (f < -32768) f = -32768; + else if (f > 32767) f = 32767; + sh = f; + if (swap) + { + unsigned char *foo = (unsigned char *)&sh, xxx; + xxx = foo[0]; foo[0] = foo[1]; foo[1] = xxx; + } + if (fwrite(&sh, sizeof(sh), 1, fd) < 1) + { + post("%s: write error", buf2); + goto closeit; + } + } +closeit: + fclose(fd); +} + +void garray_resize(t_garray *x, t_floatarg f) +{ + int was = x->x_n, elemsize; + t_glist *gl; + int dspwas; + int n = f; + char *nvec; + + if (n < 1) n = 1; + elemsize = template_findbyname(x->x_templatesym)->t_n * sizeof(t_word); + nvec = t_resizebytes(x->x_vec, was * elemsize, n * elemsize); + if (!nvec) + { + pd_error(x, "array resize failed: out of memory"); + return; + } + x->x_vec = nvec; + /* LATER should check t_resizebytes result */ + if (n > was) + memset(x->x_vec + was*elemsize, + 0, (n - was) * elemsize); + x->x_n = n; + + /* if this is the only array in the graph, + reset the graph's coordinates */ + gl = x->x_glist; + if (gl->gl_list == &x->x_gobj && !x->x_gobj.g_next) + { + vmess(&gl->gl_pd, gensym("bounds"), "ffff", + 0., gl->gl_y1, (double)(n > 1 ? n-1 : 1), gl->gl_y2); + /* close any dialogs that might have the wrong info now... */ + gfxstub_deleteforkey(gl); + } + else garray_redraw(x); + if (x->x_usedindsp) canvas_update_dsp(); +} + +static void garray_print(t_garray *x) +{ + post("garray %s: template %s, length %d", + x->x_name->s_name, x->x_templatesym->s_name, x->x_n); +} + +void g_array_setup(void) +{ + garray_class = class_new(gensym("array"), 0, (t_method)garray_free, + sizeof(t_garray), CLASS_GOBJ, 0); + class_setwidget(garray_class, &garray_widgetbehavior); + class_addmethod(garray_class, (t_method)garray_const, gensym("const"), + A_DEFFLOAT, A_NULL); + class_addlist(garray_class, garray_list); + class_addmethod(garray_class, (t_method)garray_bounds, gensym("bounds"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(garray_class, (t_method)garray_xticks, gensym("xticks"), + A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(garray_class, (t_method)garray_xlabel, gensym("xlabel"), + A_GIMME, 0); + class_addmethod(garray_class, (t_method)garray_yticks, gensym("yticks"), + A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(garray_class, (t_method)garray_ylabel, gensym("ylabel"), + A_GIMME, 0); + class_addmethod(garray_class, (t_method)garray_rename, gensym("rename"), + A_SYMBOL, 0); + class_addmethod(garray_class, (t_method)garray_read, gensym("read"), + A_SYMBOL, A_NULL); + class_addmethod(garray_class, (t_method)garray_read16, gensym("read16"), + A_SYMBOL, A_DEFFLOAT, A_DEFSYM, A_NULL); + class_addmethod(garray_class, (t_method)garray_write, gensym("write"), + A_SYMBOL, A_NULL); + class_addmethod(garray_class, (t_method)garray_write16, gensym("write16"), + A_SYMBOL, A_DEFSYM, A_NULL); + class_addmethod(garray_class, (t_method)garray_resize, gensym("resize"), + A_FLOAT, A_NULL); + class_addmethod(garray_class, (t_method)garray_print, gensym("print"), + A_NULL); + class_addmethod(garray_class, (t_method)garray_sinesum, gensym("sinesum"), + A_GIMME, 0); + class_addmethod(garray_class, (t_method)garray_cosinesum, + gensym("cosinesum"), A_GIMME, 0); + class_addmethod(garray_class, (t_method)garray_normalize, + gensym("normalize"), A_DEFFLOAT, 0); + class_addmethod(garray_class, (t_method)garray_arraydialog, + gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_setsavefn(garray_class, garray_save); +} + + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include +#include +#include /* for read/write to files */ +#include "m_pd.h" +#include "g_canvas.h" +#include + +/* see also the "plot" object in g_scalar.c which deals with graphing +arrays which are fields in scalars. Someday we should unify the +two, but how? */ + + /* aux routine to bash leading '#' to '$' for dialogs in u_main.tk + which can't send symbols starting with '$' (because the Pd message + interpreter would change them!) */ + +static t_symbol *sharptodollar(t_symbol *s) +{ + if (*s->s_name == '#') + { + char buf[MAXPDSTRING]; + strncpy(buf, s->s_name, MAXPDSTRING); + buf[MAXPDSTRING-1] = 0; + buf[0] = '$'; + return (gensym(buf)); + } + else return (s); +} + +/* --------- "pure" arrays with scalars for elements. --------------- */ + +/* Pure arrays have no a priori graphical capabilities. +They are instantiated by "garrays" below or can be elements of other +scalars (g_scalar.c); their graphical behavior is defined accordingly. */ + +t_array *array_new(t_symbol *templatesym, t_gpointer *parent) +{ + t_array *x = (t_array *)getbytes(sizeof (*x)); + t_template *template; + t_gpointer *gp; + template = template_findbyname(templatesym); + x->a_templatesym = templatesym; + x->a_n = 1; + x->a_elemsize = sizeof(t_word) * template->t_n; + x->a_vec = (char *)getbytes(x->a_elemsize); + /* note here we blithely copy a gpointer instead of "setting" a + new one; this gpointer isn't accounted for and needn't be since + we'll be deleted before the thing pointed to gets deleted anyway; + see array_free. */ + x->a_gp = *parent; + x->a_stub = gstub_new(0, x); + word_init((t_word *)(x->a_vec), template, parent); + return (x); +} + +void array_resize(t_array *x, t_template *template, int n) +{ + int elemsize, oldn; + t_gpointer *gp; + + if (n < 1) + n = 1; + oldn = x->a_n; + elemsize = sizeof(t_word) * template->t_n; + + x->a_vec = (char *)resizebytes(x->a_vec, oldn * elemsize, + n * elemsize); + x->a_n = n; + if (n > oldn) + { + char *cp = x->a_vec + elemsize * oldn; + int i = n - oldn; + for (; i--; cp += elemsize) + { + t_word *wp = (t_word *)cp; + word_init(wp, template, &x->a_gp); + } + } +} + +void word_free(t_word *wp, t_template *template); + +void array_free(t_array *x) +{ + int i; + t_template *scalartemplate = template_findbyname(x->a_templatesym); + /* we don't unset our gpointer here since it was never "set." */ + /* gpointer_unset(&x->a_gp); */ + gstub_cutoff(x->a_stub); + for (i = 0; i < x->a_n; i++) + { + t_word *wp = (t_word *)(x->a_vec + x->a_elemsize * i); + word_free(wp, scalartemplate); + } + freebytes(x->a_vec, x->a_elemsize * x->a_n); + freebytes(x, sizeof *x); +} + +/* --------------------- graphical arrays (garrays) ------------------- */ + +t_class *garray_class; +static int gcount = 0; + +struct _garray +{ + t_gobj x_gobj; + t_glist *x_glist; + t_array x_array; /* actual array; note only 4 fields used as below */ + t_symbol *x_name; + t_symbol *x_realname; /* name with "$" expanded */ + t_float x_firstx; /* X value of first item */ + t_float x_xinc; /* X increment */ + char x_usedindsp; /* true if some DSP routine is using this */ + char x_saveit; /* true if we should save this with parent */ +}; + + /* macros to get into the "array" structure */ +#define x_n x_array.a_n +#define x_elemsize x_array.a_elemsize +#define x_vec x_array.a_vec +#define x_templatesym x_array.a_templatesym + +t_garray *graph_array(t_glist *gl, t_symbol *s, t_symbol *templatesym, + t_floatarg f, t_floatarg saveit) +{ + int n = f, i; + int zz, nwords; + t_garray *x; + t_pd *x2; + t_template *template; + char *str; + if (s == &s_) + { + char buf[40]; + sprintf(buf, "array%d", ++gcount); + s = gensym(buf); + templatesym = &s_float; + n = 100; + } + else if (!strncmp((str = s->s_name), "array", 5) + && (zz = atoi(str + 5)) > gcount) gcount = zz; + template = template_findbyname(templatesym); + if (!template) + { + error("array: couldn't find template %s", templatesym->s_name); + return (0); + } + nwords = template->t_n; + for (i = 0; i < nwords; i++) + { + /* we can't have array or list elements yet because what scalar + can act as their "parent"??? */ + if (template->t_vec[i].ds_type == DT_ARRAY + || template->t_vec[i].ds_type == DT_LIST) + { + error("array: template %s can't have sublists or arrays", + templatesym->s_name); + return (0); + } + } + x = (t_garray *)pd_new(garray_class); + + if (n <= 0) n = 100; + x->x_n = n; + x->x_elemsize = nwords * sizeof(t_word); + x->x_vec = getbytes(x->x_n * x->x_elemsize); + memset(x->x_vec, 0, x->x_n * x->x_elemsize); + /* LATER should check that malloc */ + x->x_name = s; + x->x_realname = canvas_realizedollar(gl, s); + pd_bind(&x->x_gobj.g_pd, x->x_realname); + x->x_templatesym = templatesym; + x->x_firstx = 0; + x->x_xinc = 1; /* LATER make methods to set this... */ + glist_add(gl, &x->x_gobj); + x->x_glist = gl; + x->x_usedindsp = 0; + x->x_saveit = (saveit != 0); + if (x2 = pd_findbyclass(gensym("#A"), garray_class)) + pd_unbind(x2, gensym("#A")); + + pd_bind(&x->x_gobj.g_pd, gensym("#A")); + + return (x); +} + + /* called from array menu item to create a new one */ +void canvas_menuarray(t_glist *canvas) +{ + t_glist *x = (t_glist *)canvas; + char cmdbuf[200]; + sprintf(cmdbuf, "pdtk_array_dialog %%s array%d 100 1 1\n", + ++gcount); + gfxstub_new(&x->gl_pd, x, cmdbuf); +} + + /* called from graph_dialog to set properties */ +void garray_properties(t_garray *x) +{ + char cmdbuf[200]; + gfxstub_deleteforkey(x); + /* create dialog window. LATER fix this to escape '$' + properly; right now we just detect a leading '$' and escape + it. There should be a systematic way of doing this. */ + if (x->x_name->s_name[0] == '$') + sprintf(cmdbuf, "pdtk_array_dialog %%s \\%s %d %d 0\n", + x->x_name->s_name, x->x_n, x->x_saveit); + else sprintf(cmdbuf, "pdtk_array_dialog %%s %s %d %d 0\n", + x->x_name->s_name, x->x_n, x->x_saveit); + gfxstub_new(&x->x_gobj.g_pd, x, cmdbuf); +} + + /* this is called back from the dialog window to create a garray. + The otherflag requests that we find an existing graph to put it in. */ +void glist_arraydialog(t_glist *parent, t_symbol *name, t_floatarg size, + t_floatarg saveit, t_floatarg otherflag) +{ + t_glist *gl; + t_garray *a; + if (size < 1) + size = 1; + if (otherflag == 0 || (!(gl = glist_findgraph(parent)))) + gl = glist_addglist(parent, &s_, 0, 1, + (size > 1 ? size-1 : size), -1, 0, 0, 0, 0); + a = graph_array(gl, sharptodollar(name), &s_float, size, saveit); +} + + /* this is called from the properties dialog window for an existing array */ +void garray_arraydialog(t_garray *x, t_symbol *name, t_floatarg fsize, + t_floatarg saveit, t_floatarg deleteit) +{ + if (deleteit != 0) + { + glist_delete(x->x_glist, &x->x_gobj); + } + else + { + int size; + t_symbol *argname = sharptodollar(name); + if (argname != x->x_name) + { + x->x_name = argname; + pd_unbind(&x->x_gobj.g_pd, x->x_realname); + x->x_realname = canvas_realizedollar(x->x_glist, argname); + pd_bind(&x->x_gobj.g_pd, x->x_realname); + } + size = fsize; + if (size < 1) + size = 1; + if (size != x->x_n) + garray_resize(x, size); + garray_setsaveit(x, (saveit != 0)); + garray_redraw(x); + } +} + +static void garray_free(t_garray *x) +{ + t_pd *x2; + gfxstub_deleteforkey(x); + pd_unbind(&x->x_gobj.g_pd, x->x_realname); + /* LATER find a way to get #A unbound earlier (at end of load?) */ + while (x2 = pd_findbyclass(gensym("#A"), garray_class)) + pd_unbind(x2, gensym("#A")); + freebytes(x->x_vec, x->x_n * x->x_elemsize); +} + +/* ------------- code used by both array and plot widget functions ---- */ + + /* routine to get screen coordinates of a point in an array */ +void array_getcoordinate(t_glist *glist, + char *elem, int xonset, int yonset, int wonset, int indx, + float basex, float basey, float xinc, + float *xp, float *yp, float *wp) +{ + float xval, yval, ypix, wpix; + if (xonset >= 0) + xval = fixtof(*(t_sample *)(elem + xonset)); + else xval = indx * xinc; + if (yonset >= 0) + yval = fixtof(*(t_sample *)(elem + yonset)); + else yval = 0; + ypix = glist_ytopixels(glist, basey + yval); + if (wonset >= 0) + { + /* found "w" field which controls linewidth. */ + float wval = *(float *)(elem + wonset); + wpix = glist_ytopixels(glist, basey + yval + wval) - ypix; + if (wpix < 0) + wpix = -wpix; + } + else wpix = 1; + *xp = glist_xtopixels(glist, basex + xval); + *yp = ypix; + *wp = wpix; +} + +static float array_motion_xcumulative; +static float array_motion_ycumulative; +static t_symbol *array_motion_xfield; +static t_symbol *array_motion_yfield; +static t_glist *array_motion_glist; +static t_gobj *array_motion_gobj; +static t_word *array_motion_wp; +static t_template *array_motion_template; +static int array_motion_npoints; +static int array_motion_elemsize; +static int array_motion_altkey; +static float array_motion_initx; +static float array_motion_xperpix; +static float array_motion_yperpix; +static int array_motion_lastx; +static int array_motion_fatten; + + /* LATER protect against the template changing or the scalar disappearing + probably by attaching a gpointer here ... */ + +static void array_motion(void *z, t_floatarg dx, t_floatarg dy) +{ + array_motion_xcumulative += dx * array_motion_xperpix; + array_motion_ycumulative += dy * array_motion_yperpix; + if (*array_motion_xfield->s_name) + { + /* it's an x, y plot; can drag many points at once */ + int i; + char *charword = (char *)array_motion_wp; + for (i = 0; i < array_motion_npoints; i++) + { + t_word *thisword = (t_word *)(charword + i * array_motion_elemsize); + if (*array_motion_xfield->s_name) + { + float xwas = template_getfloat(array_motion_template, + array_motion_xfield, thisword, 1); + template_setfloat(array_motion_template, + array_motion_xfield, thisword, xwas + dx, 1); + } + if (*array_motion_yfield->s_name) + { + float ywas = template_getfloat(array_motion_template, + array_motion_yfield, thisword, 1); + if (array_motion_fatten) + { + if (i == 0) + { + float newy = ywas + dy * array_motion_yperpix; + if (newy < 0) + newy = 0; + template_setfloat(array_motion_template, + array_motion_yfield, thisword, newy, 1); + } + } + else + { + template_setfloat(array_motion_template, + array_motion_yfield, thisword, + ywas + dy * array_motion_yperpix, 1); + } + } + } + } + else + { + /* a y-only plot. */ + int thisx = array_motion_initx + + array_motion_xcumulative, x2; + int increment, i, nchange; + char *charword = (char *)array_motion_wp; + float newy = array_motion_ycumulative, + oldy = template_getfloat( + array_motion_template, array_motion_yfield, + (t_word *)(charword + array_motion_elemsize * array_motion_lastx), 1); + float ydiff = newy - oldy; + if (thisx < 0) thisx = 0; + else if (thisx >= array_motion_npoints) + thisx = array_motion_npoints - 1; + increment = (thisx > array_motion_lastx ? -1 : 1); + nchange = 1 + increment * (array_motion_lastx - thisx); + + for (i = 0, x2 = thisx; i < nchange; i++, x2 += increment) + { + template_setfloat(array_motion_template, + array_motion_yfield, + (t_word *)(charword + array_motion_elemsize * x2), + newy, 1); + if (nchange > 1) + newy -= ydiff * (1./(nchange - 1)); + } + array_motion_lastx = thisx; + } + glist_redrawitem(array_motion_glist, array_motion_gobj); +} + +int array_doclick(t_array *array, t_glist *glist, t_gobj *gobj, + t_symbol *elemtemplatesym, + float linewidth, float xloc, float xinc, float yloc, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_canvas *elemtemplatecanvas; + t_template *elemtemplate; + int elemsize, yonset, wonset, xonset, i; + + if (!array_getfields(elemtemplatesym, &elemtemplatecanvas, + &elemtemplate, &elemsize, &xonset, &yonset, &wonset)) + { + float best = 100; + int incr; + /* if it has more than 2000 points, just check 300 of them. */ + if (array->a_n < 2000) + incr = 1; + else incr = array->a_n / 300; + for (i = 0; i < array->a_n; i += incr) + { + float pxpix, pypix, pwpix, dx, dy; + array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, + xonset, yonset, wonset, i, xloc, yloc, xinc, + &pxpix, &pypix, &pwpix); + if (pwpix < 4) + pwpix = 4; + dx = pxpix - xpix; + if (dx < 0) dx = -dx; + if (dx > 8) + continue; + dy = pypix - ypix; + if (dy < 0) dy = -dy; + if (dx + dy < best) + best = dx + dy; + if (wonset >= 0) + { + dy = (pypix + pwpix) - ypix; + if (dy < 0) dy = -dy; + if (dx + dy < best) + best = dx + dy; + dy = (pypix - pwpix) - ypix; + if (dy < 0) dy = -dy; + if (dx + dy < best) + best = dx + dy; + } + } + if (best > 8) + return (0); + best += 0.001; /* add truncation error margin */ + for (i = 0; i < array->a_n; i += incr) + { + float pxpix, pypix, pwpix, dx, dy, dy2, dy3; + array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, + xonset, yonset, wonset, i, xloc, yloc, xinc, + &pxpix, &pypix, &pwpix); + if (pwpix < 4) + pwpix = 4; + dx = pxpix - xpix; + if (dx < 0) dx = -dx; + dy = pypix - ypix; + if (dy < 0) dy = -dy; + if (wonset >= 0) + { + dy2 = (pypix + pwpix) - ypix; + if (dy2 < 0) dy2 = -dy2; + dy3 = (pypix - pwpix) - ypix; + if (dy3 < 0) dy3 = -dy3; + if (yonset <= 0) + dy = 100; + } + else dy2 = dy3 = 100; + if (dx + dy <= best || dx + dy2 <= best || dx + dy3 <= best) + { + if (dy < dy2 && dy < dy3) + array_motion_fatten = 0; + else if (dy2 < dy3) + array_motion_fatten = -1; + else array_motion_fatten = 1; + if (doit) + { + char *elem = (char *)array->a_vec; + array_motion_elemsize = elemsize; + array_motion_glist = glist; + array_motion_gobj = gobj; + array_motion_template = elemtemplate; + array_motion_xperpix = glist_dpixtodx(glist, 1); + array_motion_yperpix = glist_dpixtody(glist, 1); + if (alt && xpix < pxpix) /* delete a point */ + { + if (array->a_n <= 1) + return (0); + memmove((char *)(array->a_vec) + elemsize * i, + (char *)(array->a_vec) + elemsize * (i+1), + (array->a_n - 1 - i) * elemsize); + array_resize(array, elemtemplate, array->a_n - 1); + glist_redrawitem(array_motion_glist, array_motion_gobj); + return (0); + } + else if (alt) + { + /* add a point (after the clicked-on one) */ + array_resize(array, elemtemplate, array->a_n + 1); + elem = (char *)array->a_vec; + memmove(elem + elemsize * (i+1), + elem + elemsize * i, + (array->a_n - i - 1) * elemsize); + i++; + } + if (xonset >= 0) + { + array_motion_xfield = gensym("x"); + array_motion_xcumulative = + *(float *)((elem + elemsize * i) + xonset); + array_motion_wp = (t_word *)(elem + i * elemsize); + array_motion_npoints = array->a_n - i; + } + else + { + array_motion_xfield = &s_; + array_motion_xcumulative = 0; + array_motion_wp = (t_word *)elem; + array_motion_npoints = array->a_n; + + array_motion_initx = i; + array_motion_lastx = i; + array_motion_xperpix *= (xinc == 0 ? 1 : 1./xinc); + } + if (array_motion_fatten) + { + array_motion_yfield = gensym("w"); + array_motion_ycumulative = + *(float *)((elem + elemsize * i) + wonset); + array_motion_yperpix *= array_motion_fatten; + } + else if (yonset >= 0) + { + array_motion_yfield = gensym("y"); + array_motion_ycumulative = + *(float *)((elem + elemsize * i) + yonset); + } + else + { + array_motion_yfield = &s_; + array_motion_ycumulative = 0; + } + glist_grab(glist, 0, array_motion, 0, xpix, ypix); + } + if (alt) + { + if (xpix < pxpix) + return (CURSOR_EDITMODE_DISCONNECT); + else return (CURSOR_RUNMODE_ADDPOINT); + } + else return (array_motion_fatten ? + CURSOR_RUNMODE_THICKEN : CURSOR_RUNMODE_CLICKME); + } + } + } + return (0); +} + +/* -------------------- widget behavior for garray ------------ */ + +static void garray_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_garray *x = (t_garray *)z; + float x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff; + t_canvas *elemtemplatecanvas; + t_template *elemtemplate; + int elemsize, yonset, wonset, xonset, i; + + if (!array_getfields(x->x_templatesym, &elemtemplatecanvas, + &elemtemplate, &elemsize, &xonset, &yonset, &wonset)) + { + int incr; + /* if it has more than 2000 points, just check 300 of them. */ + if (x->x_array.a_n < 2000) + incr = 1; + else incr = x->x_array.a_n / 300; + for (i = 0; i < x->x_array.a_n; i += incr) + { + float pxpix, pypix, pwpix, dx, dy; + array_getcoordinate(glist, (char *)(x->x_array.a_vec) + + i * elemsize, + xonset, yonset, wonset, i, 0, 0, 1, + &pxpix, &pypix, &pwpix); + if (pwpix < 2) + pwpix = 2; + if (pxpix < x1) + x1 = pxpix; + if (pxpix > x2) + x2 = pxpix; + if (pypix - pwpix < y1) + y1 = pypix - pwpix; + if (pypix + pwpix > y2) + y2 = pypix + pwpix; + } + } + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void garray_displace(t_gobj *z, t_glist *glist, int dx, int dy) +{ + /* refuse */ +} + +static void garray_select(t_gobj *z, t_glist *glist, int state) +{ + t_garray *x = (t_garray *)z; + /* fill in later */ +} + +static void garray_activate(t_gobj *z, t_glist *glist, int state) +{ +} + +static void garray_delete(t_gobj *z, t_glist *glist) +{ + /* nothing to do */ +} + +static void garray_vis(t_gobj *z, t_glist *glist, int vis) +{ + t_garray *x = (t_garray *)z; + if (vis) + { + int i, xonset, yonset, type; + t_symbol *arraytype; + t_template *template = template_findbyname(x->x_templatesym); + if (!template) + return; + if (!template_find_field(template, gensym("y"), &yonset, &type, + &arraytype) || type != DT_FLOAT) + { + error("%s: needs floating-point 'y' field", + x->x_templatesym->s_name); + sys_vgui(".x%x.c create text 50 50 -text foo\ + -tags .x%x.a%x\n", + glist_getcanvas(glist), glist_getcanvas(glist), x); + } + else if (!template_find_field(template, gensym("x"), &xonset, &type, + &arraytype) || type != DT_FLOAT) + { + float firsty, xcum = x->x_firstx; + int lastpixel = -1, ndrawn = 0; + float yval = 0, xpix; + int ixpix = 0; + sys_vgui(".x%x.c create line \\\n", glist_getcanvas(glist)); + for (i = 0; i < x->x_n; i++) + { + yval = fixtof(*(t_sample *)(x->x_vec + + template->t_n * i * sizeof (t_word) + yonset)); + xpix = glist_xtopixels(glist, xcum); + ixpix = xpix + 0.5; + if (ixpix != lastpixel) + { + sys_vgui("%d %f \\\n", ixpix, + glist_ytopixels(glist, yval)); + ndrawn++; + } + lastpixel = ixpix; + if (ndrawn >= 1000) break; + xcum += x->x_xinc; + } + /* TK will complain if there aren't at least 2 points... */ + if (ndrawn == 0) sys_vgui("0 0 0 0 \\\n"); + else if (ndrawn == 1) sys_vgui("%d %f \\\n", ixpix, + glist_ytopixels(glist, yval)); + sys_vgui("-tags .x%x.a%x\n", glist_getcanvas(glist), x); + firsty = fixtof(*(t_sample *)(x->x_vec + yonset)); + sys_vgui(".x%x.c create text %f %f -text {%s} -anchor e\ + -font -*-courier-bold--normal--%d-* -tags .x%x.a%x\n", + glist_getcanvas(glist), + glist_xtopixels(glist, x->x_firstx) - 5., + glist_ytopixels(glist, firsty), + x->x_name->s_name, glist_getfont(glist), + glist_getcanvas(glist), x); + } + else + { + post("x, y arrays not yet supported"); + } + } + else + { + sys_vgui(".x%x.c delete .x%x.a%x\n", + glist_getcanvas(glist), glist_getcanvas(glist), x); + } +} + +static int garray_click(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_garray *x = (t_garray *)z; + return (array_doclick(&x->x_array, glist, z, x->x_templatesym, 1, 0, 1, 0, + xpix, ypix, shift, alt, dbl, doit)); +} + +#define ARRAYWRITECHUNKSIZE 1000 + +static void garray_save(t_gobj *z, t_binbuf *b) +{ + t_garray *x = (t_garray *)z; + binbuf_addv(b, "sssisi;", gensym("#X"), gensym("array"), + x->x_name, x->x_n, x->x_templatesym, x->x_saveit); + fprintf(stderr,"array save\n"); + if (x->x_saveit) + { + int n = x->x_n, n2 = 0; + if (x->x_templatesym != &s_float) + { + pd_error(x, "sorry, you can only save 'float' arrays now"); + return; + } + if (n > 200000) + post("warning: I'm saving an array with %d points!\n", n); + while (n2 < n) + { + int chunk = n - n2, i; + if (chunk > ARRAYWRITECHUNKSIZE) + chunk = ARRAYWRITECHUNKSIZE; + binbuf_addv(b, "si", gensym("#A"), n2); + for (i = 0; i < chunk; i++) + binbuf_addv(b, "f", fixtof(((t_sample *)(x->x_vec))[n2+i])); + binbuf_addv(b, ";"); + n2 += chunk; + } + } +} + +t_widgetbehavior garray_widgetbehavior = +{ + garray_getrect, + garray_displace, + garray_select, + garray_activate, + garray_delete, + garray_vis, + garray_click +}; + +/* ----------------------- public functions -------------------- */ + +void garray_usedindsp(t_garray *x) +{ + x->x_usedindsp = 1; +} + +void garray_redraw(t_garray *x) +{ + if (glist_isvisible(x->x_glist)) + { + garray_vis(&x->x_gobj, x->x_glist, 0); + garray_vis(&x->x_gobj, x->x_glist, 1); + } +} + + /* This functiopn gets the template of an array; if we can't figure + out what template an array's elements belong to we're in grave trouble + when it's time to free or resize it. */ +t_template *garray_template(t_garray *x) +{ + t_template *template = template_findbyname(x->x_templatesym); + if (!template) + bug("garray_template"); + return (template); +} + +int garray_npoints(t_garray *x) /* get the length */ +{ + return (x->x_n); +} + +char *garray_vec(t_garray *x) /* get the contents */ +{ + return ((char *)(x->x_vec)); +} + + /* routine that checks if we're just an array of floats and if + so returns the goods */ + +int garray_getfloatarray(t_garray *x, int *size, t_sample **vec) +{ + t_template *template = garray_template(x); + int yonset, type; + t_symbol *arraytype; + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + error("%s: needs floating-point 'y' field", + x->x_templatesym->s_name); + else if (template->t_n != 1) + error("%s: has more than one field", x->x_templatesym->s_name); + else + { + *size = garray_npoints(x); + *vec = (t_sample *)garray_vec(x); + return (1); + } + return (0); +} + + /* get any floating-point field of any element of an array */ +float garray_get(t_garray *x, t_symbol *s, t_int indx) +{ + t_template *template = garray_template(x); + int yonset, type; + t_symbol *arraytype; + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + { + error("%s: needs floating-point '%s' field", x->x_templatesym->s_name, + s->s_name); + return (0); + } + if (indx < 0) indx = 0; + else if (indx >= x->x_n) indx = x->x_n - 1; + return (*(float *)((x->x_vec + sizeof(t_word) * indx) + yonset)); +} + + /* set the "saveit" flag */ +void garray_setsaveit(t_garray *x, int saveit) +{ + if (x->x_saveit && !saveit) + post("warning: array %s: clearing save-in-patch flag", + x->x_name->s_name); + x->x_saveit = saveit; +} + +/*------------------- Pd messages ------------------------ */ +static void garray_const(t_garray *x, t_floatarg g) +{ + t_template *template = garray_template(x); + int yonset, type, i; + t_symbol *arraytype; + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + error("%s: needs floating-point 'y' field", + x->x_templatesym->s_name); + else for (i = 0; i < x->x_n; i++) + *(float *)(((char *)x->x_vec + sizeof(t_word) * i) + yonset) = g; + garray_redraw(x); +} + + /* sum of Fourier components; called from routines below */ +static void garray_dofo(t_garray *x, int npoints, float dcval, + int nsin, t_float *vsin, int sineflag) +{ + t_template *template = garray_template(x); + int yonset, type, i, j; + t_symbol *arraytype; + double phase, phaseincr, fj; + if (npoints == 0) + npoints = 512; /* dunno what a good default would be... */ + if (npoints != (1 << ilog2(npoints))) + post("%s: rounnding to %d points", x->x_templatesym->s_name, + (npoints = (1<x_templatesym->s_name); + return; + } + for (i = 0, phase = -phaseincr; i < x->x_n; i++, phase += phaseincr ) + { + double sum = dcval; + if (sineflag) + for (j = 0, fj = phase; j < nsin; j++, fj += phase) + sum += vsin[j] * sin(fj); + else + for (j = 0, fj = 0; j < nsin; j++, fj += phase) + sum += vsin[j] * cos(fj); + *(float *)((x->x_vec + sizeof(t_word) * i) + yonset) = sum; + } + garray_redraw(x); +} + +static void garray_sinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv) +{ + t_template *template = garray_template(x); + + t_float *svec = (t_float *)t_getbytes(sizeof(t_float) * argc); + int npoints, i; + if (argc < 2) + { + error("sinesum: %s: need number of points and partial strengths", + x->x_templatesym->s_name); + return; + } + + npoints = atom_getfloatarg(0, argc, argv); + argv++, argc--; + + svec = (t_float *)t_getbytes(sizeof(t_float) * argc); + if (!svec) return; + + for (i = 0; i < argc; i++) + svec[i] = atom_getfloatarg(i, argc, argv); + garray_dofo(x, npoints, 0, argc, svec, 1); + t_freebytes(svec, sizeof(t_float) * argc); +} + +static void garray_cosinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv) +{ + t_template *template = garray_template(x); + + t_float *svec = (t_float *)t_getbytes(sizeof(t_float) * argc); + int npoints, i; + if (argc < 2) + { + error("sinesum: %s: need number of points and partial strengths", + x->x_templatesym->s_name); + return; + } + + npoints = atom_getfloatarg(0, argc, argv); + argv++, argc--; + + svec = (t_float *)t_getbytes(sizeof(t_float) * argc); + if (!svec) return; + + for (i = 0; i < argc; i++) + svec[i] = atom_getfloatarg(i, argc, argv); + garray_dofo(x, npoints, 0, argc, svec, 0); + t_freebytes(svec, sizeof(t_float) * argc); +} + +static void garray_normalize(t_garray *x, t_float f) +{ + t_template *template = garray_template(x); + int yonset, type, npoints, i; + double maxv, renormer; + t_symbol *arraytype; + + if (f <= 0) + f = 1; + + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + { + error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); + return; + } + for (i = 0, maxv = 0; i < x->x_n; i++) + { + double v = *(float *)((x->x_vec + sizeof(t_word) * i) + yonset); + if (v > maxv) + maxv = v; + if (-v > maxv) + maxv = -v; + } + if (maxv >= 0) + { + renormer = f / maxv; + for (i = 0; i < x->x_n; i++) + { + *(float *)((x->x_vec + sizeof(t_word) * i) + yonset) + *= renormer; + } + } + garray_redraw(x); +} + + /* list -- the first value is an index; subsequent values are put in + the "y" slot of the array. This generalizes Max's "table", sort of. */ +static void garray_list(t_garray *x, t_symbol *s, int argc, t_atom *argv) +{ + t_template *template = garray_template(x); + int yonset, type, i; + t_symbol *arraytype; + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + error("%s: needs floating-point 'y' field", + x->x_templatesym->s_name); + else if (argc < 2) return; + else + { + int firstindex = atom_getfloat(argv); + argc--; + argv++; + /* drop negative x values */ + if (firstindex < 0) + { + argc += firstindex; + argv -= firstindex; + firstindex = 0; + if (argc <= 0) return; + } + if (argc + firstindex > x->x_n) + { + argc = x->x_n - firstindex; + if (argc <= 0) return; + } + for (i = 0; i < argc; i++) + *(t_sample *)((x->x_vec + sizeof(t_word) * (i + firstindex)) + yonset) = + ftofix(atom_getfloat(argv + i)); + } + garray_redraw(x); +} + + /* forward a "bounds" message to the owning graph */ +static void garray_bounds(t_garray *x, t_floatarg x1, t_floatarg y1, + t_floatarg x2, t_floatarg y2) +{ + vmess(&x->x_glist->gl_pd, gensym("bounds"), "ffff", x1, y1, x2, y2); +} + + /* same for "xticks", etc */ +static void garray_xticks(t_garray *x, + t_floatarg point, t_floatarg inc, t_floatarg f) +{ + vmess(&x->x_glist->gl_pd, gensym("xticks"), "fff", point, inc, f); +} + +static void garray_yticks(t_garray *x, + t_floatarg point, t_floatarg inc, t_floatarg f) +{ + vmess(&x->x_glist->gl_pd, gensym("yticks"), "fff", point, inc, f); +} + +static void garray_xlabel(t_garray *x, t_symbol *s, int argc, t_atom *argv) +{ + typedmess(&x->x_glist->gl_pd, s, argc, argv); +} + +static void garray_ylabel(t_garray *x, t_symbol *s, int argc, t_atom *argv) +{ + typedmess(&x->x_glist->gl_pd, s, argc, argv); +} + /* change the name of a garray. */ +static void garray_rename(t_garray *x, t_symbol *s) +{ + pd_unbind(&x->x_gobj.g_pd, x->x_realname); + pd_bind(&x->x_gobj.g_pd, x->x_realname = x->x_name = s); + garray_redraw(x); +} + +static void garray_read(t_garray *x, t_symbol *filename) +{ + int nelem = x->x_n, filedesc; + FILE *fd; + char buf[MAXPDSTRING], *bufptr; + t_template *template = garray_template(x); + int yonset, type, i; + t_symbol *arraytype; + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + { + error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); + return; + } + if ((filedesc = open_via_path( + canvas_getdir(glist_getcanvas(x->x_glist))->s_name, + filename->s_name, "", buf, &bufptr, MAXPDSTRING, 0)) < 0 + || !(fd = fdopen(filedesc, "r"))) + { + error("%s: can't open", filename->s_name); + return; + } + for (i = 0; i < nelem; i++) + { + if (!fscanf(fd, "%f", (float *)((x->x_vec + sizeof(t_word) * i) + + yonset))) + { + post("%s: read %d elements into table of size %d", + filename->s_name, i, nelem); + break; + } + } + while (i < nelem) + *(float *)((x->x_vec + sizeof(t_word) * i) + yonset) = 0, i++; + fclose(fd); + garray_redraw(x); +} + + /* this should be renamed and moved... */ +int garray_ambigendian(void) +{ + unsigned short s = 1; + unsigned char c = *(char *)(&s); + return (c==0); +} + +#define BINREADMODE "rb" +#define BINWRITEMODE "wb" + +static void garray_read16(t_garray *x, t_symbol *filename, + t_symbol *endian, t_floatarg fskip) +{ + int skip = fskip, filedesc; + int i, nelem; + t_sample *vec; + FILE *fd; + char buf[MAXPDSTRING], *bufptr; + short s; + int cpubig = garray_ambigendian(), swap = 0; + char c = endian->s_name[0]; + if (c == 'b') + { + if (!cpubig) swap = 1; + } + else if (c == 'l') + { + if (cpubig) swap = 1; + } + else if (c) + { + error("array_read16: endianness is 'l' (low byte first ala INTEL)"); + post("... or 'b' (high byte first ala MIPS,DEC,PPC)"); + } + if (!garray_getfloatarray(x, &nelem, &vec)) + { + error("%s: not a float array", x->x_templatesym->s_name); + return; + } + if ((filedesc = open_via_path( + canvas_getdir(glist_getcanvas(x->x_glist))->s_name, + filename->s_name, "", buf, &bufptr, MAXPDSTRING, 1)) < 0 + || !(fd = fdopen(filedesc, BINREADMODE))) + { + error("%s: can't open", filename->s_name); + return; + } + if (skip) + { + long pos = fseek(fd, (long)skip, SEEK_SET); + if (pos < 0) + { + error("%s: can't seek to byte %d", buf, skip); + fclose(fd); + return; + } + } + + for (i = 0; i < nelem; i++) + { + if (fread(&s, sizeof(s), 1, fd) < 1) + { + post("%s: read %d elements into table of size %d", + filename->s_name, i, nelem); + break; + } + if (swap) s = ((s & 0xff) << 8) | ((s & 0xff00) >> 8); + vec[i] = s * (1./32768.); + } + while (i < nelem) vec[i++] = 0; + fclose(fd); + garray_redraw(x); +} + +static void garray_write(t_garray *x, t_symbol *filename) +{ + FILE *fd; + char buf[MAXPDSTRING]; + t_template *template = garray_template(x); + int yonset, type, i; + t_symbol *arraytype; + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + { + error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); + return; + } + canvas_makefilename(glist_getcanvas(x->x_glist), filename->s_name, + buf, MAXPDSTRING); + sys_bashfilename(buf, buf); + if (!(fd = fopen(buf, "w"))) + { + error("%s: can't create", buf); + return; + } + for (i = 0; i < x->x_n; i++) + { + if (fprintf(fd, "%g\n", + *(float *)((x->x_vec + sizeof(t_word) * i) + yonset)) < 1) + { + post("%s: write error", filename->s_name); + break; + } + } + fclose(fd); +} + +static unsigned char waveheader[] = { +0x52, 0x49, 0x46, 0x46, +0x00, 0x00, 0x00, 0x00, +0x57, 0x41, 0x56, 0x45, +0x66, 0x6d, 0x74, 0x20, + +0x10, 0x00, 0x00, 0x00, +0x01, 0x00, 0x01, 0x00, +0x44, 0xac, 0x00, 0x00, +0x88, 0x58, 0x01, 0x00, + +0x02, 0x00, 0x10, 0x00, +0x64, 0x61, 0x74, 0x61, +0x00, 0x00, 0x00, 0x00, +}; + + /* wave format only so far */ +static void garray_write16(t_garray *x, t_symbol *filename, t_symbol *format) +{ + t_template *template = garray_template(x); + int yonset, type, i; + t_symbol *arraytype; + FILE *fd; + int aiff = (format == gensym("aiff")); + char filenamebuf[MAXPDSTRING], buf2[MAXPDSTRING]; + int swap = garray_ambigendian(); /* wave is only little endian */ + int intbuf; + strncpy(filenamebuf, filename->s_name, MAXPDSTRING-10); + filenamebuf[MAXPDSTRING-10] = 0; + if (sizeof(int) != 4) post("write16: only works on 32-bit machines"); + if (aiff) + { + if (strcmp(filenamebuf + strlen(filenamebuf)-5, ".aiff")) + strcat(filenamebuf, ".aiff"); + } + else + { + if (strcmp(filenamebuf + strlen(filenamebuf)-4, ".wav")) + strcat(filenamebuf, ".wav"); + } + if (!template_find_field(template, gensym("y"), &yonset, + &type, &arraytype) || type != DT_FLOAT) + { + error("%s: needs floating-point 'y' field", x->x_templatesym->s_name); + return; + } + canvas_makefilename(glist_getcanvas(x->x_glist), filenamebuf, + buf2, MAXPDSTRING); + sys_bashfilename(buf2, buf2); + if (!(fd = fopen(buf2, BINWRITEMODE))) + { + error("%s: can't create", buf2); + return; + } + intbuf = 2 * x->x_n + 36; + if (swap) + { + unsigned char *foo = (unsigned char *)&intbuf, xxx; + xxx = foo[0]; foo[0] = foo[3]; foo[3] = xxx; + xxx = foo[1]; foo[1] = foo[2]; foo[2] = xxx; + } + memcpy((void *)(waveheader + 4), (void *)(&intbuf), 4); + intbuf = 2 * x->x_n; + if (swap) + { + unsigned char *foo = (unsigned char *)&intbuf, xxx; + xxx = foo[0]; foo[0] = foo[3]; foo[3] = xxx; + xxx = foo[1]; foo[1] = foo[2]; foo[2] = xxx; + } + memcpy((void *)(waveheader + 40), (void *)(&intbuf), 4); + if (fwrite(waveheader, sizeof(waveheader), 1, fd) < 1) + { + post("%s: write error", buf2); + goto closeit; + } + for (i = 0; i < x->x_n; i++) + { + float f = 32767. * *(float *)((x->x_vec + sizeof(t_word) * i) + yonset); + short sh; + if (f < -32768) f = -32768; + else if (f > 32767) f = 32767; + sh = f; + if (swap) + { + unsigned char *foo = (unsigned char *)&sh, xxx; + xxx = foo[0]; foo[0] = foo[1]; foo[1] = xxx; + } + if (fwrite(&sh, sizeof(sh), 1, fd) < 1) + { + post("%s: write error", buf2); + goto closeit; + } + } +closeit: + fclose(fd); +} + +void garray_resize(t_garray *x, t_floatarg f) +{ + int was = x->x_n, elemsize; + t_glist *gl; + int dspwas; + int n = f; + char *nvec; + + if (n < 1) n = 1; + elemsize = template_findbyname(x->x_templatesym)->t_n * sizeof(t_word); + nvec = t_resizebytes(x->x_vec, was * elemsize, n * elemsize); + if (!nvec) + { + pd_error(x, "array resize failed: out of memory"); + return; + } + x->x_vec = nvec; + /* LATER should check t_resizebytes result */ + if (n > was) + memset(x->x_vec + was*elemsize, + 0, (n - was) * elemsize); + x->x_n = n; + + /* if this is the only array in the graph, + reset the graph's coordinates */ + gl = x->x_glist; + if (gl->gl_list == &x->x_gobj && !x->x_gobj.g_next) + { + vmess(&gl->gl_pd, gensym("bounds"), "ffff", + 0., gl->gl_y1, (double)(n > 1 ? n-1 : 1), gl->gl_y2); + /* close any dialogs that might have the wrong info now... */ + gfxstub_deleteforkey(gl); + } + else garray_redraw(x); + if (x->x_usedindsp) canvas_update_dsp(); +} + +static void garray_print(t_garray *x) +{ + post("garray %s: template %s, length %d", + x->x_name->s_name, x->x_templatesym->s_name, x->x_n); +} + +void g_array_setup(void) +{ + garray_class = class_new(gensym("array"), 0, (t_method)garray_free, + sizeof(t_garray), CLASS_GOBJ, 0); + class_setwidget(garray_class, &garray_widgetbehavior); + class_addmethod(garray_class, (t_method)garray_const, gensym("const"), + A_DEFFLOAT, A_NULL); + class_addlist(garray_class, garray_list); + class_addmethod(garray_class, (t_method)garray_bounds, gensym("bounds"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(garray_class, (t_method)garray_xticks, gensym("xticks"), + A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(garray_class, (t_method)garray_xlabel, gensym("xlabel"), + A_GIMME, 0); + class_addmethod(garray_class, (t_method)garray_yticks, gensym("yticks"), + A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(garray_class, (t_method)garray_ylabel, gensym("ylabel"), + A_GIMME, 0); + class_addmethod(garray_class, (t_method)garray_rename, gensym("rename"), + A_SYMBOL, 0); + class_addmethod(garray_class, (t_method)garray_read, gensym("read"), + A_SYMBOL, A_NULL); + class_addmethod(garray_class, (t_method)garray_read16, gensym("read16"), + A_SYMBOL, A_DEFFLOAT, A_DEFSYM, A_NULL); + class_addmethod(garray_class, (t_method)garray_write, gensym("write"), + A_SYMBOL, A_NULL); + class_addmethod(garray_class, (t_method)garray_write16, gensym("write16"), + A_SYMBOL, A_DEFSYM, A_NULL); + class_addmethod(garray_class, (t_method)garray_resize, gensym("resize"), + A_FLOAT, A_NULL); + class_addmethod(garray_class, (t_method)garray_print, gensym("print"), + A_NULL); + class_addmethod(garray_class, (t_method)garray_sinesum, gensym("sinesum"), + A_GIMME, 0); + class_addmethod(garray_class, (t_method)garray_cosinesum, + gensym("cosinesum"), A_GIMME, 0); + class_addmethod(garray_class, (t_method)garray_normalize, + gensym("normalize"), A_DEFFLOAT, 0); + class_addmethod(garray_class, (t_method)garray_arraydialog, + gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_setsavefn(garray_class, garray_save); +} + + diff --git a/apps/plugins/pdbox/PDa/src/g_bang.c b/apps/plugins/pdbox/PDa/src/g_bang.c new file mode 100644 index 0000000..c676bf8 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_bang.c @@ -0,0 +1,1108 @@ +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + + +/* --------------- bng gui-bang ------------------------- */ + +t_widgetbehavior bng_widgetbehavior; +static t_class *bng_class; + +/* widget helper functions */ + + +void bng_draw_update(t_bng *x, t_glist *glist) +{ + if(glist_isvisible(glist)) + { + sys_vgui(".x%x.c itemconfigure %xBUT -fill #%6.6x\n", glist_getcanvas(glist), x, + x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol); + } +} + +void bng_draw_new(t_bng *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n", + canvas, xpos, ypos, + xpos + x->x_gui.x_w, ypos + x->x_gui.x_h, + x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create oval %d %d %d %d -fill #%6.6x -tags %xBUT\n", + canvas, xpos+1, ypos+1, + xpos + x->x_gui.x_w-1, ypos + x->x_gui.x_h-1, + x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xpos+x->x_gui.x_ldx, + ypos+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xpos, + ypos + x->x_gui.x_h-1, xpos + IOWIDTH, + ypos + x->x_gui.x_h, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xpos, ypos, + xpos + IOWIDTH, ypos+1, x, 0); +} + +void bng_draw_move(t_bng *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n", + canvas, x, xpos, ypos, + xpos + x->x_gui.x_w, ypos + x->x_gui.x_h); + sys_vgui(".x%x.c coords %xBUT %d %d %d %d\n", + canvas, x, xpos+1,ypos+1, + xpos + x->x_gui.x_w-1, ypos + x->x_gui.x_h-1); + sys_vgui(".x%x.c itemconfigure %xBUT -fill #%6.6x\n", canvas, x, + x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol); + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + canvas, x, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, xpos, + ypos + x->x_gui.x_h-1, xpos + IOWIDTH, + ypos + x->x_gui.x_h); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, xpos, ypos, + xpos + IOWIDTH, ypos+1); +} + +void bng_draw_erase(t_bng* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c delete %xBASE\n", canvas, x); + sys_vgui(".x%x.c delete %xBUT\n", canvas, x); + sys_vgui(".x%x.c delete %xLABEL\n", canvas, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void bng_draw_config(t_bng* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas, x, x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xBUT -fill #%6.6x\n", canvas, x, + x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol); +} + +void bng_draw_io(t_bng* x, t_glist* glist, int old_snd_rcv_flags) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xpos, + ypos + x->x_gui.x_h-1, xpos + IOWIDTH, + ypos + x->x_gui.x_h, x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xpos, ypos, + xpos + IOWIDTH, ypos+1, x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void bng_draw_select(t_bng* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + if(x->x_gui.x_fsf.x_selected) + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xBUT -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL); + sys_vgui(".x%x.c itemconfigure %xBUT -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol); + } +} + +void bng_draw(t_bng *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + bng_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + bng_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + bng_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + bng_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + bng_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + bng_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + bng_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ bng widgetbehaviour----------------------------- */ + +static void bng_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_bng *x = (t_bng *)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist); + *yp1 = text_ypix(&x->x_gui.x_obj, glist); + *xp2 = *xp1 + x->x_gui.x_w; + *yp2 = *yp1 + x->x_gui.x_h; +} + +static void bng_save(t_gobj *z, t_binbuf *b) +{ + t_bng *x = (t_bng *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiiiisssiiiiiii", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix, + gensym("bng"), x->x_gui.x_w, + x->x_flashtime_hold, x->x_flashtime_break, + iem_symargstoint(&x->x_gui.x_isa), + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2]); + binbuf_addv(b, ";"); +} + +void bng_check_minmax(t_bng *x, int ftbreak, int fthold) +{ + if(ftbreak > fthold) + { + int h; + + h = ftbreak; + ftbreak = fthold; + fthold = h; + } + if(ftbreak < IEM_BNG_MINBREAKFLASHTIME) + ftbreak = IEM_BNG_MINBREAKFLASHTIME; + if(fthold < IEM_BNG_MINHOLDFLASHTIME) + fthold = IEM_BNG_MINHOLDFLASHTIME; + x->x_flashtime_break = ftbreak; + x->x_flashtime_hold = fthold; +} + +static void bng_properties(t_gobj *z, t_glist *owner) +{ + t_bng *x = (t_bng *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + sprintf(buf, "pdtk_iemgui_dialog %%s BANG \ + ----------dimensions(pix):----------- %d %d size: 0 0 empty \ + --------flash-time(ms)(ms):--------- %d intrrpt: %d hold: %d \ + %d empty empty %d %d empty %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, IEM_GUI_MINSIZE, + x->x_flashtime_break, x->x_flashtime_hold, 2,/*min_max_schedule+clip*/ + -1, x->x_gui.x_isa.x_loadinit, -1, -1,/*no linlog, no multi*/ + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void bng_set(t_bng *x) +{ + if(x->x_flashed) + { + x->x_flashed = 0; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + clock_delay(x->x_clock_brk, x->x_flashtime_break); + x->x_flashed = 1; + } + else + { + x->x_flashed = 1; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } + clock_delay(x->x_clock_hld, x->x_flashtime_hold); +} + +static void bng_bout1(t_bng *x)/*wird nur mehr gesendet, wenn snd != rcv*/ +{ + if(!x->x_gui.x_fsf.x_put_in2out) + { + x->x_gui.x_isa.x_locked = 1; + clock_delay(x->x_clock_lck, 2); + } + outlet_bang(x->x_gui.x_obj.ob_outlet); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing && x->x_gui.x_fsf.x_put_in2out) + pd_bang(x->x_gui.x_snd->s_thing); +} + +static void bng_bout2(t_bng *x)/*wird immer gesendet, wenn moeglich*/ +{ + if(!x->x_gui.x_fsf.x_put_in2out) + { + x->x_gui.x_isa.x_locked = 1; + clock_delay(x->x_clock_lck, 2); + } + outlet_bang(x->x_gui.x_obj.ob_outlet); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_bang(x->x_gui.x_snd->s_thing); +} + +static void bng_bang(t_bng *x)/*wird nur mehr gesendet, wenn snd != rcv*/ +{ + if(!x->x_gui.x_isa.x_locked) + { + bng_set(x); + bng_bout1(x); + } +} + +static void bng_bang2(t_bng *x)/*wird immer gesendet, wenn moeglich*/ +{ + if(!x->x_gui.x_isa.x_locked) + { + bng_set(x); + bng_bout2(x); + } +} + +static void bng_dialog(t_bng *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int a = (int)atom_getintarg(0, argc, argv); + int fthold = (int)atom_getintarg(2, argc, argv); + int ftbreak = (int)atom_getintarg(3, argc, argv); + int sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + bng_check_minmax(x, ftbreak, fthold); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); +} + +static void bng_click(t_bng *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + bng_set(x); + bng_bout2(x); +} + +static int bng_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + if(doit) + bng_click((t_bng *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt); + return (1); +} + +static void bng_float(t_bng *x, t_floatarg f) +{bng_bang2(x);} + +static void bng_symbol(t_bng *x, t_symbol *s) +{bng_bang2(x);} + +static void bng_pointer(t_bng *x, t_gpointer *gp) +{bng_bang2(x);} + +static void bng_list(t_bng *x, t_symbol *s, int ac, t_atom *av) +{ + bng_bang2(x); +} + +static void bng_anything(t_bng *x, t_symbol *s, int argc, t_atom *argv) +{bng_bang2(x);} + +static void bng_loadbang(t_bng *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + { + bng_set(x); + bng_bout2(x); + } +} + +static void bng_size(t_bng *x, t_symbol *s, int ac, t_atom *av) +{ + x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av)); + x->x_gui.x_h = x->x_gui.x_w; + iemgui_size((void *)x, &x->x_gui); +} + +static void bng_delta(t_bng *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void bng_pos(t_bng *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void bng_flashtime(t_bng *x, t_symbol *s, int ac, t_atom *av) +{ + bng_check_minmax(x, (int)atom_getintarg(0, ac, av), + (int)atom_getintarg(1, ac, av)); +} + +static void bng_color(t_bng *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void bng_send(t_bng *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void bng_receive(t_bng *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void bng_label(t_bng *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void bng_label_pos(t_bng *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void bng_label_font(t_bng *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void bng_init(t_bng *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void bng_tick_hld(t_bng *x) +{ + x->x_flashed = 0; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); +} + +static void bng_tick_brk(t_bng *x) +{ + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); +} + +static void bng_tick_lck(t_bng *x) +{ + x->x_gui.x_isa.x_locked = 0; +} + +static void *bng_new(t_symbol *s, int argc, t_atom *argv) +{ + t_bng *x = (t_bng *)pd_new(bng_class); + int bflcol[]={-262144, -1, -1}; + int a=IEM_GUI_DEFAULTSIZE; + int ldx=0, ldy=-6; + int fs=8; + int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, + fthold=IEM_BNG_DEFAULTHOLDFLASHTIME; + char str[144]; + + iem_inttosymargs(&x->x_gui.x_isa, 0); + iem_inttofstyle(&x->x_gui.x_fsf, 0); + + if((argc == 14)&&IS_A_FLOAT(argv,0) + &&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2) + &&IS_A_FLOAT(argv,3) + &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4)) + &&(IS_A_SYMBOL(argv,5)||IS_A_FLOAT(argv,5)) + &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) + &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8) + &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)&&IS_A_FLOAT(argv,11) + &&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13)) + { + + a = (int)atom_getintarg(0, argc, argv); + fthold = (int)atom_getintarg(1, argc, argv); + ftbreak = (int)atom_getintarg(2, argc, argv); + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(3, argc, argv)); + iemgui_new_getnames(&x->x_gui, 4, argv); + ldx = (int)atom_getintarg(7, argc, argv); + ldy = (int)atom_getintarg(8, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(9, argc, argv)); + fs = (int)atom_getintarg(10, argc, argv); + bflcol[0] = (int)atom_getintarg(11, argc, argv); + bflcol[1] = (int)atom_getintarg(12, argc, argv); + bflcol[2] = (int)atom_getintarg(13, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 4, 0); + + x->x_gui.x_draw = (t_iemfunptr)bng_draw; + + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_flashed = 0; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if (!strcmp(x->x_gui.x_snd->s_name, "empty")) + x->x_gui.x_fsf.x_snd_able = 0; + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + + if (x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + bng_check_minmax(x, ftbreak, fthold); + iemgui_all_colfromload(&x->x_gui, bflcol); + x->x_gui.x_isa.x_locked = 0; + iemgui_verify_snd_ne_rcv(&x->x_gui); + x->x_clock_hld = clock_new(x, (t_method)bng_tick_hld); + x->x_clock_brk = clock_new(x, (t_method)bng_tick_brk); + x->x_clock_lck = clock_new(x, (t_method)bng_tick_lck); + outlet_new(&x->x_gui.x_obj, &s_bang); + return (x); +} + +static void bng_ff(t_bng *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + clock_free(x->x_clock_lck); + clock_free(x->x_clock_brk); + clock_free(x->x_clock_hld); + gfxstub_deleteforkey(x); +} + +void g_bang_setup(void) +{ + bng_class = class_new(gensym("bng"), (t_newmethod)bng_new, + (t_method)bng_ff, sizeof(t_bng), 0, A_GIMME, 0); + class_addbang(bng_class, bng_bang); + class_addfloat(bng_class, bng_float); + class_addsymbol(bng_class, bng_symbol); + class_addpointer(bng_class, bng_pointer); + class_addlist(bng_class, bng_list); + class_addanything(bng_class, bng_anything); + class_addmethod(bng_class, (t_method)bng_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(bng_class, (t_method)bng_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_loadbang, gensym("loadbang"), 0); + class_addmethod(bng_class, (t_method)bng_size, gensym("size"), A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_flashtime, gensym("flashtime"), A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_color, gensym("color"), A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_send, gensym("send"), A_DEFSYM, 0); + class_addmethod(bng_class, (t_method)bng_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(bng_class, (t_method)bng_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(bng_class, (t_method)bng_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_label_font, gensym("label_font"), A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_init, gensym("init"), A_FLOAT, 0); + bng_widgetbehavior.w_getrectfn = bng_getrect; + bng_widgetbehavior.w_displacefn = iemgui_displace; + bng_widgetbehavior.w_selectfn = iemgui_select; + bng_widgetbehavior.w_activatefn = NULL; + bng_widgetbehavior.w_deletefn = iemgui_delete; + bng_widgetbehavior.w_visfn = iemgui_vis; + bng_widgetbehavior.w_clickfn = bng_newclick; + class_setwidget(bng_class, &bng_widgetbehavior); + class_sethelpsymbol(bng_class, gensym("bng")); + class_setsavefn(bng_class, bng_save); + class_setpropertiesfn(bng_class, bng_properties); +} +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + + +/* --------------- bng gui-bang ------------------------- */ + +t_widgetbehavior bng_widgetbehavior; +static t_class *bng_class; + +/* widget helper functions */ + + +void bng_draw_update(t_bng *x, t_glist *glist) +{ + if(glist_isvisible(glist)) + { + sys_vgui(".x%x.c itemconfigure %xBUT -fill #%6.6x\n", glist_getcanvas(glist), x, + x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol); + } +} + +void bng_draw_new(t_bng *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n", + canvas, xpos, ypos, + xpos + x->x_gui.x_w, ypos + x->x_gui.x_h, + x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create oval %d %d %d %d -fill #%6.6x -tags %xBUT\n", + canvas, xpos+1, ypos+1, + xpos + x->x_gui.x_w-1, ypos + x->x_gui.x_h-1, + x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xpos+x->x_gui.x_ldx, + ypos+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xpos, + ypos + x->x_gui.x_h-1, xpos + IOWIDTH, + ypos + x->x_gui.x_h, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xpos, ypos, + xpos + IOWIDTH, ypos+1, x, 0); +} + +void bng_draw_move(t_bng *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n", + canvas, x, xpos, ypos, + xpos + x->x_gui.x_w, ypos + x->x_gui.x_h); + sys_vgui(".x%x.c coords %xBUT %d %d %d %d\n", + canvas, x, xpos+1,ypos+1, + xpos + x->x_gui.x_w-1, ypos + x->x_gui.x_h-1); + sys_vgui(".x%x.c itemconfigure %xBUT -fill #%6.6x\n", canvas, x, + x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol); + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + canvas, x, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, xpos, + ypos + x->x_gui.x_h-1, xpos + IOWIDTH, + ypos + x->x_gui.x_h); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, xpos, ypos, + xpos + IOWIDTH, ypos+1); +} + +void bng_draw_erase(t_bng* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c delete %xBASE\n", canvas, x); + sys_vgui(".x%x.c delete %xBUT\n", canvas, x); + sys_vgui(".x%x.c delete %xLABEL\n", canvas, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void bng_draw_config(t_bng* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas, x, x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xBUT -fill #%6.6x\n", canvas, x, + x->x_flashed?x->x_gui.x_fcol:x->x_gui.x_bcol); +} + +void bng_draw_io(t_bng* x, t_glist* glist, int old_snd_rcv_flags) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xpos, + ypos + x->x_gui.x_h-1, xpos + IOWIDTH, + ypos + x->x_gui.x_h, x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xpos, ypos, + xpos + IOWIDTH, ypos+1, x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void bng_draw_select(t_bng* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + if(x->x_gui.x_fsf.x_selected) + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xBUT -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL); + sys_vgui(".x%x.c itemconfigure %xBUT -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol); + } +} + +void bng_draw(t_bng *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + bng_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + bng_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + bng_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + bng_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + bng_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + bng_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + bng_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ bng widgetbehaviour----------------------------- */ + +static void bng_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_bng *x = (t_bng *)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist); + *yp1 = text_ypix(&x->x_gui.x_obj, glist); + *xp2 = *xp1 + x->x_gui.x_w; + *yp2 = *yp1 + x->x_gui.x_h; +} + +static void bng_save(t_gobj *z, t_binbuf *b) +{ + t_bng *x = (t_bng *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiiiisssiiiiiii", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix, + gensym("bng"), x->x_gui.x_w, + x->x_flashtime_hold, x->x_flashtime_break, + iem_symargstoint(&x->x_gui.x_isa), + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2]); + binbuf_addv(b, ";"); +} + +void bng_check_minmax(t_bng *x, int ftbreak, int fthold) +{ + if(ftbreak > fthold) + { + int h; + + h = ftbreak; + ftbreak = fthold; + fthold = h; + } + if(ftbreak < IEM_BNG_MINBREAKFLASHTIME) + ftbreak = IEM_BNG_MINBREAKFLASHTIME; + if(fthold < IEM_BNG_MINHOLDFLASHTIME) + fthold = IEM_BNG_MINHOLDFLASHTIME; + x->x_flashtime_break = ftbreak; + x->x_flashtime_hold = fthold; +} + +static void bng_properties(t_gobj *z, t_glist *owner) +{ + t_bng *x = (t_bng *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + sprintf(buf, "pdtk_iemgui_dialog %%s BANG \ + ----------dimensions(pix):----------- %d %d size: 0 0 empty \ + --------flash-time(ms)(ms):--------- %d intrrpt: %d hold: %d \ + %d empty empty %d %d empty %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, IEM_GUI_MINSIZE, + x->x_flashtime_break, x->x_flashtime_hold, 2,/*min_max_schedule+clip*/ + -1, x->x_gui.x_isa.x_loadinit, -1, -1,/*no linlog, no multi*/ + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void bng_set(t_bng *x) +{ + if(x->x_flashed) + { + x->x_flashed = 0; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + clock_delay(x->x_clock_brk, x->x_flashtime_break); + x->x_flashed = 1; + } + else + { + x->x_flashed = 1; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } + clock_delay(x->x_clock_hld, x->x_flashtime_hold); +} + +static void bng_bout1(t_bng *x)/*wird nur mehr gesendet, wenn snd != rcv*/ +{ + if(!x->x_gui.x_fsf.x_put_in2out) + { + x->x_gui.x_isa.x_locked = 1; + clock_delay(x->x_clock_lck, 2); + } + outlet_bang(x->x_gui.x_obj.ob_outlet); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing && x->x_gui.x_fsf.x_put_in2out) + pd_bang(x->x_gui.x_snd->s_thing); +} + +static void bng_bout2(t_bng *x)/*wird immer gesendet, wenn moeglich*/ +{ + if(!x->x_gui.x_fsf.x_put_in2out) + { + x->x_gui.x_isa.x_locked = 1; + clock_delay(x->x_clock_lck, 2); + } + outlet_bang(x->x_gui.x_obj.ob_outlet); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_bang(x->x_gui.x_snd->s_thing); +} + +static void bng_bang(t_bng *x)/*wird nur mehr gesendet, wenn snd != rcv*/ +{ + if(!x->x_gui.x_isa.x_locked) + { + bng_set(x); + bng_bout1(x); + } +} + +static void bng_bang2(t_bng *x)/*wird immer gesendet, wenn moeglich*/ +{ + if(!x->x_gui.x_isa.x_locked) + { + bng_set(x); + bng_bout2(x); + } +} + +static void bng_dialog(t_bng *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int a = (int)atom_getintarg(0, argc, argv); + int fthold = (int)atom_getintarg(2, argc, argv); + int ftbreak = (int)atom_getintarg(3, argc, argv); + int sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + bng_check_minmax(x, ftbreak, fthold); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); +} + +static void bng_click(t_bng *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + bng_set(x); + bng_bout2(x); +} + +static int bng_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + if(doit) + bng_click((t_bng *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt); + return (1); +} + +static void bng_float(t_bng *x, t_floatarg f) +{bng_bang2(x);} + +static void bng_symbol(t_bng *x, t_symbol *s) +{bng_bang2(x);} + +static void bng_pointer(t_bng *x, t_gpointer *gp) +{bng_bang2(x);} + +static void bng_list(t_bng *x, t_symbol *s, int ac, t_atom *av) +{ + bng_bang2(x); +} + +static void bng_anything(t_bng *x, t_symbol *s, int argc, t_atom *argv) +{bng_bang2(x);} + +static void bng_loadbang(t_bng *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + { + bng_set(x); + bng_bout2(x); + } +} + +static void bng_size(t_bng *x, t_symbol *s, int ac, t_atom *av) +{ + x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av)); + x->x_gui.x_h = x->x_gui.x_w; + iemgui_size((void *)x, &x->x_gui); +} + +static void bng_delta(t_bng *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void bng_pos(t_bng *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void bng_flashtime(t_bng *x, t_symbol *s, int ac, t_atom *av) +{ + bng_check_minmax(x, (int)atom_getintarg(0, ac, av), + (int)atom_getintarg(1, ac, av)); +} + +static void bng_color(t_bng *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void bng_send(t_bng *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void bng_receive(t_bng *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void bng_label(t_bng *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void bng_label_pos(t_bng *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void bng_label_font(t_bng *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void bng_init(t_bng *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void bng_tick_hld(t_bng *x) +{ + x->x_flashed = 0; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); +} + +static void bng_tick_brk(t_bng *x) +{ + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); +} + +static void bng_tick_lck(t_bng *x) +{ + x->x_gui.x_isa.x_locked = 0; +} + +static void *bng_new(t_symbol *s, int argc, t_atom *argv) +{ + t_bng *x = (t_bng *)pd_new(bng_class); + int bflcol[]={-262144, -1, -1}; + int a=IEM_GUI_DEFAULTSIZE; + int ldx=0, ldy=-6; + int fs=8; + int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, + fthold=IEM_BNG_DEFAULTHOLDFLASHTIME; + char str[144]; + + iem_inttosymargs(&x->x_gui.x_isa, 0); + iem_inttofstyle(&x->x_gui.x_fsf, 0); + + if((argc == 14)&&IS_A_FLOAT(argv,0) + &&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2) + &&IS_A_FLOAT(argv,3) + &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4)) + &&(IS_A_SYMBOL(argv,5)||IS_A_FLOAT(argv,5)) + &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) + &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8) + &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)&&IS_A_FLOAT(argv,11) + &&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13)) + { + + a = (int)atom_getintarg(0, argc, argv); + fthold = (int)atom_getintarg(1, argc, argv); + ftbreak = (int)atom_getintarg(2, argc, argv); + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(3, argc, argv)); + iemgui_new_getnames(&x->x_gui, 4, argv); + ldx = (int)atom_getintarg(7, argc, argv); + ldy = (int)atom_getintarg(8, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(9, argc, argv)); + fs = (int)atom_getintarg(10, argc, argv); + bflcol[0] = (int)atom_getintarg(11, argc, argv); + bflcol[1] = (int)atom_getintarg(12, argc, argv); + bflcol[2] = (int)atom_getintarg(13, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 4, 0); + + x->x_gui.x_draw = (t_iemfunptr)bng_draw; + + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_flashed = 0; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if (!strcmp(x->x_gui.x_snd->s_name, "empty")) + x->x_gui.x_fsf.x_snd_able = 0; + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + + if (x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + bng_check_minmax(x, ftbreak, fthold); + iemgui_all_colfromload(&x->x_gui, bflcol); + x->x_gui.x_isa.x_locked = 0; + iemgui_verify_snd_ne_rcv(&x->x_gui); + x->x_clock_hld = clock_new(x, (t_method)bng_tick_hld); + x->x_clock_brk = clock_new(x, (t_method)bng_tick_brk); + x->x_clock_lck = clock_new(x, (t_method)bng_tick_lck); + outlet_new(&x->x_gui.x_obj, &s_bang); + return (x); +} + +static void bng_ff(t_bng *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + clock_free(x->x_clock_lck); + clock_free(x->x_clock_brk); + clock_free(x->x_clock_hld); + gfxstub_deleteforkey(x); +} + +void g_bang_setup(void) +{ + bng_class = class_new(gensym("bng"), (t_newmethod)bng_new, + (t_method)bng_ff, sizeof(t_bng), 0, A_GIMME, 0); + class_addbang(bng_class, bng_bang); + class_addfloat(bng_class, bng_float); + class_addsymbol(bng_class, bng_symbol); + class_addpointer(bng_class, bng_pointer); + class_addlist(bng_class, bng_list); + class_addanything(bng_class, bng_anything); + class_addmethod(bng_class, (t_method)bng_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(bng_class, (t_method)bng_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_loadbang, gensym("loadbang"), 0); + class_addmethod(bng_class, (t_method)bng_size, gensym("size"), A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_flashtime, gensym("flashtime"), A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_color, gensym("color"), A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_send, gensym("send"), A_DEFSYM, 0); + class_addmethod(bng_class, (t_method)bng_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(bng_class, (t_method)bng_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(bng_class, (t_method)bng_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_label_font, gensym("label_font"), A_GIMME, 0); + class_addmethod(bng_class, (t_method)bng_init, gensym("init"), A_FLOAT, 0); + bng_widgetbehavior.w_getrectfn = bng_getrect; + bng_widgetbehavior.w_displacefn = iemgui_displace; + bng_widgetbehavior.w_selectfn = iemgui_select; + bng_widgetbehavior.w_activatefn = NULL; + bng_widgetbehavior.w_deletefn = iemgui_delete; + bng_widgetbehavior.w_visfn = iemgui_vis; + bng_widgetbehavior.w_clickfn = bng_newclick; + class_setwidget(bng_class, &bng_widgetbehavior); + class_sethelpsymbol(bng_class, gensym("bng")); + class_setsavefn(bng_class, bng_save); + class_setpropertiesfn(bng_class, bng_properties); +} diff --git a/apps/plugins/pdbox/PDa/src/g_canvas.c b/apps/plugins/pdbox/PDa/src/g_canvas.c new file mode 100644 index 0000000..f4ef8b1 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_canvas.c @@ -0,0 +1,2952 @@ +/* Copyright (c) 1997-2001 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* this file defines the "glist" class, also known as "canvas" (the two used +to be different but are now unified except for some fossilized names.) */ + +/* changes by Thomas Musil IEM KUG Graz Austria 2001 */ + +/* bug-fix: canvas_menuclose(): by Krzysztof Czaja */ +/* bug-fix: table_new(): I reversed the y-bounds */ + +/* IOhannes : + * changed the canvas_restore, so that it might accept $args as well + * (like "pd $0_test") + * so you can make multiple & distinguishable templates + * 1511:forum::für::umläute:2001 + * changes marked with IOhannes + */ + +#include +#include +#include "m_pd.h" +#include "m_imp.h" +#include "s_stuff.h" +#include "g_canvas.h" +#include +#include "g_all_guis.h" + +struct _canvasenvironment +{ + t_symbol *ce_dir; /* directory patch lives in */ + int ce_argc; /* number of "$" arguments */ + t_atom *ce_argv; /* array of "$" arguments */ + int ce_dollarzero; /* value of "$0" */ +}; + +#define GLIST_DEFCANVASWIDTH 240 +#define GLIST_DEFCANVASHEIGHT 300 + +#ifdef MACOSX +#define GLIST_DEFCANVASYLOC 22 +#else +#define GLIST_DEFCANVASYLOC 0 +#endif + +/* ---------------------- variables --------------------------- */ + +extern t_pd *newest; +t_class *canvas_class; +static int canvas_dspstate; /* whether DSP is on or off */ +t_canvas *canvas_editing; /* last canvas to start text edting */ +t_canvas *canvas_whichfind; /* last canvas we did a find in */ +t_canvas *canvas_list; /* list of all root canvases */ + +/* ------------------ forward function declarations --------------- */ +static void canvas_start_dsp(void); +static void canvas_stop_dsp(void); +static void canvas_drawlines(t_canvas *x); +static void canvas_setbounds(t_canvas *x, int x1, int y1, int x2, int y2); +static void canvas_reflecttitle(t_canvas *x); +static void canvas_addtolist(t_canvas *x); +static void canvas_takeofflist(t_canvas *x); +static void canvas_pop(t_canvas *x, t_floatarg fvis); +void canvas_create_editor(t_glist *x, int createit); + +/* --------- functions to handle the canvas environment ----------- */ + +static t_symbol *canvas_newfilename = &s_; +static t_symbol *canvas_newdirectory = &s_; +static int canvas_newargc; +static t_atom *canvas_newargv; + +static void glist_doupdatewindowlist(t_glist *gl, char *sbuf) +{ + t_gobj *g; + if (!gl->gl_owner) + { + /* this is a canvas; if we have a window, put on "windows" list */ + t_canvas *canvas = (t_canvas *)gl; + if (canvas->gl_havewindow) + { + if (strlen(sbuf) + strlen(gl->gl_name->s_name) + 100 <= 1024) + { + char tbuf[1024]; + sprintf(tbuf, "{%s .x%x} ", gl->gl_name->s_name, (t_int)canvas); + strcat(sbuf, tbuf); + } + } + } + for (g = gl->gl_list; g; g = g->g_next) + { + if (pd_class(&g->g_pd) == canvas_class) + glist_doupdatewindowlist((t_glist *)g, sbuf); + } + return; +} + + /* maintain the list of visible toplevels for the GUI's "windows" menu */ +void canvas_updatewindowlist( void) +{ + t_canvas *x; + char sbuf[1024]; + strcpy(sbuf, "set menu_windowlist {"); + /* find all root canvases */ + for (x = canvas_list; x; x = x->gl_next) + glist_doupdatewindowlist(x, sbuf); + /* next line updates the window menu state before -postcommand tries it */ + strcat(sbuf, "}\npdtk_fixwindowmenu\n"); + sys_gui(sbuf); +} + + /* add a glist the list of "root" canvases (toplevels without parents.) */ +static void canvas_addtolist(t_canvas *x) +{ + x->gl_next = canvas_list; + canvas_list = x; +} + +static void canvas_takeofflist(t_canvas *x) +{ + /* take it off the window list */ + if (x == canvas_list) canvas_list = x->gl_next; + else + { + t_canvas *z; + for (z = canvas_list; z->gl_next != x; z = z->gl_next) + ; + z->gl_next = x->gl_next; + } +} + + +void canvas_setargs(int argc, t_atom *argv) +{ + /* if there's an old one lying around free it here. This + happens if an abstraction is loaded but never gets as far + as calling canvas_new(). */ + if (canvas_newargv) + freebytes(canvas_newargv, canvas_newargc * sizeof(t_atom)); + canvas_newargc = argc; + canvas_newargv = copybytes(argv, argc * sizeof(t_atom)); +} + +void glob_setfilename(void *dummy, t_symbol *filesym, t_symbol *dirsym) +{ + canvas_newfilename = filesym; + canvas_newdirectory = dirsym; +} + +t_canvas *canvas_getcurrent(void) +{ + return ((t_canvas *)pd_findbyclass(&s__X, canvas_class)); +} + +void canvas_setcurrent(t_canvas *x) +{ + pd_pushsym(&x->gl_pd); +} + +void canvas_unsetcurrent(t_canvas *x) +{ + pd_popsym(&x->gl_pd); +} + +t_canvasenvironment *canvas_getenv(t_canvas *x) +{ + if (!x) bug("canvas_getenv"); + while (!x->gl_env) + if (!(x = x->gl_owner)) + bug("t_canvasenvironment", x); + return (x->gl_env); +} + +int canvas_getdollarzero( void) +{ + t_canvas *x = canvas_getcurrent(); + t_canvasenvironment *env = (x ? canvas_getenv(x) : 0); + if (env) + return (env->ce_dollarzero); + else return (0); +} + +void canvas_getargs(int *argcp, t_atom **argvp) +{ + t_canvasenvironment *e = canvas_getenv(canvas_getcurrent()); + *argcp = e->ce_argc; + *argvp = e->ce_argv; +} + +t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s) +{ + t_symbol *ret; + char *name = s->s_name; + if (*name == '$' && name[1] >= '0' && name[1] <= '9') + { + t_canvasenvironment *env = canvas_getenv(x); + canvas_setcurrent(x); + ret = binbuf_realizedollsym(gensym(name+1), + env->ce_argc, env->ce_argv, 1); + canvas_unsetcurrent(x); + } + else ret = s; + return (ret); +} + +t_symbol *canvas_getcurrentdir(void) +{ + t_canvasenvironment *e = canvas_getenv(canvas_getcurrent()); + return (e->ce_dir); +} + +t_symbol *canvas_getdir(t_canvas *x) +{ + t_canvasenvironment *e = canvas_getenv(x); + return (e->ce_dir); +} + +void canvas_makefilename(t_canvas *x, char *file, char *result, int resultsize) +{ + char *dir = canvas_getenv(x)->ce_dir->s_name; + if (file[0] == '/' || (file[0] && file[1] == ':') || !*dir) + { + strncpy(result, file, resultsize); + result[resultsize-1] = 0; + } + else + { + int nleft; + strncpy(result, dir, resultsize); + result[resultsize-1] = 0; + nleft = resultsize - strlen(result) - 1; + if (nleft <= 0) return; + strcat(result, "/"); + strncat(result, file, nleft); + result[resultsize-1] = 0; + } +} + +void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir) +{ + if (strcmp(x->gl_name->s_name, "Pd")) + pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name)); + x->gl_name = s; + if (strcmp(x->gl_name->s_name, "Pd")) + pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name)); + if (glist_isvisible(x)) + canvas_reflecttitle(x); + if (dir && dir != &s_) + { + t_canvasenvironment *e = canvas_getenv(x); + e->ce_dir = dir; + } +} + +/* --------------- traversing the set of lines in a canvas ----------- */ + +int canvas_getindex(t_canvas *x, t_gobj *y) +{ + t_gobj *y2; + int indexno; + for (indexno = 0, y2 = x->gl_list; y2 && y2 != y; y2 = y2->g_next) + indexno++; + return (indexno); +} + +void linetraverser_start(t_linetraverser *t, t_canvas *x) +{ + t->tr_ob = 0; + t->tr_x = x; + t->tr_nextoc = 0; + t->tr_nextoutno = t->tr_nout = 0; +} + +t_outconnect *linetraverser_next(t_linetraverser *t) +{ + t_outconnect *rval = t->tr_nextoc; + int outno; + while (!rval) + { + outno = t->tr_nextoutno; + while (outno == t->tr_nout) + { + t_gobj *y; + t_object *ob = 0; + if (!t->tr_ob) y = t->tr_x->gl_list; + else y = t->tr_ob->ob_g.g_next; + for (; y; y = y->g_next) + if (ob = pd_checkobject(&y->g_pd)) break; + if (!ob) return (0); + t->tr_ob = ob; + t->tr_nout = obj_noutlets(ob); + outno = 0; + if (glist_isvisible(t->tr_x)) + gobj_getrect(y, t->tr_x, + &t->tr_x11, &t->tr_y11, &t->tr_x12, &t->tr_y12); + else t->tr_x11 = t->tr_y11 = t->tr_x12 = t->tr_y12 = 0; + } + t->tr_nextoutno = outno + 1; + rval = obj_starttraverseoutlet(t->tr_ob, &t->tr_outlet, outno); + t->tr_outno = outno; + } + t->tr_nextoc = obj_nexttraverseoutlet(rval, &t->tr_ob2, + &t->tr_inlet, &t->tr_inno); + t->tr_nin = obj_ninlets(t->tr_ob2); + if (!t->tr_nin) bug("drawline"); + if (glist_isvisible(t->tr_x)) + { + int inplus = (t->tr_nin == 1 ? 1 : t->tr_nin - 1); + int outplus = (t->tr_nout == 1 ? 1 : t->tr_nout - 1); + gobj_getrect(&t->tr_ob2->ob_g, t->tr_x, + &t->tr_x21, &t->tr_y21, &t->tr_x22, &t->tr_y22); + t->tr_lx1 = t->tr_x11 + + ((t->tr_x12 - t->tr_x11 - IOWIDTH) * t->tr_outno) / + outplus + IOMIDDLE; + t->tr_ly1 = t->tr_y12; + t->tr_lx2 = t->tr_x21 + + ((t->tr_x22 - t->tr_x21 - IOWIDTH) * t->tr_inno)/inplus + + IOMIDDLE; + t->tr_ly2 = t->tr_y21; + } + else + { + t->tr_x21 = t->tr_y21 = t->tr_x22 = t->tr_y22 = 0; + t->tr_lx1 = t->tr_ly1 = t->tr_lx2 = t->tr_ly2 = 0; + } + + return (rval); +} + +void linetraverser_skipobject(t_linetraverser *t) +{ + t->tr_nextoc = 0; + t->tr_nextoutno = t->tr_nout; +} + +/* -------------------- the canvas object -------------------------- */ +int glist_valid = 10000; + +void glist_init(t_glist *x) +{ + /* zero out everyone except "pd" field */ + memset(((char *)x) + sizeof(x->gl_pd), 0, sizeof(*x) - sizeof(x->gl_pd)); + x->gl_stub = gstub_new(x, 0); + x->gl_valid = ++glist_valid; + x->gl_xlabel = (t_symbol **)t_getbytes(0); + x->gl_ylabel = (t_symbol **)t_getbytes(0); +} + + /* make a new glist. It will either be a "root" canvas or else + its parent will be a "text" object in another window... we don't + know which yet. */ +t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv) +{ + t_canvas *x = (t_canvas *)pd_new(canvas_class); + t_canvas *owner = canvas_getcurrent(); + t_symbol *s = &s_; + int vis = 0, width = GLIST_DEFCANVASWIDTH, height = GLIST_DEFCANVASHEIGHT; + int xloc = 0, yloc = GLIST_DEFCANVASYLOC; + int font = (owner ? owner->gl_font : sys_defaultfont); + glist_init(x); + x->gl_obj.te_type = T_OBJECT; + if (!owner) + canvas_addtolist(x); + /* post("canvas %x, owner %x", x, owner); */ + + if (argc == 5) /* toplevel: x, y, w, h, font */ + { + xloc = atom_getintarg(0, argc, argv); + yloc = atom_getintarg(1, argc, argv); + width = atom_getintarg(2, argc, argv); + height = atom_getintarg(3, argc, argv); + font = atom_getintarg(4, argc, argv); + } + else if (argc == 6) /* subwindow: x, y, w, h, name, vis */ + { + xloc = atom_getintarg(0, argc, argv); + yloc = atom_getintarg(1, argc, argv); + width = atom_getintarg(2, argc, argv); + height = atom_getintarg(3, argc, argv); + s = atom_getsymbolarg(4, argc, argv); + vis = atom_getintarg(5, argc, argv); + } + /* (otherwise assume we're being created from the menu.) */ + + if (canvas_newdirectory->s_name[0]) + { + static int dollarzero = 1000; + t_canvasenvironment *env = x->gl_env = + (t_canvasenvironment *)getbytes(sizeof(*x->gl_env)); + env->ce_dir = canvas_newdirectory; + env->ce_argc = canvas_newargc; + env->ce_argv = canvas_newargv; + env->ce_dollarzero = dollarzero++; + canvas_newdirectory = &s_; + canvas_newargc = 0; + canvas_newargv = 0; + } + else x->gl_env = 0; + + if (yloc < GLIST_DEFCANVASYLOC) + yloc = GLIST_DEFCANVASYLOC; + if (xloc < 0) + xloc = 0; + x->gl_x1 = 0; + x->gl_y1 = 0; + x->gl_x2 = 1; + x->gl_y2 = 1; + canvas_setbounds(x, xloc, yloc, xloc + width, yloc + height); + x->gl_owner = owner; + x->gl_name = (*s->s_name ? s : + (canvas_newfilename ? canvas_newfilename : gensym("Pd"))); + if (strcmp(x->gl_name->s_name, "Pd")) + pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name)); + x->gl_loading = 1; + x->gl_willvis = vis; + x->gl_edit = !strncmp(x->gl_name->s_name, "Untitled", 8); + x->gl_font = sys_nearestfontsize(font); + pd_pushsym(&x->gl_pd); + return(x); +} + +void canvas_setgraph(t_glist *x, int flag); + +static void canvas_coords(t_glist *x, t_symbol *s, int argc, t_atom *argv) +{ + x->gl_x1 = atom_getfloatarg(0, argc, argv); + x->gl_y1 = atom_getfloatarg(1, argc, argv); + x->gl_x2 = atom_getfloatarg(2, argc, argv); + x->gl_y2 = atom_getfloatarg(3, argc, argv); + x->gl_pixwidth = atom_getintarg(4, argc, argv); + x->gl_pixheight = atom_getintarg(5, argc, argv); + canvas_setgraph(x, atom_getintarg(6, argc, argv)); +} + + /* make a new glist and add it to this glist. It will appear as + a "graph", not a text object. */ +t_glist *glist_addglist(t_glist *g, t_symbol *sym, + float x1, float y1, float x2, float y2, + float px1, float py1, float px2, float py2) +{ + static int gcount = 0; + int zz; + int menu = 0; + char *str; + t_glist *x = (t_glist *)pd_new(canvas_class); + glist_init(x); + x->gl_obj.te_type = T_OBJECT; + if (!*sym->s_name) + { + char buf[40]; + sprintf(buf, "graph%d", ++gcount); + sym = gensym(buf); + menu = 1; + } + else if (!strncmp((str = sym->s_name), "graph", 5) + && (zz = atoi(str + 5)) > gcount) + gcount = zz; + /* in 0.34 and earlier, the pixel rectangle and the y bounds were + reversed; this would behave the same, except that the dialog window + would be confusing. The "correct" way is to have "py1" be the value + that is higher on the screen. */ + if (py2 < py1) + { + float zz; + zz = y2; + y2 = y1; + y1 = zz; + zz = py2; + py2 = py1; + py1 = zz; + } + if (x1 == x2 || y1 == y2) + x1 = 0, x2 = 100, y1 = 1, y2 = -1; + if (px1 >= px2 || py1 >= py2) + px1 = 100, py1 = 20, px2 = 100 + GLIST_DEFGRAPHWIDTH, + py2 = 20 + GLIST_DEFGRAPHHEIGHT; + x->gl_name = sym; + x->gl_x1 = x1; + x->gl_x2 = x2; + x->gl_y1 = y1; + x->gl_y2 = y2; + x->gl_obj.te_xpix = px1; + x->gl_obj.te_ypix = py1; + x->gl_pixwidth = px2 - px1; + x->gl_pixheight = py2 - py1; + x->gl_font = (canvas_getcurrent() ? + canvas_getcurrent()->gl_font : sys_defaultfont); + x->gl_screenx1 = x->gl_screeny1 = 0; + x->gl_screenx2 = 240; + x->gl_screeny2 = 300; + if (strcmp(x->gl_name->s_name, "Pd")) + pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name)); + x->gl_owner = g; + x->gl_stretch = 1; + x->gl_isgraph = 1; + x->gl_obj.te_binbuf = binbuf_new(); + binbuf_addv(x->gl_obj.te_binbuf, "s", gensym("graph")); + if (!menu) + pd_pushsym(&x->gl_pd); + glist_add(g, &x->gl_gobj); + if (glist_isvisible(g)) + canvas_create_editor(x, 1); + return (x); +} + + /* call glist_addglist from a Pd message */ +void glist_glist(t_glist *g, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *sym = atom_getsymbolarg(0, argc, argv); + float x1 = atom_getfloatarg(1, argc, argv); + float y1 = atom_getfloatarg(2, argc, argv); + float x2 = atom_getfloatarg(3, argc, argv); + float y2 = atom_getfloatarg(4, argc, argv); + float px1 = atom_getfloatarg(5, argc, argv); + float py1 = atom_getfloatarg(6, argc, argv); + float px2 = atom_getfloatarg(7, argc, argv); + float py2 = atom_getfloatarg(8, argc, argv); + glist_addglist(g, sym, x1, y1, x2, y2, px1, py1, px2, py2); +} + + /* return true if the glist should appear as a graph on parent; + otherwise it appears as a text box. */ +int glist_isgraph(t_glist *x) +{ + return (x->gl_isgraph); +} + + /* This is sent from the GUI to inform a toplevel that its window has been + moved or resized. */ +static void canvas_setbounds(t_canvas *x, int x1, int y1, int x2, int y2) +{ + int heightwas = y2 - y1; + int heightchange = y2 - y1 - (x->gl_screeny2 - x->gl_screeny1); + x->gl_screenx1 = x1; + x->gl_screeny1 = y1; + x->gl_screenx2 = x2; + x->gl_screeny2 = y2; + /* post("set bounds %d %d %d %d", x1, y1, x2, y2); */ + if (!glist_isgraph(x) && (x->gl_y2 < x->gl_y1)) + { + /* if it's flipped so that y grows upward, + fix so that zero is bottom edge and redraw. This is + only appropriate if we're a regular "text" object on the + parent. */ + float diff = x->gl_y1 - x->gl_y2; + t_gobj *y; + x->gl_y1 = heightwas * diff; + x->gl_y2 = x->gl_y1 - diff; + /* and move text objects accordingly; they should stick + to the bottom, not the top. */ + for (y = x->gl_list; y; y = y->g_next) + if (pd_checkobject(&y->g_pd)) + gobj_displace(y, x, 0, heightchange); + canvas_redraw(x); + } +} + +t_symbol *canvas_makebindsym(t_symbol *s) +{ + char buf[MAXPDSTRING]; + strcpy(buf, "pd-"); + strcat(buf, s->s_name); + return (gensym(buf)); +} + +void canvas_reflecttitle(t_canvas *x) +{ + char namebuf[MAXPDSTRING]; + t_canvasenvironment *env = canvas_getenv(x); + if (env->ce_argc) + { + int i; + strcpy(namebuf, " ("); + for (i = 0; i < env->ce_argc; i++) + { + if (strlen(namebuf) > MAXPDSTRING/2 - 5) + break; + if (i != 0) + strcat(namebuf, " "); + atom_string(&env->ce_argv[i], namebuf + strlen(namebuf), + MAXPDSTRING/2); + } + strcat(namebuf, ")"); + } + else namebuf[0] = 0; + sys_vgui("wm title .x%x {%s%c%s - %s}\n", + x, x->gl_name->s_name, (x->gl_dirty? '*' : ' '), namebuf, + canvas_getdir(x)->s_name); +} + +void canvas_dirty(t_canvas *x, t_int n) +{ + t_canvas *x2 = canvas_getrootfor(x); + if ((unsigned)n != x2->gl_dirty) + { + x2->gl_dirty = n; + canvas_reflecttitle(x2); + } +} + + /* the window becomes "mapped" (visible and not miniaturized) or + "unmapped" (either miniaturized or just plain gone.) This should be + called from the GUI after the fact to "notify" us that we're mapped. */ +void canvas_map(t_canvas *x, t_floatarg f) +{ + int flag = (f != 0); + t_gobj *y; + if (flag) + { + if (!glist_isvisible(x)) + { + t_selection *sel; + if (!x->gl_havewindow) + { + bug("canvas_map"); + canvas_vis(x, 1); + } + for (y = x->gl_list; y; y = y->g_next) + gobj_vis(y, x, 1); + for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next) + gobj_select(sel->sel_what, x, 1); + x->gl_mapped = 1; + canvas_drawlines(x); + /* simulate a mouse up so u_main will calculate scrollbars... + ugly! */ + sys_vgui("pdtk_canvas_mouseup .x%x.c 0 0 0\n", x); + } + } + else + { + if (glist_isvisible(x)) + { + /* just clear out the whole canvas... */ + sys_vgui(".x%x.c delete all\n", x); + /* alternatively, we could have erased them one by one... + for (y = x->gl_list; y; y = y->g_next) + gobj_vis(y, x, 0); + ... but we should go through and erase the lines as well + if we do it that way. */ + x->gl_mapped = 0; + } + } +} + +void canvas_redraw(t_canvas *x) +{ + if (glist_isvisible(x)) + { + canvas_map(x, 0); + canvas_map(x, 1); + } +} + +/* ---- editors -- perhaps this and "vis" should go to g_editor.c ------- */ + +static t_editor *editor_new(t_glist *owner) +{ + char buf[40]; + t_editor *x = (t_editor *)getbytes(sizeof(*x)); + x->e_connectbuf = binbuf_new(); + x->e_deleted = binbuf_new(); + x->e_glist = owner; + sprintf(buf, ".x%x", (t_int)owner); + x->e_guiconnect = guiconnect_new(&owner->gl_pd, gensym(buf)); + return (x); +} + +static void editor_free(t_editor *x, t_glist *y) +{ + glist_noselect(y); + guiconnect_notarget(x->e_guiconnect, 1000); + binbuf_free(x->e_connectbuf); + binbuf_free(x->e_deleted); + freebytes((void *)x, sizeof(*x)); +} + + /* recursively create or destroy all editors of a glist and its + sub-glists, as long as they aren't toplevels. */ +void canvas_create_editor(t_glist *x, int createit) +{ + t_gobj *y; + t_object *ob; + if (createit) + { + if (x->gl_editor) + bug("canvas_create_editor"); + else + { + x->gl_editor = editor_new(x); + for (y = x->gl_list; y; y = y->g_next) + if (ob = pd_checkobject(&y->g_pd)) + rtext_new(x, ob); + } + } + else + { + if (!x->gl_editor) + bug("canvas_create_editor"); + else + { + for (y = x->gl_list; y; y = y->g_next) + if (ob = pd_checkobject(&y->g_pd)) + rtext_free(glist_findrtext(x, ob)); + editor_free(x->gl_editor, x); + x->gl_editor = 0; + } + } + for (y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == canvas_class && + ((t_canvas *)y)->gl_isgraph) + canvas_create_editor((t_canvas *)y, createit); +} + + /* we call this when we want the window to become visible, mapped, and + in front of all windows; or with "f" zero, when we want to get rid of + the window. */ +void canvas_vis(t_canvas *x, t_floatarg f) +{ + char buf[30]; + int flag = (f != 0); + if (flag) + { + /* test if we're already visible and toplevel */ + if (glist_isvisible(x) && !x->gl_isgraph) + { /* just put us in front */ +#ifdef MSW + canvas_vis(x, 0); + canvas_vis(x, 1); +#else + sys_vgui("raise .x%x\n", x); + sys_vgui("focus .x%x.c\n", x); + sys_vgui("wm deiconify .x%x\n", x); +#endif + } + else + { + canvas_create_editor(x, 1); + sys_vgui("pdtk_canvas_new .x%x %d %d +%d+%d %d\n", x, + (int)(x->gl_screenx2 - x->gl_screenx1), + (int)(x->gl_screeny2 - x->gl_screeny1), + (int)(x->gl_screenx1), (int)(x->gl_screeny1), + x->gl_edit); + canvas_reflecttitle(x); + x->gl_havewindow = 1; + canvas_updatewindowlist(); + } + } + else /* make invisible */ + { + int i; + t_canvas *x2; + if (!x->gl_havewindow) + { + /* bug workaround -- a graph in a visible patch gets "invised" + when the patch is closed, and must lose the editor here. It's + probably not the natural place to do this. Other cases like + subpatches fall here too but don'd need the editor freed, so + we check if it exists. */ + if (x->gl_editor) + canvas_create_editor(x, 0); + return; + } + glist_noselect(x); + if (glist_isvisible(x)) + canvas_map(x, 0); + canvas_create_editor(x, 0); + sys_vgui("destroy .x%x\n", x); + for (i = 1, x2 = x; x2; x2 = x2->gl_next, i++) + ; + sys_vgui(".mbar.find delete %d\n", i); + /* if we're a graph on our parent, and if the parent exists + and is visible, show ourselves on parent. */ + if (glist_isgraph(x) && x->gl_owner) + { + t_glist *gl2 = x->gl_owner; + canvas_create_editor(x, 1); + if (glist_isvisible(gl2)) + gobj_vis(&x->gl_gobj, gl2, 0); + x->gl_havewindow = 0; + if (glist_isvisible(gl2)) + gobj_vis(&x->gl_gobj, gl2, 1); + } + else x->gl_havewindow = 0; + canvas_updatewindowlist(); + } +} + + /* we call this on a non-toplevel glist to "open" it into its + own window. */ +void glist_menu_open(t_glist *x) +{ + if (glist_isvisible(x) && !glist_istoplevel(x)) + { + t_glist *gl2 = x->gl_owner; + if (!gl2) + bug("canvas_vis"); /* shouldn't happen but don't get too upset. */ + else + { + /* erase ourself in parent window */ + gobj_vis(&x->gl_gobj, gl2, 0); + /* get rid of our editor (and subeditors) */ + canvas_create_editor(x, 0); + x->gl_havewindow = 1; + /* redraw ourself in parent window (blanked out this time) */ + gobj_vis(&x->gl_gobj, gl2, 1); + } + } + canvas_vis(x, 1); +} + +int glist_isvisible(t_glist *x) +{ + return ((!x->gl_loading) && glist_getcanvas(x)->gl_mapped); +} + +int glist_istoplevel(t_glist *x) +{ + /* we consider a graph "toplevel" if it has its own window + or if it appears as a box in its parent window so that we + don't draw the actual contents there. */ + return (x->gl_havewindow || !x->gl_isgraph); +} + +int glist_getfont(t_glist *x) +{ + return (glist_getcanvas(x)->gl_font); +} + +void canvas_free(t_canvas *x) +{ + t_gobj *y; + int dspstate = canvas_suspend_dsp(); + canvas_noundo(x); + if (canvas_editing == x) + canvas_editing = 0; + if (canvas_whichfind == x) + canvas_whichfind = 0; + glist_noselect(x); + while (y = x->gl_list) + glist_delete(x, y); + canvas_vis(x, 0); + + if (strcmp(x->gl_name->s_name, "Pd")) + pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name)); + if (x->gl_env) + { + freebytes(x->gl_env->ce_argv, x->gl_env->ce_argc * sizeof(t_atom)); + freebytes(x->gl_env, sizeof(*x->gl_env)); + } + canvas_resume_dsp(dspstate); + glist_cleanup(x); + gfxstub_deleteforkey(x); /* probably unnecessary */ + if (!x->gl_owner) + canvas_takeofflist(x); +} + +/* ----------------- lines ---------- */ + +static void canvas_drawlines(t_canvas *x) +{ + t_linetraverser t; + t_outconnect *oc; + { + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n", + glist_getcanvas(x), + t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2, + (outlet_getsymbol(t.tr_outlet) == &s_signal ? 2:1), + oc); + } +} + +void canvas_fixlinesfor(t_canvas *x, t_text *text) +{ + t_linetraverser t; + t_outconnect *oc; + + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + if (t.tr_ob == text || t.tr_ob2 == text) + { + sys_vgui(".x%x.c coords l%x %d %d %d %d\n", + glist_getcanvas(x), oc, + t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2); + } + } +} + + /* kill all lines for the object */ +void canvas_deletelinesfor(t_canvas *x, t_text *text) +{ + t_linetraverser t; + t_outconnect *oc; + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + if (t.tr_ob == text || t.tr_ob2 == text) + { + if (x->gl_editor) + { + sys_vgui(".x%x.c delete l%x\n", + glist_getcanvas(x), oc); + } + obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno); + } + } +} + + /* kill all lines for one inlet or outlet */ +void canvas_deletelinesforio(t_canvas *x, t_text *text, + t_inlet *inp, t_outlet *outp) +{ + t_linetraverser t; + t_outconnect *oc; + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + if ((t.tr_ob == text && t.tr_outlet == outp) || + (t.tr_ob2 == text && t.tr_inlet == inp)) + { + if (x->gl_editor) + { + sys_vgui(".x%x.c delete l%x\n", + glist_getcanvas(x), oc); + } + obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno); + } + } +} + +static void canvas_pop(t_canvas *x, t_floatarg fvis) +{ + if (fvis != 0) + canvas_vis(x, 1); + pd_popsym(&x->gl_pd); + canvas_resortinlets(x); + canvas_resortoutlets(x); + x->gl_loading = 0; +} + +void canvas_objfor(t_glist *gl, t_text *x, int argc, t_atom *argv); + + +void canvas_restore(t_canvas *x, t_symbol *s, int argc, t_atom *argv) +{ /* IOhannes */ + t_pd *z; + /* this should be unnecessary, but sometimes the canvas's name gets + out of sync with the owning box's argument; this fixes that */ + if (argc > 3) + { + t_atom *ap=argv+3; + if (ap->a_type == A_SYMBOL) + { + char *buf=ap->a_w.w_symbol->s_name, *bufp; + if (*buf == '$' && buf[1] >= '0' && buf[1] <= '9') + { + for (bufp = buf+2; *bufp; bufp++) + if (*bufp < '0' || *bufp > '9') + { + SETDOLLSYM(ap, gensym(buf+1)); + goto didit; + } + SETDOLLAR(ap, atoi(buf+1)); + didit: ; + } + } + + if (ap->a_type == A_DOLLSYM) + { + t_canvasenvironment *e = canvas_getenv(canvas_getcurrent()); + canvas_rename(x, binbuf_realizedollsym(ap->a_w.w_symbol, + e->ce_argc, e->ce_argv, 1), 0); + } + else if (ap->a_type == A_SYMBOL) + canvas_rename(x, argv[3].a_w.w_symbol, 0); + } + canvas_pop(x, x->gl_willvis); + + if (!(z = gensym("#X")->s_thing)) error("canvas_restore: out of context"); + else if (*z != canvas_class) error("canvas_restore: wasn't a canvas"); + else + { + t_canvas *x2 = (t_canvas *)z; + x->gl_owner = x2; + canvas_objfor(x2, &x->gl_obj, argc, argv); + } +} + +static void canvas_loadbangabstractions(t_canvas *x) +{ + t_gobj *y; + t_symbol *s = gensym("loadbang"); + for (y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == canvas_class) + { + if (canvas_isabstraction((t_canvas *)y)) + canvas_loadbang((t_canvas *)y); + else + canvas_loadbangabstractions((t_canvas *)y); + } +} + +void canvas_loadbangsubpatches(t_canvas *x) +{ + t_gobj *y; + t_symbol *s = gensym("loadbang"); + for (y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == canvas_class) + { + if (!canvas_isabstraction((t_canvas *)y)) + canvas_loadbangsubpatches((t_canvas *)y); + } + for (y = x->gl_list; y; y = y->g_next) + if ((pd_class(&y->g_pd) != canvas_class) && + zgetfn(&y->g_pd, s)) + pd_vmess(&y->g_pd, s, ""); +} + +void canvas_loadbang(t_canvas *x) +{ + t_gobj *y; + canvas_loadbangabstractions(x); + canvas_loadbangsubpatches(x); +} + + /* When you ask a canvas its size the result is 2 pixels more than what + you gave it to open it; perhaps there's a 1-pixel border all around it + or something. Anyway, we just add the 2 pixels back here; seems we + have to do this for linux but not MSW; not sure about MacOS. */ + +#ifdef MSW +#define HORIZBORDER 0 +#define VERTBORDER 0 +#else +#define HORIZBORDER 2 +#define VERTBORDER 2 +#endif + +static void canvas_relocate(t_canvas *x, t_symbol *canvasgeom, + t_symbol *topgeom) +{ + int cxpix, cypix, cw, ch, txpix, typix, tw, th; + if (sscanf(canvasgeom->s_name, "%dx%d+%d+%d", &cw, &ch, &cxpix, &cypix) + < 4 || + sscanf(topgeom->s_name, "%dx%d+%d+%d", &tw, &th, &txpix, &typix) < 4) + bug("canvas_relocate"); + /* for some reason this is initially called with cw=ch=1 so + we just suppress that here. */ + if (cw > 5 && ch > 5) + canvas_setbounds(x, txpix, typix, + txpix + cw - HORIZBORDER, typix + ch - VERTBORDER); +} + +void canvas_popabstraction(t_canvas *x) +{ + newest = &x->gl_pd; + pd_popsym(&x->gl_pd); + x->gl_loading = 0; + canvas_resortinlets(x); + canvas_resortoutlets(x); +} + +void canvas_logerror(t_object *y) +{ +#ifdef LATER + canvas_vis(x, 1); + if (!glist_isselected(x, &y->ob_g)) + glist_select(x, &y->ob_g); +#endif +} + +/* -------------------------- subcanvases ---------------------- */ + +static void *subcanvas_new(t_symbol *s) +{ + t_atom a[6]; + t_canvas *x, *z = canvas_getcurrent(); + if (!*s->s_name) s = gensym("/SUBPATCH/"); + SETFLOAT(a, 0); + SETFLOAT(a+1, GLIST_DEFCANVASYLOC); + SETFLOAT(a+2, GLIST_DEFCANVASWIDTH); + SETFLOAT(a+3, GLIST_DEFCANVASHEIGHT); + SETSYMBOL(a+4, s); + SETFLOAT(a+5, 1); + x = canvas_new(0, 0, 6, a); + x->gl_owner = z; + canvas_pop(x, 1); + return (x); +} + +static void canvas_click(t_canvas *x, + t_floatarg xpos, t_floatarg ypos, + t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + canvas_vis(x, 1); +} + + + /* find out from subcanvas contents how much to fatten the box */ +void canvas_fattensub(t_canvas *x, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_gobj *y; + *xp2 += 50; /* fake for now */ + *yp2 += 50; +} + +static void canvas_rename_method(t_canvas *x, t_symbol *s, int ac, t_atom *av) +{ + if (ac && av->a_type == A_SYMBOL) + canvas_rename(x, av->a_w.w_symbol, 0); + else canvas_rename(x, gensym("Pd"), 0); +} + +/* ------------------ table ---------------------------*/ + +static int tabcount = 0; + +static void *table_new(t_symbol *s, t_floatarg f) +{ + t_atom a[9]; + t_glist *gl; + t_canvas *x, *z = canvas_getcurrent(); + if (s == &s_) + { + char tabname[255]; + t_symbol *t = gensym("table"); + sprintf(tabname, "%s%d", t->s_name, tabcount++); + s = gensym(tabname); + } + if (f <= 1) + f = 100; + SETFLOAT(a, 0); + SETFLOAT(a+1, GLIST_DEFCANVASYLOC); + SETFLOAT(a+2, 600); + SETFLOAT(a+3, 400); + SETSYMBOL(a+4, s); + SETFLOAT(a+5, 0); + x = canvas_new(0, 0, 6, a); + + x->gl_owner = z; + + /* create a graph for the table */ + gl = glist_addglist((t_glist*)x, &s_, 0, -1, (f > 1 ? f-1 : 1), 1, + 50, 350, 550, 50); + + graph_array(gl, s, &s_float, f, 0); + + canvas_pop(x, 0); + + return (x); +} + + /* return true if the "canvas" object is an abstraction (so we don't + save its contents, fogr example.) */ +int canvas_isabstraction(t_canvas *x) +{ + return (x->gl_env != 0); +} + + /* return true if the "canvas" object is a "table". */ +int canvas_istable(t_canvas *x) +{ + t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0); + int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0); + int istable = (argc && argv[0].a_type == A_SYMBOL && + argv[0].a_w.w_symbol == gensym("table")); + return (istable); +} + + /* return true if the "canvas" object should be treated as a text + object. This is true for abstractions but also for "table"s... */ +int canvas_showtext(t_canvas *x) +{ + t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0); + int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0); + int isarray = (argc && argv[0].a_type == A_SYMBOL && + argv[0].a_w.w_symbol == gensym("graph")); + return (!isarray); +} + +static void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp); +static void canvas_dsp(t_canvas *x, t_signal **sp) +{ + canvas_dodsp(x, 0, sp); +} + + /* get the document containing this canvas */ +t_canvas *canvas_getrootfor(t_canvas *x) +{ + if ((!x->gl_owner) || canvas_isabstraction(x)) + return (x); + else return (canvas_getrootfor(x->gl_owner)); +} + +/* ------------------------- DSP chain handling ------------------------- */ + +EXTERN_STRUCT _dspcontext; +#define t_dspcontext struct _dspcontext + +void ugen_start(void); +void ugen_stop(void); + +t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp, + int ninlets, int noutlets); +void ugen_add(t_dspcontext *dc, t_object *x); +void ugen_connect(t_dspcontext *dc, t_object *x1, int outno, + t_object *x2, int inno); +void ugen_done_graph(t_dspcontext *dc); + + /* schedule one canvas for DSP. This is called below for all "root" + canvases, but is also called from the "dsp" method for sub- + canvases, which are treated almost like any other tilde object. */ + +static void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp) +{ + t_linetraverser t; + t_outconnect *oc; + t_gobj *y; + t_object *ob; + t_symbol *dspsym = gensym("dsp"); + t_dspcontext *dc; + + /* create a new "DSP graph" object to use in sorting this canvas. + If we aren't toplevel, there are already other dspcontexts around. */ + + dc = ugen_start_graph(toplevel, sp, + obj_nsiginlets(&x->gl_obj), + obj_nsigoutlets(&x->gl_obj)); + + /* find all the "dsp" boxes and add them to the graph */ + + for (y = x->gl_list; y; y = y->g_next) + if ((ob = pd_checkobject(&y->g_pd)) && zgetfn(&y->g_pd, dspsym)) + ugen_add(dc, ob); + + /* ... and all dsp interconnections */ + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + if (obj_issignaloutlet(t.tr_ob, t.tr_outno)) + ugen_connect(dc, t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno); + + /* finally, sort them and add them to the DSP chain */ + ugen_done_graph(dc); +} + + /* this routine starts DSP for all root canvases. */ +static void canvas_start_dsp(void) +{ + t_canvas *x; + if (canvas_dspstate) ugen_stop(); + else sys_gui("pdtk_pd_dsp ON\n"); + ugen_start(); + + for (x = canvas_list; x; x = x->gl_next) + canvas_dodsp(x, 1, 0); + + canvas_dspstate = 1; +} + +static void canvas_stop_dsp(void) +{ + if (canvas_dspstate) + { + ugen_stop(); + sys_gui("pdtk_pd_dsp OFF\n"); + canvas_dspstate = 0; + } +} + + /* DSP can be suspended before, and resumed after, operations which + might affect the DSP chain. For example, we suspend before loading and + resume afterward, so that DSP doesn't get resorted for every DSP object + int the patch. */ + +int canvas_suspend_dsp(void) +{ + int rval = canvas_dspstate; + if (rval) canvas_stop_dsp(); + return (rval); +} + +void canvas_resume_dsp(int oldstate) +{ + if (oldstate) canvas_start_dsp(); +} + + /* this is equivalent to suspending and resuming in one step. */ +void canvas_update_dsp(void) +{ + if (canvas_dspstate) canvas_start_dsp(); +} + +void glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv) +{ + int newstate; + if (argc) + { + newstate = atom_getintarg(0, argc, argv); + if (newstate && !canvas_dspstate) + { + sys_set_audio_state(1); + canvas_start_dsp(); + } + else if (!newstate && canvas_dspstate) + { + canvas_stop_dsp(); + sys_set_audio_state(0); + } + } + else post("dsp state %d", canvas_dspstate); +} + + /* LATER replace this with a queueing scheme */ +void glist_redrawitem(t_glist *owner, t_gobj *gobj) +{ + if (glist_isvisible(owner)) + { + gobj_vis(gobj, owner, 0); + gobj_vis(gobj, owner, 1); + } +} + + /* redraw all "scalars" (do this if a drawing command is changed.) + LATER we'll use the "template" information to select which ones we + redraw. */ +static void glist_redrawall(t_glist *gl) +{ + t_gobj *g; + int vis = glist_isvisible(gl); + for (g = gl->gl_list; g; g = g->g_next) + { + t_class *cl; + if (vis && g->g_pd == scalar_class) + glist_redrawitem(gl, g); + else if (g->g_pd == canvas_class) + glist_redrawall((t_glist *)g); + } +} + + /* public interface for above */ +void canvas_redrawallfortemplate(t_canvas *templatecanvas) +{ + t_canvas *x; + /* find all root canvases */ + for (x = canvas_list; x; x = x->gl_next) + glist_redrawall(x); +} + +/* ------------------------------- setup routine ------------------------ */ + + /* why are some of these "glist" and others "canvas"? */ +extern void glist_text(t_glist *x, t_symbol *s, int argc, t_atom *argv); +extern void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_bng(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_toggle(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_vslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_hslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_vdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv); + /* old version... */ +extern void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv); + /* new version: */ +extern void canvas_hradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_vradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_vumeter(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_mycnv(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void glist_scalar(t_glist *canvas, t_symbol *s, int argc, t_atom *argv); + +void g_graph_setup(void); +void g_editor_setup(void); +void g_readwrite_setup(void); +extern void graph_properties(t_gobj *z, t_glist *owner); + +void g_canvas_setup(void) +{ + /* we prevent the user from typing "canvas" in an object box + by sending 0 for a creator function. */ + canvas_class = class_new(gensym("canvas"), 0, + (t_method)canvas_free, sizeof(t_canvas), CLASS_NOINLET, 0); + /* here is the real creator function, invoked in patch files + by sending the "canvas" message to #N, which is bound + to pd_camvasmaker. */ + class_addmethod(pd_canvasmaker, (t_method)canvas_new, gensym("canvas"), + A_GIMME, 0); + class_addmethod(canvas_class, (t_method)canvas_restore, + gensym("restore"), A_GIMME, 0); + class_addmethod(canvas_class, (t_method)canvas_coords, + gensym("coords"), A_GIMME, 0); + +/* -------------------------- objects ----------------------------- */ + class_addmethod(canvas_class, (t_method)canvas_obj, + gensym("obj"), A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_msg, + gensym("msg"), A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_floatatom, + gensym("floatatom"), A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_symbolatom, + gensym("symbolatom"), A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)glist_text, + gensym("text"), A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)glist_glist, gensym("graph"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)glist_scalar, + gensym("scalar"), A_GIMME, A_NULL); + + /* -------------- Thomas Musil's GUI objects ------------ */ + class_addmethod(canvas_class, (t_method)canvas_bng, gensym("bng"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_toggle, gensym("toggle"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_vslider, gensym("vslider"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_hslider, gensym("hslider"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_hdial, gensym("hdial"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_vdial, gensym("vdial"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_hradio, gensym("hradio"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_vradio, gensym("vradio"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_vumeter, gensym("vumeter"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_mycnv, gensym("mycnv"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_numbox, gensym("numbox"), + A_GIMME, A_NULL); + +/* ------------------------ gui stuff --------------------------- */ + class_addmethod(canvas_class, (t_method)canvas_pop, gensym("pop"), + A_DEFFLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_loadbang, + gensym("loadbang"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_relocate, + gensym("relocate"), A_SYMBOL, A_SYMBOL, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_vis, + gensym("vis"), A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)glist_menu_open, + gensym("menu-open"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_map, + gensym("map"), A_FLOAT, A_NULL); + class_setpropertiesfn(canvas_class, graph_properties); + +/* ---------------------- list handling ------------------------ */ + class_addmethod(canvas_class, (t_method)glist_clear, gensym("clear"), + A_NULL); + +/* ----- subcanvases, which you get by typing "pd" in a box ---- */ + class_addcreator((t_newmethod)subcanvas_new, gensym("pd"), A_DEFSYMBOL, 0); + class_addcreator((t_newmethod)subcanvas_new, gensym("page"), A_DEFSYMBOL, 0); + + class_addmethod(canvas_class, (t_method)canvas_click, + gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(canvas_class, (t_method)canvas_dsp, gensym("dsp"), 0); + class_addmethod(canvas_class, (t_method)canvas_rename_method, + gensym("rename"), A_GIMME, 0); + +/*---------------------------- tables -- GG ------------------- */ + + class_addcreator((t_newmethod)table_new, gensym("table"), + A_DEFSYM, A_DEFFLOAT, 0); + +/* -------------- setups from other files for canvas_class ---------------- */ + g_graph_setup(); + g_editor_setup(); + g_readwrite_setup(); +} +/* Copyright (c) 1997-2001 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* this file defines the "glist" class, also known as "canvas" (the two used +to be different but are now unified except for some fossilized names.) */ + +/* changes by Thomas Musil IEM KUG Graz Austria 2001 */ + +/* bug-fix: canvas_menuclose(): by Krzysztof Czaja */ +/* bug-fix: table_new(): I reversed the y-bounds */ + +/* IOhannes : + * changed the canvas_restore, so that it might accept $args as well + * (like "pd $0_test") + * so you can make multiple & distinguishable templates + * 1511:forum::für::umläute:2001 + * changes marked with IOhannes + */ + +#include +#include +#include "m_pd.h" +#include "m_imp.h" +#include "s_stuff.h" +#include "g_canvas.h" +#include +#include "g_all_guis.h" + +struct _canvasenvironment +{ + t_symbol *ce_dir; /* directory patch lives in */ + int ce_argc; /* number of "$" arguments */ + t_atom *ce_argv; /* array of "$" arguments */ + int ce_dollarzero; /* value of "$0" */ +}; + +#define GLIST_DEFCANVASWIDTH 240 +#define GLIST_DEFCANVASHEIGHT 300 + +#ifdef MACOSX +#define GLIST_DEFCANVASYLOC 22 +#else +#define GLIST_DEFCANVASYLOC 0 +#endif + +/* ---------------------- variables --------------------------- */ + +extern t_pd *newest; +t_class *canvas_class; +static int canvas_dspstate; /* whether DSP is on or off */ +t_canvas *canvas_editing; /* last canvas to start text edting */ +t_canvas *canvas_whichfind; /* last canvas we did a find in */ +t_canvas *canvas_list; /* list of all root canvases */ + +/* ------------------ forward function declarations --------------- */ +static void canvas_start_dsp(void); +static void canvas_stop_dsp(void); +static void canvas_drawlines(t_canvas *x); +static void canvas_setbounds(t_canvas *x, int x1, int y1, int x2, int y2); +static void canvas_reflecttitle(t_canvas *x); +static void canvas_addtolist(t_canvas *x); +static void canvas_takeofflist(t_canvas *x); +static void canvas_pop(t_canvas *x, t_floatarg fvis); +void canvas_create_editor(t_glist *x, int createit); + +/* --------- functions to handle the canvas environment ----------- */ + +static t_symbol *canvas_newfilename = &s_; +static t_symbol *canvas_newdirectory = &s_; +static int canvas_newargc; +static t_atom *canvas_newargv; + +static void glist_doupdatewindowlist(t_glist *gl, char *sbuf) +{ + t_gobj *g; + if (!gl->gl_owner) + { + /* this is a canvas; if we have a window, put on "windows" list */ + t_canvas *canvas = (t_canvas *)gl; + if (canvas->gl_havewindow) + { + if (strlen(sbuf) + strlen(gl->gl_name->s_name) + 100 <= 1024) + { + char tbuf[1024]; + sprintf(tbuf, "{%s .x%x} ", gl->gl_name->s_name, (t_int)canvas); + strcat(sbuf, tbuf); + } + } + } + for (g = gl->gl_list; g; g = g->g_next) + { + if (pd_class(&g->g_pd) == canvas_class) + glist_doupdatewindowlist((t_glist *)g, sbuf); + } + return; +} + + /* maintain the list of visible toplevels for the GUI's "windows" menu */ +void canvas_updatewindowlist( void) +{ + t_canvas *x; + char sbuf[1024]; + strcpy(sbuf, "set menu_windowlist {"); + /* find all root canvases */ + for (x = canvas_list; x; x = x->gl_next) + glist_doupdatewindowlist(x, sbuf); + /* next line updates the window menu state before -postcommand tries it */ + strcat(sbuf, "}\npdtk_fixwindowmenu\n"); + sys_gui(sbuf); +} + + /* add a glist the list of "root" canvases (toplevels without parents.) */ +static void canvas_addtolist(t_canvas *x) +{ + x->gl_next = canvas_list; + canvas_list = x; +} + +static void canvas_takeofflist(t_canvas *x) +{ + /* take it off the window list */ + if (x == canvas_list) canvas_list = x->gl_next; + else + { + t_canvas *z; + for (z = canvas_list; z->gl_next != x; z = z->gl_next) + ; + z->gl_next = x->gl_next; + } +} + + +void canvas_setargs(int argc, t_atom *argv) +{ + /* if there's an old one lying around free it here. This + happens if an abstraction is loaded but never gets as far + as calling canvas_new(). */ + if (canvas_newargv) + freebytes(canvas_newargv, canvas_newargc * sizeof(t_atom)); + canvas_newargc = argc; + canvas_newargv = copybytes(argv, argc * sizeof(t_atom)); +} + +void glob_setfilename(void *dummy, t_symbol *filesym, t_symbol *dirsym) +{ + canvas_newfilename = filesym; + canvas_newdirectory = dirsym; +} + +t_canvas *canvas_getcurrent(void) +{ + return ((t_canvas *)pd_findbyclass(&s__X, canvas_class)); +} + +void canvas_setcurrent(t_canvas *x) +{ + pd_pushsym(&x->gl_pd); +} + +void canvas_unsetcurrent(t_canvas *x) +{ + pd_popsym(&x->gl_pd); +} + +t_canvasenvironment *canvas_getenv(t_canvas *x) +{ + if (!x) bug("canvas_getenv"); + while (!x->gl_env) + if (!(x = x->gl_owner)) + bug("t_canvasenvironment", x); + return (x->gl_env); +} + +int canvas_getdollarzero( void) +{ + t_canvas *x = canvas_getcurrent(); + t_canvasenvironment *env = (x ? canvas_getenv(x) : 0); + if (env) + return (env->ce_dollarzero); + else return (0); +} + +void canvas_getargs(int *argcp, t_atom **argvp) +{ + t_canvasenvironment *e = canvas_getenv(canvas_getcurrent()); + *argcp = e->ce_argc; + *argvp = e->ce_argv; +} + +t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s) +{ + t_symbol *ret; + char *name = s->s_name; + if (*name == '$' && name[1] >= '0' && name[1] <= '9') + { + t_canvasenvironment *env = canvas_getenv(x); + canvas_setcurrent(x); + ret = binbuf_realizedollsym(gensym(name+1), + env->ce_argc, env->ce_argv, 1); + canvas_unsetcurrent(x); + } + else ret = s; + return (ret); +} + +t_symbol *canvas_getcurrentdir(void) +{ + t_canvasenvironment *e = canvas_getenv(canvas_getcurrent()); + return (e->ce_dir); +} + +t_symbol *canvas_getdir(t_canvas *x) +{ + t_canvasenvironment *e = canvas_getenv(x); + return (e->ce_dir); +} + +void canvas_makefilename(t_canvas *x, char *file, char *result, int resultsize) +{ + char *dir = canvas_getenv(x)->ce_dir->s_name; + if (file[0] == '/' || (file[0] && file[1] == ':') || !*dir) + { + strncpy(result, file, resultsize); + result[resultsize-1] = 0; + } + else + { + int nleft; + strncpy(result, dir, resultsize); + result[resultsize-1] = 0; + nleft = resultsize - strlen(result) - 1; + if (nleft <= 0) return; + strcat(result, "/"); + strncat(result, file, nleft); + result[resultsize-1] = 0; + } +} + +void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir) +{ + if (strcmp(x->gl_name->s_name, "Pd")) + pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name)); + x->gl_name = s; + if (strcmp(x->gl_name->s_name, "Pd")) + pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name)); + if (glist_isvisible(x)) + canvas_reflecttitle(x); + if (dir && dir != &s_) + { + t_canvasenvironment *e = canvas_getenv(x); + e->ce_dir = dir; + } +} + +/* --------------- traversing the set of lines in a canvas ----------- */ + +int canvas_getindex(t_canvas *x, t_gobj *y) +{ + t_gobj *y2; + int indexno; + for (indexno = 0, y2 = x->gl_list; y2 && y2 != y; y2 = y2->g_next) + indexno++; + return (indexno); +} + +void linetraverser_start(t_linetraverser *t, t_canvas *x) +{ + t->tr_ob = 0; + t->tr_x = x; + t->tr_nextoc = 0; + t->tr_nextoutno = t->tr_nout = 0; +} + +t_outconnect *linetraverser_next(t_linetraverser *t) +{ + t_outconnect *rval = t->tr_nextoc; + int outno; + while (!rval) + { + outno = t->tr_nextoutno; + while (outno == t->tr_nout) + { + t_gobj *y; + t_object *ob = 0; + if (!t->tr_ob) y = t->tr_x->gl_list; + else y = t->tr_ob->ob_g.g_next; + for (; y; y = y->g_next) + if (ob = pd_checkobject(&y->g_pd)) break; + if (!ob) return (0); + t->tr_ob = ob; + t->tr_nout = obj_noutlets(ob); + outno = 0; + if (glist_isvisible(t->tr_x)) + gobj_getrect(y, t->tr_x, + &t->tr_x11, &t->tr_y11, &t->tr_x12, &t->tr_y12); + else t->tr_x11 = t->tr_y11 = t->tr_x12 = t->tr_y12 = 0; + } + t->tr_nextoutno = outno + 1; + rval = obj_starttraverseoutlet(t->tr_ob, &t->tr_outlet, outno); + t->tr_outno = outno; + } + t->tr_nextoc = obj_nexttraverseoutlet(rval, &t->tr_ob2, + &t->tr_inlet, &t->tr_inno); + t->tr_nin = obj_ninlets(t->tr_ob2); + if (!t->tr_nin) bug("drawline"); + if (glist_isvisible(t->tr_x)) + { + int inplus = (t->tr_nin == 1 ? 1 : t->tr_nin - 1); + int outplus = (t->tr_nout == 1 ? 1 : t->tr_nout - 1); + gobj_getrect(&t->tr_ob2->ob_g, t->tr_x, + &t->tr_x21, &t->tr_y21, &t->tr_x22, &t->tr_y22); + t->tr_lx1 = t->tr_x11 + + ((t->tr_x12 - t->tr_x11 - IOWIDTH) * t->tr_outno) / + outplus + IOMIDDLE; + t->tr_ly1 = t->tr_y12; + t->tr_lx2 = t->tr_x21 + + ((t->tr_x22 - t->tr_x21 - IOWIDTH) * t->tr_inno)/inplus + + IOMIDDLE; + t->tr_ly2 = t->tr_y21; + } + else + { + t->tr_x21 = t->tr_y21 = t->tr_x22 = t->tr_y22 = 0; + t->tr_lx1 = t->tr_ly1 = t->tr_lx2 = t->tr_ly2 = 0; + } + + return (rval); +} + +void linetraverser_skipobject(t_linetraverser *t) +{ + t->tr_nextoc = 0; + t->tr_nextoutno = t->tr_nout; +} + +/* -------------------- the canvas object -------------------------- */ +int glist_valid = 10000; + +void glist_init(t_glist *x) +{ + /* zero out everyone except "pd" field */ + memset(((char *)x) + sizeof(x->gl_pd), 0, sizeof(*x) - sizeof(x->gl_pd)); + x->gl_stub = gstub_new(x, 0); + x->gl_valid = ++glist_valid; + x->gl_xlabel = (t_symbol **)t_getbytes(0); + x->gl_ylabel = (t_symbol **)t_getbytes(0); +} + + /* make a new glist. It will either be a "root" canvas or else + its parent will be a "text" object in another window... we don't + know which yet. */ +t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv) +{ + t_canvas *x = (t_canvas *)pd_new(canvas_class); + t_canvas *owner = canvas_getcurrent(); + t_symbol *s = &s_; + int vis = 0, width = GLIST_DEFCANVASWIDTH, height = GLIST_DEFCANVASHEIGHT; + int xloc = 0, yloc = GLIST_DEFCANVASYLOC; + int font = (owner ? owner->gl_font : sys_defaultfont); + glist_init(x); + x->gl_obj.te_type = T_OBJECT; + if (!owner) + canvas_addtolist(x); + /* post("canvas %x, owner %x", x, owner); */ + + if (argc == 5) /* toplevel: x, y, w, h, font */ + { + xloc = atom_getintarg(0, argc, argv); + yloc = atom_getintarg(1, argc, argv); + width = atom_getintarg(2, argc, argv); + height = atom_getintarg(3, argc, argv); + font = atom_getintarg(4, argc, argv); + } + else if (argc == 6) /* subwindow: x, y, w, h, name, vis */ + { + xloc = atom_getintarg(0, argc, argv); + yloc = atom_getintarg(1, argc, argv); + width = atom_getintarg(2, argc, argv); + height = atom_getintarg(3, argc, argv); + s = atom_getsymbolarg(4, argc, argv); + vis = atom_getintarg(5, argc, argv); + } + /* (otherwise assume we're being created from the menu.) */ + + if (canvas_newdirectory->s_name[0]) + { + static int dollarzero = 1000; + t_canvasenvironment *env = x->gl_env = + (t_canvasenvironment *)getbytes(sizeof(*x->gl_env)); + env->ce_dir = canvas_newdirectory; + env->ce_argc = canvas_newargc; + env->ce_argv = canvas_newargv; + env->ce_dollarzero = dollarzero++; + canvas_newdirectory = &s_; + canvas_newargc = 0; + canvas_newargv = 0; + } + else x->gl_env = 0; + + if (yloc < GLIST_DEFCANVASYLOC) + yloc = GLIST_DEFCANVASYLOC; + if (xloc < 0) + xloc = 0; + x->gl_x1 = 0; + x->gl_y1 = 0; + x->gl_x2 = 1; + x->gl_y2 = 1; + canvas_setbounds(x, xloc, yloc, xloc + width, yloc + height); + x->gl_owner = owner; + x->gl_name = (*s->s_name ? s : + (canvas_newfilename ? canvas_newfilename : gensym("Pd"))); + if (strcmp(x->gl_name->s_name, "Pd")) + pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name)); + x->gl_loading = 1; + x->gl_willvis = vis; + x->gl_edit = !strncmp(x->gl_name->s_name, "Untitled", 8); + x->gl_font = sys_nearestfontsize(font); + pd_pushsym(&x->gl_pd); + return(x); +} + +void canvas_setgraph(t_glist *x, int flag); + +static void canvas_coords(t_glist *x, t_symbol *s, int argc, t_atom *argv) +{ + x->gl_x1 = atom_getfloatarg(0, argc, argv); + x->gl_y1 = atom_getfloatarg(1, argc, argv); + x->gl_x2 = atom_getfloatarg(2, argc, argv); + x->gl_y2 = atom_getfloatarg(3, argc, argv); + x->gl_pixwidth = atom_getintarg(4, argc, argv); + x->gl_pixheight = atom_getintarg(5, argc, argv); + canvas_setgraph(x, atom_getintarg(6, argc, argv)); +} + + /* make a new glist and add it to this glist. It will appear as + a "graph", not a text object. */ +t_glist *glist_addglist(t_glist *g, t_symbol *sym, + float x1, float y1, float x2, float y2, + float px1, float py1, float px2, float py2) +{ + static int gcount = 0; + int zz; + int menu = 0; + char *str; + t_glist *x = (t_glist *)pd_new(canvas_class); + glist_init(x); + x->gl_obj.te_type = T_OBJECT; + if (!*sym->s_name) + { + char buf[40]; + sprintf(buf, "graph%d", ++gcount); + sym = gensym(buf); + menu = 1; + } + else if (!strncmp((str = sym->s_name), "graph", 5) + && (zz = atoi(str + 5)) > gcount) + gcount = zz; + /* in 0.34 and earlier, the pixel rectangle and the y bounds were + reversed; this would behave the same, except that the dialog window + would be confusing. The "correct" way is to have "py1" be the value + that is higher on the screen. */ + if (py2 < py1) + { + float zz; + zz = y2; + y2 = y1; + y1 = zz; + zz = py2; + py2 = py1; + py1 = zz; + } + if (x1 == x2 || y1 == y2) + x1 = 0, x2 = 100, y1 = 1, y2 = -1; + if (px1 >= px2 || py1 >= py2) + px1 = 100, py1 = 20, px2 = 100 + GLIST_DEFGRAPHWIDTH, + py2 = 20 + GLIST_DEFGRAPHHEIGHT; + x->gl_name = sym; + x->gl_x1 = x1; + x->gl_x2 = x2; + x->gl_y1 = y1; + x->gl_y2 = y2; + x->gl_obj.te_xpix = px1; + x->gl_obj.te_ypix = py1; + x->gl_pixwidth = px2 - px1; + x->gl_pixheight = py2 - py1; + x->gl_font = (canvas_getcurrent() ? + canvas_getcurrent()->gl_font : sys_defaultfont); + x->gl_screenx1 = x->gl_screeny1 = 0; + x->gl_screenx2 = 240; + x->gl_screeny2 = 300; + if (strcmp(x->gl_name->s_name, "Pd")) + pd_bind(&x->gl_pd, canvas_makebindsym(x->gl_name)); + x->gl_owner = g; + x->gl_stretch = 1; + x->gl_isgraph = 1; + x->gl_obj.te_binbuf = binbuf_new(); + binbuf_addv(x->gl_obj.te_binbuf, "s", gensym("graph")); + if (!menu) + pd_pushsym(&x->gl_pd); + glist_add(g, &x->gl_gobj); + if (glist_isvisible(g)) + canvas_create_editor(x, 1); + return (x); +} + + /* call glist_addglist from a Pd message */ +void glist_glist(t_glist *g, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *sym = atom_getsymbolarg(0, argc, argv); + float x1 = atom_getfloatarg(1, argc, argv); + float y1 = atom_getfloatarg(2, argc, argv); + float x2 = atom_getfloatarg(3, argc, argv); + float y2 = atom_getfloatarg(4, argc, argv); + float px1 = atom_getfloatarg(5, argc, argv); + float py1 = atom_getfloatarg(6, argc, argv); + float px2 = atom_getfloatarg(7, argc, argv); + float py2 = atom_getfloatarg(8, argc, argv); + glist_addglist(g, sym, x1, y1, x2, y2, px1, py1, px2, py2); +} + + /* return true if the glist should appear as a graph on parent; + otherwise it appears as a text box. */ +int glist_isgraph(t_glist *x) +{ + return (x->gl_isgraph); +} + + /* This is sent from the GUI to inform a toplevel that its window has been + moved or resized. */ +static void canvas_setbounds(t_canvas *x, int x1, int y1, int x2, int y2) +{ + int heightwas = y2 - y1; + int heightchange = y2 - y1 - (x->gl_screeny2 - x->gl_screeny1); + x->gl_screenx1 = x1; + x->gl_screeny1 = y1; + x->gl_screenx2 = x2; + x->gl_screeny2 = y2; + /* post("set bounds %d %d %d %d", x1, y1, x2, y2); */ + if (!glist_isgraph(x) && (x->gl_y2 < x->gl_y1)) + { + /* if it's flipped so that y grows upward, + fix so that zero is bottom edge and redraw. This is + only appropriate if we're a regular "text" object on the + parent. */ + float diff = x->gl_y1 - x->gl_y2; + t_gobj *y; + x->gl_y1 = heightwas * diff; + x->gl_y2 = x->gl_y1 - diff; + /* and move text objects accordingly; they should stick + to the bottom, not the top. */ + for (y = x->gl_list; y; y = y->g_next) + if (pd_checkobject(&y->g_pd)) + gobj_displace(y, x, 0, heightchange); + canvas_redraw(x); + } +} + +t_symbol *canvas_makebindsym(t_symbol *s) +{ + char buf[MAXPDSTRING]; + strcpy(buf, "pd-"); + strcat(buf, s->s_name); + return (gensym(buf)); +} + +void canvas_reflecttitle(t_canvas *x) +{ + char namebuf[MAXPDSTRING]; + t_canvasenvironment *env = canvas_getenv(x); + if (env->ce_argc) + { + int i; + strcpy(namebuf, " ("); + for (i = 0; i < env->ce_argc; i++) + { + if (strlen(namebuf) > MAXPDSTRING/2 - 5) + break; + if (i != 0) + strcat(namebuf, " "); + atom_string(&env->ce_argv[i], namebuf + strlen(namebuf), + MAXPDSTRING/2); + } + strcat(namebuf, ")"); + } + else namebuf[0] = 0; + sys_vgui("wm title .x%x {%s%c%s - %s}\n", + x, x->gl_name->s_name, (x->gl_dirty? '*' : ' '), namebuf, + canvas_getdir(x)->s_name); +} + +void canvas_dirty(t_canvas *x, t_int n) +{ + t_canvas *x2 = canvas_getrootfor(x); + if ((unsigned)n != x2->gl_dirty) + { + x2->gl_dirty = n; + canvas_reflecttitle(x2); + } +} + + /* the window becomes "mapped" (visible and not miniaturized) or + "unmapped" (either miniaturized or just plain gone.) This should be + called from the GUI after the fact to "notify" us that we're mapped. */ +void canvas_map(t_canvas *x, t_floatarg f) +{ + int flag = (f != 0); + t_gobj *y; + if (flag) + { + if (!glist_isvisible(x)) + { + t_selection *sel; + if (!x->gl_havewindow) + { + bug("canvas_map"); + canvas_vis(x, 1); + } + for (y = x->gl_list; y; y = y->g_next) + gobj_vis(y, x, 1); + for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next) + gobj_select(sel->sel_what, x, 1); + x->gl_mapped = 1; + canvas_drawlines(x); + /* simulate a mouse up so u_main will calculate scrollbars... + ugly! */ + sys_vgui("pdtk_canvas_mouseup .x%x.c 0 0 0\n", x); + } + } + else + { + if (glist_isvisible(x)) + { + /* just clear out the whole canvas... */ + sys_vgui(".x%x.c delete all\n", x); + /* alternatively, we could have erased them one by one... + for (y = x->gl_list; y; y = y->g_next) + gobj_vis(y, x, 0); + ... but we should go through and erase the lines as well + if we do it that way. */ + x->gl_mapped = 0; + } + } +} + +void canvas_redraw(t_canvas *x) +{ + if (glist_isvisible(x)) + { + canvas_map(x, 0); + canvas_map(x, 1); + } +} + +/* ---- editors -- perhaps this and "vis" should go to g_editor.c ------- */ + +static t_editor *editor_new(t_glist *owner) +{ + char buf[40]; + t_editor *x = (t_editor *)getbytes(sizeof(*x)); + x->e_connectbuf = binbuf_new(); + x->e_deleted = binbuf_new(); + x->e_glist = owner; + sprintf(buf, ".x%x", (t_int)owner); + x->e_guiconnect = guiconnect_new(&owner->gl_pd, gensym(buf)); + return (x); +} + +static void editor_free(t_editor *x, t_glist *y) +{ + glist_noselect(y); + guiconnect_notarget(x->e_guiconnect, 1000); + binbuf_free(x->e_connectbuf); + binbuf_free(x->e_deleted); + freebytes((void *)x, sizeof(*x)); +} + + /* recursively create or destroy all editors of a glist and its + sub-glists, as long as they aren't toplevels. */ +void canvas_create_editor(t_glist *x, int createit) +{ + t_gobj *y; + t_object *ob; + if (createit) + { + if (x->gl_editor) + bug("canvas_create_editor"); + else + { + x->gl_editor = editor_new(x); + for (y = x->gl_list; y; y = y->g_next) + if (ob = pd_checkobject(&y->g_pd)) + rtext_new(x, ob); + } + } + else + { + if (!x->gl_editor) + bug("canvas_create_editor"); + else + { + for (y = x->gl_list; y; y = y->g_next) + if (ob = pd_checkobject(&y->g_pd)) + rtext_free(glist_findrtext(x, ob)); + editor_free(x->gl_editor, x); + x->gl_editor = 0; + } + } + for (y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == canvas_class && + ((t_canvas *)y)->gl_isgraph) + canvas_create_editor((t_canvas *)y, createit); +} + + /* we call this when we want the window to become visible, mapped, and + in front of all windows; or with "f" zero, when we want to get rid of + the window. */ +void canvas_vis(t_canvas *x, t_floatarg f) +{ + char buf[30]; + int flag = (f != 0); + if (flag) + { + /* test if we're already visible and toplevel */ + if (glist_isvisible(x) && !x->gl_isgraph) + { /* just put us in front */ +#ifdef MSW + canvas_vis(x, 0); + canvas_vis(x, 1); +#else + sys_vgui("raise .x%x\n", x); + sys_vgui("focus .x%x.c\n", x); + sys_vgui("wm deiconify .x%x\n", x); +#endif + } + else + { + canvas_create_editor(x, 1); + sys_vgui("pdtk_canvas_new .x%x %d %d +%d+%d %d\n", x, + (int)(x->gl_screenx2 - x->gl_screenx1), + (int)(x->gl_screeny2 - x->gl_screeny1), + (int)(x->gl_screenx1), (int)(x->gl_screeny1), + x->gl_edit); + canvas_reflecttitle(x); + x->gl_havewindow = 1; + canvas_updatewindowlist(); + } + } + else /* make invisible */ + { + int i; + t_canvas *x2; + if (!x->gl_havewindow) + { + /* bug workaround -- a graph in a visible patch gets "invised" + when the patch is closed, and must lose the editor here. It's + probably not the natural place to do this. Other cases like + subpatches fall here too but don'd need the editor freed, so + we check if it exists. */ + if (x->gl_editor) + canvas_create_editor(x, 0); + return; + } + glist_noselect(x); + if (glist_isvisible(x)) + canvas_map(x, 0); + canvas_create_editor(x, 0); + sys_vgui("destroy .x%x\n", x); + for (i = 1, x2 = x; x2; x2 = x2->gl_next, i++) + ; + sys_vgui(".mbar.find delete %d\n", i); + /* if we're a graph on our parent, and if the parent exists + and is visible, show ourselves on parent. */ + if (glist_isgraph(x) && x->gl_owner) + { + t_glist *gl2 = x->gl_owner; + canvas_create_editor(x, 1); + if (glist_isvisible(gl2)) + gobj_vis(&x->gl_gobj, gl2, 0); + x->gl_havewindow = 0; + if (glist_isvisible(gl2)) + gobj_vis(&x->gl_gobj, gl2, 1); + } + else x->gl_havewindow = 0; + canvas_updatewindowlist(); + } +} + + /* we call this on a non-toplevel glist to "open" it into its + own window. */ +void glist_menu_open(t_glist *x) +{ + if (glist_isvisible(x) && !glist_istoplevel(x)) + { + t_glist *gl2 = x->gl_owner; + if (!gl2) + bug("canvas_vis"); /* shouldn't happen but don't get too upset. */ + else + { + /* erase ourself in parent window */ + gobj_vis(&x->gl_gobj, gl2, 0); + /* get rid of our editor (and subeditors) */ + canvas_create_editor(x, 0); + x->gl_havewindow = 1; + /* redraw ourself in parent window (blanked out this time) */ + gobj_vis(&x->gl_gobj, gl2, 1); + } + } + canvas_vis(x, 1); +} + +int glist_isvisible(t_glist *x) +{ + return ((!x->gl_loading) && glist_getcanvas(x)->gl_mapped); +} + +int glist_istoplevel(t_glist *x) +{ + /* we consider a graph "toplevel" if it has its own window + or if it appears as a box in its parent window so that we + don't draw the actual contents there. */ + return (x->gl_havewindow || !x->gl_isgraph); +} + +int glist_getfont(t_glist *x) +{ + return (glist_getcanvas(x)->gl_font); +} + +void canvas_free(t_canvas *x) +{ + t_gobj *y; + int dspstate = canvas_suspend_dsp(); + canvas_noundo(x); + if (canvas_editing == x) + canvas_editing = 0; + if (canvas_whichfind == x) + canvas_whichfind = 0; + glist_noselect(x); + while (y = x->gl_list) + glist_delete(x, y); + canvas_vis(x, 0); + + if (strcmp(x->gl_name->s_name, "Pd")) + pd_unbind(&x->gl_pd, canvas_makebindsym(x->gl_name)); + if (x->gl_env) + { + freebytes(x->gl_env->ce_argv, x->gl_env->ce_argc * sizeof(t_atom)); + freebytes(x->gl_env, sizeof(*x->gl_env)); + } + canvas_resume_dsp(dspstate); + glist_cleanup(x); + gfxstub_deleteforkey(x); /* probably unnecessary */ + if (!x->gl_owner) + canvas_takeofflist(x); +} + +/* ----------------- lines ---------- */ + +static void canvas_drawlines(t_canvas *x) +{ + t_linetraverser t; + t_outconnect *oc; + { + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n", + glist_getcanvas(x), + t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2, + (outlet_getsymbol(t.tr_outlet) == &s_signal ? 2:1), + oc); + } +} + +void canvas_fixlinesfor(t_canvas *x, t_text *text) +{ + t_linetraverser t; + t_outconnect *oc; + + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + if (t.tr_ob == text || t.tr_ob2 == text) + { + sys_vgui(".x%x.c coords l%x %d %d %d %d\n", + glist_getcanvas(x), oc, + t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2); + } + } +} + + /* kill all lines for the object */ +void canvas_deletelinesfor(t_canvas *x, t_text *text) +{ + t_linetraverser t; + t_outconnect *oc; + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + if (t.tr_ob == text || t.tr_ob2 == text) + { + if (x->gl_editor) + { + sys_vgui(".x%x.c delete l%x\n", + glist_getcanvas(x), oc); + } + obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno); + } + } +} + + /* kill all lines for one inlet or outlet */ +void canvas_deletelinesforio(t_canvas *x, t_text *text, + t_inlet *inp, t_outlet *outp) +{ + t_linetraverser t; + t_outconnect *oc; + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + if ((t.tr_ob == text && t.tr_outlet == outp) || + (t.tr_ob2 == text && t.tr_inlet == inp)) + { + if (x->gl_editor) + { + sys_vgui(".x%x.c delete l%x\n", + glist_getcanvas(x), oc); + } + obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno); + } + } +} + +static void canvas_pop(t_canvas *x, t_floatarg fvis) +{ + if (fvis != 0) + canvas_vis(x, 1); + pd_popsym(&x->gl_pd); + canvas_resortinlets(x); + canvas_resortoutlets(x); + x->gl_loading = 0; +} + +void canvas_objfor(t_glist *gl, t_text *x, int argc, t_atom *argv); + + +void canvas_restore(t_canvas *x, t_symbol *s, int argc, t_atom *argv) +{ /* IOhannes */ + t_pd *z; + /* this should be unnecessary, but sometimes the canvas's name gets + out of sync with the owning box's argument; this fixes that */ + if (argc > 3) + { + t_atom *ap=argv+3; + if (ap->a_type == A_SYMBOL) + { + char *buf=ap->a_w.w_symbol->s_name, *bufp; + if (*buf == '$' && buf[1] >= '0' && buf[1] <= '9') + { + for (bufp = buf+2; *bufp; bufp++) + if (*bufp < '0' || *bufp > '9') + { + SETDOLLSYM(ap, gensym(buf+1)); + goto didit; + } + SETDOLLAR(ap, atoi(buf+1)); + didit: ; + } + } + + if (ap->a_type == A_DOLLSYM) + { + t_canvasenvironment *e = canvas_getenv(canvas_getcurrent()); + canvas_rename(x, binbuf_realizedollsym(ap->a_w.w_symbol, + e->ce_argc, e->ce_argv, 1), 0); + } + else if (ap->a_type == A_SYMBOL) + canvas_rename(x, argv[3].a_w.w_symbol, 0); + } + canvas_pop(x, x->gl_willvis); + + if (!(z = gensym("#X")->s_thing)) error("canvas_restore: out of context"); + else if (*z != canvas_class) error("canvas_restore: wasn't a canvas"); + else + { + t_canvas *x2 = (t_canvas *)z; + x->gl_owner = x2; + canvas_objfor(x2, &x->gl_obj, argc, argv); + } +} + +static void canvas_loadbangabstractions(t_canvas *x) +{ + t_gobj *y; + t_symbol *s = gensym("loadbang"); + for (y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == canvas_class) + { + if (canvas_isabstraction((t_canvas *)y)) + canvas_loadbang((t_canvas *)y); + else + canvas_loadbangabstractions((t_canvas *)y); + } +} + +void canvas_loadbangsubpatches(t_canvas *x) +{ + t_gobj *y; + t_symbol *s = gensym("loadbang"); + for (y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == canvas_class) + { + if (!canvas_isabstraction((t_canvas *)y)) + canvas_loadbangsubpatches((t_canvas *)y); + } + for (y = x->gl_list; y; y = y->g_next) + if ((pd_class(&y->g_pd) != canvas_class) && + zgetfn(&y->g_pd, s)) + pd_vmess(&y->g_pd, s, ""); +} + +void canvas_loadbang(t_canvas *x) +{ + t_gobj *y; + canvas_loadbangabstractions(x); + canvas_loadbangsubpatches(x); +} + + /* When you ask a canvas its size the result is 2 pixels more than what + you gave it to open it; perhaps there's a 1-pixel border all around it + or something. Anyway, we just add the 2 pixels back here; seems we + have to do this for linux but not MSW; not sure about MacOS. */ + +#ifdef MSW +#define HORIZBORDER 0 +#define VERTBORDER 0 +#else +#define HORIZBORDER 2 +#define VERTBORDER 2 +#endif + +static void canvas_relocate(t_canvas *x, t_symbol *canvasgeom, + t_symbol *topgeom) +{ + int cxpix, cypix, cw, ch, txpix, typix, tw, th; + if (sscanf(canvasgeom->s_name, "%dx%d+%d+%d", &cw, &ch, &cxpix, &cypix) + < 4 || + sscanf(topgeom->s_name, "%dx%d+%d+%d", &tw, &th, &txpix, &typix) < 4) + bug("canvas_relocate"); + /* for some reason this is initially called with cw=ch=1 so + we just suppress that here. */ + if (cw > 5 && ch > 5) + canvas_setbounds(x, txpix, typix, + txpix + cw - HORIZBORDER, typix + ch - VERTBORDER); +} + +void canvas_popabstraction(t_canvas *x) +{ + newest = &x->gl_pd; + pd_popsym(&x->gl_pd); + x->gl_loading = 0; + canvas_resortinlets(x); + canvas_resortoutlets(x); +} + +void canvas_logerror(t_object *y) +{ +#ifdef LATER + canvas_vis(x, 1); + if (!glist_isselected(x, &y->ob_g)) + glist_select(x, &y->ob_g); +#endif +} + +/* -------------------------- subcanvases ---------------------- */ + +static void *subcanvas_new(t_symbol *s) +{ + t_atom a[6]; + t_canvas *x, *z = canvas_getcurrent(); + if (!*s->s_name) s = gensym("/SUBPATCH/"); + SETFLOAT(a, 0); + SETFLOAT(a+1, GLIST_DEFCANVASYLOC); + SETFLOAT(a+2, GLIST_DEFCANVASWIDTH); + SETFLOAT(a+3, GLIST_DEFCANVASHEIGHT); + SETSYMBOL(a+4, s); + SETFLOAT(a+5, 1); + x = canvas_new(0, 0, 6, a); + x->gl_owner = z; + canvas_pop(x, 1); + return (x); +} + +static void canvas_click(t_canvas *x, + t_floatarg xpos, t_floatarg ypos, + t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + canvas_vis(x, 1); +} + + + /* find out from subcanvas contents how much to fatten the box */ +void canvas_fattensub(t_canvas *x, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_gobj *y; + *xp2 += 50; /* fake for now */ + *yp2 += 50; +} + +static void canvas_rename_method(t_canvas *x, t_symbol *s, int ac, t_atom *av) +{ + if (ac && av->a_type == A_SYMBOL) + canvas_rename(x, av->a_w.w_symbol, 0); + else canvas_rename(x, gensym("Pd"), 0); +} + +/* ------------------ table ---------------------------*/ + +static int tabcount = 0; + +static void *table_new(t_symbol *s, t_floatarg f) +{ + t_atom a[9]; + t_glist *gl; + t_canvas *x, *z = canvas_getcurrent(); + if (s == &s_) + { + char tabname[255]; + t_symbol *t = gensym("table"); + sprintf(tabname, "%s%d", t->s_name, tabcount++); + s = gensym(tabname); + } + if (f <= 1) + f = 100; + SETFLOAT(a, 0); + SETFLOAT(a+1, GLIST_DEFCANVASYLOC); + SETFLOAT(a+2, 600); + SETFLOAT(a+3, 400); + SETSYMBOL(a+4, s); + SETFLOAT(a+5, 0); + x = canvas_new(0, 0, 6, a); + + x->gl_owner = z; + + /* create a graph for the table */ + gl = glist_addglist((t_glist*)x, &s_, 0, -1, (f > 1 ? f-1 : 1), 1, + 50, 350, 550, 50); + + graph_array(gl, s, &s_float, f, 0); + + canvas_pop(x, 0); + + return (x); +} + + /* return true if the "canvas" object is an abstraction (so we don't + save its contents, fogr example.) */ +int canvas_isabstraction(t_canvas *x) +{ + return (x->gl_env != 0); +} + + /* return true if the "canvas" object is a "table". */ +int canvas_istable(t_canvas *x) +{ + t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0); + int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0); + int istable = (argc && argv[0].a_type == A_SYMBOL && + argv[0].a_w.w_symbol == gensym("table")); + return (istable); +} + + /* return true if the "canvas" object should be treated as a text + object. This is true for abstractions but also for "table"s... */ +int canvas_showtext(t_canvas *x) +{ + t_atom *argv = (x->gl_obj.te_binbuf? binbuf_getvec(x->gl_obj.te_binbuf):0); + int argc = (x->gl_obj.te_binbuf? binbuf_getnatom(x->gl_obj.te_binbuf) : 0); + int isarray = (argc && argv[0].a_type == A_SYMBOL && + argv[0].a_w.w_symbol == gensym("graph")); + return (!isarray); +} + +static void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp); +static void canvas_dsp(t_canvas *x, t_signal **sp) +{ + canvas_dodsp(x, 0, sp); +} + + /* get the document containing this canvas */ +t_canvas *canvas_getrootfor(t_canvas *x) +{ + if ((!x->gl_owner) || canvas_isabstraction(x)) + return (x); + else return (canvas_getrootfor(x->gl_owner)); +} + +/* ------------------------- DSP chain handling ------------------------- */ + +EXTERN_STRUCT _dspcontext; +#define t_dspcontext struct _dspcontext + +void ugen_start(void); +void ugen_stop(void); + +t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp, + int ninlets, int noutlets); +void ugen_add(t_dspcontext *dc, t_object *x); +void ugen_connect(t_dspcontext *dc, t_object *x1, int outno, + t_object *x2, int inno); +void ugen_done_graph(t_dspcontext *dc); + + /* schedule one canvas for DSP. This is called below for all "root" + canvases, but is also called from the "dsp" method for sub- + canvases, which are treated almost like any other tilde object. */ + +static void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp) +{ + t_linetraverser t; + t_outconnect *oc; + t_gobj *y; + t_object *ob; + t_symbol *dspsym = gensym("dsp"); + t_dspcontext *dc; + + /* create a new "DSP graph" object to use in sorting this canvas. + If we aren't toplevel, there are already other dspcontexts around. */ + + dc = ugen_start_graph(toplevel, sp, + obj_nsiginlets(&x->gl_obj), + obj_nsigoutlets(&x->gl_obj)); + + /* find all the "dsp" boxes and add them to the graph */ + + for (y = x->gl_list; y; y = y->g_next) + if ((ob = pd_checkobject(&y->g_pd)) && zgetfn(&y->g_pd, dspsym)) + ugen_add(dc, ob); + + /* ... and all dsp interconnections */ + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + if (obj_issignaloutlet(t.tr_ob, t.tr_outno)) + ugen_connect(dc, t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno); + + /* finally, sort them and add them to the DSP chain */ + ugen_done_graph(dc); +} + + /* this routine starts DSP for all root canvases. */ +static void canvas_start_dsp(void) +{ + t_canvas *x; + if (canvas_dspstate) ugen_stop(); + else sys_gui("pdtk_pd_dsp ON\n"); + ugen_start(); + + for (x = canvas_list; x; x = x->gl_next) + canvas_dodsp(x, 1, 0); + + canvas_dspstate = 1; +} + +static void canvas_stop_dsp(void) +{ + if (canvas_dspstate) + { + ugen_stop(); + sys_gui("pdtk_pd_dsp OFF\n"); + canvas_dspstate = 0; + } +} + + /* DSP can be suspended before, and resumed after, operations which + might affect the DSP chain. For example, we suspend before loading and + resume afterward, so that DSP doesn't get resorted for every DSP object + int the patch. */ + +int canvas_suspend_dsp(void) +{ + int rval = canvas_dspstate; + if (rval) canvas_stop_dsp(); + return (rval); +} + +void canvas_resume_dsp(int oldstate) +{ + if (oldstate) canvas_start_dsp(); +} + + /* this is equivalent to suspending and resuming in one step. */ +void canvas_update_dsp(void) +{ + if (canvas_dspstate) canvas_start_dsp(); +} + +void glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv) +{ + int newstate; + if (argc) + { + newstate = atom_getintarg(0, argc, argv); + if (newstate && !canvas_dspstate) + { + sys_set_audio_state(1); + canvas_start_dsp(); + } + else if (!newstate && canvas_dspstate) + { + canvas_stop_dsp(); + sys_set_audio_state(0); + } + } + else post("dsp state %d", canvas_dspstate); +} + + /* LATER replace this with a queueing scheme */ +void glist_redrawitem(t_glist *owner, t_gobj *gobj) +{ + if (glist_isvisible(owner)) + { + gobj_vis(gobj, owner, 0); + gobj_vis(gobj, owner, 1); + } +} + + /* redraw all "scalars" (do this if a drawing command is changed.) + LATER we'll use the "template" information to select which ones we + redraw. */ +static void glist_redrawall(t_glist *gl) +{ + t_gobj *g; + int vis = glist_isvisible(gl); + for (g = gl->gl_list; g; g = g->g_next) + { + t_class *cl; + if (vis && g->g_pd == scalar_class) + glist_redrawitem(gl, g); + else if (g->g_pd == canvas_class) + glist_redrawall((t_glist *)g); + } +} + + /* public interface for above */ +void canvas_redrawallfortemplate(t_canvas *templatecanvas) +{ + t_canvas *x; + /* find all root canvases */ + for (x = canvas_list; x; x = x->gl_next) + glist_redrawall(x); +} + +/* ------------------------------- setup routine ------------------------ */ + + /* why are some of these "glist" and others "canvas"? */ +extern void glist_text(t_glist *x, t_symbol *s, int argc, t_atom *argv); +extern void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_bng(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_toggle(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_vslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_hslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_vdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv); + /* old version... */ +extern void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv); + /* new version: */ +extern void canvas_hradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_vradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_vumeter(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_mycnv(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv); +extern void glist_scalar(t_glist *canvas, t_symbol *s, int argc, t_atom *argv); + +void g_graph_setup(void); +void g_editor_setup(void); +void g_readwrite_setup(void); +extern void graph_properties(t_gobj *z, t_glist *owner); + +void g_canvas_setup(void) +{ + /* we prevent the user from typing "canvas" in an object box + by sending 0 for a creator function. */ + canvas_class = class_new(gensym("canvas"), 0, + (t_method)canvas_free, sizeof(t_canvas), CLASS_NOINLET, 0); + /* here is the real creator function, invoked in patch files + by sending the "canvas" message to #N, which is bound + to pd_camvasmaker. */ + class_addmethod(pd_canvasmaker, (t_method)canvas_new, gensym("canvas"), + A_GIMME, 0); + class_addmethod(canvas_class, (t_method)canvas_restore, + gensym("restore"), A_GIMME, 0); + class_addmethod(canvas_class, (t_method)canvas_coords, + gensym("coords"), A_GIMME, 0); + +/* -------------------------- objects ----------------------------- */ + class_addmethod(canvas_class, (t_method)canvas_obj, + gensym("obj"), A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_msg, + gensym("msg"), A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_floatatom, + gensym("floatatom"), A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_symbolatom, + gensym("symbolatom"), A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)glist_text, + gensym("text"), A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)glist_glist, gensym("graph"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)glist_scalar, + gensym("scalar"), A_GIMME, A_NULL); + + /* -------------- Thomas Musil's GUI objects ------------ */ + class_addmethod(canvas_class, (t_method)canvas_bng, gensym("bng"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_toggle, gensym("toggle"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_vslider, gensym("vslider"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_hslider, gensym("hslider"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_hdial, gensym("hdial"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_vdial, gensym("vdial"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_hradio, gensym("hradio"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_vradio, gensym("vradio"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_vumeter, gensym("vumeter"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_mycnv, gensym("mycnv"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_numbox, gensym("numbox"), + A_GIMME, A_NULL); + +/* ------------------------ gui stuff --------------------------- */ + class_addmethod(canvas_class, (t_method)canvas_pop, gensym("pop"), + A_DEFFLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_loadbang, + gensym("loadbang"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_relocate, + gensym("relocate"), A_SYMBOL, A_SYMBOL, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_vis, + gensym("vis"), A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)glist_menu_open, + gensym("menu-open"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_map, + gensym("map"), A_FLOAT, A_NULL); + class_setpropertiesfn(canvas_class, graph_properties); + +/* ---------------------- list handling ------------------------ */ + class_addmethod(canvas_class, (t_method)glist_clear, gensym("clear"), + A_NULL); + +/* ----- subcanvases, which you get by typing "pd" in a box ---- */ + class_addcreator((t_newmethod)subcanvas_new, gensym("pd"), A_DEFSYMBOL, 0); + class_addcreator((t_newmethod)subcanvas_new, gensym("page"), A_DEFSYMBOL, 0); + + class_addmethod(canvas_class, (t_method)canvas_click, + gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(canvas_class, (t_method)canvas_dsp, gensym("dsp"), 0); + class_addmethod(canvas_class, (t_method)canvas_rename_method, + gensym("rename"), A_GIMME, 0); + +/*---------------------------- tables -- GG ------------------- */ + + class_addcreator((t_newmethod)table_new, gensym("table"), + A_DEFSYM, A_DEFFLOAT, 0); + +/* -------------- setups from other files for canvas_class ---------------- */ + g_graph_setup(); + g_editor_setup(); + g_readwrite_setup(); +} diff --git a/apps/plugins/pdbox/PDa/src/g_canvas.h b/apps/plugins/pdbox/PDa/src/g_canvas.h new file mode 100644 index 0000000..54ab985 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_canvas.h @@ -0,0 +1,1204 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* this file defines the structure for "glists" and related structures and +functions. "Glists" and "canvases" and "graphs" used to be different +structures until being unified in version 0.35. + +A glist occupies its own window if the "gl_havewindow" flag is set. Its +appearance on its "parent" or "owner" (if it has one) is as a graph if +"gl_isgraph" is set, and otherwise as a text box. + +A glist is "root" if it has no owner, i.e., a document window. In this +case "gl_havewindow" is always set. + +We maintain a list of root windows, so that we can traverse the whole +collection of everything in a Pd process. + +If a glist has a window it may still not be "mapped." Miniaturized +windows aren't mapped, for example, but a window is also not mapped +immediately upon creation. In either case gl_havewindow is true but +gl_mapped is false. + +Closing a non-root window makes it invisible; closing a root destroys it. + +A glist that's just a text object on its parent is always "toplevel." An +embedded glist can switch back and forth to appear as a toplevel by double- +clicking on it. Single-clicking a text box makes the toplevel become visible +and raises the window it's in. + +If a glist shows up as a graph on its parent, the graph is blanked while the +glist has its own window, even if miniaturized. + +*/ + +/* NOTE: this file describes Pd implementation details which may change +in future releases. The public (stable) API is in m_pd.h. */ + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +extern "C" { +#endif + +/* --------------------- geometry ---------------------------- */ +#define IOWIDTH 7 /* width of an inlet/outlet in pixels */ +#define IOMIDDLE ((IOWIDTH-1)/2) +#define GLIST_DEFGRAPHWIDTH 200 +#define GLIST_DEFGRAPHHEIGHT 140 +/* ----------------------- data ------------------------------- */ + +typedef struct _updateheader +{ + struct _updateheader *upd_next; + unsigned int upd_array:1; /* true if array, false if glist */ + unsigned int upd_queued:1; /* true if we're queued */ +} t_updateheader; + + /* types to support glists grabbing mouse motion or keys from parent */ +typedef void (*t_glistmotionfn)(void *z, t_floatarg dx, t_floatarg dy); +typedef void (*t_glistkeyfn)(void *z, t_floatarg key); + +EXTERN_STRUCT _rtext; +#define t_rtext struct _rtext + +EXTERN_STRUCT _gtemplate; +#define t_gtemplate struct _gtemplate + +EXTERN_STRUCT _guiconnect; +#define t_guiconnect struct _guiconnect + +EXTERN_STRUCT _tscalar; +#define t_tscalar struct _tscalar + +EXTERN_STRUCT _canvasenvironment; +#define t_canvasenvironment struct _canvasenvironment + +typedef struct _selection +{ + t_gobj *sel_what; + struct _selection *sel_next; +} t_selection; + + /* this structure is instantiated whenever a glist becomes visible. */ +typedef struct _editor +{ + t_updateheader e_upd; /* update header structure */ + t_selection *e_updlist; /* list of objects to update */ + t_rtext *e_rtext; /* text responder linked list */ + t_selection *e_selection; /* head of the selection list */ + t_rtext *e_textedfor; /* the rtext if any that we are editing */ + t_gobj *e_grab; /* object being "dragged" */ + t_glistmotionfn e_motionfn; /* ... motion callback */ + t_glistkeyfn e_keyfn; /* ... keypress callback */ + t_binbuf *e_connectbuf; /* connections to deleted objects */ + t_binbuf *e_deleted; /* last stuff we deleted */ + t_guiconnect *e_guiconnect; /* GUI connection for filtering messages */ + struct _glist *e_glist; /* glist which owns this */ + int e_xwas; /* xpos on last mousedown or motion event */ + int e_ywas; /* ypos, similarly */ + int e_selectline_index1; /* indices for the selected line if any */ + int e_selectline_outno; /* (only valid if e_selectedline is set) */ + int e_selectline_index2; + int e_selectline_inno; + t_outconnect *e_selectline_tag; + unsigned int e_onmotion: 3; /* action to take on motion */ + unsigned int e_lastmoved: 1; /* one if mouse has moved since click */ + unsigned int e_textdirty: 1; /* one if e_textedfor has changed */ + unsigned int e_selectedline: 1; /* one if a line is selected */ +} t_editor; + +#define MA_NONE 0 /* e_onmotion: do nothing on mouse motion */ +#define MA_MOVE 1 /* drag the selection around */ +#define MA_CONNECT 2 /* make a connection */ +#define MA_REGION 3 /* selection region */ +#define MA_PASSOUT 4 /* send on to e_grab */ +#define MA_DRAGTEXT 5 /* drag in text editor to alter selection */ + +/* editor structure for "garrays". We don't bother to delete and regenerate +this structure when the "garray" becomes invisible or visible, although we +could do so if the structure gets big (like the "editor" above.) */ + +typedef struct _arrayvis +{ + t_updateheader av_upd; /* update header structure */ + t_garray *av_garray; /* owning structure */ +} t_arrayvis; + +/* the t_tick structure describes where to draw x and y "ticks" for a glist */ + +typedef struct _tick /* where to put ticks on x or y axes */ +{ + float k_point; /* one point to draw a big tick at */ + float k_inc; /* x or y increment per little tick */ + int k_lperb; /* little ticks per big; 0 if no ticks to draw */ +} t_tick; + +/* the t_glist structure, which describes a list of elements that live on an +area of a window. + +*/ + +struct _glist +{ + t_object gl_obj; /* header in case we're a glist */ + t_gobj *gl_list; /* the actual data */ + struct _gstub *gl_stub; /* safe pointer handler */ + int gl_valid; /* incremented when pointers might be stale */ + struct _glist *gl_owner; /* parent glist, supercanvas, or 0 if none */ + int gl_pixwidth; /* width in pixels (on parent, if a graph) */ + int gl_pixheight; + float gl_x1; /* bounding rectangle in our own coordinates */ + float gl_y1; + float gl_x2; + float gl_y2; + int gl_screenx1; /* screen coordinates when toplevel */ + int gl_screeny1; + int gl_screenx2; + int gl_screeny2; + t_tick gl_xtick; /* ticks marking X values */ + int gl_nxlabels; /* number of X coordinate labels */ + t_symbol **gl_xlabel; /* ... an array to hold them */ + float gl_xlabely; /* ... and their Y coordinates */ + t_tick gl_ytick; /* same as above for Y ticks and labels */ + int gl_nylabels; + t_symbol **gl_ylabel; + float gl_ylabelx; + t_editor *gl_editor; /* editor structure when visible */ + t_symbol *gl_name; /* symbol bound here */ + int gl_font; /* nominal font size in points, e.g., 10 */ + struct _glist *gl_next; /* link in list of toplevels */ + t_canvasenvironment *gl_env; /* root canvases and abstractions only */ + unsigned int gl_havewindow:1; /* true if we own a window */ + unsigned int gl_mapped:1; /* true if, moreover, it's "mapped" */ + unsigned int gl_dirty:1; /* (root canvas only:) patch has changed */ + unsigned int gl_loading:1; /* am now loading from file */ + unsigned int gl_willvis:1; /* make me visible after loading */ + unsigned int gl_edit:1; /* edit mode */ + unsigned int gl_isdeleting:1; /* we're inside glist_delete -- hack! */ + unsigned int gl_stretch:1; /* stretch contents on resize */ + unsigned int gl_isgraph:1; /* show as graph on parent */ +}; + +#define gl_gobj gl_obj.te_g +#define gl_pd gl_gobj.g_pd + +/* a data structure to describe a field in a pure datum */ + +#define DT_FLOAT 0 +#define DT_SYMBOL 1 +#define DT_LIST 2 +#define DT_ARRAY 3 + +typedef struct _dataslot +{ + int ds_type; + t_symbol *ds_name; + t_symbol *ds_arraytemplate; /* filled in for arrays only */ +} t_dataslot; + + +/* T.Grill - changed t_pd member to t_pdobj to avoid name clashed */ +typedef struct _template +{ + t_pd t_pdobj; /* header */ + struct _gtemplate *t_list; /* list of "struct"/gtemplate objects */ + t_symbol *t_sym; /* name */ + int t_n; /* number of dataslots (fields) */ + t_dataslot *t_vec; /* array of dataslots */ +} t_template; + +struct _array +{ + int a_n; /* number of elements */ + int a_elemsize; /* size in bytes; LATER get this from template */ + char *a_vec; /* array of elements */ + t_symbol *a_templatesym; /* template for elements */ + int a_valid; /* protection against stale pointers into array */ + t_gpointer a_gp; /* pointer to scalar or array element we're in */ + t_gstub *a_stub; +}; + + /* structure for traversing all the connections in a glist */ +typedef struct _linetraverser +{ + t_canvas *tr_x; + t_object *tr_ob; + int tr_nout; + int tr_outno; + t_object *tr_ob2; + t_outlet *tr_outlet; + t_inlet *tr_inlet; + int tr_nin; + int tr_inno; + int tr_x11, tr_y11, tr_x12, tr_y12; + int tr_x21, tr_y21, tr_x22, tr_y22; + int tr_lx1, tr_ly1, tr_lx2, tr_ly2; + t_outconnect *tr_nextoc; + int tr_nextoutno; +} t_linetraverser; + +/* function types used to define graphical behavior for gobjs, a bit like X +widgets. We don't use Pd methods because Pd's typechecking can't specify the +types of pointer arguments. Also it's more convenient this way, since +every "patchable" object can just get the "text" behaviors. */ + + /* Call this to get a gobj's bounding rectangle in pixels */ +typedef void (*t_getrectfn)(t_gobj *x, struct _glist *glist, + int *x1, int *y1, int *x2, int *y2); + /* and this to displace a gobj: */ +typedef void (*t_displacefn)(t_gobj *x, struct _glist *glist, int dx, int dy); + /* change color to show selection: */ +typedef void (*t_selectfn)(t_gobj *x, struct _glist *glist, int state); + /* change appearance to show activation/deactivation: */ +typedef void (*t_activatefn)(t_gobj *x, struct _glist *glist, int state); + /* warn a gobj it's about to be deleted */ +typedef void (*t_deletefn)(t_gobj *x, struct _glist *glist); + /* making visible or invisible */ +typedef void (*t_visfn)(t_gobj *x, struct _glist *glist, int flag); + /* field a mouse click (when not in "edit" mode) */ +typedef int (*t_clickfn)(t_gobj *x, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit); + /* ... and later, resizing; getting/setting font or color... */ + +struct _widgetbehavior +{ + t_getrectfn w_getrectfn; + t_displacefn w_displacefn; + t_selectfn w_selectfn; + t_activatefn w_activatefn; + t_deletefn w_deletefn; + t_visfn w_visfn; + t_clickfn w_clickfn; +}; + +/* -------- behaviors for scalars defined by objects in template --------- */ +/* these are set by "drawing commands" in g_template.c which add appearance to +scalars, which live in some other window. If the scalar is just included +in a canvas the "parent" is a misnomer. There is also a text scalar object +which really does draw the scalar on the parent window; see g_scalar.c. */ + +/* note how the click function wants the whole scalar, not the "data", so +doesn't work on array elements... LATER reconsider this */ + + /* bounding rectangle: */ +typedef void (*t_parentgetrectfn)(t_gobj *x, struct _glist *glist, + t_word *data, t_template *tmpl, float basex, float basey, + int *x1, int *y1, int *x2, int *y2); + /* displace it */ +typedef void (*t_parentdisplacefn)(t_gobj *x, struct _glist *glist, + t_word *data, t_template *tmpl, float basex, float basey, + int dx, int dy); + /* change color to show selection */ +typedef void (*t_parentselectfn)(t_gobj *x, struct _glist *glist, + t_word *data, t_template *tmpl, float basex, float basey, + int state); + /* change appearance to show activation/deactivation: */ +typedef void (*t_parentactivatefn)(t_gobj *x, struct _glist *glist, + t_word *data, t_template *tmpl, float basex, float basey, + int state); + /* making visible or invisible */ +typedef void (*t_parentvisfn)(t_gobj *x, struct _glist *glist, + t_word *data, t_template *tmpl, float basex, float basey, + int flag); + /* field a mouse click */ +typedef int (*t_parentclickfn)(t_gobj *x, struct _glist *glist, + t_scalar *sc, t_template *tmpl, float basex, float basey, + int xpix, int ypix, int shift, int alt, int dbl, int doit); + +struct _parentwidgetbehavior +{ + t_parentgetrectfn w_parentgetrectfn; + t_parentdisplacefn w_parentdisplacefn; + t_parentselectfn w_parentselectfn; + t_parentactivatefn w_parentactivatefn; + t_parentvisfn w_parentvisfn; + t_parentclickfn w_parentclickfn; +}; + + /* cursor definitions; used as return value for t_parentclickfn */ +#define CURSOR_RUNMODE_NOTHING 0 +#define CURSOR_RUNMODE_CLICKME 1 +#define CURSOR_RUNMODE_THICKEN 2 +#define CURSOR_RUNMODE_ADDPOINT 3 +#define CURSOR_EDITMODE_NOTHING 4 +#define CURSOR_EDITMODE_CONNECT 5 +#define CURSOR_EDITMODE_DISCONNECT 6 +EXTERN void canvas_setcursor(t_glist *x, unsigned int cursornum); + +extern t_canvas *canvas_editing; /* last canvas to start text edting */ +extern t_canvas *canvas_whichfind; /* last canvas we did a find in */ +extern t_canvas *canvas_list; /* list of all root canvases */ +extern t_class *vinlet_class, *voutlet_class; +extern int glist_valid; /* incremented when pointers might be stale */ + +/* ------------------- functions on any gobj ----------------------------- */ +EXTERN void gobj_getrect(t_gobj *x, t_glist *owner, int *x1, int *y1, + int *x2, int *y2); +EXTERN void gobj_displace(t_gobj *x, t_glist *owner, int dx, int dy); +EXTERN void gobj_select(t_gobj *x, t_glist *owner, int state); +EXTERN void gobj_activate(t_gobj *x, t_glist *owner, int state); +EXTERN void gobj_delete(t_gobj *x, t_glist *owner); +EXTERN void gobj_vis(t_gobj *x, t_glist *glist, int flag); +EXTERN int gobj_click(t_gobj *x, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit); +EXTERN void gobj_save(t_gobj *x, t_binbuf *b); +EXTERN void gobj_properties(t_gobj *x, struct _glist *glist); +EXTERN void gobj_save(t_gobj *x, t_binbuf *b); + +/* -------------------- functions on glists --------------------- */ +EXTERN t_glist *glist_new( void); +EXTERN void glist_init(t_glist *x); +EXTERN void glist_add(t_glist *x, t_gobj *g); +EXTERN void glist_cleanup(t_glist *x); +EXTERN void glist_free(t_glist *x); + +EXTERN void glist_clear(t_glist *x); +EXTERN t_canvas *glist_getcanvas(t_glist *x); +EXTERN int glist_isselected(t_glist *x, t_gobj *y); +EXTERN void glist_select(t_glist *x, t_gobj *y); +EXTERN void glist_deselect(t_glist *x, t_gobj *y); +EXTERN void glist_noselect(t_glist *x); +EXTERN void glist_selectall(t_glist *x); +EXTERN void glist_delete(t_glist *x, t_gobj *y); +EXTERN void glist_retext(t_glist *x, t_text *y); +EXTERN void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn, + t_glistkeyfn keyfn, int xpos, int ypos); +EXTERN int glist_isvisible(t_glist *x); +EXTERN int glist_istoplevel(t_glist *x); +EXTERN t_glist *glist_findgraph(t_glist *x); +EXTERN int glist_getfont(t_glist *x); +EXTERN void glist_sort(t_glist *canvas); +EXTERN void glist_read(t_glist *x, t_symbol *filename, t_symbol *format); +EXTERN void glist_mergefile(t_glist *x, t_symbol *filename, t_symbol *format); + +EXTERN float glist_pixelstox(t_glist *x, float xpix); +EXTERN float glist_pixelstoy(t_glist *x, float ypix); +EXTERN float glist_xtopixels(t_glist *x, float xval); +EXTERN float glist_ytopixels(t_glist *x, float yval); +EXTERN float glist_dpixtodx(t_glist *x, float dxpix); +EXTERN float glist_dpixtody(t_glist *x, float dypix); + +EXTERN void glist_redrawitem(t_glist *owner, t_gobj *gobj); +EXTERN void glist_getnextxy(t_glist *gl, int *xval, int *yval); +EXTERN void glist_glist(t_glist *g, t_symbol *s, int argc, t_atom *argv); +EXTERN t_glist *glist_addglist(t_glist *g, t_symbol *sym, + float x1, float y1, float x2, float y2, + float px1, float py1, float px2, float py2); +EXTERN void glist_arraydialog(t_glist *parent, t_symbol *name, + t_floatarg size, t_floatarg saveit, t_floatarg newgraph); +EXTERN t_binbuf *glist_writetobinbuf(t_glist *x, int wholething); +EXTERN int glist_isgraph(t_glist *x); +EXTERN void glist_redraw(t_glist *x); +EXTERN void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime, + char *tag, int x1, int y1, int x2, int y2); +EXTERN void glist_eraseiofor(t_glist *glist, t_object *ob, char *tag); +EXTERN void canvas_create_editor(t_glist *x, int createit); +void canvas_deletelinesforio(t_canvas *x, t_text *text, + t_inlet *inp, t_outlet *outp); + + +/* -------------------- functions on texts ------------------------- */ +EXTERN void text_setto(t_text *x, t_glist *glist, char *buf, int bufsize); +EXTERN void text_drawborder(t_text *x, t_glist *glist, char *tag, + int width, int height, int firsttime); +EXTERN void text_eraseborder(t_text *x, t_glist *glist, char *tag); +EXTERN int text_xcoord(t_text *x, t_glist *glist); +EXTERN int text_ycoord(t_text *x, t_glist *glist); +EXTERN int text_xpix(t_text *x, t_glist *glist); +EXTERN int text_ypix(t_text *x, t_glist *glist); +EXTERN int text_shouldvis(t_text *x, t_glist *glist); + +/* -------------------- functions on rtexts ------------------------- */ +#define RTEXT_DOWN 1 +#define RTEXT_DRAG 2 +#define RTEXT_DBL 3 +#define RTEXT_SHIFT 4 + +EXTERN t_rtext *rtext_new(t_glist *glist, t_text *who); +EXTERN t_rtext *glist_findrtext(t_glist *gl, t_text *who); +EXTERN void rtext_draw(t_rtext *x); +EXTERN void rtext_erase(t_rtext *x); +EXTERN t_rtext *rtext_remove(t_rtext *first, t_rtext *x); +EXTERN int rtext_height(t_rtext *x); +EXTERN void rtext_displace(t_rtext *x, int dx, int dy); +EXTERN void rtext_select(t_rtext *x, int state); +EXTERN void rtext_activate(t_rtext *x, int state); +EXTERN void rtext_free(t_rtext *x); +EXTERN void rtext_key(t_rtext *x, int n, t_symbol *s); +EXTERN void rtext_mouse(t_rtext *x, int xval, int yval, int flag); +EXTERN void rtext_retext(t_rtext *x); +EXTERN int rtext_width(t_rtext *x); +EXTERN int rtext_height(t_rtext *x); +EXTERN char *rtext_gettag(t_rtext *x); +EXTERN void rtext_gettext(t_rtext *x, char **buf, int *bufsize); + +/* -------------------- functions on canvases ------------------------ */ +EXTERN t_class *canvas_class; + +EXTERN t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv); +EXTERN t_symbol *canvas_makebindsym(t_symbol *s); +EXTERN void canvas_vistext(t_canvas *x, t_text *y); +EXTERN void canvas_fixlinesfor(t_canvas *x, t_text *text); +EXTERN void canvas_deletelinesfor(t_canvas *x, t_text *text); +EXTERN void canvas_stowconnections(t_canvas *x); +EXTERN void canvas_restoreconnections(t_canvas *x); +EXTERN void canvas_redraw(t_canvas *x); + +EXTERN t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *sym); +EXTERN void canvas_rminlet(t_canvas *x, t_inlet *ip); +EXTERN t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *sym); +EXTERN void canvas_rmoutlet(t_canvas *x, t_outlet *op); +EXTERN void canvas_redrawallfortemplate(t_canvas *tmpl); +EXTERN void canvas_zapallfortemplate(t_canvas *tmpl); +EXTERN void canvas_setusedastemplate(t_canvas *x); +EXTERN t_canvas *canvas_getcurrent(void); +EXTERN void canvas_setcurrent(t_canvas *x); +EXTERN void canvas_unsetcurrent(t_canvas *x); +EXTERN t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s); +EXTERN t_canvas *canvas_getrootfor(t_canvas *x); +EXTERN void canvas_dirty(t_canvas *x, t_int n); +EXTERN int canvas_getfont(t_canvas *x); +typedef int (*t_canvasapply)(t_canvas *x, t_int x1, t_int x2, t_int x3); + +EXTERN t_int *canvas_recurapply(t_canvas *x, t_canvasapply *fn, + t_int x1, t_int x2, t_int x3); + +EXTERN void canvas_resortinlets(t_canvas *x); +EXTERN void canvas_resortoutlets(t_canvas *x); +EXTERN void canvas_free(t_canvas *x); +EXTERN void canvas_updatewindowlist( void); +EXTERN void canvas_editmode(t_canvas *x, t_floatarg yesplease); +EXTERN int canvas_isabstraction(t_canvas *x); +EXTERN int canvas_istable(t_canvas *x); +EXTERN int canvas_showtext(t_canvas *x); +EXTERN void canvas_vis(t_canvas *x, t_floatarg f); +EXTERN t_canvasenvironment *canvas_getenv(t_canvas *x); +EXTERN void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir); +EXTERN void canvas_loadbang(t_canvas *x); +EXTERN int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos, + int *x1p, int *y1p, int *x2p, int *y2p); +EXTERN int canvas_setdeleting(t_canvas *x, int flag); + +typedef void (*t_undofn)(t_canvas *canvas, void *buf, + int action); /* a function that does UNDO/REDO */ +#define UNDO_FREE 0 /* free current undo/redo buffer */ +#define UNDO_UNDO 1 /* undo */ +#define UNDO_REDO 2 /* redo */ +EXTERN void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf, + const char *name); +EXTERN void canvas_noundo(t_canvas *x); +EXTERN int canvas_getindex(t_canvas *x, t_gobj *y); + +/* T.Grill - made public for dynamic object creation */ +/* in g_editor.c */ +EXTERN void canvas_connect(t_canvas *x, + t_floatarg fwhoout, t_floatarg foutno,t_floatarg fwhoin, t_floatarg finno); +EXTERN void canvas_disconnect(t_canvas *x, + float index1, float outno, float index2, float inno); +EXTERN int canvas_isconnected (t_canvas *x, + t_text *ob1, int n1, t_text *ob2, int n2); +EXTERN void canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy); + + +/* ---- functions on canvasses as objects --------------------- */ + +EXTERN void canvas_fattenforscalars(t_canvas *x, + int *x1, int *y1, int *x2, int *y2); +EXTERN void canvas_visforscalars(t_canvas *x, t_glist *glist, int vis); +EXTERN int canvas_clicksub(t_canvas *x, int xpix, int ypix, int shift, + int alt, int dbl, int doit); +EXTERN t_glist *canvas_getglistonsuper(void); + +EXTERN void linetraverser_start(t_linetraverser *t, t_canvas *x); +EXTERN t_outconnect *linetraverser_next(t_linetraverser *t); +EXTERN void linetraverser_skipobject(t_linetraverser *t); + +/* --------------------- functions on tscalars --------------------- */ + +EXTERN void tscalar_getrect(t_tscalar *x, t_glist *owner, + int *xp1, int *yp1, int *xp2, int *yp2); +EXTERN void tscalar_vis(t_tscalar *x, t_glist *owner, int flag); +EXTERN int tscalar_click(t_tscalar *x, int xpix, int ypix, int shift, + int alt, int dbl, int doit); + +/* --------- functions on garrays (graphical arrays) -------------------- */ + +EXTERN t_template *garray_template(t_garray *x); + +/* -------------------- arrays --------------------- */ +EXTERN t_garray *graph_array(t_glist *gl, t_symbol *s, t_symbol *tmpl, + t_floatarg f, t_floatarg saveit); +EXTERN t_array *array_new(t_symbol *templatesym, t_gpointer *parent); +EXTERN void array_resize(t_array *x, t_template *tmpl, int n); +EXTERN void array_free(t_array *x); + +/* --------------------- gpointers and stubs ---------------- */ +EXTERN t_gstub *gstub_new(t_glist *gl, t_array *a); +EXTERN void gstub_cutoff(t_gstub *gs); +EXTERN void gpointer_setglist(t_gpointer *gp, t_glist *glist, t_scalar *x); + +/* --------------------- scalars ------------------------- */ +EXTERN void word_init(t_word *wp, t_template *tmpl, t_gpointer *gp); +EXTERN void word_restore(t_word *wp, t_template *tmpl, + int argc, t_atom *argv); +EXTERN t_scalar *scalar_new(t_glist *owner, + t_symbol *templatesym); +EXTERN void scalar_getbasexy(t_scalar *x, float *basex, float *basey); + +/* ------helper routines for "garrays" and "plots" -------------- */ +EXTERN int array_doclick(t_array *array, t_glist *glist, t_gobj *gobj, + t_symbol *elemtemplatesym, + float linewidth, float xloc, float xinc, float yloc, + int xpix, int ypix, int shift, int alt, int dbl, int doit); + +EXTERN void array_getcoordinate(t_glist *glist, + char *elem, int xonset, int yonset, int wonset, int indx, + float basex, float basey, float xinc, + float *xp, float *yp, float *wp); + +EXTERN int array_getfields(t_symbol *elemtemplatesym, + t_canvas **elemtemplatecanvasp, + t_template **elemtemplatep, int *elemsizep, + int *xonsetp, int *yonsetp, int *wonsetp); + +/* --------------------- templates ------------------------- */ +EXTERN t_template *template_new(t_symbol *sym, int argc, t_atom *argv); +EXTERN void template_free(t_template *x); +EXTERN int template_match(t_template *x1, t_template *x2); +EXTERN int template_find_field(t_template *x, t_symbol *name, int *p_onset, + int *p_type, t_symbol **p_arraytype); +EXTERN t_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp, + int loud); +EXTERN void template_setfloat(t_template *x, t_symbol *fieldname, t_word *wp, + t_float f, int loud); +EXTERN t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, + t_word *wp, int loud); +EXTERN void template_setsymbol(t_template *x, t_symbol *fieldname, + t_word *wp, t_symbol *s, int loud); + +EXTERN t_template *gtemplate_get(t_gtemplate *x); +EXTERN t_template *template_findbyname(t_symbol *s); +EXTERN t_canvas *template_findcanvas(t_template *tmpl); + +EXTERN t_float template_getfloat(t_template *x, t_symbol *fieldname, + t_word *wp, int loud); +EXTERN void template_setfloat(t_template *x, t_symbol *fieldname, + t_word *wp, t_float f, int loud); +EXTERN t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, + t_word *wp, int loud); +EXTERN void template_setsymbol(t_template *x, t_symbol *fieldname, + t_word *wp, t_symbol *s, int loud); + +/* ----------------------- guiconnects, g_guiconnect.c --------- */ +EXTERN t_guiconnect *guiconnect_new(t_pd *who, t_symbol *sym); +EXTERN void guiconnect_notarget(t_guiconnect *x, double timedelay); + +/* ------------- IEMGUI routines used in other g_ files ---------------- */ +EXTERN t_symbol *iemgui_raute2dollar(t_symbol *s); +EXTERN t_symbol *iemgui_dollar2raute(t_symbol *s); + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +} +#endif +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* this file defines the structure for "glists" and related structures and +functions. "Glists" and "canvases" and "graphs" used to be different +structures until being unified in version 0.35. + +A glist occupies its own window if the "gl_havewindow" flag is set. Its +appearance on its "parent" or "owner" (if it has one) is as a graph if +"gl_isgraph" is set, and otherwise as a text box. + +A glist is "root" if it has no owner, i.e., a document window. In this +case "gl_havewindow" is always set. + +We maintain a list of root windows, so that we can traverse the whole +collection of everything in a Pd process. + +If a glist has a window it may still not be "mapped." Miniaturized +windows aren't mapped, for example, but a window is also not mapped +immediately upon creation. In either case gl_havewindow is true but +gl_mapped is false. + +Closing a non-root window makes it invisible; closing a root destroys it. + +A glist that's just a text object on its parent is always "toplevel." An +embedded glist can switch back and forth to appear as a toplevel by double- +clicking on it. Single-clicking a text box makes the toplevel become visible +and raises the window it's in. + +If a glist shows up as a graph on its parent, the graph is blanked while the +glist has its own window, even if miniaturized. + +*/ + +/* NOTE: this file describes Pd implementation details which may change +in future releases. The public (stable) API is in m_pd.h. */ + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +extern "C" { +#endif + +/* --------------------- geometry ---------------------------- */ +#define IOWIDTH 7 /* width of an inlet/outlet in pixels */ +#define IOMIDDLE ((IOWIDTH-1)/2) +#define GLIST_DEFGRAPHWIDTH 200 +#define GLIST_DEFGRAPHHEIGHT 140 +/* ----------------------- data ------------------------------- */ + +typedef struct _updateheader +{ + struct _updateheader *upd_next; + unsigned int upd_array:1; /* true if array, false if glist */ + unsigned int upd_queued:1; /* true if we're queued */ +} t_updateheader; + + /* types to support glists grabbing mouse motion or keys from parent */ +typedef void (*t_glistmotionfn)(void *z, t_floatarg dx, t_floatarg dy); +typedef void (*t_glistkeyfn)(void *z, t_floatarg key); + +EXTERN_STRUCT _rtext; +#define t_rtext struct _rtext + +EXTERN_STRUCT _gtemplate; +#define t_gtemplate struct _gtemplate + +EXTERN_STRUCT _guiconnect; +#define t_guiconnect struct _guiconnect + +EXTERN_STRUCT _tscalar; +#define t_tscalar struct _tscalar + +EXTERN_STRUCT _canvasenvironment; +#define t_canvasenvironment struct _canvasenvironment + +typedef struct _selection +{ + t_gobj *sel_what; + struct _selection *sel_next; +} t_selection; + + /* this structure is instantiated whenever a glist becomes visible. */ +typedef struct _editor +{ + t_updateheader e_upd; /* update header structure */ + t_selection *e_updlist; /* list of objects to update */ + t_rtext *e_rtext; /* text responder linked list */ + t_selection *e_selection; /* head of the selection list */ + t_rtext *e_textedfor; /* the rtext if any that we are editing */ + t_gobj *e_grab; /* object being "dragged" */ + t_glistmotionfn e_motionfn; /* ... motion callback */ + t_glistkeyfn e_keyfn; /* ... keypress callback */ + t_binbuf *e_connectbuf; /* connections to deleted objects */ + t_binbuf *e_deleted; /* last stuff we deleted */ + t_guiconnect *e_guiconnect; /* GUI connection for filtering messages */ + struct _glist *e_glist; /* glist which owns this */ + int e_xwas; /* xpos on last mousedown or motion event */ + int e_ywas; /* ypos, similarly */ + int e_selectline_index1; /* indices for the selected line if any */ + int e_selectline_outno; /* (only valid if e_selectedline is set) */ + int e_selectline_index2; + int e_selectline_inno; + t_outconnect *e_selectline_tag; + unsigned int e_onmotion: 3; /* action to take on motion */ + unsigned int e_lastmoved: 1; /* one if mouse has moved since click */ + unsigned int e_textdirty: 1; /* one if e_textedfor has changed */ + unsigned int e_selectedline: 1; /* one if a line is selected */ +} t_editor; + +#define MA_NONE 0 /* e_onmotion: do nothing on mouse motion */ +#define MA_MOVE 1 /* drag the selection around */ +#define MA_CONNECT 2 /* make a connection */ +#define MA_REGION 3 /* selection region */ +#define MA_PASSOUT 4 /* send on to e_grab */ +#define MA_DRAGTEXT 5 /* drag in text editor to alter selection */ + +/* editor structure for "garrays". We don't bother to delete and regenerate +this structure when the "garray" becomes invisible or visible, although we +could do so if the structure gets big (like the "editor" above.) */ + +typedef struct _arrayvis +{ + t_updateheader av_upd; /* update header structure */ + t_garray *av_garray; /* owning structure */ +} t_arrayvis; + +/* the t_tick structure describes where to draw x and y "ticks" for a glist */ + +typedef struct _tick /* where to put ticks on x or y axes */ +{ + float k_point; /* one point to draw a big tick at */ + float k_inc; /* x or y increment per little tick */ + int k_lperb; /* little ticks per big; 0 if no ticks to draw */ +} t_tick; + +/* the t_glist structure, which describes a list of elements that live on an +area of a window. + +*/ + +struct _glist +{ + t_object gl_obj; /* header in case we're a glist */ + t_gobj *gl_list; /* the actual data */ + struct _gstub *gl_stub; /* safe pointer handler */ + int gl_valid; /* incremented when pointers might be stale */ + struct _glist *gl_owner; /* parent glist, supercanvas, or 0 if none */ + int gl_pixwidth; /* width in pixels (on parent, if a graph) */ + int gl_pixheight; + float gl_x1; /* bounding rectangle in our own coordinates */ + float gl_y1; + float gl_x2; + float gl_y2; + int gl_screenx1; /* screen coordinates when toplevel */ + int gl_screeny1; + int gl_screenx2; + int gl_screeny2; + t_tick gl_xtick; /* ticks marking X values */ + int gl_nxlabels; /* number of X coordinate labels */ + t_symbol **gl_xlabel; /* ... an array to hold them */ + float gl_xlabely; /* ... and their Y coordinates */ + t_tick gl_ytick; /* same as above for Y ticks and labels */ + int gl_nylabels; + t_symbol **gl_ylabel; + float gl_ylabelx; + t_editor *gl_editor; /* editor structure when visible */ + t_symbol *gl_name; /* symbol bound here */ + int gl_font; /* nominal font size in points, e.g., 10 */ + struct _glist *gl_next; /* link in list of toplevels */ + t_canvasenvironment *gl_env; /* root canvases and abstractions only */ + unsigned int gl_havewindow:1; /* true if we own a window */ + unsigned int gl_mapped:1; /* true if, moreover, it's "mapped" */ + unsigned int gl_dirty:1; /* (root canvas only:) patch has changed */ + unsigned int gl_loading:1; /* am now loading from file */ + unsigned int gl_willvis:1; /* make me visible after loading */ + unsigned int gl_edit:1; /* edit mode */ + unsigned int gl_isdeleting:1; /* we're inside glist_delete -- hack! */ + unsigned int gl_stretch:1; /* stretch contents on resize */ + unsigned int gl_isgraph:1; /* show as graph on parent */ +}; + +#define gl_gobj gl_obj.te_g +#define gl_pd gl_gobj.g_pd + +/* a data structure to describe a field in a pure datum */ + +#define DT_FLOAT 0 +#define DT_SYMBOL 1 +#define DT_LIST 2 +#define DT_ARRAY 3 + +typedef struct _dataslot +{ + int ds_type; + t_symbol *ds_name; + t_symbol *ds_arraytemplate; /* filled in for arrays only */ +} t_dataslot; + + +/* T.Grill - changed t_pd member to t_pdobj to avoid name clashed */ +typedef struct _template +{ + t_pd t_pdobj; /* header */ + struct _gtemplate *t_list; /* list of "struct"/gtemplate objects */ + t_symbol *t_sym; /* name */ + int t_n; /* number of dataslots (fields) */ + t_dataslot *t_vec; /* array of dataslots */ +} t_template; + +struct _array +{ + int a_n; /* number of elements */ + int a_elemsize; /* size in bytes; LATER get this from template */ + char *a_vec; /* array of elements */ + t_symbol *a_templatesym; /* template for elements */ + int a_valid; /* protection against stale pointers into array */ + t_gpointer a_gp; /* pointer to scalar or array element we're in */ + t_gstub *a_stub; +}; + + /* structure for traversing all the connections in a glist */ +typedef struct _linetraverser +{ + t_canvas *tr_x; + t_object *tr_ob; + int tr_nout; + int tr_outno; + t_object *tr_ob2; + t_outlet *tr_outlet; + t_inlet *tr_inlet; + int tr_nin; + int tr_inno; + int tr_x11, tr_y11, tr_x12, tr_y12; + int tr_x21, tr_y21, tr_x22, tr_y22; + int tr_lx1, tr_ly1, tr_lx2, tr_ly2; + t_outconnect *tr_nextoc; + int tr_nextoutno; +} t_linetraverser; + +/* function types used to define graphical behavior for gobjs, a bit like X +widgets. We don't use Pd methods because Pd's typechecking can't specify the +types of pointer arguments. Also it's more convenient this way, since +every "patchable" object can just get the "text" behaviors. */ + + /* Call this to get a gobj's bounding rectangle in pixels */ +typedef void (*t_getrectfn)(t_gobj *x, struct _glist *glist, + int *x1, int *y1, int *x2, int *y2); + /* and this to displace a gobj: */ +typedef void (*t_displacefn)(t_gobj *x, struct _glist *glist, int dx, int dy); + /* change color to show selection: */ +typedef void (*t_selectfn)(t_gobj *x, struct _glist *glist, int state); + /* change appearance to show activation/deactivation: */ +typedef void (*t_activatefn)(t_gobj *x, struct _glist *glist, int state); + /* warn a gobj it's about to be deleted */ +typedef void (*t_deletefn)(t_gobj *x, struct _glist *glist); + /* making visible or invisible */ +typedef void (*t_visfn)(t_gobj *x, struct _glist *glist, int flag); + /* field a mouse click (when not in "edit" mode) */ +typedef int (*t_clickfn)(t_gobj *x, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit); + /* ... and later, resizing; getting/setting font or color... */ + +struct _widgetbehavior +{ + t_getrectfn w_getrectfn; + t_displacefn w_displacefn; + t_selectfn w_selectfn; + t_activatefn w_activatefn; + t_deletefn w_deletefn; + t_visfn w_visfn; + t_clickfn w_clickfn; +}; + +/* -------- behaviors for scalars defined by objects in template --------- */ +/* these are set by "drawing commands" in g_template.c which add appearance to +scalars, which live in some other window. If the scalar is just included +in a canvas the "parent" is a misnomer. There is also a text scalar object +which really does draw the scalar on the parent window; see g_scalar.c. */ + +/* note how the click function wants the whole scalar, not the "data", so +doesn't work on array elements... LATER reconsider this */ + + /* bounding rectangle: */ +typedef void (*t_parentgetrectfn)(t_gobj *x, struct _glist *glist, + t_word *data, t_template *tmpl, float basex, float basey, + int *x1, int *y1, int *x2, int *y2); + /* displace it */ +typedef void (*t_parentdisplacefn)(t_gobj *x, struct _glist *glist, + t_word *data, t_template *tmpl, float basex, float basey, + int dx, int dy); + /* change color to show selection */ +typedef void (*t_parentselectfn)(t_gobj *x, struct _glist *glist, + t_word *data, t_template *tmpl, float basex, float basey, + int state); + /* change appearance to show activation/deactivation: */ +typedef void (*t_parentactivatefn)(t_gobj *x, struct _glist *glist, + t_word *data, t_template *tmpl, float basex, float basey, + int state); + /* making visible or invisible */ +typedef void (*t_parentvisfn)(t_gobj *x, struct _glist *glist, + t_word *data, t_template *tmpl, float basex, float basey, + int flag); + /* field a mouse click */ +typedef int (*t_parentclickfn)(t_gobj *x, struct _glist *glist, + t_scalar *sc, t_template *tmpl, float basex, float basey, + int xpix, int ypix, int shift, int alt, int dbl, int doit); + +struct _parentwidgetbehavior +{ + t_parentgetrectfn w_parentgetrectfn; + t_parentdisplacefn w_parentdisplacefn; + t_parentselectfn w_parentselectfn; + t_parentactivatefn w_parentactivatefn; + t_parentvisfn w_parentvisfn; + t_parentclickfn w_parentclickfn; +}; + + /* cursor definitions; used as return value for t_parentclickfn */ +#define CURSOR_RUNMODE_NOTHING 0 +#define CURSOR_RUNMODE_CLICKME 1 +#define CURSOR_RUNMODE_THICKEN 2 +#define CURSOR_RUNMODE_ADDPOINT 3 +#define CURSOR_EDITMODE_NOTHING 4 +#define CURSOR_EDITMODE_CONNECT 5 +#define CURSOR_EDITMODE_DISCONNECT 6 +EXTERN void canvas_setcursor(t_glist *x, unsigned int cursornum); + +extern t_canvas *canvas_editing; /* last canvas to start text edting */ +extern t_canvas *canvas_whichfind; /* last canvas we did a find in */ +extern t_canvas *canvas_list; /* list of all root canvases */ +extern t_class *vinlet_class, *voutlet_class; +extern int glist_valid; /* incremented when pointers might be stale */ + +/* ------------------- functions on any gobj ----------------------------- */ +EXTERN void gobj_getrect(t_gobj *x, t_glist *owner, int *x1, int *y1, + int *x2, int *y2); +EXTERN void gobj_displace(t_gobj *x, t_glist *owner, int dx, int dy); +EXTERN void gobj_select(t_gobj *x, t_glist *owner, int state); +EXTERN void gobj_activate(t_gobj *x, t_glist *owner, int state); +EXTERN void gobj_delete(t_gobj *x, t_glist *owner); +EXTERN void gobj_vis(t_gobj *x, t_glist *glist, int flag); +EXTERN int gobj_click(t_gobj *x, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit); +EXTERN void gobj_save(t_gobj *x, t_binbuf *b); +EXTERN void gobj_properties(t_gobj *x, struct _glist *glist); +EXTERN void gobj_save(t_gobj *x, t_binbuf *b); + +/* -------------------- functions on glists --------------------- */ +EXTERN t_glist *glist_new( void); +EXTERN void glist_init(t_glist *x); +EXTERN void glist_add(t_glist *x, t_gobj *g); +EXTERN void glist_cleanup(t_glist *x); +EXTERN void glist_free(t_glist *x); + +EXTERN void glist_clear(t_glist *x); +EXTERN t_canvas *glist_getcanvas(t_glist *x); +EXTERN int glist_isselected(t_glist *x, t_gobj *y); +EXTERN void glist_select(t_glist *x, t_gobj *y); +EXTERN void glist_deselect(t_glist *x, t_gobj *y); +EXTERN void glist_noselect(t_glist *x); +EXTERN void glist_selectall(t_glist *x); +EXTERN void glist_delete(t_glist *x, t_gobj *y); +EXTERN void glist_retext(t_glist *x, t_text *y); +EXTERN void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn, + t_glistkeyfn keyfn, int xpos, int ypos); +EXTERN int glist_isvisible(t_glist *x); +EXTERN int glist_istoplevel(t_glist *x); +EXTERN t_glist *glist_findgraph(t_glist *x); +EXTERN int glist_getfont(t_glist *x); +EXTERN void glist_sort(t_glist *canvas); +EXTERN void glist_read(t_glist *x, t_symbol *filename, t_symbol *format); +EXTERN void glist_mergefile(t_glist *x, t_symbol *filename, t_symbol *format); + +EXTERN float glist_pixelstox(t_glist *x, float xpix); +EXTERN float glist_pixelstoy(t_glist *x, float ypix); +EXTERN float glist_xtopixels(t_glist *x, float xval); +EXTERN float glist_ytopixels(t_glist *x, float yval); +EXTERN float glist_dpixtodx(t_glist *x, float dxpix); +EXTERN float glist_dpixtody(t_glist *x, float dypix); + +EXTERN void glist_redrawitem(t_glist *owner, t_gobj *gobj); +EXTERN void glist_getnextxy(t_glist *gl, int *xval, int *yval); +EXTERN void glist_glist(t_glist *g, t_symbol *s, int argc, t_atom *argv); +EXTERN t_glist *glist_addglist(t_glist *g, t_symbol *sym, + float x1, float y1, float x2, float y2, + float px1, float py1, float px2, float py2); +EXTERN void glist_arraydialog(t_glist *parent, t_symbol *name, + t_floatarg size, t_floatarg saveit, t_floatarg newgraph); +EXTERN t_binbuf *glist_writetobinbuf(t_glist *x, int wholething); +EXTERN int glist_isgraph(t_glist *x); +EXTERN void glist_redraw(t_glist *x); +EXTERN void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime, + char *tag, int x1, int y1, int x2, int y2); +EXTERN void glist_eraseiofor(t_glist *glist, t_object *ob, char *tag); +EXTERN void canvas_create_editor(t_glist *x, int createit); +void canvas_deletelinesforio(t_canvas *x, t_text *text, + t_inlet *inp, t_outlet *outp); + + +/* -------------------- functions on texts ------------------------- */ +EXTERN void text_setto(t_text *x, t_glist *glist, char *buf, int bufsize); +EXTERN void text_drawborder(t_text *x, t_glist *glist, char *tag, + int width, int height, int firsttime); +EXTERN void text_eraseborder(t_text *x, t_glist *glist, char *tag); +EXTERN int text_xcoord(t_text *x, t_glist *glist); +EXTERN int text_ycoord(t_text *x, t_glist *glist); +EXTERN int text_xpix(t_text *x, t_glist *glist); +EXTERN int text_ypix(t_text *x, t_glist *glist); +EXTERN int text_shouldvis(t_text *x, t_glist *glist); + +/* -------------------- functions on rtexts ------------------------- */ +#define RTEXT_DOWN 1 +#define RTEXT_DRAG 2 +#define RTEXT_DBL 3 +#define RTEXT_SHIFT 4 + +EXTERN t_rtext *rtext_new(t_glist *glist, t_text *who); +EXTERN t_rtext *glist_findrtext(t_glist *gl, t_text *who); +EXTERN void rtext_draw(t_rtext *x); +EXTERN void rtext_erase(t_rtext *x); +EXTERN t_rtext *rtext_remove(t_rtext *first, t_rtext *x); +EXTERN int rtext_height(t_rtext *x); +EXTERN void rtext_displace(t_rtext *x, int dx, int dy); +EXTERN void rtext_select(t_rtext *x, int state); +EXTERN void rtext_activate(t_rtext *x, int state); +EXTERN void rtext_free(t_rtext *x); +EXTERN void rtext_key(t_rtext *x, int n, t_symbol *s); +EXTERN void rtext_mouse(t_rtext *x, int xval, int yval, int flag); +EXTERN void rtext_retext(t_rtext *x); +EXTERN int rtext_width(t_rtext *x); +EXTERN int rtext_height(t_rtext *x); +EXTERN char *rtext_gettag(t_rtext *x); +EXTERN void rtext_gettext(t_rtext *x, char **buf, int *bufsize); + +/* -------------------- functions on canvases ------------------------ */ +EXTERN t_class *canvas_class; + +EXTERN t_canvas *canvas_new(void *dummy, t_symbol *sel, int argc, t_atom *argv); +EXTERN t_symbol *canvas_makebindsym(t_symbol *s); +EXTERN void canvas_vistext(t_canvas *x, t_text *y); +EXTERN void canvas_fixlinesfor(t_canvas *x, t_text *text); +EXTERN void canvas_deletelinesfor(t_canvas *x, t_text *text); +EXTERN void canvas_stowconnections(t_canvas *x); +EXTERN void canvas_restoreconnections(t_canvas *x); +EXTERN void canvas_redraw(t_canvas *x); + +EXTERN t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *sym); +EXTERN void canvas_rminlet(t_canvas *x, t_inlet *ip); +EXTERN t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *sym); +EXTERN void canvas_rmoutlet(t_canvas *x, t_outlet *op); +EXTERN void canvas_redrawallfortemplate(t_canvas *tmpl); +EXTERN void canvas_zapallfortemplate(t_canvas *tmpl); +EXTERN void canvas_setusedastemplate(t_canvas *x); +EXTERN t_canvas *canvas_getcurrent(void); +EXTERN void canvas_setcurrent(t_canvas *x); +EXTERN void canvas_unsetcurrent(t_canvas *x); +EXTERN t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s); +EXTERN t_canvas *canvas_getrootfor(t_canvas *x); +EXTERN void canvas_dirty(t_canvas *x, t_int n); +EXTERN int canvas_getfont(t_canvas *x); +typedef int (*t_canvasapply)(t_canvas *x, t_int x1, t_int x2, t_int x3); + +EXTERN t_int *canvas_recurapply(t_canvas *x, t_canvasapply *fn, + t_int x1, t_int x2, t_int x3); + +EXTERN void canvas_resortinlets(t_canvas *x); +EXTERN void canvas_resortoutlets(t_canvas *x); +EXTERN void canvas_free(t_canvas *x); +EXTERN void canvas_updatewindowlist( void); +EXTERN void canvas_editmode(t_canvas *x, t_floatarg yesplease); +EXTERN int canvas_isabstraction(t_canvas *x); +EXTERN int canvas_istable(t_canvas *x); +EXTERN int canvas_showtext(t_canvas *x); +EXTERN void canvas_vis(t_canvas *x, t_floatarg f); +EXTERN t_canvasenvironment *canvas_getenv(t_canvas *x); +EXTERN void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir); +EXTERN void canvas_loadbang(t_canvas *x); +EXTERN int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos, + int *x1p, int *y1p, int *x2p, int *y2p); +EXTERN int canvas_setdeleting(t_canvas *x, int flag); + +typedef void (*t_undofn)(t_canvas *canvas, void *buf, + int action); /* a function that does UNDO/REDO */ +#define UNDO_FREE 0 /* free current undo/redo buffer */ +#define UNDO_UNDO 1 /* undo */ +#define UNDO_REDO 2 /* redo */ +EXTERN void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf, + const char *name); +EXTERN void canvas_noundo(t_canvas *x); +EXTERN int canvas_getindex(t_canvas *x, t_gobj *y); + +/* T.Grill - made public for dynamic object creation */ +/* in g_editor.c */ +EXTERN void canvas_connect(t_canvas *x, + t_floatarg fwhoout, t_floatarg foutno,t_floatarg fwhoin, t_floatarg finno); +EXTERN void canvas_disconnect(t_canvas *x, + float index1, float outno, float index2, float inno); +EXTERN int canvas_isconnected (t_canvas *x, + t_text *ob1, int n1, t_text *ob2, int n2); +EXTERN void canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy); + + +/* ---- functions on canvasses as objects --------------------- */ + +EXTERN void canvas_fattenforscalars(t_canvas *x, + int *x1, int *y1, int *x2, int *y2); +EXTERN void canvas_visforscalars(t_canvas *x, t_glist *glist, int vis); +EXTERN int canvas_clicksub(t_canvas *x, int xpix, int ypix, int shift, + int alt, int dbl, int doit); +EXTERN t_glist *canvas_getglistonsuper(void); + +EXTERN void linetraverser_start(t_linetraverser *t, t_canvas *x); +EXTERN t_outconnect *linetraverser_next(t_linetraverser *t); +EXTERN void linetraverser_skipobject(t_linetraverser *t); + +/* --------------------- functions on tscalars --------------------- */ + +EXTERN void tscalar_getrect(t_tscalar *x, t_glist *owner, + int *xp1, int *yp1, int *xp2, int *yp2); +EXTERN void tscalar_vis(t_tscalar *x, t_glist *owner, int flag); +EXTERN int tscalar_click(t_tscalar *x, int xpix, int ypix, int shift, + int alt, int dbl, int doit); + +/* --------- functions on garrays (graphical arrays) -------------------- */ + +EXTERN t_template *garray_template(t_garray *x); + +/* -------------------- arrays --------------------- */ +EXTERN t_garray *graph_array(t_glist *gl, t_symbol *s, t_symbol *tmpl, + t_floatarg f, t_floatarg saveit); +EXTERN t_array *array_new(t_symbol *templatesym, t_gpointer *parent); +EXTERN void array_resize(t_array *x, t_template *tmpl, int n); +EXTERN void array_free(t_array *x); + +/* --------------------- gpointers and stubs ---------------- */ +EXTERN t_gstub *gstub_new(t_glist *gl, t_array *a); +EXTERN void gstub_cutoff(t_gstub *gs); +EXTERN void gpointer_setglist(t_gpointer *gp, t_glist *glist, t_scalar *x); + +/* --------------------- scalars ------------------------- */ +EXTERN void word_init(t_word *wp, t_template *tmpl, t_gpointer *gp); +EXTERN void word_restore(t_word *wp, t_template *tmpl, + int argc, t_atom *argv); +EXTERN t_scalar *scalar_new(t_glist *owner, + t_symbol *templatesym); +EXTERN void scalar_getbasexy(t_scalar *x, float *basex, float *basey); + +/* ------helper routines for "garrays" and "plots" -------------- */ +EXTERN int array_doclick(t_array *array, t_glist *glist, t_gobj *gobj, + t_symbol *elemtemplatesym, + float linewidth, float xloc, float xinc, float yloc, + int xpix, int ypix, int shift, int alt, int dbl, int doit); + +EXTERN void array_getcoordinate(t_glist *glist, + char *elem, int xonset, int yonset, int wonset, int indx, + float basex, float basey, float xinc, + float *xp, float *yp, float *wp); + +EXTERN int array_getfields(t_symbol *elemtemplatesym, + t_canvas **elemtemplatecanvasp, + t_template **elemtemplatep, int *elemsizep, + int *xonsetp, int *yonsetp, int *wonsetp); + +/* --------------------- templates ------------------------- */ +EXTERN t_template *template_new(t_symbol *sym, int argc, t_atom *argv); +EXTERN void template_free(t_template *x); +EXTERN int template_match(t_template *x1, t_template *x2); +EXTERN int template_find_field(t_template *x, t_symbol *name, int *p_onset, + int *p_type, t_symbol **p_arraytype); +EXTERN t_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp, + int loud); +EXTERN void template_setfloat(t_template *x, t_symbol *fieldname, t_word *wp, + t_float f, int loud); +EXTERN t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, + t_word *wp, int loud); +EXTERN void template_setsymbol(t_template *x, t_symbol *fieldname, + t_word *wp, t_symbol *s, int loud); + +EXTERN t_template *gtemplate_get(t_gtemplate *x); +EXTERN t_template *template_findbyname(t_symbol *s); +EXTERN t_canvas *template_findcanvas(t_template *tmpl); + +EXTERN t_float template_getfloat(t_template *x, t_symbol *fieldname, + t_word *wp, int loud); +EXTERN void template_setfloat(t_template *x, t_symbol *fieldname, + t_word *wp, t_float f, int loud); +EXTERN t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, + t_word *wp, int loud); +EXTERN void template_setsymbol(t_template *x, t_symbol *fieldname, + t_word *wp, t_symbol *s, int loud); + +/* ----------------------- guiconnects, g_guiconnect.c --------- */ +EXTERN t_guiconnect *guiconnect_new(t_pd *who, t_symbol *sym); +EXTERN void guiconnect_notarget(t_guiconnect *x, double timedelay); + +/* ------------- IEMGUI routines used in other g_ files ---------------- */ +EXTERN t_symbol *iemgui_raute2dollar(t_symbol *s); +EXTERN t_symbol *iemgui_dollar2raute(t_symbol *s); + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +} +#endif diff --git a/apps/plugins/pdbox/PDa/src/g_editor.c b/apps/plugins/pdbox/PDa/src/g_editor.c new file mode 100644 index 0000000..a1dea79 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_editor.c @@ -0,0 +1,4548 @@ +/* Copyright (c) 1997-2001 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include +#include +#include "m_pd.h" +#include "m_imp.h" +#include "s_stuff.h" +#include "g_canvas.h" +#include + +void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename, + int selectem); + +void open_via_helppath(const char *name, const char *dir); +char *class_gethelpdir(t_class *c); + +/* ------------------ forward declarations --------------- */ +static void canvas_doclear(t_canvas *x); +static void glist_setlastxy(t_glist *gl, int xval, int yval); +static void glist_donewloadbangs(t_glist *x); +static t_binbuf *canvas_docopy(t_canvas *x); +static void canvas_dopaste(t_canvas *x, t_binbuf *b); +static void canvas_paste(t_canvas *x); +static void canvas_clearline(t_canvas *x); +static t_binbuf *copy_binbuf; + +/* ---------------- generic widget behavior ------------------------- */ + +void gobj_getrect(t_gobj *x, t_glist *glist, int *x1, int *y1, + int *x2, int *y2) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_getrectfn) + (*x->g_pd->c_wb->w_getrectfn)(x, glist, x1, y1, x2, y2); +} + +void gobj_displace(t_gobj *x, t_glist *glist, int dx, int dy) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_displacefn) + (*x->g_pd->c_wb->w_displacefn)(x, glist, dx, dy); +} + +void gobj_select(t_gobj *x, t_glist *glist, int state) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_selectfn) + (*x->g_pd->c_wb->w_selectfn)(x, glist, state); +} + +void gobj_activate(t_gobj *x, t_glist *glist, int state) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_activatefn) + (*x->g_pd->c_wb->w_activatefn)(x, glist, state); +} + +void gobj_delete(t_gobj *x, t_glist *glist) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_deletefn) + (*x->g_pd->c_wb->w_deletefn)(x, glist); +} + +void gobj_vis(t_gobj *x, struct _glist *glist, int flag) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_visfn) + (*x->g_pd->c_wb->w_visfn)(x, glist, flag); +} + +int gobj_click(t_gobj *x, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_clickfn) + return ((*x->g_pd->c_wb->w_clickfn)(x, + glist, xpix, ypix, shift, alt, dbl, doit)); + else return (0); +} + +/* ------------------------ managing the selection ----------------- */ + +void glist_selectline(t_glist *x, t_outconnect *oc, int index1, + int outno, int index2, int inno) +{ + if (x->gl_editor) + { + glist_noselect(x); + x->gl_editor->e_selectedline = 1; + x->gl_editor->e_selectline_index1 = index1; + x->gl_editor->e_selectline_outno = outno; + x->gl_editor->e_selectline_index2 = index2; + x->gl_editor->e_selectline_inno = inno; + x->gl_editor->e_selectline_tag = oc; + sys_vgui(".x%x.c itemconfigure l%x -fill blue\n", + x, x->gl_editor->e_selectline_tag); + } +} + +void glist_deselectline(t_glist *x) +{ + if (x->gl_editor) + { + x->gl_editor->e_selectedline = 0; + sys_vgui(".x%x.c itemconfigure l%x -fill black\n", + x, x->gl_editor->e_selectline_tag); + } +} + +int glist_isselected(t_glist *x, t_gobj *y) +{ + if (x->gl_editor) + { + t_selection *sel; + for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next) + if (sel->sel_what == y) return (1); + } + return (0); +} + + /* call this for unselected objects only */ +void glist_select(t_glist *x, t_gobj *y) +{ + if (x->gl_editor) + { + t_selection *sel = (t_selection *)getbytes(sizeof(*sel)); + if (x->gl_editor->e_selectedline) + glist_deselectline(x); + /* LATER #ifdef out the following check */ + if (glist_isselected(x, y)) bug("glist_select"); + sel->sel_next = x->gl_editor->e_selection; + sel->sel_what = y; + x->gl_editor->e_selection = sel; + gobj_select(y, x, 1); + } +} + + /* call this for selected objects only */ +void glist_deselect(t_glist *x, t_gobj *y) +{ + int fixdsp = 0; + static int reenter = 0; + if (reenter) return; + reenter = 1; + if (x->gl_editor) + { + t_selection *sel, *sel2; + t_rtext *z = 0; + if (!glist_isselected(x, y)) bug("glist_deselect"); + if (x->gl_editor->e_textedfor) + { + t_rtext *fuddy = glist_findrtext(x, (t_text *)y); + if (x->gl_editor->e_textedfor == fuddy) + { + if (x->gl_editor->e_textdirty) + { + z = fuddy; + canvas_stowconnections(glist_getcanvas(x)); + } + gobj_activate(y, x, 0); + } + if (zgetfn(&y->g_pd, gensym("dsp"))) + fixdsp = 1; + } + if ((sel = x->gl_editor->e_selection)->sel_what == y) + { + x->gl_editor->e_selection = x->gl_editor->e_selection->sel_next; + gobj_select(sel->sel_what, x, 0); + freebytes(sel, sizeof(*sel)); + } + else + { + for (sel = x->gl_editor->e_selection; sel2 = sel->sel_next; + sel = sel2) + { + if (sel2->sel_what == y) + { + sel->sel_next = sel2->sel_next; + gobj_select(sel2->sel_what, x, 0); + freebytes(sel2, sizeof(*sel2)); + break; + } + } + } + if (z) + { + char *buf; + int bufsize; + + rtext_gettext(z, &buf, &bufsize); + text_setto((t_text *)y, x, buf, bufsize); + canvas_fixlinesfor(glist_getcanvas(x), (t_text *)y); + x->gl_editor->e_textedfor = 0; + } + if (fixdsp) + canvas_update_dsp(); + } + reenter = 0; +} + +void glist_noselect(t_glist *x) +{ + if (x->gl_editor) + { + while (x->gl_editor->e_selection) + glist_deselect(x, x->gl_editor->e_selection->sel_what); + if (x->gl_editor->e_selectedline) + glist_deselectline(x); + } +} + +void glist_selectall(t_glist *x) +{ + if (x->gl_editor) + { + glist_noselect(x); + if (x->gl_list) + { + t_selection *sel = (t_selection *)getbytes(sizeof(*sel)); + t_gobj *y = x->gl_list; + x->gl_editor->e_selection = sel; + sel->sel_what = y; + gobj_select(y, x, 1); + while (y = y->g_next) + { + t_selection *sel2 = (t_selection *)getbytes(sizeof(*sel2)); + sel->sel_next = sel2; + sel = sel2; + sel->sel_what = y; + gobj_select(y, x, 1); + } + sel->sel_next = 0; + } + } +} + + /* get the index of a gobj in a glist. If y is zero, return the + total number of objects. */ +int glist_getindex(t_glist *x, t_gobj *y) +{ + t_gobj *y2; + int indx; + + for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next) + indx++; + return (indx); +} + + /* get the index of the object, among selected items, if "selected" + is set; otherwise, among unselected ones. If y is zero, just + counts the selected or unselected objects. */ +int glist_selectionindex(t_glist *x, t_gobj *y, int selected) +{ + t_gobj *y2; + int indx; + + for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next) + if (selected == glist_isselected(x, y2)) + indx++; + return (indx); +} + +static t_gobj *glist_nth(t_glist *x, int n) +{ + t_gobj *y; + int indx; + for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++) + if (indx == n) + return (y); + return (0); +} + +/* ------------------- support for undo/redo -------------------------- */ + +static t_undofn canvas_undo_fn; /* current undo function if any */ +static int canvas_undo_whatnext; /* whether we can now UNDO or REDO */ +static void *canvas_undo_buf; /* data private to the undo function */ +static t_canvas *canvas_undo_canvas; /* which canvas we can undo on */ +static const char *canvas_undo_name; + +void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf, + const char *name) +{ + int hadone = 0; + /* blow away the old undo information. In one special case the + old undo info is re-used; if so we shouldn't free it here. */ + if (canvas_undo_fn && canvas_undo_buf && (buf != canvas_undo_buf)) + { + (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_FREE); + hadone = 1; + } + canvas_undo_canvas = x; + canvas_undo_fn = undofn; + canvas_undo_buf = buf; + canvas_undo_whatnext = UNDO_UNDO; + canvas_undo_name = name; + if (x && glist_isvisible(x) && glist_istoplevel(x)) + /* enable undo in menu */ + sys_vgui("pdtk_undomenu .x%x %s no\n", x, name); + else if (hadone) + sys_vgui("pdtk_undomenu nobody no no\n"); +} + + /* clear undo if it happens to be for the canvas x. + (but if x is 0, clear it regardless of who owns it.) */ +void canvas_noundo(t_canvas *x) +{ + if (!x || (x == canvas_undo_canvas)) + canvas_setundo(0, 0, 0, "foo"); +} + +static void canvas_undo(t_canvas *x) +{ + if (x != canvas_undo_canvas) + bug("canvas_undo 1"); + else if (canvas_undo_whatnext != UNDO_UNDO) + bug("canvas_undo 2"); + else + { + /* post("undo"); */ + (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_UNDO); + /* enable redo in menu */ + if (glist_isvisible(x) && glist_istoplevel(x)) + sys_vgui("pdtk_undomenu .x%x no %s\n", x, canvas_undo_name); + canvas_undo_whatnext = UNDO_REDO; + } +} + +static void canvas_redo(t_canvas *x) +{ + if (x != canvas_undo_canvas) + bug("canvas_undo 1"); + else if (canvas_undo_whatnext != UNDO_REDO) + bug("canvas_undo 2"); + else + { + /* post("redo"); */ + (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_REDO); + /* enable undo in menu */ + if (glist_isvisible(x) && glist_istoplevel(x)) + sys_vgui("pdtk_undomenu .x%x %s no\n", x, canvas_undo_name); + canvas_undo_whatnext = UNDO_UNDO; + } +} + +/* ------- specific undo methods: 1. connect and disconnect -------- */ + +typedef struct _undo_connect +{ + int u_index1; + int u_outletno; + int u_index2; + int u_inletno; +} t_undo_connect; + +static void *canvas_undo_set_disconnect(t_canvas *x, + int index1, int outno, int index2, int inno) +{ + t_undo_connect *buf = (t_undo_connect *)getbytes(sizeof(*buf)); + buf->u_index1 = index1; + buf->u_outletno = outno; + buf->u_index2 = index2; + buf->u_inletno = inno; + return (buf); +} + +void canvas_disconnect(t_canvas *x, + float index1, float outno, float index2, float inno) +{ + t_linetraverser t; + t_outconnect *oc; + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + int srcno = canvas_getindex(x, &t.tr_ob->ob_g); + int sinkno = canvas_getindex(x, &t.tr_ob2->ob_g); + if (srcno == index1 && t.tr_outno == outno && + sinkno == index2 && t.tr_inno == inno) + { + sys_vgui(".x%x.c delete l%x\n", x, oc); + obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno); + break; + } + } +} + +static void canvas_undo_disconnect(t_canvas *x, void *z, int action) +{ + t_undo_connect *buf = z; + if (action == UNDO_UNDO) + { + canvas_connect(x, buf->u_index1, buf->u_outletno, + buf->u_index2, buf->u_inletno); + } + else if (action == UNDO_REDO) + { + canvas_disconnect(x, buf->u_index1, buf->u_outletno, + buf->u_index2, buf->u_inletno); + } + else if (action == UNDO_FREE) + t_freebytes(buf, sizeof(*buf)); +} + + /* connect just calls disconnect actions backward... */ +static void *canvas_undo_set_connect(t_canvas *x, + int index1, int outno, int index2, int inno) +{ + return (canvas_undo_set_disconnect(x, index1, outno, index2, inno)); +} + +static void canvas_undo_connect(t_canvas *x, void *z, int action) +{ + int myaction; + if (action == UNDO_UNDO) + myaction = UNDO_REDO; + else if (action == UNDO_REDO) + myaction = UNDO_UNDO; + else myaction = action; + canvas_undo_disconnect(x, z, myaction); +} + +/* ---------- ... 2. cut, clear, and typing into objects: -------- */ + +#define UCUT_CUT 1 /* operation was a cut */ +#define UCUT_CLEAR 2 /* .. a clear */ +#define UCUT_TEXT 3 /* text typed into a box */ + +typedef struct _undo_cut +{ + t_binbuf *u_objectbuf; /* the object cleared or typed into */ + t_binbuf *u_reconnectbuf; /* connections into and out of object */ + t_binbuf *u_redotextbuf; /* buffer to paste back for redo if TEXT */ + int u_mode; /* from flags above */ +} t_undo_cut; + +static void *canvas_undo_set_cut(t_canvas *x, int mode) +{ + t_undo_cut *buf; + t_gobj *y; + t_linetraverser t; + t_outconnect *oc; + int nnotsel= glist_selectionindex(x, 0, 0); + buf = (t_undo_cut *)getbytes(sizeof(*buf)); + buf->u_mode = mode; + buf->u_redotextbuf = 0; + + /* store connections into/out of the selection */ + buf->u_reconnectbuf = binbuf_new(); + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + int issel1 = glist_isselected(x, &t.tr_ob->ob_g); + int issel2 = glist_isselected(x, &t.tr_ob2->ob_g); + if (issel1 != issel2) + { + binbuf_addv(buf->u_reconnectbuf, "ssiiii;", + gensym("#X"), gensym("connect"), + (issel1 ? nnotsel : 0) + + glist_selectionindex(x, &t.tr_ob->ob_g, issel1), + t.tr_outno, + (issel2 ? nnotsel : 0) + + glist_selectionindex(x, &t.tr_ob2->ob_g, issel2), + t.tr_inno); + } + } + if (mode == UCUT_TEXT) + { + buf->u_objectbuf = canvas_docopy(x); + } + else if (mode == UCUT_CUT) + { + buf->u_objectbuf = 0; + } + else if (mode == UCUT_CLEAR) + { + buf->u_objectbuf = canvas_docopy(x); + } + return (buf); +} + +static void canvas_undo_cut(t_canvas *x, void *z, int action) +{ + t_undo_cut *buf = z; + int mode = buf->u_mode; + if (action == UNDO_UNDO) + { + if (mode == UCUT_CUT) + canvas_dopaste(x, copy_binbuf); + else if (mode == UCUT_CLEAR) + canvas_dopaste(x, buf->u_objectbuf); + else if (mode == UCUT_TEXT) + { + t_gobj *y1, *y2; + glist_noselect(x); + for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2) + ; + if (y1) + { + if (!buf->u_redotextbuf) + { + glist_noselect(x); + glist_select(x, y1); + buf->u_redotextbuf = canvas_docopy(x); + glist_noselect(x); + } + glist_delete(x, y1); + } + canvas_dopaste(x, buf->u_objectbuf); + } + pd_bind(&x->gl_pd, gensym("#X")); + binbuf_eval(buf->u_reconnectbuf, 0, 0, 0); + pd_unbind(&x->gl_pd, gensym("#X")); + } + else if (action == UNDO_REDO) + { + if (mode == UCUT_CUT || mode == UCUT_CLEAR) + canvas_doclear(x); + else if (mode == UCUT_TEXT) + { + t_gobj *y1, *y2; + for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2) + ; + if (y1) + glist_delete(x, y1); + canvas_dopaste(x, buf->u_redotextbuf); + pd_bind(&x->gl_pd, gensym("#X")); + binbuf_eval(buf->u_reconnectbuf, 0, 0, 0); + pd_unbind(&x->gl_pd, gensym("#X")); + } + } + else if (action == UNDO_FREE) + { + if (buf->u_objectbuf) + binbuf_free(buf->u_objectbuf); + if (buf->u_reconnectbuf) + binbuf_free(buf->u_reconnectbuf); + if (buf->u_redotextbuf) + binbuf_free(buf->u_redotextbuf); + t_freebytes(buf, sizeof(*buf)); + } +} + +/* --------- 3. motion, including "tidy up" and stretching ----------- */ + +typedef struct _undo_move_elem +{ + int e_index; + int e_xpix; + int e_ypix; +} t_undo_move_elem; + +typedef struct _undo_move +{ + t_undo_move_elem *u_vec; + int u_n; +} t_undo_move; + +static int canvas_undo_already_set_move; + +static void *canvas_undo_set_move(t_canvas *x, int selected) +{ + int x1, y1, x2, y2, i, indx; + t_gobj *y; + t_undo_move *buf = (t_undo_move *)getbytes(sizeof(*buf)); + buf->u_n = selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0); + buf->u_vec = (t_undo_move_elem *)getbytes(sizeof(*buf->u_vec) * + (selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0))); + if (selected) + { + for (y = x->gl_list, i = indx = 0; y; y = y->g_next, indx++) + if (glist_isselected(x, y)) + { + gobj_getrect(y, x, &x1, &y1, &x2, &y2); + buf->u_vec[i].e_index = indx; + buf->u_vec[i].e_xpix = x1; + buf->u_vec[i].e_ypix = y1; + i++; + } + } + else + { + for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++) + { + gobj_getrect(y, x, &x1, &y1, &x2, &y2); + buf->u_vec[indx].e_index = indx; + buf->u_vec[indx].e_xpix = x1; + buf->u_vec[indx].e_ypix = y1; + } + } + canvas_undo_already_set_move = 1; + return (buf); +} + +static void canvas_undo_move(t_canvas *x, void *z, int action) +{ + t_undo_move *buf = z; + if (action == UNDO_UNDO || action == UNDO_REDO) + { + int i; + for (i = 0; i < buf->u_n; i++) + { + int x1, y1, x2, y2, newx, newy; + t_gobj *y; + newx = buf->u_vec[i].e_xpix; + newy = buf->u_vec[i].e_ypix; + y = glist_nth(x, buf->u_vec[i].e_index); + if (y) + { + gobj_getrect(y, x, &x1, &y1, &x2, &y2); + gobj_displace(y, x, newx-x1, newy - y1); + buf->u_vec[i].e_xpix = x1; + buf->u_vec[i].e_ypix = y1; + } + } + } + else if (action == UNDO_FREE) + { + t_freebytes(buf->u_vec, buf->u_n * sizeof(*buf->u_vec)); + t_freebytes(buf, sizeof(*buf)); + } +} + +/* --------- 4. paste (also duplicate) ----------- */ + +typedef struct _undo_paste +{ + int u_index; /* index of first object pasted */ +} t_undo_paste; + +static void *canvas_undo_set_paste(t_canvas *x) +{ + t_undo_paste *buf = (t_undo_paste *)getbytes(sizeof(*buf)); + buf->u_index = glist_getindex(x, 0); + return (buf); +} + +static void canvas_undo_paste(t_canvas *x, void *z, int action) +{ + t_undo_paste *buf = z; + if (action == UNDO_UNDO) + { + t_gobj *y; + glist_noselect(x); + for (y = glist_nth(x, buf->u_index); y; y = y->g_next) + glist_select(x, y); + canvas_doclear(x); + } + else if (action == UNDO_REDO) + { + t_selection *sel; + canvas_dopaste(x, copy_binbuf); + /* if it was "duplicate" have to re-enact the displacement. */ + if (canvas_undo_name && canvas_undo_name[0] == 'd') + for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next) + gobj_displace(sel->sel_what, x, 10, 10); + } +else if (action == UNDO_FREE) + t_freebytes(buf, sizeof(*buf)); +} + + /* recursively check for abstractions to reload as result of a save. + Don't reload the one we just saved ("except") though. */ + /* LATER try to do the same trick for externs. */ +static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir, + t_gobj *except) +{ + t_gobj *g; + int i, nobj = glist_getindex(gl, 0); /* number of objects */ + for (g = gl->gl_list, i = 0; g && i < nobj; i++) + { + if (g != except && pd_class(&g->g_pd) == canvas_class && + canvas_isabstraction((t_canvas *)g) && + ((t_canvas *)g)->gl_name == name && + canvas_getdir((t_canvas *)g) == dir) + { + /* we're going to remake the object, so "g" will go stale. + Get its index here, and afterward restore g. Also, the + replacement will be at teh end of the list, so we don't + do g = g->g_next in this case. */ + int j = glist_getindex(gl, g); + if (!gl->gl_havewindow) + canvas_vis(glist_getcanvas(gl), 1); + glist_noselect(gl); + glist_select(gl, g); + canvas_setundo(gl, canvas_undo_cut, + canvas_undo_set_cut(gl, UCUT_CLEAR), "clear"); + canvas_doclear(gl); + canvas_undo(gl); + glist_noselect(gl); + g = glist_nth(gl, j); + } + else + { + if (g != except && pd_class(&g->g_pd) == canvas_class) + glist_doreload((t_canvas *)g, name, dir, except); + g = g->g_next; + } + } +} + + /* call canvas_doreload on everyone */ +void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except) +{ + t_canvas *x; + /* find all root canvases */ + for (x = canvas_list; x; x = x->gl_next) + glist_doreload(x, name, dir, except); +} + +/* ------------------------ event handling ------------------------ */ + +#define CURSOR_RUNMODE_NOTHING 0 +#define CURSOR_RUNMODE_CLICKME 1 +#define CURSOR_RUNMODE_THICKEN 2 +#define CURSOR_RUNMODE_ADDPOINT 3 +#define CURSOR_EDITMODE_NOTHING 4 +#define CURSOR_EDITMODE_CONNECT 5 +#define CURSOR_EDITMODE_DISCONNECT 6 + +static char *cursorlist[] = { +#ifdef MSW + "right_ptr", /* CURSOR_RUNMODE_NOTHING */ +#else + "left_ptr", /* CURSOR_RUNMODE_NOTHING */ +#endif + "arrow", /* CURSOR_RUNMODE_CLICKME */ + "sb_v_double_arrow", /* CURSOR_RUNMODE_THICKEN */ + "plus", /* CURSOR_RUNMODE_ADDPOINT */ + "hand2", /* CURSOR_EDITMODE_NOTHING */ + "circle", /* CURSOR_EDITMODE_CONNECT */ + "X_cursor" /* CURSOR_EDITMODE_DISCONNECT */ +}; + +void canvas_setcursor(t_canvas *x, unsigned int cursornum) +{ + static t_canvas *xwas; + static unsigned int cursorwas; + if (cursornum >= sizeof(cursorlist)/sizeof *cursorlist) + { + bug("canvas_setcursor"); + return; + } + if (xwas != x || cursorwas != cursornum) + { + sys_vgui(".x%x configure -cursor %s\n", x, cursorlist[cursornum]); + xwas = x; + cursorwas = cursornum; + } +} + + /* check if a point lies in a gobj. */ +int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos, + int *x1p, int *y1p, int *x2p, int *y2p) +{ + int x1, y1, x2, y2; + t_text *ob; + if ((ob = pd_checkobject(&y->g_pd)) && + !text_shouldvis(ob, x)) + return (0); + gobj_getrect(y, x, &x1, &y1, &x2, &y2); + if (xpos >= x1 && xpos <= x2 && ypos >= y1 && ypos <= y2) + { + *x1p = x1; + *y1p = y1; + *x2p = x2; + *y2p = y2; + return (1); + } + else return (0); +} + + /* find the last gobj, if any, containing the point. */ +static t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos, + int *x1p, int *y1p, int *x2p, int *y2p) +{ + t_gobj *y, *rval = 0; + for (y = x->gl_list; y; y = y->g_next) + { + if (canvas_hitbox(x, y, xpos, ypos, x1p, y1p, x2p, y2p)) + rval = y; + } + return (rval); +} + + /* right-clicking on a canvas object pops up a menu. */ +static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y) +{ + int canprop, canopen; + canprop = (!y || (y && class_getpropertiesfn(pd_class(&y->g_pd)))); + canopen = (y && zgetfn(&y->g_pd, gensym("menu-open"))); + sys_vgui("pdtk_canvas_popup .x%x %d %d %d %d\n", + x, xpos, ypos, canprop, canopen); +} + + /* tell GUI to create a properties dialog on the canvas. We tell + the user the negative of the "pixel" y scale to make it appear to grow + naturally upward, whereas pixels grow downward. */ +static void canvas_properties(t_glist *x) +{ + char graphbuf[200]; + sprintf(graphbuf, "pdtk_canvas_dialog %%s %g %g %g %g \n", + glist_dpixtodx(x, 1), -glist_dpixtody(x, 1), + (float)glist_isgraph(x), (float)x->gl_stretch); + gfxstub_new(&x->gl_pd, x, graphbuf); +} + + +void canvas_setgraph(t_glist *x, int flag) +{ + if (!flag && glist_isgraph(x)) + { + if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner)) + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + x->gl_isgraph = 0; + if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner)) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } + } + else if (flag && !glist_isgraph(x)) + { + if (x->gl_pixwidth <= 0) + x->gl_pixwidth = GLIST_DEFGRAPHWIDTH; + + if (x->gl_pixheight <= 0) + x->gl_pixheight = GLIST_DEFGRAPHHEIGHT; + + if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner)) + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + x->gl_isgraph = 1; + /* if (x->gl_owner && glist_isvisible(x->gl_owner)) + canvas_vis(x, 1); */ + if (x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner)) + canvas_create_editor(x, 1); + if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner)) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } + } +} + + /* called from the gui when "OK" is selected on the canvas properties + dialog. Again we negate "y" scale. */ +static void canvas_donecanvasdialog(t_glist *x, t_floatarg xperpix, + t_floatarg yperpix, t_floatarg fgraphme) +{ + int graphme = (fgraphme != 0), redraw = 0; + yperpix = -yperpix; + if (xperpix == 0) + xperpix = 1; + if (yperpix == 0) + yperpix = 1; + canvas_setgraph(x, graphme); + if (!x->gl_isgraph && (xperpix != glist_dpixtodx(x, 1))) + { + if (xperpix > 0) + { + x->gl_x1 = 0; + x->gl_x2 = xperpix; + } + else + { + x->gl_x1 = -xperpix * (x->gl_screenx2 - x->gl_screenx1); + x->gl_x2 = x->gl_x1 + xperpix; + } + redraw = 1; + } + if (!x->gl_isgraph && (yperpix != glist_dpixtody(x, 1))) + { + if (yperpix > 0) + { + x->gl_y1 = 0; + x->gl_y2 = yperpix; + } + else + { + x->gl_y1 = -yperpix * (x->gl_screeny2 - x->gl_screeny1); + x->gl_y2 = x->gl_y1 + yperpix; + } + redraw = 1; + } + if (redraw) + canvas_redraw(x); +} + + /* called from the gui when a popup menu comes back with "properties," + "open," or "help." */ +static void canvas_done_popup(t_canvas *x, float which, float xpos, float ypos) +{ + char pathbuf[MAXPDSTRING], namebuf[MAXPDSTRING]; + t_gobj *y; + for (y = x->gl_list; y; y = y->g_next) + { + int x1, y1, x2, y2; + if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)) + { + if (which == 0) /* properties */ + { + if (!class_getpropertiesfn(pd_class(&y->g_pd))) + continue; + (*class_getpropertiesfn(pd_class(&y->g_pd)))(y, x); + return; + } + else if (which == 1) /* open */ + { + if (!zgetfn(&y->g_pd, gensym("menu-open"))) + continue; + vmess(&y->g_pd, gensym("menu-open"), ""); + return; + } + else /* help */ + { + char *dir; + if (pd_class(&y->g_pd) == canvas_class && + canvas_isabstraction((t_canvas *)y)) + { + t_object *ob = (t_object *)y; + int ac = binbuf_getnatom(ob->te_binbuf); + t_atom *av = binbuf_getvec(ob->te_binbuf); + if (ac < 1) + return; + atom_string(av, namebuf, MAXPDSTRING); + dir = canvas_getdir((t_canvas *)y)->s_name; + } + else + { + strcpy(namebuf, class_gethelpname(pd_class(&y->g_pd))); + dir = class_gethelpdir(pd_class(&y->g_pd)); + } + if (strcmp(namebuf + strlen(namebuf) - 3, ".pd")) + strcat(namebuf, ".pd"); + open_via_helppath(namebuf, dir); + return; + } + } + } + if (which == 0) + canvas_properties(x); + else if (which == 2) + { + strcpy(pathbuf, sys_libdir->s_name); + strcat(pathbuf, "/doc/5.reference/0.INTRO.txt"); + sys_vgui("menu_opentext %s\n", pathbuf); + } +} + +#define NOMOD 0 +#define SHIFTMOD 1 +#define CTRLMOD 2 +#define ALTMOD 4 +#define RIGHTCLICK 8 + +/* on one-button-mouse machines, you can use double click to + mean right click (which gets the popup menu.) Do this for Mac. */ +#ifdef MACOSX +#define SIMULATERIGHTCLICK +#endif + +#ifdef SIMULATERIGHTCLICK +static double canvas_upclicktime; +static int canvas_upx, canvas_upy; +#define DCLICKINTERVAL 0.25 +#endif + + /* mouse click */ +void canvas_doclick(t_canvas *x, int xpos, int ypos, int which, + int mod, int doit) +{ + t_gobj *y; + int shiftmod, runmode, altmod, rightclick; + int x1, y1, x2, y2, clickreturned = 0; + + if (!x->gl_editor) + { + bug("editor"); + return; + } + + shiftmod = (mod & SHIFTMOD); + runmode = ((mod & CTRLMOD) || (!x->gl_edit)); + altmod = (mod & ALTMOD); + rightclick = (mod & RIGHTCLICK); + + canvas_undo_already_set_move = 0; + + /* if keyboard was grabbed, notify grabber and cancel the grab */ + if (doit && x->gl_editor->e_grab && x->gl_editor->e_keyfn) + { + (* x->gl_editor->e_keyfn) (x->gl_editor->e_grab, 0); + glist_grab(x, 0, 0, 0, 0, 0); + } + +#ifdef SIMULATERIGHTCLICK + if (doit && !runmode && xpos == canvas_upx && ypos == canvas_upy && + sys_getrealtime() - canvas_upclicktime < DCLICKINTERVAL) + rightclick = 1; +#endif + + x->gl_editor->e_lastmoved = 0; + if (doit) + { + x->gl_editor->e_grab = 0; + x->gl_editor->e_onmotion = MA_NONE; + } + /* post("click %d %d %d %d", xpos, ypos, which, mod); */ + + if (x->gl_editor->e_onmotion != MA_NONE) + return; + + x->gl_editor->e_xwas = xpos; + x->gl_editor->e_ywas = ypos; + + if (runmode && !rightclick) + { + for (y = x->gl_list; y; y = y->g_next) + { + /* check if the object wants to be clicked */ + if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2) + && (clickreturned = gobj_click(y, x, xpos, ypos, + shiftmod, altmod, 0, doit))) + break; + } + if (!doit) + { + if (y) + canvas_setcursor(x, clickreturned); + else canvas_setcursor(x, CURSOR_RUNMODE_NOTHING); + } + return; + } + /* if not a runmode left click, fall here. */ + if (y = canvas_findhitbox(x, xpos, ypos, &x1, &y1, &x2, &y2)) + { + t_object *ob = pd_checkobject(&y->g_pd); + /* check you're in the rectangle */ + ob = pd_checkobject(&y->g_pd); + if (rightclick) + canvas_rightclick(x, xpos, ypos, y); + else if (shiftmod) + { + if (doit) + { + t_rtext *rt; + if (ob && (rt = x->gl_editor->e_textedfor) && + rt == glist_findrtext(x, ob)) + { + rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_SHIFT); + x->gl_editor->e_onmotion = MA_DRAGTEXT; + x->gl_editor->e_xwas = x1; + x->gl_editor->e_ywas = y1; + } + else + { + if (glist_isselected(x, y)) + glist_deselect(x, y); + else glist_select(x, y); + } + } + } + else + { + /* look for an outlet */ + int noutlet; + if (ob && (noutlet = obj_noutlets(ob)) && ypos >= y2-4) + { + int width = x2 - x1; + int nout1 = (noutlet > 1 ? noutlet - 1 : 1); + int closest = ((xpos-x1) * (nout1) + width/2)/width; + int hotspot = x1 + + (width - IOWIDTH) * closest / (nout1); + if (closest < noutlet && + xpos >= (hotspot-1) && xpos <= hotspot + (IOWIDTH+1)) + { + if (doit) + { + int issignal = obj_issignaloutlet(ob, closest); + x->gl_editor->e_onmotion = MA_CONNECT; + x->gl_editor->e_xwas = xpos; + x->gl_editor->e_ywas = ypos; + sys_vgui( + ".x%x.c create line %d %d %d %d -width %d -tags x\n", + x, xpos, ypos, xpos, ypos, + (issignal ? 2 : 1)); + } + else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT); + } + else if (doit) + goto nooutletafterall; + } + /* not in an outlet; select and move */ + else if (doit) + { + t_rtext *rt; + /* check if the box is being text edited */ + nooutletafterall: + if (ob && (rt = x->gl_editor->e_textedfor) && + rt == glist_findrtext(x, ob)) + { + rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_DOWN); + x->gl_editor->e_onmotion = MA_DRAGTEXT; + x->gl_editor->e_xwas = x1; + x->gl_editor->e_ywas = y1; + } + else + { + /* otherwise select and drag to displace */ + if (!glist_isselected(x, y)) + { + glist_noselect(x); + glist_select(x, y); + } + x->gl_editor->e_onmotion = MA_MOVE; + } + } + else canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); + } + return; + } + /* if right click doesn't hit any boxes, call rightclick + routine anyway */ + if (rightclick) + canvas_rightclick(x, xpos, ypos, 0); + + /* if not an editing action, and if we didn't hit a + box, set cursor and return */ + if (runmode || rightclick) + { + canvas_setcursor(x, CURSOR_RUNMODE_NOTHING); + return; + } + /* having failed to find a box, we try lines now. */ + if (!runmode && !altmod && !shiftmod) + { + t_linetraverser t; + t_outconnect *oc; + float fx = xpos, fy = ypos; + t_glist *glist2 = glist_getcanvas(x); + linetraverser_start(&t, glist2); + while (oc = linetraverser_next(&t)) + { + float lx1 = t.tr_lx1, ly1 = t.tr_ly1, + lx2 = t.tr_lx2, ly2 = t.tr_ly2; + float area = (lx2 - lx1) * (fy - ly1) - + (ly2 - ly1) * (fx - lx1); + float dsquare = (lx2-lx1) * (lx2-lx1) + (ly2-ly1) * (ly2-ly1); + if (area * area >= 50 * dsquare) continue; + if ((lx2-lx1) * (fx-lx1) + (ly2-ly1) * (fy-ly1) < 0) continue; + if ((lx2-lx1) * (lx2-fx) + (ly2-ly1) * (ly2-fy) < 0) continue; + if (doit) + { + glist_selectline(glist2, oc, + canvas_getindex(glist2, &t.tr_ob->ob_g), t.tr_outno, + canvas_getindex(glist2, &t.tr_ob2->ob_g), t.tr_inno); + } + canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT); + return; + } + } + canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); + if (doit) + { + if (!shiftmod) glist_noselect(x); + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags x\n", + x, xpos, ypos, xpos, ypos); + x->gl_editor->e_xwas = xpos; + x->gl_editor->e_ywas = ypos; + x->gl_editor->e_onmotion = MA_REGION; + } +} + +void canvas_mousedown(t_canvas *x, t_floatarg xpos, t_floatarg ypos, + t_floatarg which, t_floatarg mod) +{ + canvas_doclick(x, xpos, ypos, which, mod, 1); +} + +int canvas_isconnected (t_canvas *x, t_text *ob1, int n1, + t_text *ob2, int n2) +{ + t_linetraverser t; + t_outconnect *oc; + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + if (t.tr_ob == ob1 && t.tr_outno == n1 && + t.tr_ob2 == ob2 && t.tr_inno == n2) + return (1); + return (0); +} + +void canvas_doconnect(t_canvas *x, int xpos, int ypos, int which, int doit) +{ + int x11, y11, x12, y12; + t_gobj *y1; + int x21, y21, x22, y22; + t_gobj *y2; + int xwas = x->gl_editor->e_xwas, + ywas = x->gl_editor->e_ywas; + if (doit) sys_vgui(".x%x.c delete x\n", x); + else sys_vgui(".x%x.c coords x %d %d %d %d\n", + x, x->gl_editor->e_xwas, + x->gl_editor->e_ywas, xpos, ypos); + + if ((y1 = canvas_findhitbox(x, xwas, ywas, &x11, &y11, &x12, &y12)) + && (y2 = canvas_findhitbox(x, xpos, ypos, &x21, &y21, &x22, &y22))) + { + t_object *ob1 = pd_checkobject(&y1->g_pd); + t_object *ob2 = pd_checkobject(&y2->g_pd); + int noutlet1, ninlet2; + if (ob1 && ob2 && ob1 != ob2 && + (noutlet1 = obj_noutlets(ob1)) + && (ninlet2 = obj_ninlets(ob2))) + { + int width1 = x12 - x11, closest1, hotspot1; + int width2 = x22 - x21, closest2, hotspot2; + int lx1, lx2, ly1, ly2; + t_outconnect *oc; + + if (noutlet1 > 1) + { + closest1 = ((xwas-x11) * (noutlet1-1) + width1/2)/width1; + hotspot1 = x11 + + (width1 - IOWIDTH) * closest1 / (noutlet1-1); + } + else closest1 = 0, hotspot1 = x11; + + if (ninlet2 > 1) + { + closest2 = ((xpos-x21) * (ninlet2-1) + width2/2)/width2; + hotspot2 = x21 + + (width2 - IOWIDTH) * closest2 / (ninlet2-1); + } + else closest2 = 0, hotspot2 = x21; + + if (closest1 >= noutlet1) + closest1 = noutlet1 - 1; + if (closest2 >= ninlet2) + closest2 = ninlet2 - 1; + + if (canvas_isconnected (x, ob1, closest1, ob2, closest2)) + { + canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); + return; + } + if (obj_issignaloutlet(ob1, closest1) && + !obj_issignalinlet(ob2, closest2)) + { + if (doit) + error("can't connect signal outlet to control inlet"); + canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); + return; + } + if (doit) + { + oc = obj_connect(ob1, closest1, ob2, closest2); + lx1 = x11 + (noutlet1 > 1 ? + ((x12-x11-IOWIDTH) * closest1)/(noutlet1-1) : 0) + + IOMIDDLE; + ly1 = y12; + lx2 = x21 + (ninlet2 > 1 ? + ((x22-x21-IOWIDTH) * closest2)/(ninlet2-1) : 0) + + IOMIDDLE; + ly2 = y21; + sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n", + glist_getcanvas(x), + lx1, ly1, lx2, ly2, + (obj_issignaloutlet(ob1, closest1) ? 2 : 1), oc); + canvas_setundo(x, canvas_undo_connect, + canvas_undo_set_connect(x, + canvas_getindex(x, &ob1->ob_g), closest1, + canvas_getindex(x, &ob2->ob_g), closest2), + "connect"); + } + else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT); + return; + } + } + canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); +} + +void canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy) +{ + t_gobj *y; + for (y = x->gl_list; y; y = y->g_next) + { + int x1, y1, x2, y2; + gobj_getrect(y, x, &x1, &y1, &x2, &y2); + if (hix >= x1 && lox <= x2 && hiy >= y1 && loy <= y2 + && !glist_isselected(x, y)) + glist_select(x, y); + } +} + +static void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit) +{ + if (doit) + { + int lox, loy, hix, hiy; + if (x->gl_editor->e_xwas < xpos) + lox = x->gl_editor->e_xwas, hix = xpos; + else hix = x->gl_editor->e_xwas, lox = xpos; + if (x->gl_editor->e_ywas < ypos) + loy = x->gl_editor->e_ywas, hiy = ypos; + else hiy = x->gl_editor->e_ywas, loy = ypos; + canvas_selectinrect(x, lox, loy, hix, hiy); + sys_vgui(".x%x.c delete x\n", x); + x->gl_editor->e_onmotion = 0; + } + else sys_vgui(".x%x.c coords x %d %d %d %d\n", + x, x->gl_editor->e_xwas, + x->gl_editor->e_ywas, xpos, ypos); +} + +void canvas_mouseup(t_canvas *x, + t_floatarg fxpos, t_floatarg fypos, t_floatarg fwhich) +{ + t_gobj *y; + + int xpos = fxpos, ypos = fypos, which = fwhich; + + if (!x->gl_editor) + { + bug("editor"); + return; + } + +#ifdef SIMULATERIGHTCLICK + canvas_upclicktime = sys_getrealtime(); + canvas_upx = xpos; + canvas_upy = ypos; +#endif + + if (x->gl_editor->e_onmotion == MA_CONNECT) + canvas_doconnect(x, xpos, ypos, which, 1); + else if (x->gl_editor->e_onmotion == MA_REGION) + canvas_doregion(x, xpos, ypos, 1); + else if (x->gl_editor->e_onmotion == MA_MOVE) + { + /* after motion, if there's only one item selected, activate it */ + if (x->gl_editor->e_selection && + !(x->gl_editor->e_selection->sel_next)) + gobj_activate(x->gl_editor->e_selection->sel_what, + x, 1); + } + else if (x->gl_editor->e_onmotion == MA_PASSOUT) + x->gl_editor->e_onmotion = 0; + x->gl_editor->e_onmotion = MA_NONE; + + +#if 1 + /* GG misused the (unused) dbl parameter to tell if mouseup */ + + for (y = x->gl_list; y; y = y->g_next) { + int x1, y1, x2, y2, clickreturned = 0; + + /* check if the object wants to be clicked */ + if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2) + && (clickreturned = gobj_click(y, x, xpos, ypos, + 0, 0, 1, 0))) + break; + } +#endif + + +} + + /* displace the selection by (dx, dy) pixels */ +static void canvas_displaceselection(t_canvas *x, int dx, int dy) +{ + t_selection *y; + int resortin = 0, resortout = 0; + if (!canvas_undo_already_set_move) + { + canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 1), + "motion"); + canvas_undo_already_set_move = 1; + } + for (y = x->gl_editor->e_selection; y; y = y->sel_next) + { + t_class *cl = pd_class(&y->sel_what->g_pd); + gobj_displace(y->sel_what, x, dx, dy); + if (cl == vinlet_class) resortin = 1; + else if (cl == voutlet_class) resortout = 1; + } + if (resortin) canvas_resortinlets(x); + if (resortout) canvas_resortoutlets(x); + canvas_dirty(x, 1); +} + + /* this routine is called whenever a key is pressed or released. "x" + may be zero if there's no current canvas. The first argument is true or + fals for down/up; the second one is either a symbolic key name (e.g., + "Right" or an Ascii key number. */ +void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av) +{ + static t_symbol *keynumsym, *keyupsym, *keynamesym; + int keynum, fflag; + t_symbol *gotkeysym; + + int down, shift; + + if (ac < 3) + return; + if (!x->gl_editor) + { + bug("editor"); + return; + } + canvas_undo_already_set_move = 0; + down = (atom_getfloat(av) != 0); /* nonzero if it's a key down */ + shift = (atom_getfloat(av+2) != 0); /* nonzero if shift-ed */ + if (av[1].a_type == A_SYMBOL) + gotkeysym = av[1].a_w.w_symbol; + else if (av[1].a_type == A_FLOAT) + { + char buf[3]; + sprintf(buf, "%c", (int)(av[1].a_w.w_float)); + gotkeysym = gensym(buf); + } + else gotkeysym = gensym("?"); + fflag = (av[0].a_type == A_FLOAT ? av[0].a_w.w_float : 0); + keynum = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float : 0); + if (keynum == '\\' || keynum == '{' || keynum == '}') + { + post("%c: dropped", (int)keynum); + return; + } + if (keynum == '\r') keynum = '\n'; + if (av[1].a_type == A_SYMBOL && + !strcmp(av[1].a_w.w_symbol->s_name, "Return")) + keynum = '\n'; + if (!keynumsym) + { + keynumsym = gensym("#key"); + keyupsym = gensym("#keyup"); + keynamesym = gensym("#keyname"); + } +#ifdef MACOSX + if (keynum == 30) + keynum = 0, gotkeysym = gensym("Up"); + else if (keynum == 31) + keynum = 0, gotkeysym = gensym("Down"); + else if (keynum == 28) + keynum = 0, gotkeysym = gensym("Left"); + else if (keynum == 29) + keynum = 0, gotkeysym = gensym("Right"); +#endif + if (keynumsym->s_thing && down) + pd_float(keynumsym->s_thing, (float)keynum); + if (keyupsym->s_thing && !down) + pd_float(keyupsym->s_thing, (float)keynum); + if (keynamesym->s_thing) + { + t_atom at[2]; + at[0] = av[0]; + SETFLOAT(at, down); + SETSYMBOL(at+1, gotkeysym); + pd_list(keynamesym->s_thing, 0, 2, at); + } + if (!x->gl_editor) /* if that 'invis'ed the window, we'd better stop. */ + return; + if (x && down) + { + /* if an object has "grabbed" keys just send them on */ + if (x->gl_editor->e_grab + && x->gl_editor->e_keyfn && keynum) + (* x->gl_editor->e_keyfn) + (x->gl_editor->e_grab, (float)keynum); + /* if a text editor is open send it on */ + else if (x->gl_editor->e_textedfor) + { + if (!x->gl_editor->e_textdirty) + { + canvas_setundo(x, canvas_undo_cut, + canvas_undo_set_cut(x, UCUT_TEXT), "typing"); + } + rtext_key(x->gl_editor->e_textedfor, + (int)keynum, gotkeysym); + if (x->gl_editor->e_textdirty) + canvas_dirty(x, 1); + } + /* check for backspace or clear */ + else if (keynum == 8 || keynum == 127) + { + if (x->gl_editor->e_selectedline) + canvas_clearline(x); + else if (x->gl_editor->e_selection) + { + canvas_setundo(x, canvas_undo_cut, + canvas_undo_set_cut(x, UCUT_CLEAR), "clear"); + canvas_doclear(x); + } + } + /* check for arrow keys */ + else if (!strcmp(gotkeysym->s_name, "Up")) + canvas_displaceselection(x, 0, shift ? -10 : -1); + else if (!strcmp(gotkeysym->s_name, "Down")) + canvas_displaceselection(x, 0, shift ? 10 : 1); + else if (!strcmp(gotkeysym->s_name, "Left")) + canvas_displaceselection(x, shift ? -10 : -1, 0); + else if (!strcmp(gotkeysym->s_name, "Right")) + canvas_displaceselection(x, shift ? 10 : 1, 0); + } +} + +void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos, + t_floatarg fmod) +{ + /* post("motion %d %d", xpos, ypos); */ + int mod = fmod; + if (!x->gl_editor) + { + bug("editor"); + return; + } + glist_setlastxy(x, xpos, ypos); + if (x->gl_editor->e_onmotion == MA_MOVE) + { + canvas_displaceselection(x, + xpos - x->gl_editor->e_xwas, ypos - x->gl_editor->e_ywas); + x->gl_editor->e_xwas = xpos; + x->gl_editor->e_ywas = ypos; + } + else if (x->gl_editor->e_onmotion == MA_REGION) + canvas_doregion(x, xpos, ypos, 0); + else if (x->gl_editor->e_onmotion == MA_CONNECT) + canvas_doconnect(x, xpos, ypos, 0, 0); + else if (x->gl_editor->e_onmotion == MA_PASSOUT) + { + if (!x->gl_editor->e_motionfn) + bug("e_motionfn"); + (*x->gl_editor->e_motionfn)(&x->gl_editor->e_grab->g_pd, + xpos - x->gl_editor->e_xwas, + ypos - x->gl_editor->e_ywas); + x->gl_editor->e_xwas = xpos; + x->gl_editor->e_ywas = ypos; + } + else if (x->gl_editor->e_onmotion == MA_DRAGTEXT) + { + t_rtext *rt = x->gl_editor->e_textedfor; + if (rt) + rtext_mouse(rt, xpos - x->gl_editor->e_xwas, + ypos - x->gl_editor->e_ywas, RTEXT_DRAG); + } + else canvas_doclick(x, xpos, ypos, 0, mod, 0); + + x->gl_editor->e_lastmoved = 1; +} + +void canvas_startmotion(t_canvas *x) +{ + int xval, yval; + if (!x->gl_editor) return; + glist_getnextxy(x, &xval, &yval); + if (xval == 0 && yval == 0) return; + x->gl_editor->e_onmotion = MA_MOVE; + x->gl_editor->e_xwas = xval; + x->gl_editor->e_ywas = yval; +} + +/* ----------------------------- window stuff ----------------------- */ + +void canvas_print(t_canvas *x, t_symbol *s) +{ + if (*s->s_name) sys_vgui(".x%x.c postscript -file %s\n", x, s->s_name); + else sys_vgui(".x%x.c postscript -file x.ps\n", x); +} + +void canvas_menuclose(t_canvas *x, t_floatarg force) +{ + if (x->gl_owner) + canvas_vis(x, 0); + else if ((force != 0) || (!x->gl_dirty)) + pd_free(&x->gl_pd); + else sys_vgui("pdtk_check {This window has been modified. Close anyway?}\ + {.x%x menuclose 1;\n}\n", x); +} + + /* put up a dialog which may call canvas_font back to do the work */ +static void canvas_menufont(t_canvas *x) +{ + char buf[80]; + t_canvas *x2 = canvas_getrootfor(x); + gfxstub_deleteforkey(x2); + sprintf(buf, "pdtk_canvas_dofont %%s %d\n", x2->gl_font); + gfxstub_new(&x2->gl_pd, &x2->gl_pd, buf); +} + +static int canvas_find_index1, canvas_find_index2; +static t_binbuf *canvas_findbuf; +int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf); + + /* find an atom or string of atoms */ +static int canvas_dofind(t_canvas *x, int *myindex1p) +{ + t_gobj *y; + int myindex1 = *myindex1p, myindex2; + if (myindex1 >= canvas_find_index1) + { + for (y = x->gl_list, myindex2 = 0; y; + y = y->g_next, myindex2++) + { + t_object *ob = 0; + if (ob = pd_checkobject(&y->g_pd)) + { + if (binbuf_match(ob->ob_binbuf, canvas_findbuf)) + { + if (myindex1 > canvas_find_index1 || + myindex1 == canvas_find_index1 && + myindex2 > canvas_find_index2) + { + canvas_find_index1 = myindex1; + canvas_find_index2 = myindex2; + glist_noselect(x); + canvas_vis(x, 1); + canvas_editmode(x, 1.); + glist_select(x, y); + return (1); + } + } + } + } + } + for (y = x->gl_list, myindex2 = 0; y; y = y->g_next, myindex2++) + { + if (pd_class(&y->g_pd) == canvas_class) + { + (*myindex1p)++; + if (canvas_dofind((t_canvas *)y, myindex1p)) + return (1); + } + } + return (0); +} + +static void canvas_find(t_canvas *x, t_symbol *s, int ac, t_atom *av) +{ + int myindex1 = 0, i; + for (i = 0; i < ac; i++) + { + if (av[i].a_type == A_SYMBOL) + { + if (!strcmp(av[i].a_w.w_symbol->s_name, "_semi_")) + SETSEMI(&av[i]); + else if (!strcmp(av[i].a_w.w_symbol->s_name, "_comma_")) + SETCOMMA(&av[i]); + } + } + if (!canvas_findbuf) + canvas_findbuf = binbuf_new(); + binbuf_clear(canvas_findbuf); + binbuf_add(canvas_findbuf, ac, av); + canvas_find_index1 = 0; + canvas_find_index2 = -1; + canvas_whichfind = x; + if (!canvas_dofind(x, &myindex1)) + { + binbuf_print(canvas_findbuf); + post("... couldn't find"); + } +} + +static void canvas_find_again(t_canvas *x) +{ + int myindex1 = 0; + if (!canvas_findbuf || !canvas_whichfind) + return; + if (!canvas_dofind(canvas_whichfind, &myindex1)) + { + binbuf_print(canvas_findbuf); + post("... couldn't find"); + } +} + +static void canvas_find_parent(t_canvas *x) +{ + if (x->gl_owner) + canvas_vis(glist_getcanvas(x->gl_owner), 1); +} + +static int glist_dofinderror(t_glist *gl, void *error_object) +{ + t_gobj *g; + for (g = gl->gl_list; g; g = g->g_next) + { + if ((void *)g == error_object) + { + /* got it... now show it. */ + glist_noselect(gl); + canvas_vis(glist_getcanvas(gl), 1); + canvas_editmode(glist_getcanvas(gl), 1.); + glist_select(gl, g); + return (1); + } + else if (g->g_pd == canvas_class) + { + if (glist_dofinderror((t_canvas *)g, error_object)) + return (1); + } + } + return (0); +} + +void canvas_finderror(void *error_object) +{ + t_canvas *x; + /* find all root canvases */ + for (x = canvas_list; x; x = x->gl_next) + { + if (glist_dofinderror(x, error_object)) + return; + } + post("... sorry, I couldn't find the source of that error."); +} + +void canvas_stowconnections(t_canvas *x) +{ + t_gobj *selhead = 0, *seltail = 0, *nonhead = 0, *nontail = 0, *y, *y2; + t_linetraverser t; + t_outconnect *oc; + if (!x->gl_editor) return; + /* split list to "selected" and "unselected" parts */ + for (y = x->gl_list; y; y = y2) + { + y2 = y->g_next; + if (glist_isselected(x, y)) + { + if (seltail) + { + seltail->g_next = y; + seltail = y; + y->g_next = 0; + } + else + { + selhead = seltail = y; + seltail->g_next = 0; + } + } + else + { + if (nontail) + { + nontail->g_next = y; + nontail = y; + y->g_next = 0; + } + else + { + nonhead = nontail = y; + nontail->g_next = 0; + } + } + } + /* move the selected part to the end */ + if (!nonhead) x->gl_list = selhead; + else x->gl_list = nonhead, nontail->g_next = selhead; + + /* add connections to binbuf */ + binbuf_clear(x->gl_editor->e_connectbuf); + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + int s1 = glist_isselected(x, &t.tr_ob->ob_g); + int s2 = glist_isselected(x, &t.tr_ob2->ob_g); + if (s1 != s2) + binbuf_addv(x->gl_editor->e_connectbuf, "ssiiii;", + gensym("#X"), gensym("connect"), + glist_getindex(x, &t.tr_ob->ob_g), t.tr_outno, + glist_getindex(x, &t.tr_ob2->ob_g), t.tr_inno); + } +} + +void canvas_restoreconnections(t_canvas *x) +{ + pd_bind(&x->gl_pd, gensym("#X")); + binbuf_eval(x->gl_editor->e_connectbuf, 0, 0, 0); + pd_unbind(&x->gl_pd, gensym("#X")); +} + +static t_binbuf *canvas_docopy(t_canvas *x) +{ + t_gobj *y; + t_linetraverser t; + t_outconnect *oc; + t_binbuf *b = binbuf_new(); + for (y = x->gl_list; y; y = y->g_next) + { + if (glist_isselected(x, y)) + gobj_save(y, b); + } + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + if (glist_isselected(x, &t.tr_ob->ob_g) + && glist_isselected(x, &t.tr_ob2->ob_g)) + { + binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"), + glist_selectionindex(x, &t.tr_ob->ob_g, 1), t.tr_outno, + glist_selectionindex(x, &t.tr_ob2->ob_g, 1), t.tr_inno); + } + } + return (b); +} + +static void canvas_copy(t_canvas *x) +{ + if (!x->gl_editor || !x->gl_editor->e_selection) + return; + binbuf_free(copy_binbuf); + copy_binbuf = canvas_docopy(x); +} + +static void canvas_clearline(t_canvas *x) +{ + if (x->gl_editor->e_selectedline) + { + canvas_disconnect(x, x->gl_editor->e_selectline_index1, + x->gl_editor->e_selectline_outno, + x->gl_editor->e_selectline_index2, + x->gl_editor->e_selectline_inno); + canvas_setundo(x, canvas_undo_disconnect, + canvas_undo_set_disconnect(x, + x->gl_editor->e_selectline_index1, + x->gl_editor->e_selectline_outno, + x->gl_editor->e_selectline_index2, + x->gl_editor->e_selectline_inno), + "disconnect"); + } +} + +extern t_pd *newest; +static void canvas_doclear(t_canvas *x) +{ + t_gobj *y, *y2; + int dspstate; + + dspstate = canvas_suspend_dsp(); + if (x->gl_editor->e_selectedline) + { + canvas_disconnect(x, x->gl_editor->e_selectline_index1, + x->gl_editor->e_selectline_outno, + x->gl_editor->e_selectline_index2, + x->gl_editor->e_selectline_inno); + canvas_setundo(x, canvas_undo_disconnect, + canvas_undo_set_disconnect(x, + x->gl_editor->e_selectline_index1, + x->gl_editor->e_selectline_outno, + x->gl_editor->e_selectline_index2, + x->gl_editor->e_selectline_inno), + "disconnect"); + } + /* if text is selected, deselecting it might remake the + object. So we deselect it and hunt for a "new" object on + the glist to reselect. */ + if (x->gl_editor->e_textedfor) + { + newest = 0; + glist_noselect(x); + if (newest) + { + for (y = x->gl_list; y; y = y->g_next) + if (&y->g_pd == newest) glist_select(x, y); + } + } + while (1) /* this is pretty wierd... should rewrite it */ + { + for (y = x->gl_list; y; y = y2) + { + y2 = y->g_next; + if (glist_isselected(x, y)) + { + glist_delete(x, y); +#if 0 + if (y2) post("cut 5 %x %x", y2, y2->g_next); + else post("cut 6"); +#endif + goto next; + } + } + goto restore; + next: ; + } +restore: + canvas_resume_dsp(dspstate); + canvas_dirty(x, 1); +} + +static void canvas_cut(t_canvas *x) +{ + if (x->gl_editor && x->gl_editor->e_selectedline) + canvas_clearline(x); + else if (x->gl_editor && x->gl_editor->e_selection) + { + canvas_setundo(x, canvas_undo_cut, + canvas_undo_set_cut(x, UCUT_CUT), "cut"); + canvas_copy(x); + canvas_doclear(x); + } +} + +static int paste_onset; +static t_canvas *paste_canvas; + +static void glist_donewloadbangs(t_glist *x) +{ + if (x->gl_editor) + { + t_selection *sel; + for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next) + if (pd_class(&sel->sel_what->g_pd) == canvas_class) + canvas_loadbang((t_canvas *)(&sel->sel_what->g_pd)); + } +} + +static void canvas_dopaste(t_canvas *x, t_binbuf *b) +{ + t_gobj *newgobj, *last, *g2; + int dspstate = canvas_suspend_dsp(), nbox, count; + + canvas_editmode(x, 1.); + glist_noselect(x); + for (g2 = x->gl_list, nbox = 0; g2; g2 = g2->g_next) nbox++; + + paste_onset = nbox; + paste_canvas = x; + + pd_bind(&x->gl_pd, gensym("#X")); + binbuf_eval(b, 0, 0, 0); + pd_unbind(&x->gl_pd, gensym("#X")); + for (g2 = x->gl_list, count = 0; g2; g2 = g2->g_next, count++) + if (count >= nbox) + glist_select(x, g2); + paste_canvas = 0; + canvas_resume_dsp(dspstate); + canvas_dirty(x, 1); + glist_donewloadbangs(x); +} + +static void canvas_paste(t_canvas *x) +{ + canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x), "paste"); + canvas_dopaste(x, copy_binbuf); +} + +static void canvas_duplicate(t_canvas *x) +{ + if (x->gl_editor->e_onmotion == MA_NONE) + { + t_selection *y; + canvas_copy(x); + canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x), + "duplicate"); + canvas_dopaste(x, copy_binbuf); + for (y = x->gl_editor->e_selection; y; y = y->sel_next) + gobj_displace(y->sel_what, x, + 10, 10); + canvas_dirty(x, 1); + } +} + +static void canvas_selectall(t_canvas *x) +{ + t_gobj *y; + if (!x->gl_edit) + canvas_editmode(x, 1); + for (y = x->gl_list; y; y = y->g_next) + { + if (!glist_isselected(x, y)) + glist_select(x, y); + } +} + +void canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno, + t_floatarg fwhoin, t_floatarg finno) +{ + int whoout = fwhoout, outno = foutno, whoin = fwhoin, inno = finno; + t_gobj *src = 0, *sink = 0; + t_object *objsrc, *objsink; + t_outconnect *oc; + int nin = whoin, nout = whoout; + if (paste_canvas == x) whoout += paste_onset, whoin += paste_onset; + for (src = x->gl_list; whoout; src = src->g_next, whoout--) + if (!src->g_next) goto bad; /* bug fix thanks to Hannes */ + for (sink = x->gl_list; whoin; sink = sink->g_next, whoin--) + if (!sink->g_next) goto bad; + if (!(objsrc = pd_checkobject(&src->g_pd)) || + !(objsink = pd_checkobject(&sink->g_pd))) + goto bad; + if (!(oc = obj_connect(objsrc, outno, objsink, inno))) goto bad; + if (glist_isvisible(x)) + { + sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n", + glist_getcanvas(x), 0, 0, 0, 0, + (obj_issignaloutlet(objsrc, outno) ? 2 : 1),oc); + canvas_fixlinesfor(x, objsrc); + } + return; + +bad: + post("%s %d %d %d %d (%s->%s) connection failed", + x->gl_name->s_name, nout, outno, nin, inno, + (src? class_getname(pd_class(&src->g_pd)) : "???"), + (sink? class_getname(pd_class(&sink->g_pd)) : "???")); +} + +#define XTOLERANCE 4 +#define YTOLERANCE 3 +#define NHIST 15 + + /* LATER might have to speed this up */ +static void canvas_tidy(t_canvas *x) +{ + t_gobj *y, *y2, *y3; + int ax1, ay1, ax2, ay2, bx1, by1, bx2, by2; + int histogram[NHIST], *ip, i, besthist, bestdist; + /* if nobody is selected, this means do it to all boxes; + othewise just the selection */ + int all = (x->gl_editor ? (x->gl_editor->e_selection == 0) : 1); + + canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, !all), + "motion"); + + /* tidy horizontally */ + for (y = x->gl_list; y; y = y->g_next) + if (all || glist_isselected(x, y)) + { + gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2); + + for (y2 = x->gl_list; y2; y2 = y2->g_next) + if (all || glist_isselected(x, y2)) + { + gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); + if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE && + bx1 < ax1) + goto nothorizhead; + } + + for (y2 = x->gl_list; y2; y2 = y2->g_next) + if (all || glist_isselected(x, y2)) + { + gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); + if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE + && by1 != ay1) + gobj_displace(y2, x, 0, ay1-by1); + } + nothorizhead: ; + } + /* tidy vertically. First guess the user's favorite vertical spacing */ + for (i = NHIST, ip = histogram; i--; ip++) *ip = 0; + for (y = x->gl_list; y; y = y->g_next) + if (all || glist_isselected(x, y)) + { + gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2); + for (y2 = x->gl_list; y2; y2 = y2->g_next) + if (all || glist_isselected(x, y2)) + { + gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); + if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE) + { + int distance = by1-ay2; + if (distance >= 0 && distance < NHIST) + histogram[distance]++; + } + } + } + for (i = 1, besthist = 0, bestdist = 4, ip = histogram + 1; + i < (NHIST-1); i++, ip++) + { + int hit = ip[-1] + 2 * ip[0] + ip[1]; + if (hit > besthist) + { + besthist = hit; + bestdist = i; + } + } + post("best vertical distance %d", bestdist); + for (y = x->gl_list; y; y = y->g_next) + if (all || glist_isselected(x, y)) + { + int keep = 1; + gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2); + for (y2 = x->gl_list; y2; y2 = y2->g_next) + if (all || glist_isselected(x, y2)) + { + gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); + if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE && + ay1 >= by2 - 10 && ay1 < by2 + NHIST) + goto nothead; + } + while (keep) + { + keep = 0; + for (y2 = x->gl_list; y2; y2 = y2->g_next) + if (all || glist_isselected(x, y2)) + { + gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); + if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE && + by1 > ay1 && by1 < ay2 + NHIST) + { + int vmove = ay2 + bestdist - by1; + gobj_displace(y2, x, ax1-bx1, vmove); + ay1 = by1 + vmove; + ay2 = by2 + vmove; + keep = 1; + break; + } + } + } + nothead: ; + } + canvas_dirty(x, 1); +} + +static void canvas_texteditor(t_canvas *x) +{ + t_rtext *foo; + char *buf; + int bufsize; + if (foo = x->gl_editor->e_textedfor) + rtext_gettext(foo, &buf, &bufsize); + else buf = "", bufsize = 0; + sys_vgui("pdtk_pd_texteditor {%.*s}\n", bufsize, buf); + +} + +void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av) +{ + /* canvas_editing can be zero; canvas_key checks for that */ + canvas_key(canvas_editing, s, ac, av); +} + +void canvas_editmode(t_canvas *x, t_floatarg fyesplease) +{ + int yesplease = fyesplease; + if (yesplease && x->gl_edit) + return; + x->gl_edit = !x->gl_edit; + if (x->gl_edit && glist_isvisible(x) && glist_istoplevel(x)) + canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); + else + { + glist_noselect(x); + if (glist_isvisible(x) && glist_istoplevel(x)) + canvas_setcursor(x, CURSOR_RUNMODE_NOTHING); + } + sys_vgui("pdtk_canvas_editval .x%x %d\n", + glist_getcanvas(x), x->gl_edit); +} + + /* called by canvas_font below */ +static void canvas_dofont(t_canvas *x, t_floatarg font, t_floatarg xresize, + t_floatarg yresize) +{ + t_gobj *y; + x->gl_font = font; + if (xresize != 1 || yresize != 1) + { + canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 0), + "motion"); + for (y = x->gl_list; y; y = y->g_next) + { + int x1, x2, y1, y2, nx1, ny1; + gobj_getrect(y, x, &x1, &y1, &x2, &y2); + nx1 = x1 * xresize + 0.5; + ny1 = y1 * yresize + 0.5; + gobj_displace(y, x, nx1-x1, ny1-y1); + } + } + if (glist_isvisible(x)) + glist_redraw(x); + for (y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == canvas_class + && !canvas_isabstraction((t_canvas *)y)) + canvas_dofont((t_canvas *)y, font, xresize, yresize); +} + + /* canvas_menufont calls up a TK dialog which calls this back */ +static void canvas_font(t_canvas *x, t_floatarg font, t_floatarg resize, + t_floatarg whichresize) +{ + float realresize, realresx = 1, realresy = 1; + t_canvas *x2 = canvas_getrootfor(x); + if (!resize) realresize = 1; + else + { + if (resize < 20) resize = 20; + if (resize > 500) resize = 500; + realresize = resize * 0.01; + } + if (whichresize != 3) realresx = realresize; + if (whichresize != 2) realresy = realresize; + canvas_dofont(x2, font, realresx, realresy); + sys_defaultfont = font; +} + +static t_glist *canvas_last_glist; +static int canvas_last_glist_x, canvas_last_glist_y; + +void glist_getnextxy(t_glist *gl, int *xpix, int *ypix) +{ + if (canvas_last_glist == gl) + *xpix = canvas_last_glist_x, *ypix = canvas_last_glist_y; + else *xpix = *ypix = 40; +} + +static void glist_setlastxy(t_glist *gl, int xval, int yval) +{ + canvas_last_glist = gl; + canvas_last_glist_x = xval; + canvas_last_glist_y = yval; +} + + +void g_editor_setup(void) +{ +/* ------------------------ events ---------------------------------- */ + class_addmethod(canvas_class, (t_method)canvas_mousedown, gensym("mouse"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_mouseup, gensym("mouseup"), + A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_key, gensym("key"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_motion, gensym("motion"), + A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + +/* ------------------------ menu actions ---------------------------- */ + class_addmethod(canvas_class, (t_method)canvas_menuclose, + gensym("menuclose"), A_DEFFLOAT, 0); + class_addmethod(canvas_class, (t_method)canvas_cut, + gensym("cut"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_copy, + gensym("copy"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_paste, + gensym("paste"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_duplicate, + gensym("duplicate"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_selectall, + gensym("selectall"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_undo, + gensym("undo"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_redo, + gensym("redo"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_tidy, + gensym("tidy"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_texteditor, + gensym("texteditor"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_editmode, + gensym("editmode"), A_DEFFLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_print, + gensym("print"), A_SYMBOL, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_menufont, + gensym("menufont"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_font, + gensym("font"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_find, + gensym("find"), A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_find_again, + gensym("findagain"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_find_parent, + gensym("findparent"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_done_popup, + gensym("done-popup"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_donecanvasdialog, + gensym("donecanvasdialog"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)glist_arraydialog, + gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + +/* -------------- connect method used in reading files ------------------ */ + class_addmethod(canvas_class, (t_method)canvas_connect, + gensym("connect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + + class_addmethod(canvas_class, (t_method)canvas_disconnect, + gensym("disconnect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); +/* -------------- copy buffer ------------------ */ + copy_binbuf = binbuf_new(); +} +/* Copyright (c) 1997-2001 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include +#include +#include "m_pd.h" +#include "m_imp.h" +#include "s_stuff.h" +#include "g_canvas.h" +#include + +void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename, + int selectem); + +void open_via_helppath(const char *name, const char *dir); +char *class_gethelpdir(t_class *c); + +/* ------------------ forward declarations --------------- */ +static void canvas_doclear(t_canvas *x); +static void glist_setlastxy(t_glist *gl, int xval, int yval); +static void glist_donewloadbangs(t_glist *x); +static t_binbuf *canvas_docopy(t_canvas *x); +static void canvas_dopaste(t_canvas *x, t_binbuf *b); +static void canvas_paste(t_canvas *x); +static void canvas_clearline(t_canvas *x); +static t_binbuf *copy_binbuf; + +/* ---------------- generic widget behavior ------------------------- */ + +void gobj_getrect(t_gobj *x, t_glist *glist, int *x1, int *y1, + int *x2, int *y2) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_getrectfn) + (*x->g_pd->c_wb->w_getrectfn)(x, glist, x1, y1, x2, y2); +} + +void gobj_displace(t_gobj *x, t_glist *glist, int dx, int dy) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_displacefn) + (*x->g_pd->c_wb->w_displacefn)(x, glist, dx, dy); +} + +void gobj_select(t_gobj *x, t_glist *glist, int state) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_selectfn) + (*x->g_pd->c_wb->w_selectfn)(x, glist, state); +} + +void gobj_activate(t_gobj *x, t_glist *glist, int state) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_activatefn) + (*x->g_pd->c_wb->w_activatefn)(x, glist, state); +} + +void gobj_delete(t_gobj *x, t_glist *glist) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_deletefn) + (*x->g_pd->c_wb->w_deletefn)(x, glist); +} + +void gobj_vis(t_gobj *x, struct _glist *glist, int flag) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_visfn) + (*x->g_pd->c_wb->w_visfn)(x, glist, flag); +} + +int gobj_click(t_gobj *x, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_clickfn) + return ((*x->g_pd->c_wb->w_clickfn)(x, + glist, xpix, ypix, shift, alt, dbl, doit)); + else return (0); +} + +/* ------------------------ managing the selection ----------------- */ + +void glist_selectline(t_glist *x, t_outconnect *oc, int index1, + int outno, int index2, int inno) +{ + if (x->gl_editor) + { + glist_noselect(x); + x->gl_editor->e_selectedline = 1; + x->gl_editor->e_selectline_index1 = index1; + x->gl_editor->e_selectline_outno = outno; + x->gl_editor->e_selectline_index2 = index2; + x->gl_editor->e_selectline_inno = inno; + x->gl_editor->e_selectline_tag = oc; + sys_vgui(".x%x.c itemconfigure l%x -fill blue\n", + x, x->gl_editor->e_selectline_tag); + } +} + +void glist_deselectline(t_glist *x) +{ + if (x->gl_editor) + { + x->gl_editor->e_selectedline = 0; + sys_vgui(".x%x.c itemconfigure l%x -fill black\n", + x, x->gl_editor->e_selectline_tag); + } +} + +int glist_isselected(t_glist *x, t_gobj *y) +{ + if (x->gl_editor) + { + t_selection *sel; + for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next) + if (sel->sel_what == y) return (1); + } + return (0); +} + + /* call this for unselected objects only */ +void glist_select(t_glist *x, t_gobj *y) +{ + if (x->gl_editor) + { + t_selection *sel = (t_selection *)getbytes(sizeof(*sel)); + if (x->gl_editor->e_selectedline) + glist_deselectline(x); + /* LATER #ifdef out the following check */ + if (glist_isselected(x, y)) bug("glist_select"); + sel->sel_next = x->gl_editor->e_selection; + sel->sel_what = y; + x->gl_editor->e_selection = sel; + gobj_select(y, x, 1); + } +} + + /* call this for selected objects only */ +void glist_deselect(t_glist *x, t_gobj *y) +{ + int fixdsp = 0; + static int reenter = 0; + if (reenter) return; + reenter = 1; + if (x->gl_editor) + { + t_selection *sel, *sel2; + t_rtext *z = 0; + if (!glist_isselected(x, y)) bug("glist_deselect"); + if (x->gl_editor->e_textedfor) + { + t_rtext *fuddy = glist_findrtext(x, (t_text *)y); + if (x->gl_editor->e_textedfor == fuddy) + { + if (x->gl_editor->e_textdirty) + { + z = fuddy; + canvas_stowconnections(glist_getcanvas(x)); + } + gobj_activate(y, x, 0); + } + if (zgetfn(&y->g_pd, gensym("dsp"))) + fixdsp = 1; + } + if ((sel = x->gl_editor->e_selection)->sel_what == y) + { + x->gl_editor->e_selection = x->gl_editor->e_selection->sel_next; + gobj_select(sel->sel_what, x, 0); + freebytes(sel, sizeof(*sel)); + } + else + { + for (sel = x->gl_editor->e_selection; sel2 = sel->sel_next; + sel = sel2) + { + if (sel2->sel_what == y) + { + sel->sel_next = sel2->sel_next; + gobj_select(sel2->sel_what, x, 0); + freebytes(sel2, sizeof(*sel2)); + break; + } + } + } + if (z) + { + char *buf; + int bufsize; + + rtext_gettext(z, &buf, &bufsize); + text_setto((t_text *)y, x, buf, bufsize); + canvas_fixlinesfor(glist_getcanvas(x), (t_text *)y); + x->gl_editor->e_textedfor = 0; + } + if (fixdsp) + canvas_update_dsp(); + } + reenter = 0; +} + +void glist_noselect(t_glist *x) +{ + if (x->gl_editor) + { + while (x->gl_editor->e_selection) + glist_deselect(x, x->gl_editor->e_selection->sel_what); + if (x->gl_editor->e_selectedline) + glist_deselectline(x); + } +} + +void glist_selectall(t_glist *x) +{ + if (x->gl_editor) + { + glist_noselect(x); + if (x->gl_list) + { + t_selection *sel = (t_selection *)getbytes(sizeof(*sel)); + t_gobj *y = x->gl_list; + x->gl_editor->e_selection = sel; + sel->sel_what = y; + gobj_select(y, x, 1); + while (y = y->g_next) + { + t_selection *sel2 = (t_selection *)getbytes(sizeof(*sel2)); + sel->sel_next = sel2; + sel = sel2; + sel->sel_what = y; + gobj_select(y, x, 1); + } + sel->sel_next = 0; + } + } +} + + /* get the index of a gobj in a glist. If y is zero, return the + total number of objects. */ +int glist_getindex(t_glist *x, t_gobj *y) +{ + t_gobj *y2; + int indx; + + for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next) + indx++; + return (indx); +} + + /* get the index of the object, among selected items, if "selected" + is set; otherwise, among unselected ones. If y is zero, just + counts the selected or unselected objects. */ +int glist_selectionindex(t_glist *x, t_gobj *y, int selected) +{ + t_gobj *y2; + int indx; + + for (y2 = x->gl_list, indx = 0; y2 && y2 != y; y2 = y2->g_next) + if (selected == glist_isselected(x, y2)) + indx++; + return (indx); +} + +static t_gobj *glist_nth(t_glist *x, int n) +{ + t_gobj *y; + int indx; + for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++) + if (indx == n) + return (y); + return (0); +} + +/* ------------------- support for undo/redo -------------------------- */ + +static t_undofn canvas_undo_fn; /* current undo function if any */ +static int canvas_undo_whatnext; /* whether we can now UNDO or REDO */ +static void *canvas_undo_buf; /* data private to the undo function */ +static t_canvas *canvas_undo_canvas; /* which canvas we can undo on */ +static const char *canvas_undo_name; + +void canvas_setundo(t_canvas *x, t_undofn undofn, void *buf, + const char *name) +{ + int hadone = 0; + /* blow away the old undo information. In one special case the + old undo info is re-used; if so we shouldn't free it here. */ + if (canvas_undo_fn && canvas_undo_buf && (buf != canvas_undo_buf)) + { + (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_FREE); + hadone = 1; + } + canvas_undo_canvas = x; + canvas_undo_fn = undofn; + canvas_undo_buf = buf; + canvas_undo_whatnext = UNDO_UNDO; + canvas_undo_name = name; + if (x && glist_isvisible(x) && glist_istoplevel(x)) + /* enable undo in menu */ + sys_vgui("pdtk_undomenu .x%x %s no\n", x, name); + else if (hadone) + sys_vgui("pdtk_undomenu nobody no no\n"); +} + + /* clear undo if it happens to be for the canvas x. + (but if x is 0, clear it regardless of who owns it.) */ +void canvas_noundo(t_canvas *x) +{ + if (!x || (x == canvas_undo_canvas)) + canvas_setundo(0, 0, 0, "foo"); +} + +static void canvas_undo(t_canvas *x) +{ + if (x != canvas_undo_canvas) + bug("canvas_undo 1"); + else if (canvas_undo_whatnext != UNDO_UNDO) + bug("canvas_undo 2"); + else + { + /* post("undo"); */ + (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_UNDO); + /* enable redo in menu */ + if (glist_isvisible(x) && glist_istoplevel(x)) + sys_vgui("pdtk_undomenu .x%x no %s\n", x, canvas_undo_name); + canvas_undo_whatnext = UNDO_REDO; + } +} + +static void canvas_redo(t_canvas *x) +{ + if (x != canvas_undo_canvas) + bug("canvas_undo 1"); + else if (canvas_undo_whatnext != UNDO_REDO) + bug("canvas_undo 2"); + else + { + /* post("redo"); */ + (*canvas_undo_fn)(canvas_undo_canvas, canvas_undo_buf, UNDO_REDO); + /* enable undo in menu */ + if (glist_isvisible(x) && glist_istoplevel(x)) + sys_vgui("pdtk_undomenu .x%x %s no\n", x, canvas_undo_name); + canvas_undo_whatnext = UNDO_UNDO; + } +} + +/* ------- specific undo methods: 1. connect and disconnect -------- */ + +typedef struct _undo_connect +{ + int u_index1; + int u_outletno; + int u_index2; + int u_inletno; +} t_undo_connect; + +static void *canvas_undo_set_disconnect(t_canvas *x, + int index1, int outno, int index2, int inno) +{ + t_undo_connect *buf = (t_undo_connect *)getbytes(sizeof(*buf)); + buf->u_index1 = index1; + buf->u_outletno = outno; + buf->u_index2 = index2; + buf->u_inletno = inno; + return (buf); +} + +void canvas_disconnect(t_canvas *x, + float index1, float outno, float index2, float inno) +{ + t_linetraverser t; + t_outconnect *oc; + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + int srcno = canvas_getindex(x, &t.tr_ob->ob_g); + int sinkno = canvas_getindex(x, &t.tr_ob2->ob_g); + if (srcno == index1 && t.tr_outno == outno && + sinkno == index2 && t.tr_inno == inno) + { + sys_vgui(".x%x.c delete l%x\n", x, oc); + obj_disconnect(t.tr_ob, t.tr_outno, t.tr_ob2, t.tr_inno); + break; + } + } +} + +static void canvas_undo_disconnect(t_canvas *x, void *z, int action) +{ + t_undo_connect *buf = z; + if (action == UNDO_UNDO) + { + canvas_connect(x, buf->u_index1, buf->u_outletno, + buf->u_index2, buf->u_inletno); + } + else if (action == UNDO_REDO) + { + canvas_disconnect(x, buf->u_index1, buf->u_outletno, + buf->u_index2, buf->u_inletno); + } + else if (action == UNDO_FREE) + t_freebytes(buf, sizeof(*buf)); +} + + /* connect just calls disconnect actions backward... */ +static void *canvas_undo_set_connect(t_canvas *x, + int index1, int outno, int index2, int inno) +{ + return (canvas_undo_set_disconnect(x, index1, outno, index2, inno)); +} + +static void canvas_undo_connect(t_canvas *x, void *z, int action) +{ + int myaction; + if (action == UNDO_UNDO) + myaction = UNDO_REDO; + else if (action == UNDO_REDO) + myaction = UNDO_UNDO; + else myaction = action; + canvas_undo_disconnect(x, z, myaction); +} + +/* ---------- ... 2. cut, clear, and typing into objects: -------- */ + +#define UCUT_CUT 1 /* operation was a cut */ +#define UCUT_CLEAR 2 /* .. a clear */ +#define UCUT_TEXT 3 /* text typed into a box */ + +typedef struct _undo_cut +{ + t_binbuf *u_objectbuf; /* the object cleared or typed into */ + t_binbuf *u_reconnectbuf; /* connections into and out of object */ + t_binbuf *u_redotextbuf; /* buffer to paste back for redo if TEXT */ + int u_mode; /* from flags above */ +} t_undo_cut; + +static void *canvas_undo_set_cut(t_canvas *x, int mode) +{ + t_undo_cut *buf; + t_gobj *y; + t_linetraverser t; + t_outconnect *oc; + int nnotsel= glist_selectionindex(x, 0, 0); + buf = (t_undo_cut *)getbytes(sizeof(*buf)); + buf->u_mode = mode; + buf->u_redotextbuf = 0; + + /* store connections into/out of the selection */ + buf->u_reconnectbuf = binbuf_new(); + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + int issel1 = glist_isselected(x, &t.tr_ob->ob_g); + int issel2 = glist_isselected(x, &t.tr_ob2->ob_g); + if (issel1 != issel2) + { + binbuf_addv(buf->u_reconnectbuf, "ssiiii;", + gensym("#X"), gensym("connect"), + (issel1 ? nnotsel : 0) + + glist_selectionindex(x, &t.tr_ob->ob_g, issel1), + t.tr_outno, + (issel2 ? nnotsel : 0) + + glist_selectionindex(x, &t.tr_ob2->ob_g, issel2), + t.tr_inno); + } + } + if (mode == UCUT_TEXT) + { + buf->u_objectbuf = canvas_docopy(x); + } + else if (mode == UCUT_CUT) + { + buf->u_objectbuf = 0; + } + else if (mode == UCUT_CLEAR) + { + buf->u_objectbuf = canvas_docopy(x); + } + return (buf); +} + +static void canvas_undo_cut(t_canvas *x, void *z, int action) +{ + t_undo_cut *buf = z; + int mode = buf->u_mode; + if (action == UNDO_UNDO) + { + if (mode == UCUT_CUT) + canvas_dopaste(x, copy_binbuf); + else if (mode == UCUT_CLEAR) + canvas_dopaste(x, buf->u_objectbuf); + else if (mode == UCUT_TEXT) + { + t_gobj *y1, *y2; + glist_noselect(x); + for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2) + ; + if (y1) + { + if (!buf->u_redotextbuf) + { + glist_noselect(x); + glist_select(x, y1); + buf->u_redotextbuf = canvas_docopy(x); + glist_noselect(x); + } + glist_delete(x, y1); + } + canvas_dopaste(x, buf->u_objectbuf); + } + pd_bind(&x->gl_pd, gensym("#X")); + binbuf_eval(buf->u_reconnectbuf, 0, 0, 0); + pd_unbind(&x->gl_pd, gensym("#X")); + } + else if (action == UNDO_REDO) + { + if (mode == UCUT_CUT || mode == UCUT_CLEAR) + canvas_doclear(x); + else if (mode == UCUT_TEXT) + { + t_gobj *y1, *y2; + for (y1 = x->gl_list; y2 = y1->g_next; y1 = y2) + ; + if (y1) + glist_delete(x, y1); + canvas_dopaste(x, buf->u_redotextbuf); + pd_bind(&x->gl_pd, gensym("#X")); + binbuf_eval(buf->u_reconnectbuf, 0, 0, 0); + pd_unbind(&x->gl_pd, gensym("#X")); + } + } + else if (action == UNDO_FREE) + { + if (buf->u_objectbuf) + binbuf_free(buf->u_objectbuf); + if (buf->u_reconnectbuf) + binbuf_free(buf->u_reconnectbuf); + if (buf->u_redotextbuf) + binbuf_free(buf->u_redotextbuf); + t_freebytes(buf, sizeof(*buf)); + } +} + +/* --------- 3. motion, including "tidy up" and stretching ----------- */ + +typedef struct _undo_move_elem +{ + int e_index; + int e_xpix; + int e_ypix; +} t_undo_move_elem; + +typedef struct _undo_move +{ + t_undo_move_elem *u_vec; + int u_n; +} t_undo_move; + +static int canvas_undo_already_set_move; + +static void *canvas_undo_set_move(t_canvas *x, int selected) +{ + int x1, y1, x2, y2, i, indx; + t_gobj *y; + t_undo_move *buf = (t_undo_move *)getbytes(sizeof(*buf)); + buf->u_n = selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0); + buf->u_vec = (t_undo_move_elem *)getbytes(sizeof(*buf->u_vec) * + (selected ? glist_selectionindex(x, 0, 1) : glist_getindex(x, 0))); + if (selected) + { + for (y = x->gl_list, i = indx = 0; y; y = y->g_next, indx++) + if (glist_isselected(x, y)) + { + gobj_getrect(y, x, &x1, &y1, &x2, &y2); + buf->u_vec[i].e_index = indx; + buf->u_vec[i].e_xpix = x1; + buf->u_vec[i].e_ypix = y1; + i++; + } + } + else + { + for (y = x->gl_list, indx = 0; y; y = y->g_next, indx++) + { + gobj_getrect(y, x, &x1, &y1, &x2, &y2); + buf->u_vec[indx].e_index = indx; + buf->u_vec[indx].e_xpix = x1; + buf->u_vec[indx].e_ypix = y1; + } + } + canvas_undo_already_set_move = 1; + return (buf); +} + +static void canvas_undo_move(t_canvas *x, void *z, int action) +{ + t_undo_move *buf = z; + if (action == UNDO_UNDO || action == UNDO_REDO) + { + int i; + for (i = 0; i < buf->u_n; i++) + { + int x1, y1, x2, y2, newx, newy; + t_gobj *y; + newx = buf->u_vec[i].e_xpix; + newy = buf->u_vec[i].e_ypix; + y = glist_nth(x, buf->u_vec[i].e_index); + if (y) + { + gobj_getrect(y, x, &x1, &y1, &x2, &y2); + gobj_displace(y, x, newx-x1, newy - y1); + buf->u_vec[i].e_xpix = x1; + buf->u_vec[i].e_ypix = y1; + } + } + } + else if (action == UNDO_FREE) + { + t_freebytes(buf->u_vec, buf->u_n * sizeof(*buf->u_vec)); + t_freebytes(buf, sizeof(*buf)); + } +} + +/* --------- 4. paste (also duplicate) ----------- */ + +typedef struct _undo_paste +{ + int u_index; /* index of first object pasted */ +} t_undo_paste; + +static void *canvas_undo_set_paste(t_canvas *x) +{ + t_undo_paste *buf = (t_undo_paste *)getbytes(sizeof(*buf)); + buf->u_index = glist_getindex(x, 0); + return (buf); +} + +static void canvas_undo_paste(t_canvas *x, void *z, int action) +{ + t_undo_paste *buf = z; + if (action == UNDO_UNDO) + { + t_gobj *y; + glist_noselect(x); + for (y = glist_nth(x, buf->u_index); y; y = y->g_next) + glist_select(x, y); + canvas_doclear(x); + } + else if (action == UNDO_REDO) + { + t_selection *sel; + canvas_dopaste(x, copy_binbuf); + /* if it was "duplicate" have to re-enact the displacement. */ + if (canvas_undo_name && canvas_undo_name[0] == 'd') + for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next) + gobj_displace(sel->sel_what, x, 10, 10); + } +else if (action == UNDO_FREE) + t_freebytes(buf, sizeof(*buf)); +} + + /* recursively check for abstractions to reload as result of a save. + Don't reload the one we just saved ("except") though. */ + /* LATER try to do the same trick for externs. */ +static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir, + t_gobj *except) +{ + t_gobj *g; + int i, nobj = glist_getindex(gl, 0); /* number of objects */ + for (g = gl->gl_list, i = 0; g && i < nobj; i++) + { + if (g != except && pd_class(&g->g_pd) == canvas_class && + canvas_isabstraction((t_canvas *)g) && + ((t_canvas *)g)->gl_name == name && + canvas_getdir((t_canvas *)g) == dir) + { + /* we're going to remake the object, so "g" will go stale. + Get its index here, and afterward restore g. Also, the + replacement will be at teh end of the list, so we don't + do g = g->g_next in this case. */ + int j = glist_getindex(gl, g); + if (!gl->gl_havewindow) + canvas_vis(glist_getcanvas(gl), 1); + glist_noselect(gl); + glist_select(gl, g); + canvas_setundo(gl, canvas_undo_cut, + canvas_undo_set_cut(gl, UCUT_CLEAR), "clear"); + canvas_doclear(gl); + canvas_undo(gl); + glist_noselect(gl); + g = glist_nth(gl, j); + } + else + { + if (g != except && pd_class(&g->g_pd) == canvas_class) + glist_doreload((t_canvas *)g, name, dir, except); + g = g->g_next; + } + } +} + + /* call canvas_doreload on everyone */ +void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except) +{ + t_canvas *x; + /* find all root canvases */ + for (x = canvas_list; x; x = x->gl_next) + glist_doreload(x, name, dir, except); +} + +/* ------------------------ event handling ------------------------ */ + +#define CURSOR_RUNMODE_NOTHING 0 +#define CURSOR_RUNMODE_CLICKME 1 +#define CURSOR_RUNMODE_THICKEN 2 +#define CURSOR_RUNMODE_ADDPOINT 3 +#define CURSOR_EDITMODE_NOTHING 4 +#define CURSOR_EDITMODE_CONNECT 5 +#define CURSOR_EDITMODE_DISCONNECT 6 + +static char *cursorlist[] = { +#ifdef MSW + "right_ptr", /* CURSOR_RUNMODE_NOTHING */ +#else + "left_ptr", /* CURSOR_RUNMODE_NOTHING */ +#endif + "arrow", /* CURSOR_RUNMODE_CLICKME */ + "sb_v_double_arrow", /* CURSOR_RUNMODE_THICKEN */ + "plus", /* CURSOR_RUNMODE_ADDPOINT */ + "hand2", /* CURSOR_EDITMODE_NOTHING */ + "circle", /* CURSOR_EDITMODE_CONNECT */ + "X_cursor" /* CURSOR_EDITMODE_DISCONNECT */ +}; + +void canvas_setcursor(t_canvas *x, unsigned int cursornum) +{ + static t_canvas *xwas; + static unsigned int cursorwas; + if (cursornum >= sizeof(cursorlist)/sizeof *cursorlist) + { + bug("canvas_setcursor"); + return; + } + if (xwas != x || cursorwas != cursornum) + { + sys_vgui(".x%x configure -cursor %s\n", x, cursorlist[cursornum]); + xwas = x; + cursorwas = cursornum; + } +} + + /* check if a point lies in a gobj. */ +int canvas_hitbox(t_canvas *x, t_gobj *y, int xpos, int ypos, + int *x1p, int *y1p, int *x2p, int *y2p) +{ + int x1, y1, x2, y2; + t_text *ob; + if ((ob = pd_checkobject(&y->g_pd)) && + !text_shouldvis(ob, x)) + return (0); + gobj_getrect(y, x, &x1, &y1, &x2, &y2); + if (xpos >= x1 && xpos <= x2 && ypos >= y1 && ypos <= y2) + { + *x1p = x1; + *y1p = y1; + *x2p = x2; + *y2p = y2; + return (1); + } + else return (0); +} + + /* find the last gobj, if any, containing the point. */ +static t_gobj *canvas_findhitbox(t_canvas *x, int xpos, int ypos, + int *x1p, int *y1p, int *x2p, int *y2p) +{ + t_gobj *y, *rval = 0; + for (y = x->gl_list; y; y = y->g_next) + { + if (canvas_hitbox(x, y, xpos, ypos, x1p, y1p, x2p, y2p)) + rval = y; + } + return (rval); +} + + /* right-clicking on a canvas object pops up a menu. */ +static void canvas_rightclick(t_canvas *x, int xpos, int ypos, t_gobj *y) +{ + int canprop, canopen; + canprop = (!y || (y && class_getpropertiesfn(pd_class(&y->g_pd)))); + canopen = (y && zgetfn(&y->g_pd, gensym("menu-open"))); + sys_vgui("pdtk_canvas_popup .x%x %d %d %d %d\n", + x, xpos, ypos, canprop, canopen); +} + + /* tell GUI to create a properties dialog on the canvas. We tell + the user the negative of the "pixel" y scale to make it appear to grow + naturally upward, whereas pixels grow downward. */ +static void canvas_properties(t_glist *x) +{ + char graphbuf[200]; + sprintf(graphbuf, "pdtk_canvas_dialog %%s %g %g %g %g \n", + glist_dpixtodx(x, 1), -glist_dpixtody(x, 1), + (float)glist_isgraph(x), (float)x->gl_stretch); + gfxstub_new(&x->gl_pd, x, graphbuf); +} + + +void canvas_setgraph(t_glist *x, int flag) +{ + if (!flag && glist_isgraph(x)) + { + if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner)) + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + x->gl_isgraph = 0; + if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner)) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } + } + else if (flag && !glist_isgraph(x)) + { + if (x->gl_pixwidth <= 0) + x->gl_pixwidth = GLIST_DEFGRAPHWIDTH; + + if (x->gl_pixheight <= 0) + x->gl_pixheight = GLIST_DEFGRAPHHEIGHT; + + if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner)) + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + x->gl_isgraph = 1; + /* if (x->gl_owner && glist_isvisible(x->gl_owner)) + canvas_vis(x, 1); */ + if (x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner)) + canvas_create_editor(x, 1); + if (x->gl_owner && !x->gl_loading && glist_isvisible(x->gl_owner)) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } + } +} + + /* called from the gui when "OK" is selected on the canvas properties + dialog. Again we negate "y" scale. */ +static void canvas_donecanvasdialog(t_glist *x, t_floatarg xperpix, + t_floatarg yperpix, t_floatarg fgraphme) +{ + int graphme = (fgraphme != 0), redraw = 0; + yperpix = -yperpix; + if (xperpix == 0) + xperpix = 1; + if (yperpix == 0) + yperpix = 1; + canvas_setgraph(x, graphme); + if (!x->gl_isgraph && (xperpix != glist_dpixtodx(x, 1))) + { + if (xperpix > 0) + { + x->gl_x1 = 0; + x->gl_x2 = xperpix; + } + else + { + x->gl_x1 = -xperpix * (x->gl_screenx2 - x->gl_screenx1); + x->gl_x2 = x->gl_x1 + xperpix; + } + redraw = 1; + } + if (!x->gl_isgraph && (yperpix != glist_dpixtody(x, 1))) + { + if (yperpix > 0) + { + x->gl_y1 = 0; + x->gl_y2 = yperpix; + } + else + { + x->gl_y1 = -yperpix * (x->gl_screeny2 - x->gl_screeny1); + x->gl_y2 = x->gl_y1 + yperpix; + } + redraw = 1; + } + if (redraw) + canvas_redraw(x); +} + + /* called from the gui when a popup menu comes back with "properties," + "open," or "help." */ +static void canvas_done_popup(t_canvas *x, float which, float xpos, float ypos) +{ + char pathbuf[MAXPDSTRING], namebuf[MAXPDSTRING]; + t_gobj *y; + for (y = x->gl_list; y; y = y->g_next) + { + int x1, y1, x2, y2; + if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2)) + { + if (which == 0) /* properties */ + { + if (!class_getpropertiesfn(pd_class(&y->g_pd))) + continue; + (*class_getpropertiesfn(pd_class(&y->g_pd)))(y, x); + return; + } + else if (which == 1) /* open */ + { + if (!zgetfn(&y->g_pd, gensym("menu-open"))) + continue; + vmess(&y->g_pd, gensym("menu-open"), ""); + return; + } + else /* help */ + { + char *dir; + if (pd_class(&y->g_pd) == canvas_class && + canvas_isabstraction((t_canvas *)y)) + { + t_object *ob = (t_object *)y; + int ac = binbuf_getnatom(ob->te_binbuf); + t_atom *av = binbuf_getvec(ob->te_binbuf); + if (ac < 1) + return; + atom_string(av, namebuf, MAXPDSTRING); + dir = canvas_getdir((t_canvas *)y)->s_name; + } + else + { + strcpy(namebuf, class_gethelpname(pd_class(&y->g_pd))); + dir = class_gethelpdir(pd_class(&y->g_pd)); + } + if (strcmp(namebuf + strlen(namebuf) - 3, ".pd")) + strcat(namebuf, ".pd"); + open_via_helppath(namebuf, dir); + return; + } + } + } + if (which == 0) + canvas_properties(x); + else if (which == 2) + { + strcpy(pathbuf, sys_libdir->s_name); + strcat(pathbuf, "/doc/5.reference/0.INTRO.txt"); + sys_vgui("menu_opentext %s\n", pathbuf); + } +} + +#define NOMOD 0 +#define SHIFTMOD 1 +#define CTRLMOD 2 +#define ALTMOD 4 +#define RIGHTCLICK 8 + +/* on one-button-mouse machines, you can use double click to + mean right click (which gets the popup menu.) Do this for Mac. */ +#ifdef MACOSX +#define SIMULATERIGHTCLICK +#endif + +#ifdef SIMULATERIGHTCLICK +static double canvas_upclicktime; +static int canvas_upx, canvas_upy; +#define DCLICKINTERVAL 0.25 +#endif + + /* mouse click */ +void canvas_doclick(t_canvas *x, int xpos, int ypos, int which, + int mod, int doit) +{ + t_gobj *y; + int shiftmod, runmode, altmod, rightclick; + int x1, y1, x2, y2, clickreturned = 0; + + if (!x->gl_editor) + { + bug("editor"); + return; + } + + shiftmod = (mod & SHIFTMOD); + runmode = ((mod & CTRLMOD) || (!x->gl_edit)); + altmod = (mod & ALTMOD); + rightclick = (mod & RIGHTCLICK); + + canvas_undo_already_set_move = 0; + + /* if keyboard was grabbed, notify grabber and cancel the grab */ + if (doit && x->gl_editor->e_grab && x->gl_editor->e_keyfn) + { + (* x->gl_editor->e_keyfn) (x->gl_editor->e_grab, 0); + glist_grab(x, 0, 0, 0, 0, 0); + } + +#ifdef SIMULATERIGHTCLICK + if (doit && !runmode && xpos == canvas_upx && ypos == canvas_upy && + sys_getrealtime() - canvas_upclicktime < DCLICKINTERVAL) + rightclick = 1; +#endif + + x->gl_editor->e_lastmoved = 0; + if (doit) + { + x->gl_editor->e_grab = 0; + x->gl_editor->e_onmotion = MA_NONE; + } + /* post("click %d %d %d %d", xpos, ypos, which, mod); */ + + if (x->gl_editor->e_onmotion != MA_NONE) + return; + + x->gl_editor->e_xwas = xpos; + x->gl_editor->e_ywas = ypos; + + if (runmode && !rightclick) + { + for (y = x->gl_list; y; y = y->g_next) + { + /* check if the object wants to be clicked */ + if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2) + && (clickreturned = gobj_click(y, x, xpos, ypos, + shiftmod, altmod, 0, doit))) + break; + } + if (!doit) + { + if (y) + canvas_setcursor(x, clickreturned); + else canvas_setcursor(x, CURSOR_RUNMODE_NOTHING); + } + return; + } + /* if not a runmode left click, fall here. */ + if (y = canvas_findhitbox(x, xpos, ypos, &x1, &y1, &x2, &y2)) + { + t_object *ob = pd_checkobject(&y->g_pd); + /* check you're in the rectangle */ + ob = pd_checkobject(&y->g_pd); + if (rightclick) + canvas_rightclick(x, xpos, ypos, y); + else if (shiftmod) + { + if (doit) + { + t_rtext *rt; + if (ob && (rt = x->gl_editor->e_textedfor) && + rt == glist_findrtext(x, ob)) + { + rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_SHIFT); + x->gl_editor->e_onmotion = MA_DRAGTEXT; + x->gl_editor->e_xwas = x1; + x->gl_editor->e_ywas = y1; + } + else + { + if (glist_isselected(x, y)) + glist_deselect(x, y); + else glist_select(x, y); + } + } + } + else + { + /* look for an outlet */ + int noutlet; + if (ob && (noutlet = obj_noutlets(ob)) && ypos >= y2-4) + { + int width = x2 - x1; + int nout1 = (noutlet > 1 ? noutlet - 1 : 1); + int closest = ((xpos-x1) * (nout1) + width/2)/width; + int hotspot = x1 + + (width - IOWIDTH) * closest / (nout1); + if (closest < noutlet && + xpos >= (hotspot-1) && xpos <= hotspot + (IOWIDTH+1)) + { + if (doit) + { + int issignal = obj_issignaloutlet(ob, closest); + x->gl_editor->e_onmotion = MA_CONNECT; + x->gl_editor->e_xwas = xpos; + x->gl_editor->e_ywas = ypos; + sys_vgui( + ".x%x.c create line %d %d %d %d -width %d -tags x\n", + x, xpos, ypos, xpos, ypos, + (issignal ? 2 : 1)); + } + else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT); + } + else if (doit) + goto nooutletafterall; + } + /* not in an outlet; select and move */ + else if (doit) + { + t_rtext *rt; + /* check if the box is being text edited */ + nooutletafterall: + if (ob && (rt = x->gl_editor->e_textedfor) && + rt == glist_findrtext(x, ob)) + { + rtext_mouse(rt, xpos - x1, ypos - y1, RTEXT_DOWN); + x->gl_editor->e_onmotion = MA_DRAGTEXT; + x->gl_editor->e_xwas = x1; + x->gl_editor->e_ywas = y1; + } + else + { + /* otherwise select and drag to displace */ + if (!glist_isselected(x, y)) + { + glist_noselect(x); + glist_select(x, y); + } + x->gl_editor->e_onmotion = MA_MOVE; + } + } + else canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); + } + return; + } + /* if right click doesn't hit any boxes, call rightclick + routine anyway */ + if (rightclick) + canvas_rightclick(x, xpos, ypos, 0); + + /* if not an editing action, and if we didn't hit a + box, set cursor and return */ + if (runmode || rightclick) + { + canvas_setcursor(x, CURSOR_RUNMODE_NOTHING); + return; + } + /* having failed to find a box, we try lines now. */ + if (!runmode && !altmod && !shiftmod) + { + t_linetraverser t; + t_outconnect *oc; + float fx = xpos, fy = ypos; + t_glist *glist2 = glist_getcanvas(x); + linetraverser_start(&t, glist2); + while (oc = linetraverser_next(&t)) + { + float lx1 = t.tr_lx1, ly1 = t.tr_ly1, + lx2 = t.tr_lx2, ly2 = t.tr_ly2; + float area = (lx2 - lx1) * (fy - ly1) - + (ly2 - ly1) * (fx - lx1); + float dsquare = (lx2-lx1) * (lx2-lx1) + (ly2-ly1) * (ly2-ly1); + if (area * area >= 50 * dsquare) continue; + if ((lx2-lx1) * (fx-lx1) + (ly2-ly1) * (fy-ly1) < 0) continue; + if ((lx2-lx1) * (lx2-fx) + (ly2-ly1) * (ly2-fy) < 0) continue; + if (doit) + { + glist_selectline(glist2, oc, + canvas_getindex(glist2, &t.tr_ob->ob_g), t.tr_outno, + canvas_getindex(glist2, &t.tr_ob2->ob_g), t.tr_inno); + } + canvas_setcursor(x, CURSOR_EDITMODE_DISCONNECT); + return; + } + } + canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); + if (doit) + { + if (!shiftmod) glist_noselect(x); + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags x\n", + x, xpos, ypos, xpos, ypos); + x->gl_editor->e_xwas = xpos; + x->gl_editor->e_ywas = ypos; + x->gl_editor->e_onmotion = MA_REGION; + } +} + +void canvas_mousedown(t_canvas *x, t_floatarg xpos, t_floatarg ypos, + t_floatarg which, t_floatarg mod) +{ + canvas_doclick(x, xpos, ypos, which, mod, 1); +} + +int canvas_isconnected (t_canvas *x, t_text *ob1, int n1, + t_text *ob2, int n2) +{ + t_linetraverser t; + t_outconnect *oc; + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + if (t.tr_ob == ob1 && t.tr_outno == n1 && + t.tr_ob2 == ob2 && t.tr_inno == n2) + return (1); + return (0); +} + +void canvas_doconnect(t_canvas *x, int xpos, int ypos, int which, int doit) +{ + int x11, y11, x12, y12; + t_gobj *y1; + int x21, y21, x22, y22; + t_gobj *y2; + int xwas = x->gl_editor->e_xwas, + ywas = x->gl_editor->e_ywas; + if (doit) sys_vgui(".x%x.c delete x\n", x); + else sys_vgui(".x%x.c coords x %d %d %d %d\n", + x, x->gl_editor->e_xwas, + x->gl_editor->e_ywas, xpos, ypos); + + if ((y1 = canvas_findhitbox(x, xwas, ywas, &x11, &y11, &x12, &y12)) + && (y2 = canvas_findhitbox(x, xpos, ypos, &x21, &y21, &x22, &y22))) + { + t_object *ob1 = pd_checkobject(&y1->g_pd); + t_object *ob2 = pd_checkobject(&y2->g_pd); + int noutlet1, ninlet2; + if (ob1 && ob2 && ob1 != ob2 && + (noutlet1 = obj_noutlets(ob1)) + && (ninlet2 = obj_ninlets(ob2))) + { + int width1 = x12 - x11, closest1, hotspot1; + int width2 = x22 - x21, closest2, hotspot2; + int lx1, lx2, ly1, ly2; + t_outconnect *oc; + + if (noutlet1 > 1) + { + closest1 = ((xwas-x11) * (noutlet1-1) + width1/2)/width1; + hotspot1 = x11 + + (width1 - IOWIDTH) * closest1 / (noutlet1-1); + } + else closest1 = 0, hotspot1 = x11; + + if (ninlet2 > 1) + { + closest2 = ((xpos-x21) * (ninlet2-1) + width2/2)/width2; + hotspot2 = x21 + + (width2 - IOWIDTH) * closest2 / (ninlet2-1); + } + else closest2 = 0, hotspot2 = x21; + + if (closest1 >= noutlet1) + closest1 = noutlet1 - 1; + if (closest2 >= ninlet2) + closest2 = ninlet2 - 1; + + if (canvas_isconnected (x, ob1, closest1, ob2, closest2)) + { + canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); + return; + } + if (obj_issignaloutlet(ob1, closest1) && + !obj_issignalinlet(ob2, closest2)) + { + if (doit) + error("can't connect signal outlet to control inlet"); + canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); + return; + } + if (doit) + { + oc = obj_connect(ob1, closest1, ob2, closest2); + lx1 = x11 + (noutlet1 > 1 ? + ((x12-x11-IOWIDTH) * closest1)/(noutlet1-1) : 0) + + IOMIDDLE; + ly1 = y12; + lx2 = x21 + (ninlet2 > 1 ? + ((x22-x21-IOWIDTH) * closest2)/(ninlet2-1) : 0) + + IOMIDDLE; + ly2 = y21; + sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n", + glist_getcanvas(x), + lx1, ly1, lx2, ly2, + (obj_issignaloutlet(ob1, closest1) ? 2 : 1), oc); + canvas_setundo(x, canvas_undo_connect, + canvas_undo_set_connect(x, + canvas_getindex(x, &ob1->ob_g), closest1, + canvas_getindex(x, &ob2->ob_g), closest2), + "connect"); + } + else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT); + return; + } + } + canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); +} + +void canvas_selectinrect(t_canvas *x, int lox, int loy, int hix, int hiy) +{ + t_gobj *y; + for (y = x->gl_list; y; y = y->g_next) + { + int x1, y1, x2, y2; + gobj_getrect(y, x, &x1, &y1, &x2, &y2); + if (hix >= x1 && lox <= x2 && hiy >= y1 && loy <= y2 + && !glist_isselected(x, y)) + glist_select(x, y); + } +} + +static void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit) +{ + if (doit) + { + int lox, loy, hix, hiy; + if (x->gl_editor->e_xwas < xpos) + lox = x->gl_editor->e_xwas, hix = xpos; + else hix = x->gl_editor->e_xwas, lox = xpos; + if (x->gl_editor->e_ywas < ypos) + loy = x->gl_editor->e_ywas, hiy = ypos; + else hiy = x->gl_editor->e_ywas, loy = ypos; + canvas_selectinrect(x, lox, loy, hix, hiy); + sys_vgui(".x%x.c delete x\n", x); + x->gl_editor->e_onmotion = 0; + } + else sys_vgui(".x%x.c coords x %d %d %d %d\n", + x, x->gl_editor->e_xwas, + x->gl_editor->e_ywas, xpos, ypos); +} + +void canvas_mouseup(t_canvas *x, + t_floatarg fxpos, t_floatarg fypos, t_floatarg fwhich) +{ + t_gobj *y; + + int xpos = fxpos, ypos = fypos, which = fwhich; + + if (!x->gl_editor) + { + bug("editor"); + return; + } + +#ifdef SIMULATERIGHTCLICK + canvas_upclicktime = sys_getrealtime(); + canvas_upx = xpos; + canvas_upy = ypos; +#endif + + if (x->gl_editor->e_onmotion == MA_CONNECT) + canvas_doconnect(x, xpos, ypos, which, 1); + else if (x->gl_editor->e_onmotion == MA_REGION) + canvas_doregion(x, xpos, ypos, 1); + else if (x->gl_editor->e_onmotion == MA_MOVE) + { + /* after motion, if there's only one item selected, activate it */ + if (x->gl_editor->e_selection && + !(x->gl_editor->e_selection->sel_next)) + gobj_activate(x->gl_editor->e_selection->sel_what, + x, 1); + } + else if (x->gl_editor->e_onmotion == MA_PASSOUT) + x->gl_editor->e_onmotion = 0; + x->gl_editor->e_onmotion = MA_NONE; + + +#if 1 + /* GG misused the (unused) dbl parameter to tell if mouseup */ + + for (y = x->gl_list; y; y = y->g_next) { + int x1, y1, x2, y2, clickreturned = 0; + + /* check if the object wants to be clicked */ + if (canvas_hitbox(x, y, xpos, ypos, &x1, &y1, &x2, &y2) + && (clickreturned = gobj_click(y, x, xpos, ypos, + 0, 0, 1, 0))) + break; + } +#endif + + +} + + /* displace the selection by (dx, dy) pixels */ +static void canvas_displaceselection(t_canvas *x, int dx, int dy) +{ + t_selection *y; + int resortin = 0, resortout = 0; + if (!canvas_undo_already_set_move) + { + canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 1), + "motion"); + canvas_undo_already_set_move = 1; + } + for (y = x->gl_editor->e_selection; y; y = y->sel_next) + { + t_class *cl = pd_class(&y->sel_what->g_pd); + gobj_displace(y->sel_what, x, dx, dy); + if (cl == vinlet_class) resortin = 1; + else if (cl == voutlet_class) resortout = 1; + } + if (resortin) canvas_resortinlets(x); + if (resortout) canvas_resortoutlets(x); + canvas_dirty(x, 1); +} + + /* this routine is called whenever a key is pressed or released. "x" + may be zero if there's no current canvas. The first argument is true or + fals for down/up; the second one is either a symbolic key name (e.g., + "Right" or an Ascii key number. */ +void canvas_key(t_canvas *x, t_symbol *s, int ac, t_atom *av) +{ + static t_symbol *keynumsym, *keyupsym, *keynamesym; + int keynum, fflag; + t_symbol *gotkeysym; + + int down, shift; + + if (ac < 3) + return; + if (!x->gl_editor) + { + bug("editor"); + return; + } + canvas_undo_already_set_move = 0; + down = (atom_getfloat(av) != 0); /* nonzero if it's a key down */ + shift = (atom_getfloat(av+2) != 0); /* nonzero if shift-ed */ + if (av[1].a_type == A_SYMBOL) + gotkeysym = av[1].a_w.w_symbol; + else if (av[1].a_type == A_FLOAT) + { + char buf[3]; + sprintf(buf, "%c", (int)(av[1].a_w.w_float)); + gotkeysym = gensym(buf); + } + else gotkeysym = gensym("?"); + fflag = (av[0].a_type == A_FLOAT ? av[0].a_w.w_float : 0); + keynum = (av[1].a_type == A_FLOAT ? av[1].a_w.w_float : 0); + if (keynum == '\\' || keynum == '{' || keynum == '}') + { + post("%c: dropped", (int)keynum); + return; + } + if (keynum == '\r') keynum = '\n'; + if (av[1].a_type == A_SYMBOL && + !strcmp(av[1].a_w.w_symbol->s_name, "Return")) + keynum = '\n'; + if (!keynumsym) + { + keynumsym = gensym("#key"); + keyupsym = gensym("#keyup"); + keynamesym = gensym("#keyname"); + } +#ifdef MACOSX + if (keynum == 30) + keynum = 0, gotkeysym = gensym("Up"); + else if (keynum == 31) + keynum = 0, gotkeysym = gensym("Down"); + else if (keynum == 28) + keynum = 0, gotkeysym = gensym("Left"); + else if (keynum == 29) + keynum = 0, gotkeysym = gensym("Right"); +#endif + if (keynumsym->s_thing && down) + pd_float(keynumsym->s_thing, (float)keynum); + if (keyupsym->s_thing && !down) + pd_float(keyupsym->s_thing, (float)keynum); + if (keynamesym->s_thing) + { + t_atom at[2]; + at[0] = av[0]; + SETFLOAT(at, down); + SETSYMBOL(at+1, gotkeysym); + pd_list(keynamesym->s_thing, 0, 2, at); + } + if (!x->gl_editor) /* if that 'invis'ed the window, we'd better stop. */ + return; + if (x && down) + { + /* if an object has "grabbed" keys just send them on */ + if (x->gl_editor->e_grab + && x->gl_editor->e_keyfn && keynum) + (* x->gl_editor->e_keyfn) + (x->gl_editor->e_grab, (float)keynum); + /* if a text editor is open send it on */ + else if (x->gl_editor->e_textedfor) + { + if (!x->gl_editor->e_textdirty) + { + canvas_setundo(x, canvas_undo_cut, + canvas_undo_set_cut(x, UCUT_TEXT), "typing"); + } + rtext_key(x->gl_editor->e_textedfor, + (int)keynum, gotkeysym); + if (x->gl_editor->e_textdirty) + canvas_dirty(x, 1); + } + /* check for backspace or clear */ + else if (keynum == 8 || keynum == 127) + { + if (x->gl_editor->e_selectedline) + canvas_clearline(x); + else if (x->gl_editor->e_selection) + { + canvas_setundo(x, canvas_undo_cut, + canvas_undo_set_cut(x, UCUT_CLEAR), "clear"); + canvas_doclear(x); + } + } + /* check for arrow keys */ + else if (!strcmp(gotkeysym->s_name, "Up")) + canvas_displaceselection(x, 0, shift ? -10 : -1); + else if (!strcmp(gotkeysym->s_name, "Down")) + canvas_displaceselection(x, 0, shift ? 10 : 1); + else if (!strcmp(gotkeysym->s_name, "Left")) + canvas_displaceselection(x, shift ? -10 : -1, 0); + else if (!strcmp(gotkeysym->s_name, "Right")) + canvas_displaceselection(x, shift ? 10 : 1, 0); + } +} + +void canvas_motion(t_canvas *x, t_floatarg xpos, t_floatarg ypos, + t_floatarg fmod) +{ + /* post("motion %d %d", xpos, ypos); */ + int mod = fmod; + if (!x->gl_editor) + { + bug("editor"); + return; + } + glist_setlastxy(x, xpos, ypos); + if (x->gl_editor->e_onmotion == MA_MOVE) + { + canvas_displaceselection(x, + xpos - x->gl_editor->e_xwas, ypos - x->gl_editor->e_ywas); + x->gl_editor->e_xwas = xpos; + x->gl_editor->e_ywas = ypos; + } + else if (x->gl_editor->e_onmotion == MA_REGION) + canvas_doregion(x, xpos, ypos, 0); + else if (x->gl_editor->e_onmotion == MA_CONNECT) + canvas_doconnect(x, xpos, ypos, 0, 0); + else if (x->gl_editor->e_onmotion == MA_PASSOUT) + { + if (!x->gl_editor->e_motionfn) + bug("e_motionfn"); + (*x->gl_editor->e_motionfn)(&x->gl_editor->e_grab->g_pd, + xpos - x->gl_editor->e_xwas, + ypos - x->gl_editor->e_ywas); + x->gl_editor->e_xwas = xpos; + x->gl_editor->e_ywas = ypos; + } + else if (x->gl_editor->e_onmotion == MA_DRAGTEXT) + { + t_rtext *rt = x->gl_editor->e_textedfor; + if (rt) + rtext_mouse(rt, xpos - x->gl_editor->e_xwas, + ypos - x->gl_editor->e_ywas, RTEXT_DRAG); + } + else canvas_doclick(x, xpos, ypos, 0, mod, 0); + + x->gl_editor->e_lastmoved = 1; +} + +void canvas_startmotion(t_canvas *x) +{ + int xval, yval; + if (!x->gl_editor) return; + glist_getnextxy(x, &xval, &yval); + if (xval == 0 && yval == 0) return; + x->gl_editor->e_onmotion = MA_MOVE; + x->gl_editor->e_xwas = xval; + x->gl_editor->e_ywas = yval; +} + +/* ----------------------------- window stuff ----------------------- */ + +void canvas_print(t_canvas *x, t_symbol *s) +{ + if (*s->s_name) sys_vgui(".x%x.c postscript -file %s\n", x, s->s_name); + else sys_vgui(".x%x.c postscript -file x.ps\n", x); +} + +void canvas_menuclose(t_canvas *x, t_floatarg force) +{ + if (x->gl_owner) + canvas_vis(x, 0); + else if ((force != 0) || (!x->gl_dirty)) + pd_free(&x->gl_pd); + else sys_vgui("pdtk_check {This window has been modified. Close anyway?}\ + {.x%x menuclose 1;\n}\n", x); +} + + /* put up a dialog which may call canvas_font back to do the work */ +static void canvas_menufont(t_canvas *x) +{ + char buf[80]; + t_canvas *x2 = canvas_getrootfor(x); + gfxstub_deleteforkey(x2); + sprintf(buf, "pdtk_canvas_dofont %%s %d\n", x2->gl_font); + gfxstub_new(&x2->gl_pd, &x2->gl_pd, buf); +} + +static int canvas_find_index1, canvas_find_index2; +static t_binbuf *canvas_findbuf; +int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf); + + /* find an atom or string of atoms */ +static int canvas_dofind(t_canvas *x, int *myindex1p) +{ + t_gobj *y; + int myindex1 = *myindex1p, myindex2; + if (myindex1 >= canvas_find_index1) + { + for (y = x->gl_list, myindex2 = 0; y; + y = y->g_next, myindex2++) + { + t_object *ob = 0; + if (ob = pd_checkobject(&y->g_pd)) + { + if (binbuf_match(ob->ob_binbuf, canvas_findbuf)) + { + if (myindex1 > canvas_find_index1 || + myindex1 == canvas_find_index1 && + myindex2 > canvas_find_index2) + { + canvas_find_index1 = myindex1; + canvas_find_index2 = myindex2; + glist_noselect(x); + canvas_vis(x, 1); + canvas_editmode(x, 1.); + glist_select(x, y); + return (1); + } + } + } + } + } + for (y = x->gl_list, myindex2 = 0; y; y = y->g_next, myindex2++) + { + if (pd_class(&y->g_pd) == canvas_class) + { + (*myindex1p)++; + if (canvas_dofind((t_canvas *)y, myindex1p)) + return (1); + } + } + return (0); +} + +static void canvas_find(t_canvas *x, t_symbol *s, int ac, t_atom *av) +{ + int myindex1 = 0, i; + for (i = 0; i < ac; i++) + { + if (av[i].a_type == A_SYMBOL) + { + if (!strcmp(av[i].a_w.w_symbol->s_name, "_semi_")) + SETSEMI(&av[i]); + else if (!strcmp(av[i].a_w.w_symbol->s_name, "_comma_")) + SETCOMMA(&av[i]); + } + } + if (!canvas_findbuf) + canvas_findbuf = binbuf_new(); + binbuf_clear(canvas_findbuf); + binbuf_add(canvas_findbuf, ac, av); + canvas_find_index1 = 0; + canvas_find_index2 = -1; + canvas_whichfind = x; + if (!canvas_dofind(x, &myindex1)) + { + binbuf_print(canvas_findbuf); + post("... couldn't find"); + } +} + +static void canvas_find_again(t_canvas *x) +{ + int myindex1 = 0; + if (!canvas_findbuf || !canvas_whichfind) + return; + if (!canvas_dofind(canvas_whichfind, &myindex1)) + { + binbuf_print(canvas_findbuf); + post("... couldn't find"); + } +} + +static void canvas_find_parent(t_canvas *x) +{ + if (x->gl_owner) + canvas_vis(glist_getcanvas(x->gl_owner), 1); +} + +static int glist_dofinderror(t_glist *gl, void *error_object) +{ + t_gobj *g; + for (g = gl->gl_list; g; g = g->g_next) + { + if ((void *)g == error_object) + { + /* got it... now show it. */ + glist_noselect(gl); + canvas_vis(glist_getcanvas(gl), 1); + canvas_editmode(glist_getcanvas(gl), 1.); + glist_select(gl, g); + return (1); + } + else if (g->g_pd == canvas_class) + { + if (glist_dofinderror((t_canvas *)g, error_object)) + return (1); + } + } + return (0); +} + +void canvas_finderror(void *error_object) +{ + t_canvas *x; + /* find all root canvases */ + for (x = canvas_list; x; x = x->gl_next) + { + if (glist_dofinderror(x, error_object)) + return; + } + post("... sorry, I couldn't find the source of that error."); +} + +void canvas_stowconnections(t_canvas *x) +{ + t_gobj *selhead = 0, *seltail = 0, *nonhead = 0, *nontail = 0, *y, *y2; + t_linetraverser t; + t_outconnect *oc; + if (!x->gl_editor) return; + /* split list to "selected" and "unselected" parts */ + for (y = x->gl_list; y; y = y2) + { + y2 = y->g_next; + if (glist_isselected(x, y)) + { + if (seltail) + { + seltail->g_next = y; + seltail = y; + y->g_next = 0; + } + else + { + selhead = seltail = y; + seltail->g_next = 0; + } + } + else + { + if (nontail) + { + nontail->g_next = y; + nontail = y; + y->g_next = 0; + } + else + { + nonhead = nontail = y; + nontail->g_next = 0; + } + } + } + /* move the selected part to the end */ + if (!nonhead) x->gl_list = selhead; + else x->gl_list = nonhead, nontail->g_next = selhead; + + /* add connections to binbuf */ + binbuf_clear(x->gl_editor->e_connectbuf); + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + int s1 = glist_isselected(x, &t.tr_ob->ob_g); + int s2 = glist_isselected(x, &t.tr_ob2->ob_g); + if (s1 != s2) + binbuf_addv(x->gl_editor->e_connectbuf, "ssiiii;", + gensym("#X"), gensym("connect"), + glist_getindex(x, &t.tr_ob->ob_g), t.tr_outno, + glist_getindex(x, &t.tr_ob2->ob_g), t.tr_inno); + } +} + +void canvas_restoreconnections(t_canvas *x) +{ + pd_bind(&x->gl_pd, gensym("#X")); + binbuf_eval(x->gl_editor->e_connectbuf, 0, 0, 0); + pd_unbind(&x->gl_pd, gensym("#X")); +} + +static t_binbuf *canvas_docopy(t_canvas *x) +{ + t_gobj *y; + t_linetraverser t; + t_outconnect *oc; + t_binbuf *b = binbuf_new(); + for (y = x->gl_list; y; y = y->g_next) + { + if (glist_isselected(x, y)) + gobj_save(y, b); + } + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + if (glist_isselected(x, &t.tr_ob->ob_g) + && glist_isselected(x, &t.tr_ob2->ob_g)) + { + binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"), + glist_selectionindex(x, &t.tr_ob->ob_g, 1), t.tr_outno, + glist_selectionindex(x, &t.tr_ob2->ob_g, 1), t.tr_inno); + } + } + return (b); +} + +static void canvas_copy(t_canvas *x) +{ + if (!x->gl_editor || !x->gl_editor->e_selection) + return; + binbuf_free(copy_binbuf); + copy_binbuf = canvas_docopy(x); +} + +static void canvas_clearline(t_canvas *x) +{ + if (x->gl_editor->e_selectedline) + { + canvas_disconnect(x, x->gl_editor->e_selectline_index1, + x->gl_editor->e_selectline_outno, + x->gl_editor->e_selectline_index2, + x->gl_editor->e_selectline_inno); + canvas_setundo(x, canvas_undo_disconnect, + canvas_undo_set_disconnect(x, + x->gl_editor->e_selectline_index1, + x->gl_editor->e_selectline_outno, + x->gl_editor->e_selectline_index2, + x->gl_editor->e_selectline_inno), + "disconnect"); + } +} + +extern t_pd *newest; +static void canvas_doclear(t_canvas *x) +{ + t_gobj *y, *y2; + int dspstate; + + dspstate = canvas_suspend_dsp(); + if (x->gl_editor->e_selectedline) + { + canvas_disconnect(x, x->gl_editor->e_selectline_index1, + x->gl_editor->e_selectline_outno, + x->gl_editor->e_selectline_index2, + x->gl_editor->e_selectline_inno); + canvas_setundo(x, canvas_undo_disconnect, + canvas_undo_set_disconnect(x, + x->gl_editor->e_selectline_index1, + x->gl_editor->e_selectline_outno, + x->gl_editor->e_selectline_index2, + x->gl_editor->e_selectline_inno), + "disconnect"); + } + /* if text is selected, deselecting it might remake the + object. So we deselect it and hunt for a "new" object on + the glist to reselect. */ + if (x->gl_editor->e_textedfor) + { + newest = 0; + glist_noselect(x); + if (newest) + { + for (y = x->gl_list; y; y = y->g_next) + if (&y->g_pd == newest) glist_select(x, y); + } + } + while (1) /* this is pretty wierd... should rewrite it */ + { + for (y = x->gl_list; y; y = y2) + { + y2 = y->g_next; + if (glist_isselected(x, y)) + { + glist_delete(x, y); +#if 0 + if (y2) post("cut 5 %x %x", y2, y2->g_next); + else post("cut 6"); +#endif + goto next; + } + } + goto restore; + next: ; + } +restore: + canvas_resume_dsp(dspstate); + canvas_dirty(x, 1); +} + +static void canvas_cut(t_canvas *x) +{ + if (x->gl_editor && x->gl_editor->e_selectedline) + canvas_clearline(x); + else if (x->gl_editor && x->gl_editor->e_selection) + { + canvas_setundo(x, canvas_undo_cut, + canvas_undo_set_cut(x, UCUT_CUT), "cut"); + canvas_copy(x); + canvas_doclear(x); + } +} + +static int paste_onset; +static t_canvas *paste_canvas; + +static void glist_donewloadbangs(t_glist *x) +{ + if (x->gl_editor) + { + t_selection *sel; + for (sel = x->gl_editor->e_selection; sel; sel = sel->sel_next) + if (pd_class(&sel->sel_what->g_pd) == canvas_class) + canvas_loadbang((t_canvas *)(&sel->sel_what->g_pd)); + } +} + +static void canvas_dopaste(t_canvas *x, t_binbuf *b) +{ + t_gobj *newgobj, *last, *g2; + int dspstate = canvas_suspend_dsp(), nbox, count; + + canvas_editmode(x, 1.); + glist_noselect(x); + for (g2 = x->gl_list, nbox = 0; g2; g2 = g2->g_next) nbox++; + + paste_onset = nbox; + paste_canvas = x; + + pd_bind(&x->gl_pd, gensym("#X")); + binbuf_eval(b, 0, 0, 0); + pd_unbind(&x->gl_pd, gensym("#X")); + for (g2 = x->gl_list, count = 0; g2; g2 = g2->g_next, count++) + if (count >= nbox) + glist_select(x, g2); + paste_canvas = 0; + canvas_resume_dsp(dspstate); + canvas_dirty(x, 1); + glist_donewloadbangs(x); +} + +static void canvas_paste(t_canvas *x) +{ + canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x), "paste"); + canvas_dopaste(x, copy_binbuf); +} + +static void canvas_duplicate(t_canvas *x) +{ + if (x->gl_editor->e_onmotion == MA_NONE) + { + t_selection *y; + canvas_copy(x); + canvas_setundo(x, canvas_undo_paste, canvas_undo_set_paste(x), + "duplicate"); + canvas_dopaste(x, copy_binbuf); + for (y = x->gl_editor->e_selection; y; y = y->sel_next) + gobj_displace(y->sel_what, x, + 10, 10); + canvas_dirty(x, 1); + } +} + +static void canvas_selectall(t_canvas *x) +{ + t_gobj *y; + if (!x->gl_edit) + canvas_editmode(x, 1); + for (y = x->gl_list; y; y = y->g_next) + { + if (!glist_isselected(x, y)) + glist_select(x, y); + } +} + +void canvas_connect(t_canvas *x, t_floatarg fwhoout, t_floatarg foutno, + t_floatarg fwhoin, t_floatarg finno) +{ + int whoout = fwhoout, outno = foutno, whoin = fwhoin, inno = finno; + t_gobj *src = 0, *sink = 0; + t_object *objsrc, *objsink; + t_outconnect *oc; + int nin = whoin, nout = whoout; + if (paste_canvas == x) whoout += paste_onset, whoin += paste_onset; + for (src = x->gl_list; whoout; src = src->g_next, whoout--) + if (!src->g_next) goto bad; /* bug fix thanks to Hannes */ + for (sink = x->gl_list; whoin; sink = sink->g_next, whoin--) + if (!sink->g_next) goto bad; + if (!(objsrc = pd_checkobject(&src->g_pd)) || + !(objsink = pd_checkobject(&sink->g_pd))) + goto bad; + if (!(oc = obj_connect(objsrc, outno, objsink, inno))) goto bad; + if (glist_isvisible(x)) + { + sys_vgui(".x%x.c create line %d %d %d %d -width %d -tags l%x\n", + glist_getcanvas(x), 0, 0, 0, 0, + (obj_issignaloutlet(objsrc, outno) ? 2 : 1),oc); + canvas_fixlinesfor(x, objsrc); + } + return; + +bad: + post("%s %d %d %d %d (%s->%s) connection failed", + x->gl_name->s_name, nout, outno, nin, inno, + (src? class_getname(pd_class(&src->g_pd)) : "???"), + (sink? class_getname(pd_class(&sink->g_pd)) : "???")); +} + +#define XTOLERANCE 4 +#define YTOLERANCE 3 +#define NHIST 15 + + /* LATER might have to speed this up */ +static void canvas_tidy(t_canvas *x) +{ + t_gobj *y, *y2, *y3; + int ax1, ay1, ax2, ay2, bx1, by1, bx2, by2; + int histogram[NHIST], *ip, i, besthist, bestdist; + /* if nobody is selected, this means do it to all boxes; + othewise just the selection */ + int all = (x->gl_editor ? (x->gl_editor->e_selection == 0) : 1); + + canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, !all), + "motion"); + + /* tidy horizontally */ + for (y = x->gl_list; y; y = y->g_next) + if (all || glist_isselected(x, y)) + { + gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2); + + for (y2 = x->gl_list; y2; y2 = y2->g_next) + if (all || glist_isselected(x, y2)) + { + gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); + if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE && + bx1 < ax1) + goto nothorizhead; + } + + for (y2 = x->gl_list; y2; y2 = y2->g_next) + if (all || glist_isselected(x, y2)) + { + gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); + if (by1 <= ay1 + YTOLERANCE && by1 >= ay1 - YTOLERANCE + && by1 != ay1) + gobj_displace(y2, x, 0, ay1-by1); + } + nothorizhead: ; + } + /* tidy vertically. First guess the user's favorite vertical spacing */ + for (i = NHIST, ip = histogram; i--; ip++) *ip = 0; + for (y = x->gl_list; y; y = y->g_next) + if (all || glist_isselected(x, y)) + { + gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2); + for (y2 = x->gl_list; y2; y2 = y2->g_next) + if (all || glist_isselected(x, y2)) + { + gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); + if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE) + { + int distance = by1-ay2; + if (distance >= 0 && distance < NHIST) + histogram[distance]++; + } + } + } + for (i = 1, besthist = 0, bestdist = 4, ip = histogram + 1; + i < (NHIST-1); i++, ip++) + { + int hit = ip[-1] + 2 * ip[0] + ip[1]; + if (hit > besthist) + { + besthist = hit; + bestdist = i; + } + } + post("best vertical distance %d", bestdist); + for (y = x->gl_list; y; y = y->g_next) + if (all || glist_isselected(x, y)) + { + int keep = 1; + gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2); + for (y2 = x->gl_list; y2; y2 = y2->g_next) + if (all || glist_isselected(x, y2)) + { + gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); + if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE && + ay1 >= by2 - 10 && ay1 < by2 + NHIST) + goto nothead; + } + while (keep) + { + keep = 0; + for (y2 = x->gl_list; y2; y2 = y2->g_next) + if (all || glist_isselected(x, y2)) + { + gobj_getrect(y2, x, &bx1, &by1, &bx2, &by2); + if (bx1 <= ax1 + XTOLERANCE && bx1 >= ax1 - XTOLERANCE && + by1 > ay1 && by1 < ay2 + NHIST) + { + int vmove = ay2 + bestdist - by1; + gobj_displace(y2, x, ax1-bx1, vmove); + ay1 = by1 + vmove; + ay2 = by2 + vmove; + keep = 1; + break; + } + } + } + nothead: ; + } + canvas_dirty(x, 1); +} + +static void canvas_texteditor(t_canvas *x) +{ + t_rtext *foo; + char *buf; + int bufsize; + if (foo = x->gl_editor->e_textedfor) + rtext_gettext(foo, &buf, &bufsize); + else buf = "", bufsize = 0; + sys_vgui("pdtk_pd_texteditor {%.*s}\n", bufsize, buf); + +} + +void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av) +{ + /* canvas_editing can be zero; canvas_key checks for that */ + canvas_key(canvas_editing, s, ac, av); +} + +void canvas_editmode(t_canvas *x, t_floatarg fyesplease) +{ + int yesplease = fyesplease; + if (yesplease && x->gl_edit) + return; + x->gl_edit = !x->gl_edit; + if (x->gl_edit && glist_isvisible(x) && glist_istoplevel(x)) + canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); + else + { + glist_noselect(x); + if (glist_isvisible(x) && glist_istoplevel(x)) + canvas_setcursor(x, CURSOR_RUNMODE_NOTHING); + } + sys_vgui("pdtk_canvas_editval .x%x %d\n", + glist_getcanvas(x), x->gl_edit); +} + + /* called by canvas_font below */ +static void canvas_dofont(t_canvas *x, t_floatarg font, t_floatarg xresize, + t_floatarg yresize) +{ + t_gobj *y; + x->gl_font = font; + if (xresize != 1 || yresize != 1) + { + canvas_setundo(x, canvas_undo_move, canvas_undo_set_move(x, 0), + "motion"); + for (y = x->gl_list; y; y = y->g_next) + { + int x1, x2, y1, y2, nx1, ny1; + gobj_getrect(y, x, &x1, &y1, &x2, &y2); + nx1 = x1 * xresize + 0.5; + ny1 = y1 * yresize + 0.5; + gobj_displace(y, x, nx1-x1, ny1-y1); + } + } + if (glist_isvisible(x)) + glist_redraw(x); + for (y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == canvas_class + && !canvas_isabstraction((t_canvas *)y)) + canvas_dofont((t_canvas *)y, font, xresize, yresize); +} + + /* canvas_menufont calls up a TK dialog which calls this back */ +static void canvas_font(t_canvas *x, t_floatarg font, t_floatarg resize, + t_floatarg whichresize) +{ + float realresize, realresx = 1, realresy = 1; + t_canvas *x2 = canvas_getrootfor(x); + if (!resize) realresize = 1; + else + { + if (resize < 20) resize = 20; + if (resize > 500) resize = 500; + realresize = resize * 0.01; + } + if (whichresize != 3) realresx = realresize; + if (whichresize != 2) realresy = realresize; + canvas_dofont(x2, font, realresx, realresy); + sys_defaultfont = font; +} + +static t_glist *canvas_last_glist; +static int canvas_last_glist_x, canvas_last_glist_y; + +void glist_getnextxy(t_glist *gl, int *xpix, int *ypix) +{ + if (canvas_last_glist == gl) + *xpix = canvas_last_glist_x, *ypix = canvas_last_glist_y; + else *xpix = *ypix = 40; +} + +static void glist_setlastxy(t_glist *gl, int xval, int yval) +{ + canvas_last_glist = gl; + canvas_last_glist_x = xval; + canvas_last_glist_y = yval; +} + + +void g_editor_setup(void) +{ +/* ------------------------ events ---------------------------------- */ + class_addmethod(canvas_class, (t_method)canvas_mousedown, gensym("mouse"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_mouseup, gensym("mouseup"), + A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_key, gensym("key"), + A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_motion, gensym("motion"), + A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + +/* ------------------------ menu actions ---------------------------- */ + class_addmethod(canvas_class, (t_method)canvas_menuclose, + gensym("menuclose"), A_DEFFLOAT, 0); + class_addmethod(canvas_class, (t_method)canvas_cut, + gensym("cut"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_copy, + gensym("copy"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_paste, + gensym("paste"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_duplicate, + gensym("duplicate"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_selectall, + gensym("selectall"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_undo, + gensym("undo"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_redo, + gensym("redo"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_tidy, + gensym("tidy"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_texteditor, + gensym("texteditor"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_editmode, + gensym("editmode"), A_DEFFLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_print, + gensym("print"), A_SYMBOL, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_menufont, + gensym("menufont"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_font, + gensym("font"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_find, + gensym("find"), A_GIMME, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_find_again, + gensym("findagain"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_find_parent, + gensym("findparent"), A_NULL); + class_addmethod(canvas_class, (t_method)canvas_done_popup, + gensym("done-popup"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_donecanvasdialog, + gensym("donecanvasdialog"), A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)glist_arraydialog, + gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + +/* -------------- connect method used in reading files ------------------ */ + class_addmethod(canvas_class, (t_method)canvas_connect, + gensym("connect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + + class_addmethod(canvas_class, (t_method)canvas_disconnect, + gensym("disconnect"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); +/* -------------- copy buffer ------------------ */ + copy_binbuf = binbuf_new(); +} diff --git a/apps/plugins/pdbox/PDa/src/g_graph.c b/apps/plugins/pdbox/PDa/src/g_graph.c new file mode 100644 index 0000000..6a64900 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_graph.c @@ -0,0 +1,2224 @@ +/* Copyright (c) 1997-2001 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* This file deals with the behavior of glists as either "text objects" or +"graphs" inside another glist. LATER move the inlet/outlet code of g_canvas.c +to this file... */ + +#include +#include "m_pd.h" +#include "t_tk.h" +#include "g_canvas.h" +#include +#include + +/* ---------------------- forward definitions ----------------- */ + +static void graph_vis(t_gobj *gr, t_glist *unused_glist, int vis); +static void graph_graphrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2); +static void graph_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2); + +/* -------------------- maintaining the list -------------------- */ + +void glist_add(t_glist *x, t_gobj *y) +{ + t_object *ob; + y->g_next = 0; + if (!x->gl_list) x->gl_list = y; + else + { + t_gobj *y2; + for (y2 = x->gl_list; y2->g_next; y2 = y2->g_next); + y2->g_next = y; + } + if (x->gl_editor && (ob = pd_checkobject(&y->g_pd))) + rtext_new(x, ob); + if (glist_isvisible(x)) + gobj_vis(y, x, 1); + if (class_isdrawcommand(y->g_pd)) + canvas_redrawallfortemplate(glist_getcanvas(x)); +} + + /* this is to protect against a hairy problem in which deleting + a sub-canvas might delete an inlet on a box, after the box had + been invisible-ized, so that we have to protect against redrawing it! */ +int canvas_setdeleting(t_canvas *x, int flag) +{ + int ret = x->gl_isdeleting; + x->gl_isdeleting = flag; + return (ret); +} + + /* delete an object from a glist and free it */ +void glist_delete(t_glist *x, t_gobj *y) +{ + t_gobj *g; + t_object *ob; + t_gotfn chkdsp = zgetfn(&y->g_pd, gensym("dsp")); + t_canvas *canvas = glist_getcanvas(x); + int drawcommand = class_isdrawcommand(y->g_pd); + int wasdeleting; + + wasdeleting = canvas_setdeleting(canvas, 1); + if (x->gl_editor) + { + if (x->gl_editor->e_grab == y) x->gl_editor->e_grab = 0; + if (glist_isselected(x, y)) glist_deselect(x, y); + + /* HACK -- we had phantom outlets not getting erased on the + screen because the canvas_setdeleting() mechanism is too + crude. LATER carefully set up rules for when the rtexts + should exist, so that they stay around until all the + steps of becoming invisible are done. In the meantime, just + zap the inlets and outlets here... */ + if (pd_class(&y->g_pd) == canvas_class) + { + t_glist *gl = (t_glist *)y; + if (gl->gl_isgraph) + { + char tag[80]; + sprintf(tag, "graph%x", (int)gl); + glist_eraseiofor(x, &gl->gl_obj, tag); + } + else + { + text_eraseborder(&gl->gl_obj, x, + rtext_gettag(glist_findrtext(x, &gl->gl_obj))); + } + } + } + gobj_delete(y, x); + if (glist_isvisible(canvas)) + gobj_vis(y, x, 0); + if (x->gl_editor && (ob = pd_checkobject(&y->g_pd))) + rtext_new(x, ob); + if (x->gl_list == y) x->gl_list = y->g_next; + else for (g = x->gl_list; g; g = g->g_next) + if (g->g_next == y) + { + g->g_next = y->g_next; + break; + } + pd_free(&y->g_pd); + if (chkdsp) canvas_update_dsp(); + if (drawcommand) canvas_redrawallfortemplate(canvas); + canvas_setdeleting(canvas, wasdeleting); + x->gl_valid = ++glist_valid; +} + + /* remove every object from a glist. Experimental. */ +void glist_clear(t_glist *x) +{ + t_gobj *y, *y2; + int dspstate = canvas_suspend_dsp(); + while (y = x->gl_list) + glist_delete(x, y); + canvas_resume_dsp(dspstate); +} + +void glist_retext(t_glist *glist, t_text *y) +{ + t_canvas *c = glist_getcanvas(glist); + /* check that we have built rtexts yet. LATER need a better test. */ + if (glist->gl_editor && glist->gl_editor->e_rtext) + { + t_rtext *rt = glist_findrtext(glist, y); + if (rt) + rtext_retext(rt); + } +} + +void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn, + t_glistkeyfn keyfn, int xpos, int ypos) +{ + t_glist *x2 = glist_getcanvas(x); + if (motionfn) + x2->gl_editor->e_onmotion = MA_PASSOUT; + else x2->gl_editor->e_onmotion = 0; + x2->gl_editor->e_grab = y; + x2->gl_editor->e_motionfn = motionfn; + x2->gl_editor->e_keyfn = keyfn; + x2->gl_editor->e_xwas = xpos; + x2->gl_editor->e_ywas = ypos; +} + +t_canvas *glist_getcanvas(t_glist *x) +{ + while (x->gl_owner && !x->gl_havewindow && x->gl_isgraph) + x = x->gl_owner; + return((t_canvas *)x); +} + +static float gobj_getxforsort(t_gobj *g) +{ + if (pd_class(&g->g_pd) == scalar_class) + { + float x1, y1; + scalar_getbasexy((t_scalar *)g, &x1, &y1); + return(x1); + } + else return (0); +} + +static t_gobj *glist_merge(t_glist *x, t_gobj *g1, t_gobj *g2) +{ + t_gobj *g = 0, *g9 = 0; + float f1 = 0, f2 = 0; + if (g1) + f1 = gobj_getxforsort(g1); + if (g2) + f2 = gobj_getxforsort(g2); + while (1) + { + if (g1) + { + if (g2) + { + if (f1 <= f2) + goto put1; + else goto put2; + } + else goto put1; + } + else if (g2) + goto put2; + else break; + put1: + if (g9) + g9->g_next = g1, g9 = g1; + else g9 = g = g1; + if (g1 = g1->g_next) + f1 = gobj_getxforsort(g1); + g9->g_next = 0; + continue; + put2: + if (g9) + g9->g_next = g2, g9 = g2; + else g9 = g = g2; + if (g2 = g2->g_next) + f2 = gobj_getxforsort(g2); + g9->g_next = 0; + continue; + } + return (g); +} + +static t_gobj *glist_dosort(t_glist *x, + t_gobj *g, int nitems) +{ + if (nitems < 2) + return (g); + else + { + int n1 = nitems/2, n2 = nitems - n1, i; + t_gobj *g2, *g3; + for (g2 = g, i = n1-1; i--; g2 = g2->g_next) + ; + g3 = g2->g_next; + g2->g_next = 0; + g = glist_dosort(x, g, n1); + g3 = glist_dosort(x, g3, n2); + return (glist_merge(x, g, g3)); + } +} + +void glist_sort(t_glist *x) +{ + int nitems = 0, foo = 0; + float lastx = -1e37; + t_gobj *g; + for (g = x->gl_list; g; g = g->g_next) + { + float x1 = gobj_getxforsort(g); + if (x1 < lastx) + foo = 1; + lastx = x1; + nitems++; + } + if (foo) + x->gl_list = glist_dosort(x, x->gl_list, nitems); +} + +void glist_cleanup(t_glist *x) +{ + freebytes(x->gl_xlabel, x->gl_nxlabels * sizeof(*(x->gl_xlabel))); + freebytes(x->gl_ylabel, x->gl_nylabels * sizeof(*(x->gl_ylabel))); + gstub_cutoff(x->gl_stub); +} + +void glist_free(t_glist *x) +{ + glist_cleanup(x); + freebytes(x, sizeof(*x)); +} + +/* --------------- inlets and outlets ----------- */ + + +t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *s) +{ + t_inlet *ip = inlet_new(&x->gl_obj, who, s, 0); + if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner)) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } + if (!x->gl_loading) canvas_resortinlets(x); + return (ip); +} + +void canvas_rminlet(t_canvas *x, t_inlet *ip) +{ + t_canvas *owner = x->gl_owner; + int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting) + && glist_istoplevel(owner)); + + if (owner) canvas_deletelinesforio(owner, &x->gl_obj, ip, 0); + if (redraw) + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + inlet_free(ip); + if (redraw) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } +} + +extern t_inlet *vinlet_getit(t_pd *x); +extern void obj_moveinletfirst(t_object *x, t_inlet *i); + +void canvas_resortinlets(t_canvas *x) +{ + int ninlets = 0, i, j, xmax; + t_gobj *y, **vec, **vp, **maxp; + + for (ninlets = 0, y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == vinlet_class) ninlets++; + + if (ninlets < 2) return; + + vec = (t_gobj **)getbytes(ninlets * sizeof(*vec)); + + for (y = x->gl_list, vp = vec; y; y = y->g_next) + if (pd_class(&y->g_pd) == vinlet_class) *vp++ = y; + + for (i = ninlets; i--;) + { + t_inlet *ip; + for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = ninlets; + j--; vp++) + { + int x1, y1, x2, y2; + t_gobj *g = *vp; + if (!g) continue; + gobj_getrect(g, x, &x1, &y1, &x2, &y2); + if (x1 > xmax) xmax = x1, maxp = vp; + } + if (!maxp) break; + y = *maxp; + *maxp = 0; + ip = vinlet_getit(&y->g_pd); + + obj_moveinletfirst(&x->gl_obj, ip); + } + freebytes(vec, ninlets * sizeof(*vec)); + if (x->gl_owner && glist_isvisible(x->gl_owner)) + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); +} + +t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *s) +{ + t_outlet *op = outlet_new(&x->gl_obj, s); + if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner)) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } + if (!x->gl_loading) canvas_resortoutlets(x); + return (op); +} + +void canvas_rmoutlet(t_canvas *x, t_outlet *op) +{ + t_canvas *owner = x->gl_owner; + int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting) + && glist_istoplevel(owner)); + + if (owner) canvas_deletelinesforio(owner, &x->gl_obj, 0, op); + if (redraw) + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + + outlet_free(op); + if (redraw) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } +} + +extern t_outlet *voutlet_getit(t_pd *x); +extern void obj_moveoutletfirst(t_object *x, t_outlet *i); + +void canvas_resortoutlets(t_canvas *x) +{ + int noutlets = 0, i, j, xmax; + t_gobj *y, **vec, **vp, **maxp; + + for (noutlets = 0, y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == voutlet_class) noutlets++; + + if (noutlets < 2) return; + + vec = (t_gobj **)getbytes(noutlets * sizeof(*vec)); + + for (y = x->gl_list, vp = vec; y; y = y->g_next) + if (pd_class(&y->g_pd) == voutlet_class) *vp++ = y; + + for (i = noutlets; i--;) + { + t_outlet *ip; + for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = noutlets; + j--; vp++) + { + int x1, y1, x2, y2; + t_gobj *g = *vp; + if (!g) continue; + gobj_getrect(g, x, &x1, &y1, &x2, &y2); + if (x1 > xmax) xmax = x1, maxp = vp; + } + if (!maxp) break; + y = *maxp; + *maxp = 0; + ip = voutlet_getit(&y->g_pd); + + obj_moveoutletfirst(&x->gl_obj, ip); + } + freebytes(vec, noutlets * sizeof(*vec)); + if (x->gl_owner && glist_isvisible(x->gl_owner)) + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); +} + +/* ----------calculating coordinates and controlling appearance --------- */ + + +static void graph_bounds(t_glist *x, t_floatarg x1, t_floatarg y1, + t_floatarg x2, t_floatarg y2) +{ + x->gl_x1 = x1; + x->gl_x2 = x2; + x->gl_y1 = y1; + x->gl_y2 = y2; + if (x->gl_x2 == x->gl_x1 || + x->gl_y2 == x->gl_y1) + { + error("graph: empty bounds rectangle"); + x1 = y1 = 0; + x2 = y2 = 1; + } + glist_redraw(x); +} + +static void graph_xticks(t_glist *x, + t_floatarg point, t_floatarg inc, t_floatarg f) +{ + x->gl_xtick.k_point = point; + x->gl_xtick.k_inc = inc; + x->gl_xtick.k_lperb = f; + glist_redraw(x); +} + +static void graph_yticks(t_glist *x, + t_floatarg point, t_floatarg inc, t_floatarg f) +{ + x->gl_ytick.k_point = point; + x->gl_ytick.k_inc = inc; + x->gl_ytick.k_lperb = f; + glist_redraw(x); +} + +static void graph_xlabel(t_glist *x, t_symbol *s, int argc, t_atom *argv) +{ + int i; + if (argc < 1) error("graph_xlabel: no y value given"); + else + { + x->gl_xlabely = atom_getfloat(argv); + argv++; argc--; + x->gl_xlabel = (t_symbol **)t_resizebytes(x->gl_xlabel, + x->gl_nxlabels * sizeof (t_symbol *), argc * sizeof (t_symbol *)); + x->gl_nxlabels = argc; + for (i = 0; i < argc; i++) x->gl_xlabel[i] = atom_gensym(&argv[i]); + } + glist_redraw(x); +} + +static void graph_ylabel(t_glist *x, t_symbol *s, int argc, t_atom *argv) +{ + int i; + if (argc < 1) error("graph_ylabel: no x value given"); + else + { + x->gl_ylabelx = atom_getfloat(argv); + argv++; argc--; + x->gl_ylabel = (t_symbol **)t_resizebytes(x->gl_ylabel, + x->gl_nylabels * sizeof (t_symbol *), argc * sizeof (t_symbol *)); + x->gl_nylabels = argc; + for (i = 0; i < argc; i++) x->gl_ylabel[i] = atom_gensym(&argv[i]); + } + glist_redraw(x); +} + +/****** routines to convert pixels to X or Y value and vice versa ******/ + + /* convert an x pixel value to an x coordinate value */ +float glist_pixelstox(t_glist *x, float xpix) +{ + /* if we appear as a text box on parent, our range in our + coordinates (x1, etc.) specifies the coordinate range + of a one-pixel square at top left of the window. */ + if (!x->gl_isgraph) + return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * xpix); + + /* if we're a graph when shown on parent, but own our own + window right now, our range in our coordinates (x1, etc.) is spread + over the visible window size, given by screenx1, etc. */ + else if (x->gl_isgraph && x->gl_havewindow) + return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * + (xpix) / (x->gl_screenx2 - x->gl_screenx1)); + + /* otherwise, we appear in a graph within a parent glist, + so get our screen rectangle on parent and transform. */ + else + { + int x1, y1, x2, y2; + if (!x->gl_owner) + bug("glist_pixelstox"); + graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); + return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * + (xpix - x1) / (x2 - x1)); + } +} + +float glist_pixelstoy(t_glist *x, float ypix) +{ + if (!x->gl_isgraph) + return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * ypix); + else if (x->gl_isgraph && x->gl_havewindow) + return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * + (ypix) / (x->gl_screeny2 - x->gl_screeny1)); + else + { + int x1, y1, x2, y2; + if (!x->gl_owner) + bug("glist_pixelstox"); + graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); + return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * + (ypix - y1) / (y2 - y1)); + } +} + + /* convert an x coordinate value to an x pixel location in window */ +float glist_xtopixels(t_glist *x, float xval) +{ + if (!x->gl_isgraph) + return ((xval - x->gl_x1) / (x->gl_x2 - x->gl_x1)); + else if (x->gl_isgraph && x->gl_havewindow) + return (x->gl_screenx2 - x->gl_screenx1) * + (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1); + else + { + int x1, y1, x2, y2; + if (!x->gl_owner) + bug("glist_pixelstox"); + graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); + return (x1 + (x2 - x1) * (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1)); + } +} + +float glist_ytopixels(t_glist *x, float yval) +{ + if (!x->gl_isgraph) + return ((yval - x->gl_y1) / (x->gl_y2 - x->gl_y1)); + else if (x->gl_isgraph && x->gl_havewindow) + return (x->gl_screeny2 - x->gl_screeny1) * + (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1); + else + { + int x1, y1, x2, y2; + if (!x->gl_owner) + bug("glist_pixelstox"); + graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); + return (y1 + (y2 - y1) * (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1)); + } +} + + /* convert an X screen distance to an X coordinate increment. + This is terribly inefficient; + but probably not a big enough CPU hog to warrant optimizing. */ +float glist_dpixtodx(t_glist *x, float dxpix) +{ + return (dxpix * (glist_pixelstox(x, 1) - glist_pixelstox(x, 0))); +} + +float glist_dpixtody(t_glist *x, float dypix) +{ + return (dypix * (glist_pixelstoy(x, 1) - glist_pixelstoy(x, 0))); +} + + /* get the window location in pixels of a "text" object. The + object's x and y positions are in pixels when the glist they're + in is toplevel. If it's not, we convert to pixels on the parent + window. */ +int text_xpix(t_text *x, t_glist *glist) +{ + if (glist->gl_havewindow || !glist->gl_isgraph) + return (x->te_xpix); + else return (glist_xtopixels(glist, + glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) * + x->te_xpix / (glist->gl_screenx2 - glist->gl_screenx1))); +} + +int text_ypix(t_text *x, t_glist *glist) +{ + if (glist->gl_havewindow || !glist->gl_isgraph) + return (x->te_ypix); + else return (glist_ytopixels(glist, + glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) * + x->te_ypix / (glist->gl_screeny2 - glist->gl_screeny1))); +} + + /* redraw all the items in a glist. We construe this to mean + redrawing in its own window and on parent, as needed in each case. + This is too conservative -- for instance, when you draw an "open" + rectangle on the parent, you shouldn't have to redraw the window! */ +void glist_redraw(t_glist *x) +{ + if (glist_isvisible(x)) + { + /* LATER fix the graph_vis() code to handle both cases */ + if (glist_istoplevel(x)) + { + t_gobj *g; + t_linetraverser t; + t_outconnect *oc; + for (g = x->gl_list; g; g = g->g_next) + { + gobj_vis(g, x, 0); + gobj_vis(g, x, 1); + } + /* redraw all the lines */ + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + sys_vgui(".x%x.c coords l%x %d %d %d %d\n", + glist_getcanvas(x), oc, + t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2); + } + if (x->gl_owner) + { + graph_vis(&x->gl_gobj, x->gl_owner, 0); + graph_vis(&x->gl_gobj, x->gl_owner, 1); + } + } +} + +/* --------------------------- widget behavior ------------------- */ + +extern t_widgetbehavior text_widgetbehavior; + + /* Note that some code in here would also be useful for drawing + graph decorations in toplevels... */ +static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis) +{ + t_glist *x = (t_glist *)gr; + char tag[50]; + t_gobj *g; + int x1, y1, x2, y2; + /* ordinary subpatches: just act like a text object */ + if (!x->gl_isgraph) + { + text_widgetbehavior.w_visfn(gr, parent_glist, vis); + return; + } + + if (vis && canvas_showtext(x)) + rtext_draw(glist_findrtext(parent_glist, &x->gl_obj)); + graph_getrect(gr, parent_glist, &x1, &y1, &x2, &y2); + if (!vis) + rtext_erase(glist_findrtext(parent_glist, &x->gl_obj)); + + sprintf(tag, "graph%x", (int)x); + if (vis) + glist_drawiofor(parent_glist, &x->gl_obj, 1, + tag, x1, y1, x2, y2); + else glist_eraseiofor(parent_glist, &x->gl_obj, tag); + /* if we look like a graph but have been moved to a toplevel, + just show the bounding rectangle */ + if (x->gl_havewindow) + { + if (vis) + { + sys_vgui(".x%x.c create polygon\ + %d %d %d %d %d %d %d %d %d %d -tags %s -fill #c0c0c0\n", + glist_getcanvas(x->gl_owner), + x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag); + } + else + { + sys_vgui(".x%x.c delete %s\n", + glist_getcanvas(x->gl_owner), tag); + } + return; + } + /* otherwise draw (or erase) us as a graph inside another glist. */ + if (vis) + { + int i; + float f; + + /* draw a rectangle around the graph */ + sys_vgui(".x%x.c create line\ + %d %d %d %d %d %d %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag); + + /* draw ticks on horizontal borders. If lperb field is + zero, this is disabled. */ + if (x->gl_xtick.k_lperb) + { + float upix, lpix; + if (y2 < y1) + upix = y1, lpix = y2; + else upix = y2, lpix = y1; + for (i = 0, f = x->gl_xtick.k_point; + f < 0.99 * x->gl_x2 + 0.01*x->gl_x1; i++, + f += x->gl_xtick.k_inc) + { + int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + (int)glist_xtopixels(x, f), (int)upix, + (int)glist_xtopixels(x, f), (int)upix - tickpix, tag); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + (int)glist_xtopixels(x, f), (int)lpix, + (int)glist_xtopixels(x, f), (int)lpix + tickpix, tag); + } + for (i = 1, f = x->gl_xtick.k_point - x->gl_xtick.k_inc; + f > 0.99 * x->gl_x1 + 0.01*x->gl_x2; + i++, f -= x->gl_xtick.k_inc) + { + int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + (int)glist_xtopixels(x, f), (int)upix, + (int)glist_xtopixels(x, f), (int)upix - tickpix, tag); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + (int)glist_xtopixels(x, f), (int)lpix, + (int)glist_xtopixels(x, f), (int)lpix + tickpix, tag); + } + } + + /* draw ticks in vertical borders*/ + if (x->gl_ytick.k_lperb) + { + float ubound, lbound; + if (x->gl_y2 < x->gl_y1) + ubound = x->gl_y1, lbound = x->gl_y2; + else ubound = x->gl_y2, lbound = x->gl_y1; + for (i = 0, f = x->gl_ytick.k_point; + f < 0.99 * ubound + 0.01 * lbound; + i++, f += x->gl_ytick.k_inc) + { + int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x1, (int)glist_ytopixels(x, f), + x1 + tickpix, (int)glist_ytopixels(x, f), tag); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x2, (int)glist_ytopixels(x, f), + x2 - tickpix, (int)glist_ytopixels(x, f), tag); + } + for (i = 1, f = x->gl_ytick.k_point - x->gl_ytick.k_inc; + f > 0.99 * lbound + 0.01 * ubound; + i++, f -= x->gl_ytick.k_inc) + { + int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x1, (int)glist_ytopixels(x, f), + x1 + tickpix, (int)glist_ytopixels(x, f), tag); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x2, (int)glist_ytopixels(x, f), + x2 - tickpix, (int)glist_ytopixels(x, f), tag); + } + } + /* draw x labels */ + for (i = 0; i < x->gl_nxlabels; i++) + sys_vgui(".x%x.c create text\ + %d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n", + glist_getcanvas(x), + (int)glist_xtopixels(x, atof(x->gl_xlabel[i]->s_name)), + (int)glist_ytopixels(x, x->gl_xlabely), x->gl_xlabel[i]->s_name, + glist_getfont(x), tag); + + /* draw y labels */ + for (i = 0; i < x->gl_nylabels; i++) + sys_vgui(".x%x.c create text\ + %d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n", + glist_getcanvas(x), + (int)glist_xtopixels(x, x->gl_ylabelx), + (int)glist_ytopixels(x, atof(x->gl_ylabel[i]->s_name)), + x->gl_ylabel[i]->s_name, + glist_getfont(x), tag); + + /* draw contents of graph as glist */ + for (g = x->gl_list; g; g = g->g_next) + gobj_vis(g, x, 1); + } + else + { + sys_vgui(".x%x.c delete %s\n", + glist_getcanvas(x->gl_owner), tag); + for (g = x->gl_list; g; g = g->g_next) + gobj_vis(g, x, 0); + } +} + + /* get the graph's rectangle, not counting extra swelling for controls + to keep them inside the graph. This is the "logical" pixel size. */ + +static void graph_graphrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_glist *x = (t_glist *)z; + int x1 = text_xpix(&x->gl_obj, glist); + int y1 = text_ypix(&x->gl_obj, glist); + int x2, y2; +#if 0 /* this used to adjust graph size when it was in another graph; + now we just preserve the size. */ + /* same logic here as in text_xpix(): */ + if (glist->gl_havewindow) + { + x2 = x1 + x->gl_pixwidth; + y2 = y1 + x->gl_pixheight; + } + else + { + x2 = glist_xtopixels(glist, + glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) * + (x->gl_obj.te_xpix + x->gl_pixwidth) / + (glist->gl_screenx2 - glist->gl_screenx1)); + y2 = glist_ytopixels(glist, + glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) * + (x->gl_obj.te_ypix + x->gl_pixheight) / + (glist->gl_screeny2 - glist->gl_screeny1)); + } +#endif + x2 = x1 + x->gl_pixwidth; + y2 = y1 + x->gl_pixheight; + + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + + /* get the rectangle, enlarged to contain all the "contents" -- + meaning their formal bounds rectangles. */ +static void graph_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + int x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff; + t_glist *x = (t_glist *)z; + if (x->gl_isgraph) + { + int hadwindow; + t_gobj *g; + t_text *ob; + int x21, y21, x22, y22; + + graph_graphrect(z, glist, &x1, &y1, &x2, &y2); + if (canvas_showtext(x)) + { + text_widgetbehavior.w_getrectfn(z, glist, &x21, &y21, &x22, &y22); + if (x22 > x2) + x2 = x22; + if (y22 > y2) + y2 = y22; + } + /* lie about whether we have our own window to affect gobj_getrect + calls below. (LATER add argument to gobj_getrect()?) */ + hadwindow = x->gl_havewindow; + x->gl_havewindow = 0; + for (g = x->gl_list; g; g = g->g_next) + if ((!(ob = pd_checkobject(&g->g_pd))) || text_shouldvis(ob, x)) + { + /* don't do this for arrays, just let them hang outsize the + box. */ + if (pd_class(&g->g_pd) == garray_class) + continue; + gobj_getrect(g, x, &x21, &y21, &x22, &y22); + if (x22 > x2) + x2 = x22; + if (y22 > y2) + y2 = y22; + } + x->gl_havewindow = hadwindow; + } + else text_widgetbehavior.w_getrectfn(z, glist, &x1, &y1, &x2, &y2); + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void graph_displace(t_gobj *z, t_glist *glist, int dx, int dy) +{ + t_glist *x = (t_glist *)z; + if (!x->gl_isgraph) + text_widgetbehavior.w_displacefn(z, glist, dx, dy); + else + { + x->gl_obj.te_xpix += dx; + x->gl_obj.te_ypix += dy; + glist_redraw(x); + canvas_fixlinesfor(glist_getcanvas(glist), &x->gl_obj); + } +} + +static void graph_select(t_gobj *z, t_glist *glist, int state) +{ + t_glist *x = (t_glist *)z; + if (!x->gl_isgraph) + text_widgetbehavior.w_selectfn(z, glist, state); + else + { + t_rtext *y = glist_findrtext(glist, &x->gl_obj); + if (canvas_showtext(x)) + rtext_select(y, state); + sys_vgui(".x%x.c itemconfigure %sR -fill %s\n", glist, + rtext_gettag(y), (state? "blue" : "black")); + sys_vgui(".x%x.c itemconfigure graph%x -fill %s\n", + glist_getcanvas(glist), z, (state? "blue" : "black")); + } +} + +static void graph_activate(t_gobj *z, t_glist *glist, int state) +{ + t_glist *x = (t_glist *)z; + if (canvas_showtext(x)) + text_widgetbehavior.w_activatefn(z, glist, state); +} + +#if 0 +static void graph_delete(t_gobj *z, t_glist *glist) +{ + t_glist *x = (t_glist *)z; + if (!x->gl_isgraph) + text_widgetbehavior.w_deletefn(z, glist); + else + { + t_gobj *y; + while (y = x->gl_list) glist_delete(x, y); +#if 0 /* I think this was just wrong. */ + if (glist_isvisible(x)) + sys_vgui(".x%x.c delete graph%x\n", glist_getcanvas(glist), x); +#endif + } +} +#endif + +static void graph_delete(t_gobj *z, t_glist *glist) +{ + t_glist *x = (t_glist *)z; + t_gobj *y; + text_widgetbehavior.w_deletefn(z, glist); + while (y = x->gl_list) + glist_delete(x, y); +} + +static float graph_lastxpix, graph_lastypix; + +static void graph_motion(void *z, t_floatarg dx, t_floatarg dy) +{ + t_glist *x = (t_glist *)z; + float newxpix = graph_lastxpix + dx, newypix = graph_lastypix + dy; + t_garray *a = (t_garray *)(x->gl_list); + int oldx = 0.5 + glist_pixelstox(x, graph_lastxpix); + int newx = 0.5 + glist_pixelstox(x, newxpix); + t_sample *vec; + int nelem, i; + float oldy = glist_pixelstoy(x, graph_lastypix); + float newy = glist_pixelstoy(x, newypix); + graph_lastxpix = newxpix; + graph_lastypix = newypix; + /* verify that the array is OK */ + if (!a || pd_class((t_pd *)a) != garray_class) + return; + if (!garray_getfloatarray(a, &nelem, &vec)) + return; + if (oldx < 0) oldx = 0; + if (oldx >= nelem) + oldx = nelem - 1; + if (newx < 0) newx = 0; + if (newx >= nelem) + newx = nelem - 1; + if (oldx < newx - 1) + { + for (i = oldx + 1; i <= newx; i++) + vec[i] = newy + (oldy - newy) * + ((float)(newx - i))/(float)(newx - oldx); + } + else if (oldx > newx + 1) + { + for (i = oldx - 1; i >= newx; i--) + vec[i] = newy + (oldy - newy) * + ((float)(newx - i))/(float)(newx - oldx); + } + else vec[newx] = newy; + garray_redraw(a); +} + +static int graph_click(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_glist *x = (t_glist *)z; + t_gobj *y; + int clickreturned = 0; + if (!x->gl_isgraph) + return (text_widgetbehavior.w_clickfn(z, glist, + xpix, ypix, shift, alt, dbl, doit)); + else if (x->gl_havewindow) + return (0); + else + { + for (y = x->gl_list; y; y = y->g_next) + { + int x1, y1, x2, y2; + /* check if the object wants to be clicked */ + if (canvas_hitbox(x, y, xpix, ypix, &x1, &y1, &x2, &y2) + && (clickreturned = gobj_click(y, x, xpix, ypix, + shift, alt, 0, doit))) + break; + } + if (!doit) + { + if (y) + canvas_setcursor(glist_getcanvas(x), clickreturned); + else canvas_setcursor(glist_getcanvas(x), CURSOR_RUNMODE_NOTHING); + } + return (clickreturned); + } +} + +void garray_properties(t_garray *x); + +t_widgetbehavior graph_widgetbehavior = +{ + graph_getrect, + graph_displace, + graph_select, + graph_activate, + graph_delete, + graph_vis, + graph_click, +}; + +void graph_properties(t_gobj *z, t_glist *owner) +{ + t_glist *x = (t_glist *)z; + { + t_gobj *y; + char graphbuf[200]; + sprintf(graphbuf, "pdtk_graph_dialog %%s %g %g %g %g %d %d\n", + x->gl_x1, x->gl_y1, x->gl_x2, x->gl_y2, + x->gl_pixwidth, x->gl_pixheight); + gfxstub_new(&x->gl_pd, x, graphbuf); + + for (y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == garray_class) + garray_properties((t_garray *)y); + } +} + + /* find the graph most recently added to this glist; + if none exists, return 0. */ + +t_glist *glist_findgraph(t_glist *x) +{ + t_gobj *y = 0, *z; + for (z = x->gl_list; z; z = z->g_next) + if (pd_class(&z->g_pd) == canvas_class && ((t_glist *)z)->gl_isgraph) + y = z; + return ((t_glist *)y); +} + + /* message back from dialog GUI to set parameters. Args are: + 1-4: bounds in our coordinates; 5-6: size in parent */ +static void graph_dialog(t_glist *x, t_symbol *s, int argc, t_atom *argv) +{ + t_float x1 = atom_getfloatarg(0, argc, argv); + t_float y1 = atom_getfloatarg(1, argc, argv); + t_float x2 = atom_getfloatarg(2, argc, argv); + t_float y2 = atom_getfloatarg(3, argc, argv); + t_float xpix = atom_getfloatarg(4, argc, argv); + t_float ypix = atom_getfloatarg(5, argc, argv); + if (x1 != x->gl_x1 || x2 != x->gl_x2 || + y1 != x->gl_y1 || y2 != x->gl_y2) + graph_bounds(x, x1, y1, x2, y2); + if (xpix != x->gl_pixwidth || ypix != x->gl_pixheight) + { + x->gl_pixwidth = xpix; + x->gl_pixheight = ypix; + glist_redraw(x); + if (x->gl_owner) + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } +} + +extern void canvas_menuarray(t_glist *canvas); + +void g_graph_setup(void) +{ + class_setwidget(canvas_class, &graph_widgetbehavior); + class_addmethod(canvas_class, (t_method)graph_bounds, gensym("bounds"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(canvas_class, (t_method)graph_xticks, gensym("xticks"), + A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(canvas_class, (t_method)graph_xlabel, gensym("xlabel"), + A_GIMME, 0); + class_addmethod(canvas_class, (t_method)graph_yticks, gensym("yticks"), + A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(canvas_class, (t_method)graph_ylabel, gensym("ylabel"), + A_GIMME, 0); + class_addmethod(canvas_class, (t_method)graph_array, gensym("array"), + A_SYMBOL, A_FLOAT, A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_menuarray, + gensym("menuarray"), A_NULL); + class_addmethod(canvas_class, (t_method)graph_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(canvas_class, (t_method)glist_arraydialog, + gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)glist_sort, + gensym("sort"), A_NULL); +} +/* Copyright (c) 1997-2001 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* This file deals with the behavior of glists as either "text objects" or +"graphs" inside another glist. LATER move the inlet/outlet code of g_canvas.c +to this file... */ + +#include +#include "m_pd.h" +#include "t_tk.h" +#include "g_canvas.h" +#include +#include + +/* ---------------------- forward definitions ----------------- */ + +static void graph_vis(t_gobj *gr, t_glist *unused_glist, int vis); +static void graph_graphrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2); +static void graph_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2); + +/* -------------------- maintaining the list -------------------- */ + +void glist_add(t_glist *x, t_gobj *y) +{ + t_object *ob; + y->g_next = 0; + if (!x->gl_list) x->gl_list = y; + else + { + t_gobj *y2; + for (y2 = x->gl_list; y2->g_next; y2 = y2->g_next); + y2->g_next = y; + } + if (x->gl_editor && (ob = pd_checkobject(&y->g_pd))) + rtext_new(x, ob); + if (glist_isvisible(x)) + gobj_vis(y, x, 1); + if (class_isdrawcommand(y->g_pd)) + canvas_redrawallfortemplate(glist_getcanvas(x)); +} + + /* this is to protect against a hairy problem in which deleting + a sub-canvas might delete an inlet on a box, after the box had + been invisible-ized, so that we have to protect against redrawing it! */ +int canvas_setdeleting(t_canvas *x, int flag) +{ + int ret = x->gl_isdeleting; + x->gl_isdeleting = flag; + return (ret); +} + + /* delete an object from a glist and free it */ +void glist_delete(t_glist *x, t_gobj *y) +{ + t_gobj *g; + t_object *ob; + t_gotfn chkdsp = zgetfn(&y->g_pd, gensym("dsp")); + t_canvas *canvas = glist_getcanvas(x); + int drawcommand = class_isdrawcommand(y->g_pd); + int wasdeleting; + + wasdeleting = canvas_setdeleting(canvas, 1); + if (x->gl_editor) + { + if (x->gl_editor->e_grab == y) x->gl_editor->e_grab = 0; + if (glist_isselected(x, y)) glist_deselect(x, y); + + /* HACK -- we had phantom outlets not getting erased on the + screen because the canvas_setdeleting() mechanism is too + crude. LATER carefully set up rules for when the rtexts + should exist, so that they stay around until all the + steps of becoming invisible are done. In the meantime, just + zap the inlets and outlets here... */ + if (pd_class(&y->g_pd) == canvas_class) + { + t_glist *gl = (t_glist *)y; + if (gl->gl_isgraph) + { + char tag[80]; + sprintf(tag, "graph%x", (int)gl); + glist_eraseiofor(x, &gl->gl_obj, tag); + } + else + { + text_eraseborder(&gl->gl_obj, x, + rtext_gettag(glist_findrtext(x, &gl->gl_obj))); + } + } + } + gobj_delete(y, x); + if (glist_isvisible(canvas)) + gobj_vis(y, x, 0); + if (x->gl_editor && (ob = pd_checkobject(&y->g_pd))) + rtext_new(x, ob); + if (x->gl_list == y) x->gl_list = y->g_next; + else for (g = x->gl_list; g; g = g->g_next) + if (g->g_next == y) + { + g->g_next = y->g_next; + break; + } + pd_free(&y->g_pd); + if (chkdsp) canvas_update_dsp(); + if (drawcommand) canvas_redrawallfortemplate(canvas); + canvas_setdeleting(canvas, wasdeleting); + x->gl_valid = ++glist_valid; +} + + /* remove every object from a glist. Experimental. */ +void glist_clear(t_glist *x) +{ + t_gobj *y, *y2; + int dspstate = canvas_suspend_dsp(); + while (y = x->gl_list) + glist_delete(x, y); + canvas_resume_dsp(dspstate); +} + +void glist_retext(t_glist *glist, t_text *y) +{ + t_canvas *c = glist_getcanvas(glist); + /* check that we have built rtexts yet. LATER need a better test. */ + if (glist->gl_editor && glist->gl_editor->e_rtext) + { + t_rtext *rt = glist_findrtext(glist, y); + if (rt) + rtext_retext(rt); + } +} + +void glist_grab(t_glist *x, t_gobj *y, t_glistmotionfn motionfn, + t_glistkeyfn keyfn, int xpos, int ypos) +{ + t_glist *x2 = glist_getcanvas(x); + if (motionfn) + x2->gl_editor->e_onmotion = MA_PASSOUT; + else x2->gl_editor->e_onmotion = 0; + x2->gl_editor->e_grab = y; + x2->gl_editor->e_motionfn = motionfn; + x2->gl_editor->e_keyfn = keyfn; + x2->gl_editor->e_xwas = xpos; + x2->gl_editor->e_ywas = ypos; +} + +t_canvas *glist_getcanvas(t_glist *x) +{ + while (x->gl_owner && !x->gl_havewindow && x->gl_isgraph) + x = x->gl_owner; + return((t_canvas *)x); +} + +static float gobj_getxforsort(t_gobj *g) +{ + if (pd_class(&g->g_pd) == scalar_class) + { + float x1, y1; + scalar_getbasexy((t_scalar *)g, &x1, &y1); + return(x1); + } + else return (0); +} + +static t_gobj *glist_merge(t_glist *x, t_gobj *g1, t_gobj *g2) +{ + t_gobj *g = 0, *g9 = 0; + float f1 = 0, f2 = 0; + if (g1) + f1 = gobj_getxforsort(g1); + if (g2) + f2 = gobj_getxforsort(g2); + while (1) + { + if (g1) + { + if (g2) + { + if (f1 <= f2) + goto put1; + else goto put2; + } + else goto put1; + } + else if (g2) + goto put2; + else break; + put1: + if (g9) + g9->g_next = g1, g9 = g1; + else g9 = g = g1; + if (g1 = g1->g_next) + f1 = gobj_getxforsort(g1); + g9->g_next = 0; + continue; + put2: + if (g9) + g9->g_next = g2, g9 = g2; + else g9 = g = g2; + if (g2 = g2->g_next) + f2 = gobj_getxforsort(g2); + g9->g_next = 0; + continue; + } + return (g); +} + +static t_gobj *glist_dosort(t_glist *x, + t_gobj *g, int nitems) +{ + if (nitems < 2) + return (g); + else + { + int n1 = nitems/2, n2 = nitems - n1, i; + t_gobj *g2, *g3; + for (g2 = g, i = n1-1; i--; g2 = g2->g_next) + ; + g3 = g2->g_next; + g2->g_next = 0; + g = glist_dosort(x, g, n1); + g3 = glist_dosort(x, g3, n2); + return (glist_merge(x, g, g3)); + } +} + +void glist_sort(t_glist *x) +{ + int nitems = 0, foo = 0; + float lastx = -1e37; + t_gobj *g; + for (g = x->gl_list; g; g = g->g_next) + { + float x1 = gobj_getxforsort(g); + if (x1 < lastx) + foo = 1; + lastx = x1; + nitems++; + } + if (foo) + x->gl_list = glist_dosort(x, x->gl_list, nitems); +} + +void glist_cleanup(t_glist *x) +{ + freebytes(x->gl_xlabel, x->gl_nxlabels * sizeof(*(x->gl_xlabel))); + freebytes(x->gl_ylabel, x->gl_nylabels * sizeof(*(x->gl_ylabel))); + gstub_cutoff(x->gl_stub); +} + +void glist_free(t_glist *x) +{ + glist_cleanup(x); + freebytes(x, sizeof(*x)); +} + +/* --------------- inlets and outlets ----------- */ + + +t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *s) +{ + t_inlet *ip = inlet_new(&x->gl_obj, who, s, 0); + if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner)) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } + if (!x->gl_loading) canvas_resortinlets(x); + return (ip); +} + +void canvas_rminlet(t_canvas *x, t_inlet *ip) +{ + t_canvas *owner = x->gl_owner; + int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting) + && glist_istoplevel(owner)); + + if (owner) canvas_deletelinesforio(owner, &x->gl_obj, ip, 0); + if (redraw) + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + inlet_free(ip); + if (redraw) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } +} + +extern t_inlet *vinlet_getit(t_pd *x); +extern void obj_moveinletfirst(t_object *x, t_inlet *i); + +void canvas_resortinlets(t_canvas *x) +{ + int ninlets = 0, i, j, xmax; + t_gobj *y, **vec, **vp, **maxp; + + for (ninlets = 0, y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == vinlet_class) ninlets++; + + if (ninlets < 2) return; + + vec = (t_gobj **)getbytes(ninlets * sizeof(*vec)); + + for (y = x->gl_list, vp = vec; y; y = y->g_next) + if (pd_class(&y->g_pd) == vinlet_class) *vp++ = y; + + for (i = ninlets; i--;) + { + t_inlet *ip; + for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = ninlets; + j--; vp++) + { + int x1, y1, x2, y2; + t_gobj *g = *vp; + if (!g) continue; + gobj_getrect(g, x, &x1, &y1, &x2, &y2); + if (x1 > xmax) xmax = x1, maxp = vp; + } + if (!maxp) break; + y = *maxp; + *maxp = 0; + ip = vinlet_getit(&y->g_pd); + + obj_moveinletfirst(&x->gl_obj, ip); + } + freebytes(vec, ninlets * sizeof(*vec)); + if (x->gl_owner && glist_isvisible(x->gl_owner)) + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); +} + +t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *s) +{ + t_outlet *op = outlet_new(&x->gl_obj, s); + if (!x->gl_loading && x->gl_owner && glist_isvisible(x->gl_owner)) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } + if (!x->gl_loading) canvas_resortoutlets(x); + return (op); +} + +void canvas_rmoutlet(t_canvas *x, t_outlet *op) +{ + t_canvas *owner = x->gl_owner; + int redraw = (owner && glist_isvisible(owner) && (!owner->gl_isdeleting) + && glist_istoplevel(owner)); + + if (owner) canvas_deletelinesforio(owner, &x->gl_obj, 0, op); + if (redraw) + gobj_vis(&x->gl_gobj, x->gl_owner, 0); + + outlet_free(op); + if (redraw) + { + gobj_vis(&x->gl_gobj, x->gl_owner, 1); + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } +} + +extern t_outlet *voutlet_getit(t_pd *x); +extern void obj_moveoutletfirst(t_object *x, t_outlet *i); + +void canvas_resortoutlets(t_canvas *x) +{ + int noutlets = 0, i, j, xmax; + t_gobj *y, **vec, **vp, **maxp; + + for (noutlets = 0, y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == voutlet_class) noutlets++; + + if (noutlets < 2) return; + + vec = (t_gobj **)getbytes(noutlets * sizeof(*vec)); + + for (y = x->gl_list, vp = vec; y; y = y->g_next) + if (pd_class(&y->g_pd) == voutlet_class) *vp++ = y; + + for (i = noutlets; i--;) + { + t_outlet *ip; + for (vp = vec, xmax = -0x7fffffff, maxp = 0, j = noutlets; + j--; vp++) + { + int x1, y1, x2, y2; + t_gobj *g = *vp; + if (!g) continue; + gobj_getrect(g, x, &x1, &y1, &x2, &y2); + if (x1 > xmax) xmax = x1, maxp = vp; + } + if (!maxp) break; + y = *maxp; + *maxp = 0; + ip = voutlet_getit(&y->g_pd); + + obj_moveoutletfirst(&x->gl_obj, ip); + } + freebytes(vec, noutlets * sizeof(*vec)); + if (x->gl_owner && glist_isvisible(x->gl_owner)) + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); +} + +/* ----------calculating coordinates and controlling appearance --------- */ + + +static void graph_bounds(t_glist *x, t_floatarg x1, t_floatarg y1, + t_floatarg x2, t_floatarg y2) +{ + x->gl_x1 = x1; + x->gl_x2 = x2; + x->gl_y1 = y1; + x->gl_y2 = y2; + if (x->gl_x2 == x->gl_x1 || + x->gl_y2 == x->gl_y1) + { + error("graph: empty bounds rectangle"); + x1 = y1 = 0; + x2 = y2 = 1; + } + glist_redraw(x); +} + +static void graph_xticks(t_glist *x, + t_floatarg point, t_floatarg inc, t_floatarg f) +{ + x->gl_xtick.k_point = point; + x->gl_xtick.k_inc = inc; + x->gl_xtick.k_lperb = f; + glist_redraw(x); +} + +static void graph_yticks(t_glist *x, + t_floatarg point, t_floatarg inc, t_floatarg f) +{ + x->gl_ytick.k_point = point; + x->gl_ytick.k_inc = inc; + x->gl_ytick.k_lperb = f; + glist_redraw(x); +} + +static void graph_xlabel(t_glist *x, t_symbol *s, int argc, t_atom *argv) +{ + int i; + if (argc < 1) error("graph_xlabel: no y value given"); + else + { + x->gl_xlabely = atom_getfloat(argv); + argv++; argc--; + x->gl_xlabel = (t_symbol **)t_resizebytes(x->gl_xlabel, + x->gl_nxlabels * sizeof (t_symbol *), argc * sizeof (t_symbol *)); + x->gl_nxlabels = argc; + for (i = 0; i < argc; i++) x->gl_xlabel[i] = atom_gensym(&argv[i]); + } + glist_redraw(x); +} + +static void graph_ylabel(t_glist *x, t_symbol *s, int argc, t_atom *argv) +{ + int i; + if (argc < 1) error("graph_ylabel: no x value given"); + else + { + x->gl_ylabelx = atom_getfloat(argv); + argv++; argc--; + x->gl_ylabel = (t_symbol **)t_resizebytes(x->gl_ylabel, + x->gl_nylabels * sizeof (t_symbol *), argc * sizeof (t_symbol *)); + x->gl_nylabels = argc; + for (i = 0; i < argc; i++) x->gl_ylabel[i] = atom_gensym(&argv[i]); + } + glist_redraw(x); +} + +/****** routines to convert pixels to X or Y value and vice versa ******/ + + /* convert an x pixel value to an x coordinate value */ +float glist_pixelstox(t_glist *x, float xpix) +{ + /* if we appear as a text box on parent, our range in our + coordinates (x1, etc.) specifies the coordinate range + of a one-pixel square at top left of the window. */ + if (!x->gl_isgraph) + return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * xpix); + + /* if we're a graph when shown on parent, but own our own + window right now, our range in our coordinates (x1, etc.) is spread + over the visible window size, given by screenx1, etc. */ + else if (x->gl_isgraph && x->gl_havewindow) + return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * + (xpix) / (x->gl_screenx2 - x->gl_screenx1)); + + /* otherwise, we appear in a graph within a parent glist, + so get our screen rectangle on parent and transform. */ + else + { + int x1, y1, x2, y2; + if (!x->gl_owner) + bug("glist_pixelstox"); + graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); + return (x->gl_x1 + (x->gl_x2 - x->gl_x1) * + (xpix - x1) / (x2 - x1)); + } +} + +float glist_pixelstoy(t_glist *x, float ypix) +{ + if (!x->gl_isgraph) + return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * ypix); + else if (x->gl_isgraph && x->gl_havewindow) + return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * + (ypix) / (x->gl_screeny2 - x->gl_screeny1)); + else + { + int x1, y1, x2, y2; + if (!x->gl_owner) + bug("glist_pixelstox"); + graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); + return (x->gl_y1 + (x->gl_y2 - x->gl_y1) * + (ypix - y1) / (y2 - y1)); + } +} + + /* convert an x coordinate value to an x pixel location in window */ +float glist_xtopixels(t_glist *x, float xval) +{ + if (!x->gl_isgraph) + return ((xval - x->gl_x1) / (x->gl_x2 - x->gl_x1)); + else if (x->gl_isgraph && x->gl_havewindow) + return (x->gl_screenx2 - x->gl_screenx1) * + (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1); + else + { + int x1, y1, x2, y2; + if (!x->gl_owner) + bug("glist_pixelstox"); + graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); + return (x1 + (x2 - x1) * (xval - x->gl_x1) / (x->gl_x2 - x->gl_x1)); + } +} + +float glist_ytopixels(t_glist *x, float yval) +{ + if (!x->gl_isgraph) + return ((yval - x->gl_y1) / (x->gl_y2 - x->gl_y1)); + else if (x->gl_isgraph && x->gl_havewindow) + return (x->gl_screeny2 - x->gl_screeny1) * + (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1); + else + { + int x1, y1, x2, y2; + if (!x->gl_owner) + bug("glist_pixelstox"); + graph_graphrect(&x->gl_gobj, x->gl_owner, &x1, &y1, &x2, &y2); + return (y1 + (y2 - y1) * (yval - x->gl_y1) / (x->gl_y2 - x->gl_y1)); + } +} + + /* convert an X screen distance to an X coordinate increment. + This is terribly inefficient; + but probably not a big enough CPU hog to warrant optimizing. */ +float glist_dpixtodx(t_glist *x, float dxpix) +{ + return (dxpix * (glist_pixelstox(x, 1) - glist_pixelstox(x, 0))); +} + +float glist_dpixtody(t_glist *x, float dypix) +{ + return (dypix * (glist_pixelstoy(x, 1) - glist_pixelstoy(x, 0))); +} + + /* get the window location in pixels of a "text" object. The + object's x and y positions are in pixels when the glist they're + in is toplevel. If it's not, we convert to pixels on the parent + window. */ +int text_xpix(t_text *x, t_glist *glist) +{ + if (glist->gl_havewindow || !glist->gl_isgraph) + return (x->te_xpix); + else return (glist_xtopixels(glist, + glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) * + x->te_xpix / (glist->gl_screenx2 - glist->gl_screenx1))); +} + +int text_ypix(t_text *x, t_glist *glist) +{ + if (glist->gl_havewindow || !glist->gl_isgraph) + return (x->te_ypix); + else return (glist_ytopixels(glist, + glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) * + x->te_ypix / (glist->gl_screeny2 - glist->gl_screeny1))); +} + + /* redraw all the items in a glist. We construe this to mean + redrawing in its own window and on parent, as needed in each case. + This is too conservative -- for instance, when you draw an "open" + rectangle on the parent, you shouldn't have to redraw the window! */ +void glist_redraw(t_glist *x) +{ + if (glist_isvisible(x)) + { + /* LATER fix the graph_vis() code to handle both cases */ + if (glist_istoplevel(x)) + { + t_gobj *g; + t_linetraverser t; + t_outconnect *oc; + for (g = x->gl_list; g; g = g->g_next) + { + gobj_vis(g, x, 0); + gobj_vis(g, x, 1); + } + /* redraw all the lines */ + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + sys_vgui(".x%x.c coords l%x %d %d %d %d\n", + glist_getcanvas(x), oc, + t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2); + } + if (x->gl_owner) + { + graph_vis(&x->gl_gobj, x->gl_owner, 0); + graph_vis(&x->gl_gobj, x->gl_owner, 1); + } + } +} + +/* --------------------------- widget behavior ------------------- */ + +extern t_widgetbehavior text_widgetbehavior; + + /* Note that some code in here would also be useful for drawing + graph decorations in toplevels... */ +static void graph_vis(t_gobj *gr, t_glist *parent_glist, int vis) +{ + t_glist *x = (t_glist *)gr; + char tag[50]; + t_gobj *g; + int x1, y1, x2, y2; + /* ordinary subpatches: just act like a text object */ + if (!x->gl_isgraph) + { + text_widgetbehavior.w_visfn(gr, parent_glist, vis); + return; + } + + if (vis && canvas_showtext(x)) + rtext_draw(glist_findrtext(parent_glist, &x->gl_obj)); + graph_getrect(gr, parent_glist, &x1, &y1, &x2, &y2); + if (!vis) + rtext_erase(glist_findrtext(parent_glist, &x->gl_obj)); + + sprintf(tag, "graph%x", (int)x); + if (vis) + glist_drawiofor(parent_glist, &x->gl_obj, 1, + tag, x1, y1, x2, y2); + else glist_eraseiofor(parent_glist, &x->gl_obj, tag); + /* if we look like a graph but have been moved to a toplevel, + just show the bounding rectangle */ + if (x->gl_havewindow) + { + if (vis) + { + sys_vgui(".x%x.c create polygon\ + %d %d %d %d %d %d %d %d %d %d -tags %s -fill #c0c0c0\n", + glist_getcanvas(x->gl_owner), + x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag); + } + else + { + sys_vgui(".x%x.c delete %s\n", + glist_getcanvas(x->gl_owner), tag); + } + return; + } + /* otherwise draw (or erase) us as a graph inside another glist. */ + if (vis) + { + int i; + float f; + + /* draw a rectangle around the graph */ + sys_vgui(".x%x.c create line\ + %d %d %d %d %d %d %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag); + + /* draw ticks on horizontal borders. If lperb field is + zero, this is disabled. */ + if (x->gl_xtick.k_lperb) + { + float upix, lpix; + if (y2 < y1) + upix = y1, lpix = y2; + else upix = y2, lpix = y1; + for (i = 0, f = x->gl_xtick.k_point; + f < 0.99 * x->gl_x2 + 0.01*x->gl_x1; i++, + f += x->gl_xtick.k_inc) + { + int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + (int)glist_xtopixels(x, f), (int)upix, + (int)glist_xtopixels(x, f), (int)upix - tickpix, tag); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + (int)glist_xtopixels(x, f), (int)lpix, + (int)glist_xtopixels(x, f), (int)lpix + tickpix, tag); + } + for (i = 1, f = x->gl_xtick.k_point - x->gl_xtick.k_inc; + f > 0.99 * x->gl_x1 + 0.01*x->gl_x2; + i++, f -= x->gl_xtick.k_inc) + { + int tickpix = (i % x->gl_xtick.k_lperb ? 2 : 4); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + (int)glist_xtopixels(x, f), (int)upix, + (int)glist_xtopixels(x, f), (int)upix - tickpix, tag); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + (int)glist_xtopixels(x, f), (int)lpix, + (int)glist_xtopixels(x, f), (int)lpix + tickpix, tag); + } + } + + /* draw ticks in vertical borders*/ + if (x->gl_ytick.k_lperb) + { + float ubound, lbound; + if (x->gl_y2 < x->gl_y1) + ubound = x->gl_y1, lbound = x->gl_y2; + else ubound = x->gl_y2, lbound = x->gl_y1; + for (i = 0, f = x->gl_ytick.k_point; + f < 0.99 * ubound + 0.01 * lbound; + i++, f += x->gl_ytick.k_inc) + { + int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x1, (int)glist_ytopixels(x, f), + x1 + tickpix, (int)glist_ytopixels(x, f), tag); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x2, (int)glist_ytopixels(x, f), + x2 - tickpix, (int)glist_ytopixels(x, f), tag); + } + for (i = 1, f = x->gl_ytick.k_point - x->gl_ytick.k_inc; + f > 0.99 * lbound + 0.01 * ubound; + i++, f -= x->gl_ytick.k_inc) + { + int tickpix = (i % x->gl_ytick.k_lperb ? 2 : 4); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x1, (int)glist_ytopixels(x, f), + x1 + tickpix, (int)glist_ytopixels(x, f), tag); + sys_vgui(".x%x.c create line %d %d %d %d -tags %s\n", + glist_getcanvas(x->gl_owner), + x2, (int)glist_ytopixels(x, f), + x2 - tickpix, (int)glist_ytopixels(x, f), tag); + } + } + /* draw x labels */ + for (i = 0; i < x->gl_nxlabels; i++) + sys_vgui(".x%x.c create text\ + %d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n", + glist_getcanvas(x), + (int)glist_xtopixels(x, atof(x->gl_xlabel[i]->s_name)), + (int)glist_ytopixels(x, x->gl_xlabely), x->gl_xlabel[i]->s_name, + glist_getfont(x), tag); + + /* draw y labels */ + for (i = 0; i < x->gl_nylabels; i++) + sys_vgui(".x%x.c create text\ + %d %d -text {%s} -font -*-courier-bold--normal--%d-* -tags %s\n", + glist_getcanvas(x), + (int)glist_xtopixels(x, x->gl_ylabelx), + (int)glist_ytopixels(x, atof(x->gl_ylabel[i]->s_name)), + x->gl_ylabel[i]->s_name, + glist_getfont(x), tag); + + /* draw contents of graph as glist */ + for (g = x->gl_list; g; g = g->g_next) + gobj_vis(g, x, 1); + } + else + { + sys_vgui(".x%x.c delete %s\n", + glist_getcanvas(x->gl_owner), tag); + for (g = x->gl_list; g; g = g->g_next) + gobj_vis(g, x, 0); + } +} + + /* get the graph's rectangle, not counting extra swelling for controls + to keep them inside the graph. This is the "logical" pixel size. */ + +static void graph_graphrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_glist *x = (t_glist *)z; + int x1 = text_xpix(&x->gl_obj, glist); + int y1 = text_ypix(&x->gl_obj, glist); + int x2, y2; +#if 0 /* this used to adjust graph size when it was in another graph; + now we just preserve the size. */ + /* same logic here as in text_xpix(): */ + if (glist->gl_havewindow) + { + x2 = x1 + x->gl_pixwidth; + y2 = y1 + x->gl_pixheight; + } + else + { + x2 = glist_xtopixels(glist, + glist->gl_x1 + (glist->gl_x2 - glist->gl_x1) * + (x->gl_obj.te_xpix + x->gl_pixwidth) / + (glist->gl_screenx2 - glist->gl_screenx1)); + y2 = glist_ytopixels(glist, + glist->gl_y1 + (glist->gl_y2 - glist->gl_y1) * + (x->gl_obj.te_ypix + x->gl_pixheight) / + (glist->gl_screeny2 - glist->gl_screeny1)); + } +#endif + x2 = x1 + x->gl_pixwidth; + y2 = y1 + x->gl_pixheight; + + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + + /* get the rectangle, enlarged to contain all the "contents" -- + meaning their formal bounds rectangles. */ +static void graph_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + int x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff; + t_glist *x = (t_glist *)z; + if (x->gl_isgraph) + { + int hadwindow; + t_gobj *g; + t_text *ob; + int x21, y21, x22, y22; + + graph_graphrect(z, glist, &x1, &y1, &x2, &y2); + if (canvas_showtext(x)) + { + text_widgetbehavior.w_getrectfn(z, glist, &x21, &y21, &x22, &y22); + if (x22 > x2) + x2 = x22; + if (y22 > y2) + y2 = y22; + } + /* lie about whether we have our own window to affect gobj_getrect + calls below. (LATER add argument to gobj_getrect()?) */ + hadwindow = x->gl_havewindow; + x->gl_havewindow = 0; + for (g = x->gl_list; g; g = g->g_next) + if ((!(ob = pd_checkobject(&g->g_pd))) || text_shouldvis(ob, x)) + { + /* don't do this for arrays, just let them hang outsize the + box. */ + if (pd_class(&g->g_pd) == garray_class) + continue; + gobj_getrect(g, x, &x21, &y21, &x22, &y22); + if (x22 > x2) + x2 = x22; + if (y22 > y2) + y2 = y22; + } + x->gl_havewindow = hadwindow; + } + else text_widgetbehavior.w_getrectfn(z, glist, &x1, &y1, &x2, &y2); + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void graph_displace(t_gobj *z, t_glist *glist, int dx, int dy) +{ + t_glist *x = (t_glist *)z; + if (!x->gl_isgraph) + text_widgetbehavior.w_displacefn(z, glist, dx, dy); + else + { + x->gl_obj.te_xpix += dx; + x->gl_obj.te_ypix += dy; + glist_redraw(x); + canvas_fixlinesfor(glist_getcanvas(glist), &x->gl_obj); + } +} + +static void graph_select(t_gobj *z, t_glist *glist, int state) +{ + t_glist *x = (t_glist *)z; + if (!x->gl_isgraph) + text_widgetbehavior.w_selectfn(z, glist, state); + else + { + t_rtext *y = glist_findrtext(glist, &x->gl_obj); + if (canvas_showtext(x)) + rtext_select(y, state); + sys_vgui(".x%x.c itemconfigure %sR -fill %s\n", glist, + rtext_gettag(y), (state? "blue" : "black")); + sys_vgui(".x%x.c itemconfigure graph%x -fill %s\n", + glist_getcanvas(glist), z, (state? "blue" : "black")); + } +} + +static void graph_activate(t_gobj *z, t_glist *glist, int state) +{ + t_glist *x = (t_glist *)z; + if (canvas_showtext(x)) + text_widgetbehavior.w_activatefn(z, glist, state); +} + +#if 0 +static void graph_delete(t_gobj *z, t_glist *glist) +{ + t_glist *x = (t_glist *)z; + if (!x->gl_isgraph) + text_widgetbehavior.w_deletefn(z, glist); + else + { + t_gobj *y; + while (y = x->gl_list) glist_delete(x, y); +#if 0 /* I think this was just wrong. */ + if (glist_isvisible(x)) + sys_vgui(".x%x.c delete graph%x\n", glist_getcanvas(glist), x); +#endif + } +} +#endif + +static void graph_delete(t_gobj *z, t_glist *glist) +{ + t_glist *x = (t_glist *)z; + t_gobj *y; + text_widgetbehavior.w_deletefn(z, glist); + while (y = x->gl_list) + glist_delete(x, y); +} + +static float graph_lastxpix, graph_lastypix; + +static void graph_motion(void *z, t_floatarg dx, t_floatarg dy) +{ + t_glist *x = (t_glist *)z; + float newxpix = graph_lastxpix + dx, newypix = graph_lastypix + dy; + t_garray *a = (t_garray *)(x->gl_list); + int oldx = 0.5 + glist_pixelstox(x, graph_lastxpix); + int newx = 0.5 + glist_pixelstox(x, newxpix); + t_sample *vec; + int nelem, i; + float oldy = glist_pixelstoy(x, graph_lastypix); + float newy = glist_pixelstoy(x, newypix); + graph_lastxpix = newxpix; + graph_lastypix = newypix; + /* verify that the array is OK */ + if (!a || pd_class((t_pd *)a) != garray_class) + return; + if (!garray_getfloatarray(a, &nelem, &vec)) + return; + if (oldx < 0) oldx = 0; + if (oldx >= nelem) + oldx = nelem - 1; + if (newx < 0) newx = 0; + if (newx >= nelem) + newx = nelem - 1; + if (oldx < newx - 1) + { + for (i = oldx + 1; i <= newx; i++) + vec[i] = newy + (oldy - newy) * + ((float)(newx - i))/(float)(newx - oldx); + } + else if (oldx > newx + 1) + { + for (i = oldx - 1; i >= newx; i--) + vec[i] = newy + (oldy - newy) * + ((float)(newx - i))/(float)(newx - oldx); + } + else vec[newx] = newy; + garray_redraw(a); +} + +static int graph_click(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_glist *x = (t_glist *)z; + t_gobj *y; + int clickreturned = 0; + if (!x->gl_isgraph) + return (text_widgetbehavior.w_clickfn(z, glist, + xpix, ypix, shift, alt, dbl, doit)); + else if (x->gl_havewindow) + return (0); + else + { + for (y = x->gl_list; y; y = y->g_next) + { + int x1, y1, x2, y2; + /* check if the object wants to be clicked */ + if (canvas_hitbox(x, y, xpix, ypix, &x1, &y1, &x2, &y2) + && (clickreturned = gobj_click(y, x, xpix, ypix, + shift, alt, 0, doit))) + break; + } + if (!doit) + { + if (y) + canvas_setcursor(glist_getcanvas(x), clickreturned); + else canvas_setcursor(glist_getcanvas(x), CURSOR_RUNMODE_NOTHING); + } + return (clickreturned); + } +} + +void garray_properties(t_garray *x); + +t_widgetbehavior graph_widgetbehavior = +{ + graph_getrect, + graph_displace, + graph_select, + graph_activate, + graph_delete, + graph_vis, + graph_click, +}; + +void graph_properties(t_gobj *z, t_glist *owner) +{ + t_glist *x = (t_glist *)z; + { + t_gobj *y; + char graphbuf[200]; + sprintf(graphbuf, "pdtk_graph_dialog %%s %g %g %g %g %d %d\n", + x->gl_x1, x->gl_y1, x->gl_x2, x->gl_y2, + x->gl_pixwidth, x->gl_pixheight); + gfxstub_new(&x->gl_pd, x, graphbuf); + + for (y = x->gl_list; y; y = y->g_next) + if (pd_class(&y->g_pd) == garray_class) + garray_properties((t_garray *)y); + } +} + + /* find the graph most recently added to this glist; + if none exists, return 0. */ + +t_glist *glist_findgraph(t_glist *x) +{ + t_gobj *y = 0, *z; + for (z = x->gl_list; z; z = z->g_next) + if (pd_class(&z->g_pd) == canvas_class && ((t_glist *)z)->gl_isgraph) + y = z; + return ((t_glist *)y); +} + + /* message back from dialog GUI to set parameters. Args are: + 1-4: bounds in our coordinates; 5-6: size in parent */ +static void graph_dialog(t_glist *x, t_symbol *s, int argc, t_atom *argv) +{ + t_float x1 = atom_getfloatarg(0, argc, argv); + t_float y1 = atom_getfloatarg(1, argc, argv); + t_float x2 = atom_getfloatarg(2, argc, argv); + t_float y2 = atom_getfloatarg(3, argc, argv); + t_float xpix = atom_getfloatarg(4, argc, argv); + t_float ypix = atom_getfloatarg(5, argc, argv); + if (x1 != x->gl_x1 || x2 != x->gl_x2 || + y1 != x->gl_y1 || y2 != x->gl_y2) + graph_bounds(x, x1, y1, x2, y2); + if (xpix != x->gl_pixwidth || ypix != x->gl_pixheight) + { + x->gl_pixwidth = xpix; + x->gl_pixheight = ypix; + glist_redraw(x); + if (x->gl_owner) + canvas_fixlinesfor(x->gl_owner, &x->gl_obj); + } +} + +extern void canvas_menuarray(t_glist *canvas); + +void g_graph_setup(void) +{ + class_setwidget(canvas_class, &graph_widgetbehavior); + class_addmethod(canvas_class, (t_method)graph_bounds, gensym("bounds"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(canvas_class, (t_method)graph_xticks, gensym("xticks"), + A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(canvas_class, (t_method)graph_xlabel, gensym("xlabel"), + A_GIMME, 0); + class_addmethod(canvas_class, (t_method)graph_yticks, gensym("yticks"), + A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(canvas_class, (t_method)graph_ylabel, gensym("ylabel"), + A_GIMME, 0); + class_addmethod(canvas_class, (t_method)graph_array, gensym("array"), + A_SYMBOL, A_FLOAT, A_SYMBOL, A_DEFFLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_menuarray, + gensym("menuarray"), A_NULL); + class_addmethod(canvas_class, (t_method)graph_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(canvas_class, (t_method)glist_arraydialog, + gensym("arraydialog"), A_SYMBOL, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL); + class_addmethod(canvas_class, (t_method)glist_sort, + gensym("sort"), A_NULL); +} diff --git a/apps/plugins/pdbox/PDa/src/g_guiconnect.c b/apps/plugins/pdbox/PDa/src/g_guiconnect.c new file mode 100644 index 0000000..c167382 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_guiconnect.c @@ -0,0 +1,188 @@ +/* Copyright (c) 1997-2000 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* a thing to forward messages from the GUI, dealing with race conditions +in which the "target" gets deleted while the GUI is sending it something. +*/ + +#include "m_pd.h" +#include "g_canvas.h" + +struct _guiconnect +{ + t_object x_obj; + t_pd *x_who; + t_symbol *x_sym; + t_clock *x_clock; +}; + +static t_class *guiconnect_class; + +t_guiconnect *guiconnect_new(t_pd *who, t_symbol *sym) +{ + t_guiconnect *x = (t_guiconnect *)pd_new(guiconnect_class); + x->x_who = who; + x->x_sym = sym; + pd_bind(&x->x_obj.ob_pd, sym); + return (x); +} + + /* cleanup routine; delete any resources we have */ +static void guiconnect_free(t_guiconnect *x) +{ + if (x->x_sym) + pd_unbind(&x->x_obj.ob_pd, x->x_sym); + if (x->x_clock) + clock_free(x->x_clock); +} + + /* this is called when the clock times out to indicate the GUI should + be gone by now. */ +static void guiconnect_tick(t_guiconnect *x) +{ + pd_free(&x->x_obj.ob_pd); +} + + /* the target calls this to disconnect. If the gui has "signed off" + we're ready to delete the object; otherwise we wait either for signoff + or for a timeout. */ +void guiconnect_notarget(t_guiconnect *x, double timedelay) +{ + if (!x->x_sym) + pd_free(&x->x_obj.ob_pd); + else + { + x->x_who = 0; + if (timedelay > 0) + { + x->x_clock = clock_new(x, (t_method)guiconnect_tick); + clock_delay(x->x_clock, timedelay); + } + } +} + + /* the GUI calls this to send messages to the target. */ +static void guiconnect_anything(t_guiconnect *x, + t_symbol *s, int ac, t_atom *av) +{ + if (x->x_who) + typedmess(x->x_who, s, ac, av); +} + + /* the GUI calls this when it disappears. (If there's any chance the + GUI will fail to do this, the "target", when it signs off, should specify + a timeout after which the guiconnect will disappear.) */ +static void guiconnect_signoff(t_guiconnect *x) +{ + if (!x->x_who) + pd_free(&x->x_obj.ob_pd); + else + { + pd_unbind(&x->x_obj.ob_pd, x->x_sym); + x->x_sym = 0; + } +} + +void g_guiconnect_setup(void) +{ + guiconnect_class = class_new(gensym("guiconnect"), 0, + (t_method)guiconnect_free, sizeof(t_guiconnect), CLASS_PD, 0); + class_addanything(guiconnect_class, guiconnect_anything); + class_addmethod(guiconnect_class, (t_method)guiconnect_signoff, + gensym("signoff"), 0); +} +/* Copyright (c) 1997-2000 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* a thing to forward messages from the GUI, dealing with race conditions +in which the "target" gets deleted while the GUI is sending it something. +*/ + +#include "m_pd.h" +#include "g_canvas.h" + +struct _guiconnect +{ + t_object x_obj; + t_pd *x_who; + t_symbol *x_sym; + t_clock *x_clock; +}; + +static t_class *guiconnect_class; + +t_guiconnect *guiconnect_new(t_pd *who, t_symbol *sym) +{ + t_guiconnect *x = (t_guiconnect *)pd_new(guiconnect_class); + x->x_who = who; + x->x_sym = sym; + pd_bind(&x->x_obj.ob_pd, sym); + return (x); +} + + /* cleanup routine; delete any resources we have */ +static void guiconnect_free(t_guiconnect *x) +{ + if (x->x_sym) + pd_unbind(&x->x_obj.ob_pd, x->x_sym); + if (x->x_clock) + clock_free(x->x_clock); +} + + /* this is called when the clock times out to indicate the GUI should + be gone by now. */ +static void guiconnect_tick(t_guiconnect *x) +{ + pd_free(&x->x_obj.ob_pd); +} + + /* the target calls this to disconnect. If the gui has "signed off" + we're ready to delete the object; otherwise we wait either for signoff + or for a timeout. */ +void guiconnect_notarget(t_guiconnect *x, double timedelay) +{ + if (!x->x_sym) + pd_free(&x->x_obj.ob_pd); + else + { + x->x_who = 0; + if (timedelay > 0) + { + x->x_clock = clock_new(x, (t_method)guiconnect_tick); + clock_delay(x->x_clock, timedelay); + } + } +} + + /* the GUI calls this to send messages to the target. */ +static void guiconnect_anything(t_guiconnect *x, + t_symbol *s, int ac, t_atom *av) +{ + if (x->x_who) + typedmess(x->x_who, s, ac, av); +} + + /* the GUI calls this when it disappears. (If there's any chance the + GUI will fail to do this, the "target", when it signs off, should specify + a timeout after which the guiconnect will disappear.) */ +static void guiconnect_signoff(t_guiconnect *x) +{ + if (!x->x_who) + pd_free(&x->x_obj.ob_pd); + else + { + pd_unbind(&x->x_obj.ob_pd, x->x_sym); + x->x_sym = 0; + } +} + +void g_guiconnect_setup(void) +{ + guiconnect_class = class_new(gensym("guiconnect"), 0, + (t_method)guiconnect_free, sizeof(t_guiconnect), CLASS_PD, 0); + class_addanything(guiconnect_class, guiconnect_anything); + class_addmethod(guiconnect_class, (t_method)guiconnect_signoff, + gensym("signoff"), 0); +} diff --git a/apps/plugins/pdbox/PDa/src/g_hdial.c b/apps/plugins/pdbox/PDa/src/g_hdial.c new file mode 100644 index 0000000..447e984 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_hdial.c @@ -0,0 +1,1470 @@ +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + +/* name change to hradio by MSP and changed to +put out a "float" as in sliders, toggles, etc. */ + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + +/* ------------- hdl gui-horicontal dial ---------------------- */ + +t_widgetbehavior hradio_widgetbehavior; +static t_class *hradio_class, *hradio_old_class; + +/* widget helper functions */ + +void hradio_draw_update(t_hradio *x, t_glist *glist) +{ + if(glist_isvisible(glist)) + { + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n", + canvas, x, x->x_on_old, + x->x_gui.x_bcol, x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n", + canvas, x, x->x_on, + x->x_gui.x_fcol, x->x_gui.x_fcol); + } +} + +void hradio_draw_new(t_hradio *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i, dx=x->x_gui.x_w, s4=dx/4; + int yy11=text_ypix(&x->x_gui.x_obj, glist), yy12=yy11+dx; + int yy21=yy11+s4, yy22=yy12-s4; + int xx11b=text_xpix(&x->x_gui.x_obj, glist), xx11=xx11b, xx21=xx11b+s4; + int xx22=xx11b+dx-s4; + + + for(i=0; ix_gui.x_bcol, x, i); + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -outline #%6.6x -tags %xBUT%d\n", + canvas, xx21, yy21, xx22, yy22, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol, x, i); + xx11 += dx; + xx21 += dx; + xx22 += dx; + } + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xx11b+x->x_gui.x_ldx, yy11+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_lcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xx11b, yy12-1, xx11b + IOWIDTH, yy12, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xx11b, yy11, xx11b + IOWIDTH, yy11+1, x, 0); + +} + +void hradio_draw_move(t_hradio *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i, dx=x->x_gui.x_w, s4=dx/4; + int yy11=text_ypix(&x->x_gui.x_obj, glist), yy12=yy11+dx; + int yy21=yy11+s4, yy22=yy12-s4; + int xx11b=text_xpix(&x->x_gui.x_obj, glist), xx11=xx11b, xx21=xx11b+s4; + int xx22=xx11b+dx-s4; + + xx11 = xx11b; + xx21=xx11b+s4; + xx22=xx11b+dx-s4; + for(i=0; ix_gui.x_ldx, yy11+x->x_gui.x_ldy); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, xx11b, yy12-1, xx11b + IOWIDTH, yy12); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, xx11b, yy11, xx11b + IOWIDTH, yy11+1); +} + +void hradio_draw_erase(t_hradio* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i; + + for(i=0; ix_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void hradio_draw_config(t_hradio* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i; + + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + for(i=0; ix_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n", canvas, x, i, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol); + } +} + +void hradio_draw_io(t_hradio* x, t_glist* glist, int old_snd_rcv_flags) +{ + t_canvas *canvas=glist_getcanvas(glist); + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos, ypos + x->x_gui.x_w-1, + xpos + IOWIDTH, ypos + x->x_gui.x_w, + x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos, ypos, + xpos + IOWIDTH, ypos+1, x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void hradio_draw_select(t_hradio* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i; + + if(x->x_gui.x_fsf.x_selected) + { + for(i=0; ix_gui.x_lcol); + } +} + +void hradio_draw(t_hradio *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + hradio_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + hradio_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + hradio_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + hradio_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + hradio_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + hradio_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + hradio_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ hdl widgetbehaviour----------------------------- */ + +static void hradio_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_hradio *x = (t_hradio *)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist); + *yp1 = text_ypix(&x->x_gui.x_obj, glist); + *xp2 = *xp1 + x->x_gui.x_w*x->x_number; + *yp2 = *yp1 + x->x_gui.x_h; +} + +static void hradio_save(t_gobj *z, t_binbuf *b) +{ + t_hradio *x = (t_hradio *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiiiisssiiiiiiii", gensym("#X"),gensym("obj"), + (t_int)text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist), + (t_int)text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist), + (pd_class(&x->x_gui.x_obj.ob_pd) == hradio_old_class ? + gensym("hdl") : gensym("hradio")), + x->x_gui.x_w, + x->x_change, iem_symargstoint(&x->x_gui.x_isa), x->x_number, + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2], x->x_on); + binbuf_addv(b, ";"); +} + +static void hradio_properties(t_gobj *z, t_glist *owner) +{ + t_hradio *x = (t_hradio *)z; + char buf[800]; + t_symbol *srl[3]; + int hchange=-1; + + iemgui_properties(&x->x_gui, srl); + if (pd_class(&x->x_gui.x_obj.ob_pd) == hradio_old_class) + hchange = x->x_change; + sprintf(buf, "pdtk_iemgui_dialog %%s hradio \ + ----------dimensions(pix):----------- %d %d size: 0 0 empty \ + empty 0.0 empty 0.0 empty %d \ + %d new-only new&old %d %d number: %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, IEM_GUI_MINSIZE, + 0,/*no_schedule*/ + hchange, x->x_gui.x_isa.x_loadinit, -1, x->x_number, + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void hradio_dialog(t_hradio *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int a = (int)atom_getintarg(0, argc, argv); + int chg = (int)atom_getintarg(4, argc, argv); + int num = (int)atom_getintarg(6, argc, argv); + int sr_flags; + + if(chg != 0) chg = 1; + x->x_change = chg; + sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + if(x->x_number != num) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE); + x->x_number = num; + if(x->x_on >= x->x_number) + { + x->x_on = x->x_number - 1; + x->x_on_old = x->x_on; + } + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW); + } + else + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); + } + +} + +static void hradio_set(t_hradio *x, t_floatarg f) +{ + int i=(int)f; + int old=x->x_on_old; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + if(x->x_on != x->x_on_old) + { + old = x->x_on_old; + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + x->x_on_old = old; + } + else + { + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } +} + +static void hradio_bang(t_hradio *x) +{ + /* compatibility with earlier "hdial" behavior */ + if (pd_class(&x->x_gui.x_obj.ob_pd) == hradio_old_class) + { + if((x->x_change)&&(x->x_on != x->x_on_old)) + { + SETFLOAT(x->x_at, (float)x->x_on_old); + SETFLOAT(x->x_at+1, 0.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + x->x_on_old = x->x_on; + SETFLOAT(x->x_at, (float)x->x_on); + SETFLOAT(x->x_at+1, 1.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + else + { + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); + } +} + +static void hradio_fout(t_hradio *x, t_floatarg f) +{ + int i=(int)f; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + + if (pd_class(&x->x_gui.x_obj.ob_pd) == hradio_old_class) + { + if((x->x_change)&&(i != x->x_on_old)) + { + SETFLOAT(x->x_at, (float)x->x_on_old); + SETFLOAT(x->x_at+1, 0.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + if(x->x_on != x->x_on_old) + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + x->x_on_old = x->x_on; + SETFLOAT(x->x_at, (float)x->x_on); + SETFLOAT(x->x_at+1, 1.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + else + { + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); + } +} + +static void hradio_float(t_hradio *x, t_floatarg f) +{ + int i=(int)f; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + + if (pd_class(&x->x_gui.x_obj.ob_pd) == hradio_old_class) + { + /* compatibility with earlier "vdial" behavior */ + if((x->x_change)&&(i != x->x_on_old)) + { + if(x->x_gui.x_fsf.x_put_in2out) + { + SETFLOAT(x->x_at, (float)x->x_on_old); + SETFLOAT(x->x_at+1, 0.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + } + if(x->x_on != x->x_on_old) + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + x->x_on_old = x->x_on; + if(x->x_gui.x_fsf.x_put_in2out) + { + SETFLOAT(x->x_at, (float)x->x_on); + SETFLOAT(x->x_at+1, 1.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + } + else + { + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + if (x->x_gui.x_fsf.x_put_in2out) + { + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); + } + } +} + +static void hradio_click(t_hradio *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + int xx = (int)xpos - (int)text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist); + + hradio_fout(x, (float)(xx / x->x_gui.x_w)); +} + +static int hradio_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + if(doit) + hradio_click((t_hradio *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt); + return (1); +} + +static void hradio_loadbang(t_hradio *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + hradio_bang(x); +} + +static void hradio_number(t_hradio *x, t_floatarg num) +{ + int n=(int)num; + + if(n < 1) + n = 1; + if(n > IEM_RADIO_MAX) + n = IEM_RADIO_MAX; + if(n != x->x_number) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE); + x->x_number = n; + if(x->x_on >= x->x_number) + x->x_on = x->x_number - 1; + x->x_on_old = x->x_on; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW); + } +} + +static void hradio_size(t_hradio *x, t_symbol *s, int ac, t_atom *av) +{ + x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av)); + x->x_gui.x_h = x->x_gui.x_w; + iemgui_size((void *)x, &x->x_gui); +} + +static void hradio_delta(t_hradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void hradio_pos(t_hradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void hradio_color(t_hradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void hradio_send(t_hradio *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void hradio_receive(t_hradio *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void hradio_label(t_hradio *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void hradio_label_pos(t_hradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void hradio_label_font(t_hradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void hradio_init(t_hradio *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void hradio_double_change(t_hradio *x) +{x->x_change = 1;} + +static void hradio_single_change(t_hradio *x) +{x->x_change = 0;} + +static void *hradio_donew(t_symbol *s, int argc, t_atom *argv, int old) +{ + t_hradio *x = (t_hradio *)pd_new(old? hradio_old_class : hradio_class); + int bflcol[]={-262144, -1, -1}; + int a=IEM_GUI_DEFAULTSIZE, on=0, f=0; + int ldx=0, ldy=-6, chg=1, num=8; + int fs=8; + int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, fthold=IEM_BNG_DEFAULTHOLDFLASHTIME; + char str[144]; + + iem_inttosymargs(&x->x_gui.x_isa, 0); + iem_inttofstyle(&x->x_gui.x_fsf, 0); + + if((argc == 15)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2) + &&IS_A_FLOAT(argv,3) + &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4)) + &&(IS_A_SYMBOL(argv,5)||IS_A_FLOAT(argv,5)) + &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) + &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8) + &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)&&IS_A_FLOAT(argv,11) + &&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13)&&IS_A_FLOAT(argv,14)) + { + a = (int)atom_getintarg(0, argc, argv); + chg = (int)atom_getintarg(1, argc, argv); + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(2, argc, argv)); + num = (int)atom_getintarg(3, argc, argv); + iemgui_new_getnames(&x->x_gui, 4, argv); + ldx = (int)atom_getintarg(7, argc, argv); + ldy = (int)atom_getintarg(8, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(9, argc, argv)); + fs = (int)atom_getintarg(10, argc, argv); + bflcol[0] = (int)atom_getintarg(11, argc, argv); + bflcol[1] = (int)atom_getintarg(12, argc, argv); + bflcol[2] = (int)atom_getintarg(13, argc, argv); + on = (int)atom_getintarg(14, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 4, 0); + x->x_gui.x_draw = (t_iemfunptr)hradio_draw; + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if (!strcmp(x->x_gui.x_snd->s_name, "empty")) + x->x_gui.x_fsf.x_snd_able = 0; + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + if(num < 1) + num = 1; + if(num > IEM_RADIO_MAX) + num = IEM_RADIO_MAX; + x->x_number = num; + if(on < 0) + on = 0; + if(on >= x->x_number) + on = x->x_number - 1; + if(x->x_gui.x_isa.x_loadinit) + x->x_on = on; + else + x->x_on = 0; + x->x_on_old = x->x_on; + x->x_change = (chg==0)?0:1; + if (x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + iemgui_verify_snd_ne_rcv(&x->x_gui); + iemgui_all_colfromload(&x->x_gui, bflcol); + outlet_new(&x->x_gui.x_obj, &s_list); + return (x); +} + +static void *hradio_new(t_symbol *s, int argc, t_atom *argv) +{ + return (hradio_donew(s, argc, argv, 0)); +} + +static void *hdial_new(t_symbol *s, int argc, t_atom *argv) +{ + return (hradio_donew(s, argc, argv, 1)); +} + +static void hradio_ff(t_hradio *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + gfxstub_deleteforkey(x); +} + +void g_hradio_setup(void) +{ + hradio_class = class_new(gensym("hradio"), (t_newmethod)hradio_new, + (t_method)hradio_ff, sizeof(t_hradio), 0, A_GIMME, 0); + class_addbang(hradio_class, hradio_bang); + class_addfloat(hradio_class, hradio_float); + class_addmethod(hradio_class, (t_method)hradio_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(hradio_class, (t_method)hradio_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(hradio_class, (t_method)hradio_loadbang, + gensym("loadbang"), 0); + class_addmethod(hradio_class, (t_method)hradio_set, + gensym("set"), A_FLOAT, 0); + class_addmethod(hradio_class, (t_method)hradio_size, + gensym("size"), A_GIMME, 0); + class_addmethod(hradio_class, (t_method)hradio_delta, + gensym("delta"), A_GIMME, 0); + class_addmethod(hradio_class, (t_method)hradio_pos, + gensym("pos"), A_GIMME, 0); + class_addmethod(hradio_class, (t_method)hradio_color, + gensym("color"), A_GIMME, 0); + class_addmethod(hradio_class, (t_method)hradio_send, + gensym("send"), A_DEFSYM, 0); + class_addmethod(hradio_class, (t_method)hradio_receive, + gensym("receive"), A_DEFSYM, 0); + class_addmethod(hradio_class, (t_method)hradio_label, + gensym("label"), A_DEFSYM, 0); + class_addmethod(hradio_class, (t_method)hradio_label_pos, + gensym("label_pos"), A_GIMME, 0); + class_addmethod(hradio_class, (t_method)hradio_label_font, + gensym("label_font"), A_GIMME, 0); + class_addmethod(hradio_class, (t_method)hradio_init, + gensym("init"), A_FLOAT, 0); + class_addmethod(hradio_class, (t_method)hradio_number, + gensym("number"), A_FLOAT, 0); + class_addmethod(hradio_class, (t_method)hradio_single_change, + gensym("single_change"), 0); + class_addmethod(hradio_class, (t_method)hradio_double_change, + gensym("double_change"), 0); + hradio_widgetbehavior.w_getrectfn = hradio_getrect; + hradio_widgetbehavior.w_displacefn = iemgui_displace; + hradio_widgetbehavior.w_selectfn = iemgui_select; + hradio_widgetbehavior.w_activatefn = NULL; + hradio_widgetbehavior.w_deletefn = iemgui_delete; + hradio_widgetbehavior.w_visfn = iemgui_vis; + hradio_widgetbehavior.w_clickfn = hradio_newclick; + class_setwidget(hradio_class, &hradio_widgetbehavior); + class_sethelpsymbol(hradio_class, gensym("hradio")); + class_setsavefn(hradio_class, hradio_save); + class_setpropertiesfn(hradio_class, hradio_properties); + + /*obsolete version (0.34-0.35) */ + hradio_old_class = class_new(gensym("hdl"), (t_newmethod)hdial_new, + (t_method)hradio_ff, sizeof(t_hradio), 0, A_GIMME, 0); + class_addcreator((t_newmethod)hradio_new, gensym("rdb"), A_GIMME, 0); + class_addcreator((t_newmethod)hradio_new, gensym("radiobut"), A_GIMME, 0); + class_addcreator((t_newmethod)hradio_new, gensym("radiobutton"), + A_GIMME, 0); + class_addbang(hradio_old_class, hradio_bang); + class_addfloat(hradio_old_class, hradio_float); + class_addmethod(hradio_old_class, (t_method)hradio_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(hradio_old_class, (t_method)hradio_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(hradio_old_class, (t_method)hradio_loadbang, + gensym("loadbang"), 0); + class_addmethod(hradio_old_class, (t_method)hradio_set, + gensym("set"), A_FLOAT, 0); + class_addmethod(hradio_old_class, (t_method)hradio_size, + gensym("size"), A_GIMME, 0); + class_addmethod(hradio_old_class, (t_method)hradio_delta, + gensym("delta"), A_GIMME, 0); + class_addmethod(hradio_old_class, (t_method)hradio_pos, + gensym("pos"), A_GIMME, 0); + class_addmethod(hradio_old_class, (t_method)hradio_color, + gensym("color"), A_GIMME, 0); + class_addmethod(hradio_old_class, (t_method)hradio_send, + gensym("send"), A_DEFSYM, 0); + class_addmethod(hradio_old_class, (t_method)hradio_receive, + gensym("receive"), A_DEFSYM, 0); + class_addmethod(hradio_old_class, (t_method)hradio_label, + gensym("label"), A_DEFSYM, 0); + class_addmethod(hradio_old_class, (t_method)hradio_label_pos, + gensym("label_pos"), A_GIMME, 0); + class_addmethod(hradio_old_class, (t_method)hradio_label_font, + gensym("label_font"), A_GIMME, 0); + class_addmethod(hradio_old_class, (t_method)hradio_init, + gensym("init"), A_FLOAT, 0); + class_addmethod(hradio_old_class, (t_method)hradio_number, + gensym("number"), A_FLOAT, 0); + class_addmethod(hradio_old_class, (t_method)hradio_single_change, + gensym("single_change"), 0); + class_addmethod(hradio_old_class, (t_method)hradio_double_change, + gensym("double_change"), 0); + class_setwidget(hradio_old_class, &hradio_widgetbehavior); + class_sethelpsymbol(hradio_old_class, gensym("hradio")); +} +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + +/* name change to hradio by MSP and changed to +put out a "float" as in sliders, toggles, etc. */ + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + +/* ------------- hdl gui-horicontal dial ---------------------- */ + +t_widgetbehavior hradio_widgetbehavior; +static t_class *hradio_class, *hradio_old_class; + +/* widget helper functions */ + +void hradio_draw_update(t_hradio *x, t_glist *glist) +{ + if(glist_isvisible(glist)) + { + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n", + canvas, x, x->x_on_old, + x->x_gui.x_bcol, x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n", + canvas, x, x->x_on, + x->x_gui.x_fcol, x->x_gui.x_fcol); + } +} + +void hradio_draw_new(t_hradio *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i, dx=x->x_gui.x_w, s4=dx/4; + int yy11=text_ypix(&x->x_gui.x_obj, glist), yy12=yy11+dx; + int yy21=yy11+s4, yy22=yy12-s4; + int xx11b=text_xpix(&x->x_gui.x_obj, glist), xx11=xx11b, xx21=xx11b+s4; + int xx22=xx11b+dx-s4; + + + for(i=0; ix_gui.x_bcol, x, i); + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -outline #%6.6x -tags %xBUT%d\n", + canvas, xx21, yy21, xx22, yy22, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol, x, i); + xx11 += dx; + xx21 += dx; + xx22 += dx; + } + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xx11b+x->x_gui.x_ldx, yy11+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_lcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xx11b, yy12-1, xx11b + IOWIDTH, yy12, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xx11b, yy11, xx11b + IOWIDTH, yy11+1, x, 0); + +} + +void hradio_draw_move(t_hradio *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i, dx=x->x_gui.x_w, s4=dx/4; + int yy11=text_ypix(&x->x_gui.x_obj, glist), yy12=yy11+dx; + int yy21=yy11+s4, yy22=yy12-s4; + int xx11b=text_xpix(&x->x_gui.x_obj, glist), xx11=xx11b, xx21=xx11b+s4; + int xx22=xx11b+dx-s4; + + xx11 = xx11b; + xx21=xx11b+s4; + xx22=xx11b+dx-s4; + for(i=0; ix_gui.x_ldx, yy11+x->x_gui.x_ldy); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, xx11b, yy12-1, xx11b + IOWIDTH, yy12); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, xx11b, yy11, xx11b + IOWIDTH, yy11+1); +} + +void hradio_draw_erase(t_hradio* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i; + + for(i=0; ix_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void hradio_draw_config(t_hradio* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i; + + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + for(i=0; ix_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n", canvas, x, i, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol); + } +} + +void hradio_draw_io(t_hradio* x, t_glist* glist, int old_snd_rcv_flags) +{ + t_canvas *canvas=glist_getcanvas(glist); + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos, ypos + x->x_gui.x_w-1, + xpos + IOWIDTH, ypos + x->x_gui.x_w, + x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos, ypos, + xpos + IOWIDTH, ypos+1, x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void hradio_draw_select(t_hradio* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i; + + if(x->x_gui.x_fsf.x_selected) + { + for(i=0; ix_gui.x_lcol); + } +} + +void hradio_draw(t_hradio *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + hradio_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + hradio_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + hradio_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + hradio_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + hradio_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + hradio_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + hradio_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ hdl widgetbehaviour----------------------------- */ + +static void hradio_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_hradio *x = (t_hradio *)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist); + *yp1 = text_ypix(&x->x_gui.x_obj, glist); + *xp2 = *xp1 + x->x_gui.x_w*x->x_number; + *yp2 = *yp1 + x->x_gui.x_h; +} + +static void hradio_save(t_gobj *z, t_binbuf *b) +{ + t_hradio *x = (t_hradio *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiiiisssiiiiiiii", gensym("#X"),gensym("obj"), + (t_int)text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist), + (t_int)text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist), + (pd_class(&x->x_gui.x_obj.ob_pd) == hradio_old_class ? + gensym("hdl") : gensym("hradio")), + x->x_gui.x_w, + x->x_change, iem_symargstoint(&x->x_gui.x_isa), x->x_number, + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2], x->x_on); + binbuf_addv(b, ";"); +} + +static void hradio_properties(t_gobj *z, t_glist *owner) +{ + t_hradio *x = (t_hradio *)z; + char buf[800]; + t_symbol *srl[3]; + int hchange=-1; + + iemgui_properties(&x->x_gui, srl); + if (pd_class(&x->x_gui.x_obj.ob_pd) == hradio_old_class) + hchange = x->x_change; + sprintf(buf, "pdtk_iemgui_dialog %%s hradio \ + ----------dimensions(pix):----------- %d %d size: 0 0 empty \ + empty 0.0 empty 0.0 empty %d \ + %d new-only new&old %d %d number: %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, IEM_GUI_MINSIZE, + 0,/*no_schedule*/ + hchange, x->x_gui.x_isa.x_loadinit, -1, x->x_number, + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void hradio_dialog(t_hradio *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int a = (int)atom_getintarg(0, argc, argv); + int chg = (int)atom_getintarg(4, argc, argv); + int num = (int)atom_getintarg(6, argc, argv); + int sr_flags; + + if(chg != 0) chg = 1; + x->x_change = chg; + sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + if(x->x_number != num) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE); + x->x_number = num; + if(x->x_on >= x->x_number) + { + x->x_on = x->x_number - 1; + x->x_on_old = x->x_on; + } + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW); + } + else + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); + } + +} + +static void hradio_set(t_hradio *x, t_floatarg f) +{ + int i=(int)f; + int old=x->x_on_old; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + if(x->x_on != x->x_on_old) + { + old = x->x_on_old; + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + x->x_on_old = old; + } + else + { + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } +} + +static void hradio_bang(t_hradio *x) +{ + /* compatibility with earlier "hdial" behavior */ + if (pd_class(&x->x_gui.x_obj.ob_pd) == hradio_old_class) + { + if((x->x_change)&&(x->x_on != x->x_on_old)) + { + SETFLOAT(x->x_at, (float)x->x_on_old); + SETFLOAT(x->x_at+1, 0.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + x->x_on_old = x->x_on; + SETFLOAT(x->x_at, (float)x->x_on); + SETFLOAT(x->x_at+1, 1.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + else + { + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); + } +} + +static void hradio_fout(t_hradio *x, t_floatarg f) +{ + int i=(int)f; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + + if (pd_class(&x->x_gui.x_obj.ob_pd) == hradio_old_class) + { + if((x->x_change)&&(i != x->x_on_old)) + { + SETFLOAT(x->x_at, (float)x->x_on_old); + SETFLOAT(x->x_at+1, 0.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + if(x->x_on != x->x_on_old) + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + x->x_on_old = x->x_on; + SETFLOAT(x->x_at, (float)x->x_on); + SETFLOAT(x->x_at+1, 1.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + else + { + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); + } +} + +static void hradio_float(t_hradio *x, t_floatarg f) +{ + int i=(int)f; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + + if (pd_class(&x->x_gui.x_obj.ob_pd) == hradio_old_class) + { + /* compatibility with earlier "vdial" behavior */ + if((x->x_change)&&(i != x->x_on_old)) + { + if(x->x_gui.x_fsf.x_put_in2out) + { + SETFLOAT(x->x_at, (float)x->x_on_old); + SETFLOAT(x->x_at+1, 0.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + } + if(x->x_on != x->x_on_old) + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + x->x_on_old = x->x_on; + if(x->x_gui.x_fsf.x_put_in2out) + { + SETFLOAT(x->x_at, (float)x->x_on); + SETFLOAT(x->x_at+1, 1.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + } + else + { + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + if (x->x_gui.x_fsf.x_put_in2out) + { + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); + } + } +} + +static void hradio_click(t_hradio *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + int xx = (int)xpos - (int)text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist); + + hradio_fout(x, (float)(xx / x->x_gui.x_w)); +} + +static int hradio_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + if(doit) + hradio_click((t_hradio *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt); + return (1); +} + +static void hradio_loadbang(t_hradio *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + hradio_bang(x); +} + +static void hradio_number(t_hradio *x, t_floatarg num) +{ + int n=(int)num; + + if(n < 1) + n = 1; + if(n > IEM_RADIO_MAX) + n = IEM_RADIO_MAX; + if(n != x->x_number) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE); + x->x_number = n; + if(x->x_on >= x->x_number) + x->x_on = x->x_number - 1; + x->x_on_old = x->x_on; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW); + } +} + +static void hradio_size(t_hradio *x, t_symbol *s, int ac, t_atom *av) +{ + x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av)); + x->x_gui.x_h = x->x_gui.x_w; + iemgui_size((void *)x, &x->x_gui); +} + +static void hradio_delta(t_hradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void hradio_pos(t_hradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void hradio_color(t_hradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void hradio_send(t_hradio *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void hradio_receive(t_hradio *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void hradio_label(t_hradio *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void hradio_label_pos(t_hradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void hradio_label_font(t_hradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void hradio_init(t_hradio *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void hradio_double_change(t_hradio *x) +{x->x_change = 1;} + +static void hradio_single_change(t_hradio *x) +{x->x_change = 0;} + +static void *hradio_donew(t_symbol *s, int argc, t_atom *argv, int old) +{ + t_hradio *x = (t_hradio *)pd_new(old? hradio_old_class : hradio_class); + int bflcol[]={-262144, -1, -1}; + int a=IEM_GUI_DEFAULTSIZE, on=0, f=0; + int ldx=0, ldy=-6, chg=1, num=8; + int fs=8; + int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, fthold=IEM_BNG_DEFAULTHOLDFLASHTIME; + char str[144]; + + iem_inttosymargs(&x->x_gui.x_isa, 0); + iem_inttofstyle(&x->x_gui.x_fsf, 0); + + if((argc == 15)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2) + &&IS_A_FLOAT(argv,3) + &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4)) + &&(IS_A_SYMBOL(argv,5)||IS_A_FLOAT(argv,5)) + &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) + &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8) + &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)&&IS_A_FLOAT(argv,11) + &&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13)&&IS_A_FLOAT(argv,14)) + { + a = (int)atom_getintarg(0, argc, argv); + chg = (int)atom_getintarg(1, argc, argv); + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(2, argc, argv)); + num = (int)atom_getintarg(3, argc, argv); + iemgui_new_getnames(&x->x_gui, 4, argv); + ldx = (int)atom_getintarg(7, argc, argv); + ldy = (int)atom_getintarg(8, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(9, argc, argv)); + fs = (int)atom_getintarg(10, argc, argv); + bflcol[0] = (int)atom_getintarg(11, argc, argv); + bflcol[1] = (int)atom_getintarg(12, argc, argv); + bflcol[2] = (int)atom_getintarg(13, argc, argv); + on = (int)atom_getintarg(14, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 4, 0); + x->x_gui.x_draw = (t_iemfunptr)hradio_draw; + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if (!strcmp(x->x_gui.x_snd->s_name, "empty")) + x->x_gui.x_fsf.x_snd_able = 0; + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + if(num < 1) + num = 1; + if(num > IEM_RADIO_MAX) + num = IEM_RADIO_MAX; + x->x_number = num; + if(on < 0) + on = 0; + if(on >= x->x_number) + on = x->x_number - 1; + if(x->x_gui.x_isa.x_loadinit) + x->x_on = on; + else + x->x_on = 0; + x->x_on_old = x->x_on; + x->x_change = (chg==0)?0:1; + if (x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + iemgui_verify_snd_ne_rcv(&x->x_gui); + iemgui_all_colfromload(&x->x_gui, bflcol); + outlet_new(&x->x_gui.x_obj, &s_list); + return (x); +} + +static void *hradio_new(t_symbol *s, int argc, t_atom *argv) +{ + return (hradio_donew(s, argc, argv, 0)); +} + +static void *hdial_new(t_symbol *s, int argc, t_atom *argv) +{ + return (hradio_donew(s, argc, argv, 1)); +} + +static void hradio_ff(t_hradio *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + gfxstub_deleteforkey(x); +} + +void g_hradio_setup(void) +{ + hradio_class = class_new(gensym("hradio"), (t_newmethod)hradio_new, + (t_method)hradio_ff, sizeof(t_hradio), 0, A_GIMME, 0); + class_addbang(hradio_class, hradio_bang); + class_addfloat(hradio_class, hradio_float); + class_addmethod(hradio_class, (t_method)hradio_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(hradio_class, (t_method)hradio_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(hradio_class, (t_method)hradio_loadbang, + gensym("loadbang"), 0); + class_addmethod(hradio_class, (t_method)hradio_set, + gensym("set"), A_FLOAT, 0); + class_addmethod(hradio_class, (t_method)hradio_size, + gensym("size"), A_GIMME, 0); + class_addmethod(hradio_class, (t_method)hradio_delta, + gensym("delta"), A_GIMME, 0); + class_addmethod(hradio_class, (t_method)hradio_pos, + gensym("pos"), A_GIMME, 0); + class_addmethod(hradio_class, (t_method)hradio_color, + gensym("color"), A_GIMME, 0); + class_addmethod(hradio_class, (t_method)hradio_send, + gensym("send"), A_DEFSYM, 0); + class_addmethod(hradio_class, (t_method)hradio_receive, + gensym("receive"), A_DEFSYM, 0); + class_addmethod(hradio_class, (t_method)hradio_label, + gensym("label"), A_DEFSYM, 0); + class_addmethod(hradio_class, (t_method)hradio_label_pos, + gensym("label_pos"), A_GIMME, 0); + class_addmethod(hradio_class, (t_method)hradio_label_font, + gensym("label_font"), A_GIMME, 0); + class_addmethod(hradio_class, (t_method)hradio_init, + gensym("init"), A_FLOAT, 0); + class_addmethod(hradio_class, (t_method)hradio_number, + gensym("number"), A_FLOAT, 0); + class_addmethod(hradio_class, (t_method)hradio_single_change, + gensym("single_change"), 0); + class_addmethod(hradio_class, (t_method)hradio_double_change, + gensym("double_change"), 0); + hradio_widgetbehavior.w_getrectfn = hradio_getrect; + hradio_widgetbehavior.w_displacefn = iemgui_displace; + hradio_widgetbehavior.w_selectfn = iemgui_select; + hradio_widgetbehavior.w_activatefn = NULL; + hradio_widgetbehavior.w_deletefn = iemgui_delete; + hradio_widgetbehavior.w_visfn = iemgui_vis; + hradio_widgetbehavior.w_clickfn = hradio_newclick; + class_setwidget(hradio_class, &hradio_widgetbehavior); + class_sethelpsymbol(hradio_class, gensym("hradio")); + class_setsavefn(hradio_class, hradio_save); + class_setpropertiesfn(hradio_class, hradio_properties); + + /*obsolete version (0.34-0.35) */ + hradio_old_class = class_new(gensym("hdl"), (t_newmethod)hdial_new, + (t_method)hradio_ff, sizeof(t_hradio), 0, A_GIMME, 0); + class_addcreator((t_newmethod)hradio_new, gensym("rdb"), A_GIMME, 0); + class_addcreator((t_newmethod)hradio_new, gensym("radiobut"), A_GIMME, 0); + class_addcreator((t_newmethod)hradio_new, gensym("radiobutton"), + A_GIMME, 0); + class_addbang(hradio_old_class, hradio_bang); + class_addfloat(hradio_old_class, hradio_float); + class_addmethod(hradio_old_class, (t_method)hradio_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(hradio_old_class, (t_method)hradio_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(hradio_old_class, (t_method)hradio_loadbang, + gensym("loadbang"), 0); + class_addmethod(hradio_old_class, (t_method)hradio_set, + gensym("set"), A_FLOAT, 0); + class_addmethod(hradio_old_class, (t_method)hradio_size, + gensym("size"), A_GIMME, 0); + class_addmethod(hradio_old_class, (t_method)hradio_delta, + gensym("delta"), A_GIMME, 0); + class_addmethod(hradio_old_class, (t_method)hradio_pos, + gensym("pos"), A_GIMME, 0); + class_addmethod(hradio_old_class, (t_method)hradio_color, + gensym("color"), A_GIMME, 0); + class_addmethod(hradio_old_class, (t_method)hradio_send, + gensym("send"), A_DEFSYM, 0); + class_addmethod(hradio_old_class, (t_method)hradio_receive, + gensym("receive"), A_DEFSYM, 0); + class_addmethod(hradio_old_class, (t_method)hradio_label, + gensym("label"), A_DEFSYM, 0); + class_addmethod(hradio_old_class, (t_method)hradio_label_pos, + gensym("label_pos"), A_GIMME, 0); + class_addmethod(hradio_old_class, (t_method)hradio_label_font, + gensym("label_font"), A_GIMME, 0); + class_addmethod(hradio_old_class, (t_method)hradio_init, + gensym("init"), A_FLOAT, 0); + class_addmethod(hradio_old_class, (t_method)hradio_number, + gensym("number"), A_FLOAT, 0); + class_addmethod(hradio_old_class, (t_method)hradio_single_change, + gensym("single_change"), 0); + class_addmethod(hradio_old_class, (t_method)hradio_double_change, + gensym("double_change"), 0); + class_setwidget(hradio_old_class, &hradio_widgetbehavior); + class_sethelpsymbol(hradio_old_class, gensym("hradio")); +} diff --git a/apps/plugins/pdbox/PDa/src/g_hslider.c b/apps/plugins/pdbox/PDa/src/g_hslider.c new file mode 100644 index 0000000..77cd05c --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_hslider.c @@ -0,0 +1,1308 @@ +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + + +/* ------------ hsl gui-horicontal slider ----------------------- */ + +t_widgetbehavior hslider_widgetbehavior; +static t_class *hslider_class; + +/* widget helper functions */ + +static void hslider_draw_update(t_hslider *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + + if (glist_isvisible(glist)) + { + int r = text_xpix(&x->x_gui.x_obj, glist) + (x->x_val + 50)/100; + sys_vgui(".x%x.c coords %xKNOB %d %d %d %d\n", + canvas, x, r, ypos+1, + r, ypos + x->x_gui.x_h); + if(x->x_val == x->x_center) + { + if(!x->x_thick) + { + sys_vgui(".x%x.c itemconfigure %xKNOB -width 7\n", canvas, x); + x->x_thick = 1; + } + } + else + { + if(x->x_thick) + { + sys_vgui(".x%x.c itemconfigure %xKNOB -width 3\n", canvas, x); + x->x_thick = 0; + } + } + } +} + +static void hslider_draw_new(t_hslider *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + int r = xpos + (x->x_val + 50)/100; + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n", + canvas, xpos-3, ypos, + xpos + x->x_gui.x_w+2, ypos + x->x_gui.x_h, + x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create line %d %d %d %d -width 3 -fill #%6.6x -tags %xKNOB\n", + canvas, r, ypos+1, r, + ypos + x->x_gui.x_h, x->x_gui.x_fcol, x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xpos+x->x_gui.x_ldx, + ypos+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xpos-3, ypos + x->x_gui.x_h-1, + xpos+4, ypos + x->x_gui.x_h, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xpos-3, ypos, + xpos+4, ypos+1, x, 0); +} + +static void hslider_draw_move(t_hslider *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + int r = xpos + (x->x_val + 50)/100; + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n", + canvas, x, + xpos-3, ypos, + xpos + x->x_gui.x_w+2, ypos + x->x_gui.x_h); + sys_vgui(".x%x.c coords %xKNOB %d %d %d %d\n", + canvas, x, r, ypos+1, + r, ypos + x->x_gui.x_h); + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + canvas, x, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, + xpos-3, ypos + x->x_gui.x_h-1, + xpos+4, ypos + x->x_gui.x_h); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, + xpos-3, ypos, + xpos+4, ypos+1); +} + +static void hslider_draw_erase(t_hslider* x,t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c delete %xBASE\n", canvas, x); + sys_vgui(".x%x.c delete %xKNOB\n", canvas, x); + sys_vgui(".x%x.c delete %xLABEL\n", canvas, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +static void hslider_draw_config(t_hslider* x,t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + sys_vgui(".x%x.c itemconfigure %xKNOB -fill #%6.6x\n", canvas, x, x->x_gui.x_fcol); + sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas, x, x->x_gui.x_bcol); +} + +static void hslider_draw_io(t_hslider* x,t_glist* glist, int old_snd_rcv_flags) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xpos-3, ypos + x->x_gui.x_h-1, + xpos+4, ypos + x->x_gui.x_h, x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xpos-3, ypos, + xpos+4, ypos+1, x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +static void hslider_draw_select(t_hslider* x,t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + if(x->x_gui.x_fsf.x_selected) + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol); + } +} + +void hslider_draw(t_hslider *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + hslider_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + hslider_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + hslider_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + hslider_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + hslider_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + hslider_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + hslider_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ hsl widgetbehaviour----------------------------- */ + + +static void hslider_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_hslider* x = (t_hslider*)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist) - 3; + *yp1 = text_ypix(&x->x_gui.x_obj, glist); + *xp2 = *xp1 + x->x_gui.x_w + 5; + *yp2 = *yp1 + x->x_gui.x_h; +} + +static void hslider_save(t_gobj *z, t_binbuf *b) +{ + t_hslider *x = (t_hslider *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiiffiisssiiiiiiiii", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix, + gensym("hsl"), x->x_gui.x_w, x->x_gui.x_h, + (float)x->x_min, (float)x->x_max, + x->x_lin0_log1, iem_symargstoint(&x->x_gui.x_isa), + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2], + x->x_val, x->x_steady); + binbuf_addv(b, ";"); +} + +void hslider_check_width(t_hslider *x, int w) +{ + if(w < IEM_SL_MINSIZE) + w = IEM_SL_MINSIZE; + x->x_gui.x_w = w; + x->x_center = (x->x_gui.x_w-1)*50; + if(x->x_val > (x->x_gui.x_w*100 - 100)) + { + x->x_pos = x->x_gui.x_w*100 - 100; + x->x_val = x->x_pos; + } + if(x->x_lin0_log1) + x->x_k = log(x->x_max/x->x_min)/(double)(x->x_gui.x_w - 1); + else + x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_w - 1); +} + +void hslider_check_minmax(t_hslider *x, double min, double max) +{ + if(x->x_lin0_log1) + { + if((min == 0.0)&&(max == 0.0)) + max = 1.0; + if(max > 0.0) + { + if(min <= 0.0) + min = 0.01*max; + } + else + { + if(min > 0.0) + max = 0.01*min; + } + } + x->x_min = min; + x->x_max = max; + if(x->x_min > x->x_max) /* bugfix */ + x->x_gui.x_isa.x_reverse = 1; + else + x->x_gui.x_isa.x_reverse = 0; + if(x->x_lin0_log1) + x->x_k = log(x->x_max/x->x_min)/(double)(x->x_gui.x_w - 1); + else + x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_w - 1); +} + +static void hslider_properties(t_gobj *z, t_glist *owner) +{ + t_hslider *x = (t_hslider *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + sprintf(buf, "pdtk_iemgui_dialog %%s HSLIDER \ + --------dimensions(pix)(pix):-------- %d %d width: %d %d height: \ + -----------output-range:----------- %g left: %g right: %g \ + %d lin log %d %d empty %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, IEM_SL_MINSIZE, x->x_gui.x_h, IEM_GUI_MINSIZE, + x->x_min, x->x_max, 0.0,/*no_schedule*/ + x->x_lin0_log1, x->x_gui.x_isa.x_loadinit, x->x_steady, -1,/*no multi, but iem-characteristic*/ + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void hslider_set(t_hslider *x, t_floatarg f) /* bugfix */ +{ + double g; + + if(x->x_gui.x_isa.x_reverse) /* bugfix */ + { + if(f > x->x_min) + f = x->x_min; + if(f < x->x_max) + f = x->x_max; + } + else + { + if(f > x->x_max) + f = x->x_max; + if(f < x->x_min) + f = x->x_min; + } + if(x->x_lin0_log1) + g = log(f/x->x_min)/x->x_k; + else + g = (f - x->x_min) / x->x_k; + x->x_val = (int)(100.0*g + 0.49999); + x->x_pos = x->x_val; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); +} + +static void hslider_bang(t_hslider *x) +{ + double out; + + if(x->x_lin0_log1) + out = x->x_min*exp(x->x_k*(double)(x->x_val)*0.01); + else + out = (double)(x->x_val)*0.01*x->x_k + x->x_min; + if((out < 1.0e-10)&&(out > -1.0e-10)) + out = 0.0; + outlet_float(x->x_gui.x_obj.ob_outlet, out); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, out); +} + +static void hslider_dialog(t_hslider *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int w = (int)atom_getintarg(0, argc, argv); + int h = (int)atom_getintarg(1, argc, argv); + double min = (double)atom_getfloatarg(2, argc, argv); + double max = (double)atom_getfloatarg(3, argc, argv); + int lilo = (int)atom_getintarg(4, argc, argv); + int steady = (int)atom_getintarg(17, argc, argv); + int sr_flags; + + if(lilo != 0) lilo = 1; + x->x_lin0_log1 = lilo; + if(steady) + x->x_steady = 1; + else + x->x_steady = 0; + sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + x->x_gui.x_h = iemgui_clip_size(h); + hslider_check_width(x, w); + hslider_check_minmax(x, min, max); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); +} + +static void hslider_motion(t_hslider *x, t_floatarg dx, t_floatarg dy) +{ + int old = x->x_val; + + if(x->x_gui.x_fsf.x_finemoved) + x->x_pos += (int)dx; + else + x->x_pos += 100*(int)dx; + x->x_val = x->x_pos; + if(x->x_val > (100*x->x_gui.x_w - 100)) + { + x->x_val = 100*x->x_gui.x_w - 100; + x->x_pos += 50; + x->x_pos -= x->x_pos%100; + } + if(x->x_val < 0) + { + x->x_val = 0; + x->x_pos -= 50; + x->x_pos -= x->x_pos%100; + } + if(old != x->x_val) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + hslider_bang(x); + } +} + +static void hslider_click(t_hslider *x, t_floatarg xpos, t_floatarg ypos, + t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + if(!x->x_steady) + x->x_val = (int)(100.0 * (xpos - text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist))); + if(x->x_val > (100*x->x_gui.x_w - 100)) + x->x_val = 100*x->x_gui.x_w - 100; + if(x->x_val < 0) + x->x_val = 0; + x->x_pos = x->x_val; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + hslider_bang(x); + glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.te_g, (t_glistmotionfn)hslider_motion, + 0, xpos, ypos); +} + +static int hslider_newclick(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_hslider* x = (t_hslider *)z; + + if(doit) + { + hslider_click( x, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, + 0, (t_floatarg)alt); + if(shift) + x->x_gui.x_fsf.x_finemoved = 1; + else + x->x_gui.x_fsf.x_finemoved = 0; + } + return (1); +} + +static void hslider_size(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{ + hslider_check_width(x, (int)atom_getintarg(0, ac, av)); + if(ac > 1) + x->x_gui.x_h = iemgui_clip_size((int)atom_getintarg(1, ac, av)); + iemgui_size((void *)x, &x->x_gui); +} + +static void hslider_delta(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void hslider_pos(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void hslider_range(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{ + hslider_check_minmax(x, (double)atom_getfloatarg(0, ac, av), + (double)atom_getfloatarg(1, ac, av)); +} + +static void hslider_color(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void hslider_send(t_hslider *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void hslider_receive(t_hslider *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void hslider_label(t_hslider *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void hslider_label_pos(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void hslider_label_font(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void hslider_log(t_hslider *x) +{ + x->x_lin0_log1 = 1; + hslider_check_minmax(x, x->x_min, x->x_max); +} + +static void hslider_lin(t_hslider *x) +{ + x->x_lin0_log1 = 0; + x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_w - 1); +} + +static void hslider_init(t_hslider *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void hslider_steady(t_hslider *x, t_floatarg f) +{ + x->x_steady = (f==0.0)?0:1; +} + +static void hslider_float(t_hslider *x, t_floatarg f) +{ + double out; + + hslider_set(x, f); + if(x->x_lin0_log1) + out = x->x_min*exp(x->x_k*(double)(x->x_val)*0.01); + else + out = (double)(x->x_val)*0.01*x->x_k + x->x_min; + if((out < 1.0e-10)&&(out > -1.0e-10)) + out = 0.0; + if(x->x_gui.x_fsf.x_put_in2out) + { + outlet_float(x->x_gui.x_obj.ob_outlet, out); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, out); + } +} + +static void hslider_loadbang(t_hslider *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + hslider_bang(x); + } +} + +static void *hslider_new(t_symbol *s, int argc, t_atom *argv) +{ + t_hslider *x = (t_hslider *)pd_new(hslider_class); + int bflcol[]={-262144, -1, -1}; + int w=IEM_SL_DEFAULTSIZE, h=IEM_GUI_DEFAULTSIZE; + int lilo=0, ldx=-2, ldy=-6, f=0, v=0, steady=1; + int fs=8; + double min=0.0, max=(double)(IEM_SL_DEFAULTSIZE-1); + char str[144]; + + iem_inttosymargs(&x->x_gui.x_isa, 0); + iem_inttofstyle(&x->x_gui.x_fsf, 0); + + if(((argc == 17)||(argc == 18))&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1) + &&IS_A_FLOAT(argv,2)&&IS_A_FLOAT(argv,3) + &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5) + &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) + &&(IS_A_SYMBOL(argv,7)||IS_A_FLOAT(argv,7)) + &&(IS_A_SYMBOL(argv,8)||IS_A_FLOAT(argv,8)) + &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10) + &&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13) + &&IS_A_FLOAT(argv,14)&&IS_A_FLOAT(argv,15)&&IS_A_FLOAT(argv,16)) + { + w = (int)atom_getintarg(0, argc, argv); + h = (int)atom_getintarg(1, argc, argv); + min = (double)atom_getfloatarg(2, argc, argv); + max = (double)atom_getfloatarg(3, argc, argv); + lilo = (int)atom_getintarg(4, argc, argv); + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(5, argc, argv)); + iemgui_new_getnames(&x->x_gui, 6, argv); + ldx = (int)atom_getintarg(9, argc, argv); + ldy = (int)atom_getintarg(10, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(11, argc, argv)); + fs = (int)atom_getintarg(12, argc, argv); + bflcol[0] = (int)atom_getintarg(13, argc, argv); + bflcol[1] = (int)atom_getintarg(14, argc, argv); + bflcol[2] = (int)atom_getintarg(15, argc, argv); + v = (int)atom_getintarg(16, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 6, 0); + if((argc == 18)&&IS_A_FLOAT(argv,17)) + steady = (int)atom_getintarg(17, argc, argv); + + x->x_gui.x_draw = (t_iemfunptr)hslider_draw; + + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if(x->x_gui.x_isa.x_loadinit) + x->x_val = v; + else + x->x_val = 0; + x->x_pos = x->x_val; + if(lilo != 0) lilo = 1; + x->x_lin0_log1 = lilo; + if(steady != 0) steady = 1; + x->x_steady = steady; + if (!strcmp(x->x_gui.x_snd->s_name, "empty")) + x->x_gui.x_fsf.x_snd_able = 0; + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + if(x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + x->x_gui.x_h = iemgui_clip_size(h); + hslider_check_width(x, w); + hslider_check_minmax(x, min, max); + iemgui_all_colfromload(&x->x_gui, bflcol); + x->x_thick = 0; + iemgui_verify_snd_ne_rcv(&x->x_gui); + outlet_new(&x->x_gui.x_obj, &s_float); + return (x); +} + +static void hslider_free(t_hslider *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + gfxstub_deleteforkey(x); +} + +void g_hslider_setup(void) +{ + hslider_class = class_new(gensym("hsl"), (t_newmethod)hslider_new, + (t_method)hslider_free, sizeof(t_hslider), 0, A_GIMME, 0); +#ifndef GGEE_HSLIDER_COMPATIBLE + class_addcreator((t_newmethod)hslider_new, gensym("hslider"), A_GIMME, 0); +#endif + class_addbang(hslider_class,hslider_bang); + class_addfloat(hslider_class,hslider_float); + class_addmethod(hslider_class, (t_method)hslider_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(hslider_class, (t_method)hslider_motion, gensym("motion"), + A_FLOAT, A_FLOAT, 0); + class_addmethod(hslider_class, (t_method)hslider_dialog, gensym("dialog"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_loadbang, gensym("loadbang"), 0); + class_addmethod(hslider_class, (t_method)hslider_set, gensym("set"), A_FLOAT, 0); + class_addmethod(hslider_class, (t_method)hslider_size, gensym("size"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_range, gensym("range"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_color, gensym("color"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_send, gensym("send"), A_DEFSYM, 0); + class_addmethod(hslider_class, (t_method)hslider_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(hslider_class, (t_method)hslider_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(hslider_class, (t_method)hslider_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_label_font, gensym("label_font"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_log, gensym("log"), 0); + class_addmethod(hslider_class, (t_method)hslider_lin, gensym("lin"), 0); + class_addmethod(hslider_class, (t_method)hslider_init, gensym("init"), A_FLOAT, 0); + class_addmethod(hslider_class, (t_method)hslider_steady, gensym("steady"), A_FLOAT, 0); + hslider_widgetbehavior.w_getrectfn = hslider_getrect; + hslider_widgetbehavior.w_displacefn = iemgui_displace; + hslider_widgetbehavior.w_selectfn = iemgui_select; + hslider_widgetbehavior.w_activatefn = NULL; + hslider_widgetbehavior.w_deletefn = iemgui_delete; + hslider_widgetbehavior.w_visfn = iemgui_vis; + hslider_widgetbehavior.w_clickfn = hslider_newclick; + class_setwidget(hslider_class, &hslider_widgetbehavior); + class_sethelpsymbol(hslider_class, gensym("hslider")); + class_setsavefn(hslider_class, hslider_save); + class_setpropertiesfn(hslider_class, hslider_properties); +} +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + + +/* ------------ hsl gui-horicontal slider ----------------------- */ + +t_widgetbehavior hslider_widgetbehavior; +static t_class *hslider_class; + +/* widget helper functions */ + +static void hslider_draw_update(t_hslider *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + + if (glist_isvisible(glist)) + { + int r = text_xpix(&x->x_gui.x_obj, glist) + (x->x_val + 50)/100; + sys_vgui(".x%x.c coords %xKNOB %d %d %d %d\n", + canvas, x, r, ypos+1, + r, ypos + x->x_gui.x_h); + if(x->x_val == x->x_center) + { + if(!x->x_thick) + { + sys_vgui(".x%x.c itemconfigure %xKNOB -width 7\n", canvas, x); + x->x_thick = 1; + } + } + else + { + if(x->x_thick) + { + sys_vgui(".x%x.c itemconfigure %xKNOB -width 3\n", canvas, x); + x->x_thick = 0; + } + } + } +} + +static void hslider_draw_new(t_hslider *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + int r = xpos + (x->x_val + 50)/100; + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n", + canvas, xpos-3, ypos, + xpos + x->x_gui.x_w+2, ypos + x->x_gui.x_h, + x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create line %d %d %d %d -width 3 -fill #%6.6x -tags %xKNOB\n", + canvas, r, ypos+1, r, + ypos + x->x_gui.x_h, x->x_gui.x_fcol, x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xpos+x->x_gui.x_ldx, + ypos+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xpos-3, ypos + x->x_gui.x_h-1, + xpos+4, ypos + x->x_gui.x_h, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xpos-3, ypos, + xpos+4, ypos+1, x, 0); +} + +static void hslider_draw_move(t_hslider *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + int r = xpos + (x->x_val + 50)/100; + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n", + canvas, x, + xpos-3, ypos, + xpos + x->x_gui.x_w+2, ypos + x->x_gui.x_h); + sys_vgui(".x%x.c coords %xKNOB %d %d %d %d\n", + canvas, x, r, ypos+1, + r, ypos + x->x_gui.x_h); + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + canvas, x, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, + xpos-3, ypos + x->x_gui.x_h-1, + xpos+4, ypos + x->x_gui.x_h); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, + xpos-3, ypos, + xpos+4, ypos+1); +} + +static void hslider_draw_erase(t_hslider* x,t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c delete %xBASE\n", canvas, x); + sys_vgui(".x%x.c delete %xKNOB\n", canvas, x); + sys_vgui(".x%x.c delete %xLABEL\n", canvas, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +static void hslider_draw_config(t_hslider* x,t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + sys_vgui(".x%x.c itemconfigure %xKNOB -fill #%6.6x\n", canvas, x, x->x_gui.x_fcol); + sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas, x, x->x_gui.x_bcol); +} + +static void hslider_draw_io(t_hslider* x,t_glist* glist, int old_snd_rcv_flags) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xpos-3, ypos + x->x_gui.x_h-1, + xpos+4, ypos + x->x_gui.x_h, x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xpos-3, ypos, + xpos+4, ypos+1, x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +static void hslider_draw_select(t_hslider* x,t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + if(x->x_gui.x_fsf.x_selected) + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol); + } +} + +void hslider_draw(t_hslider *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + hslider_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + hslider_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + hslider_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + hslider_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + hslider_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + hslider_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + hslider_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ hsl widgetbehaviour----------------------------- */ + + +static void hslider_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_hslider* x = (t_hslider*)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist) - 3; + *yp1 = text_ypix(&x->x_gui.x_obj, glist); + *xp2 = *xp1 + x->x_gui.x_w + 5; + *yp2 = *yp1 + x->x_gui.x_h; +} + +static void hslider_save(t_gobj *z, t_binbuf *b) +{ + t_hslider *x = (t_hslider *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiiffiisssiiiiiiiii", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix, + gensym("hsl"), x->x_gui.x_w, x->x_gui.x_h, + (float)x->x_min, (float)x->x_max, + x->x_lin0_log1, iem_symargstoint(&x->x_gui.x_isa), + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2], + x->x_val, x->x_steady); + binbuf_addv(b, ";"); +} + +void hslider_check_width(t_hslider *x, int w) +{ + if(w < IEM_SL_MINSIZE) + w = IEM_SL_MINSIZE; + x->x_gui.x_w = w; + x->x_center = (x->x_gui.x_w-1)*50; + if(x->x_val > (x->x_gui.x_w*100 - 100)) + { + x->x_pos = x->x_gui.x_w*100 - 100; + x->x_val = x->x_pos; + } + if(x->x_lin0_log1) + x->x_k = log(x->x_max/x->x_min)/(double)(x->x_gui.x_w - 1); + else + x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_w - 1); +} + +void hslider_check_minmax(t_hslider *x, double min, double max) +{ + if(x->x_lin0_log1) + { + if((min == 0.0)&&(max == 0.0)) + max = 1.0; + if(max > 0.0) + { + if(min <= 0.0) + min = 0.01*max; + } + else + { + if(min > 0.0) + max = 0.01*min; + } + } + x->x_min = min; + x->x_max = max; + if(x->x_min > x->x_max) /* bugfix */ + x->x_gui.x_isa.x_reverse = 1; + else + x->x_gui.x_isa.x_reverse = 0; + if(x->x_lin0_log1) + x->x_k = log(x->x_max/x->x_min)/(double)(x->x_gui.x_w - 1); + else + x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_w - 1); +} + +static void hslider_properties(t_gobj *z, t_glist *owner) +{ + t_hslider *x = (t_hslider *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + sprintf(buf, "pdtk_iemgui_dialog %%s HSLIDER \ + --------dimensions(pix)(pix):-------- %d %d width: %d %d height: \ + -----------output-range:----------- %g left: %g right: %g \ + %d lin log %d %d empty %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, IEM_SL_MINSIZE, x->x_gui.x_h, IEM_GUI_MINSIZE, + x->x_min, x->x_max, 0.0,/*no_schedule*/ + x->x_lin0_log1, x->x_gui.x_isa.x_loadinit, x->x_steady, -1,/*no multi, but iem-characteristic*/ + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void hslider_set(t_hslider *x, t_floatarg f) /* bugfix */ +{ + double g; + + if(x->x_gui.x_isa.x_reverse) /* bugfix */ + { + if(f > x->x_min) + f = x->x_min; + if(f < x->x_max) + f = x->x_max; + } + else + { + if(f > x->x_max) + f = x->x_max; + if(f < x->x_min) + f = x->x_min; + } + if(x->x_lin0_log1) + g = log(f/x->x_min)/x->x_k; + else + g = (f - x->x_min) / x->x_k; + x->x_val = (int)(100.0*g + 0.49999); + x->x_pos = x->x_val; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); +} + +static void hslider_bang(t_hslider *x) +{ + double out; + + if(x->x_lin0_log1) + out = x->x_min*exp(x->x_k*(double)(x->x_val)*0.01); + else + out = (double)(x->x_val)*0.01*x->x_k + x->x_min; + if((out < 1.0e-10)&&(out > -1.0e-10)) + out = 0.0; + outlet_float(x->x_gui.x_obj.ob_outlet, out); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, out); +} + +static void hslider_dialog(t_hslider *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int w = (int)atom_getintarg(0, argc, argv); + int h = (int)atom_getintarg(1, argc, argv); + double min = (double)atom_getfloatarg(2, argc, argv); + double max = (double)atom_getfloatarg(3, argc, argv); + int lilo = (int)atom_getintarg(4, argc, argv); + int steady = (int)atom_getintarg(17, argc, argv); + int sr_flags; + + if(lilo != 0) lilo = 1; + x->x_lin0_log1 = lilo; + if(steady) + x->x_steady = 1; + else + x->x_steady = 0; + sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + x->x_gui.x_h = iemgui_clip_size(h); + hslider_check_width(x, w); + hslider_check_minmax(x, min, max); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); +} + +static void hslider_motion(t_hslider *x, t_floatarg dx, t_floatarg dy) +{ + int old = x->x_val; + + if(x->x_gui.x_fsf.x_finemoved) + x->x_pos += (int)dx; + else + x->x_pos += 100*(int)dx; + x->x_val = x->x_pos; + if(x->x_val > (100*x->x_gui.x_w - 100)) + { + x->x_val = 100*x->x_gui.x_w - 100; + x->x_pos += 50; + x->x_pos -= x->x_pos%100; + } + if(x->x_val < 0) + { + x->x_val = 0; + x->x_pos -= 50; + x->x_pos -= x->x_pos%100; + } + if(old != x->x_val) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + hslider_bang(x); + } +} + +static void hslider_click(t_hslider *x, t_floatarg xpos, t_floatarg ypos, + t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + if(!x->x_steady) + x->x_val = (int)(100.0 * (xpos - text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist))); + if(x->x_val > (100*x->x_gui.x_w - 100)) + x->x_val = 100*x->x_gui.x_w - 100; + if(x->x_val < 0) + x->x_val = 0; + x->x_pos = x->x_val; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + hslider_bang(x); + glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.te_g, (t_glistmotionfn)hslider_motion, + 0, xpos, ypos); +} + +static int hslider_newclick(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_hslider* x = (t_hslider *)z; + + if(doit) + { + hslider_click( x, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, + 0, (t_floatarg)alt); + if(shift) + x->x_gui.x_fsf.x_finemoved = 1; + else + x->x_gui.x_fsf.x_finemoved = 0; + } + return (1); +} + +static void hslider_size(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{ + hslider_check_width(x, (int)atom_getintarg(0, ac, av)); + if(ac > 1) + x->x_gui.x_h = iemgui_clip_size((int)atom_getintarg(1, ac, av)); + iemgui_size((void *)x, &x->x_gui); +} + +static void hslider_delta(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void hslider_pos(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void hslider_range(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{ + hslider_check_minmax(x, (double)atom_getfloatarg(0, ac, av), + (double)atom_getfloatarg(1, ac, av)); +} + +static void hslider_color(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void hslider_send(t_hslider *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void hslider_receive(t_hslider *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void hslider_label(t_hslider *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void hslider_label_pos(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void hslider_label_font(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void hslider_log(t_hslider *x) +{ + x->x_lin0_log1 = 1; + hslider_check_minmax(x, x->x_min, x->x_max); +} + +static void hslider_lin(t_hslider *x) +{ + x->x_lin0_log1 = 0; + x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_w - 1); +} + +static void hslider_init(t_hslider *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void hslider_steady(t_hslider *x, t_floatarg f) +{ + x->x_steady = (f==0.0)?0:1; +} + +static void hslider_float(t_hslider *x, t_floatarg f) +{ + double out; + + hslider_set(x, f); + if(x->x_lin0_log1) + out = x->x_min*exp(x->x_k*(double)(x->x_val)*0.01); + else + out = (double)(x->x_val)*0.01*x->x_k + x->x_min; + if((out < 1.0e-10)&&(out > -1.0e-10)) + out = 0.0; + if(x->x_gui.x_fsf.x_put_in2out) + { + outlet_float(x->x_gui.x_obj.ob_outlet, out); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, out); + } +} + +static void hslider_loadbang(t_hslider *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + hslider_bang(x); + } +} + +static void *hslider_new(t_symbol *s, int argc, t_atom *argv) +{ + t_hslider *x = (t_hslider *)pd_new(hslider_class); + int bflcol[]={-262144, -1, -1}; + int w=IEM_SL_DEFAULTSIZE, h=IEM_GUI_DEFAULTSIZE; + int lilo=0, ldx=-2, ldy=-6, f=0, v=0, steady=1; + int fs=8; + double min=0.0, max=(double)(IEM_SL_DEFAULTSIZE-1); + char str[144]; + + iem_inttosymargs(&x->x_gui.x_isa, 0); + iem_inttofstyle(&x->x_gui.x_fsf, 0); + + if(((argc == 17)||(argc == 18))&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1) + &&IS_A_FLOAT(argv,2)&&IS_A_FLOAT(argv,3) + &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5) + &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) + &&(IS_A_SYMBOL(argv,7)||IS_A_FLOAT(argv,7)) + &&(IS_A_SYMBOL(argv,8)||IS_A_FLOAT(argv,8)) + &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10) + &&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13) + &&IS_A_FLOAT(argv,14)&&IS_A_FLOAT(argv,15)&&IS_A_FLOAT(argv,16)) + { + w = (int)atom_getintarg(0, argc, argv); + h = (int)atom_getintarg(1, argc, argv); + min = (double)atom_getfloatarg(2, argc, argv); + max = (double)atom_getfloatarg(3, argc, argv); + lilo = (int)atom_getintarg(4, argc, argv); + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(5, argc, argv)); + iemgui_new_getnames(&x->x_gui, 6, argv); + ldx = (int)atom_getintarg(9, argc, argv); + ldy = (int)atom_getintarg(10, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(11, argc, argv)); + fs = (int)atom_getintarg(12, argc, argv); + bflcol[0] = (int)atom_getintarg(13, argc, argv); + bflcol[1] = (int)atom_getintarg(14, argc, argv); + bflcol[2] = (int)atom_getintarg(15, argc, argv); + v = (int)atom_getintarg(16, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 6, 0); + if((argc == 18)&&IS_A_FLOAT(argv,17)) + steady = (int)atom_getintarg(17, argc, argv); + + x->x_gui.x_draw = (t_iemfunptr)hslider_draw; + + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if(x->x_gui.x_isa.x_loadinit) + x->x_val = v; + else + x->x_val = 0; + x->x_pos = x->x_val; + if(lilo != 0) lilo = 1; + x->x_lin0_log1 = lilo; + if(steady != 0) steady = 1; + x->x_steady = steady; + if (!strcmp(x->x_gui.x_snd->s_name, "empty")) + x->x_gui.x_fsf.x_snd_able = 0; + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + if(x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + x->x_gui.x_h = iemgui_clip_size(h); + hslider_check_width(x, w); + hslider_check_minmax(x, min, max); + iemgui_all_colfromload(&x->x_gui, bflcol); + x->x_thick = 0; + iemgui_verify_snd_ne_rcv(&x->x_gui); + outlet_new(&x->x_gui.x_obj, &s_float); + return (x); +} + +static void hslider_free(t_hslider *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + gfxstub_deleteforkey(x); +} + +void g_hslider_setup(void) +{ + hslider_class = class_new(gensym("hsl"), (t_newmethod)hslider_new, + (t_method)hslider_free, sizeof(t_hslider), 0, A_GIMME, 0); +#ifndef GGEE_HSLIDER_COMPATIBLE + class_addcreator((t_newmethod)hslider_new, gensym("hslider"), A_GIMME, 0); +#endif + class_addbang(hslider_class,hslider_bang); + class_addfloat(hslider_class,hslider_float); + class_addmethod(hslider_class, (t_method)hslider_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(hslider_class, (t_method)hslider_motion, gensym("motion"), + A_FLOAT, A_FLOAT, 0); + class_addmethod(hslider_class, (t_method)hslider_dialog, gensym("dialog"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_loadbang, gensym("loadbang"), 0); + class_addmethod(hslider_class, (t_method)hslider_set, gensym("set"), A_FLOAT, 0); + class_addmethod(hslider_class, (t_method)hslider_size, gensym("size"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_range, gensym("range"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_color, gensym("color"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_send, gensym("send"), A_DEFSYM, 0); + class_addmethod(hslider_class, (t_method)hslider_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(hslider_class, (t_method)hslider_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(hslider_class, (t_method)hslider_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_label_font, gensym("label_font"), A_GIMME, 0); + class_addmethod(hslider_class, (t_method)hslider_log, gensym("log"), 0); + class_addmethod(hslider_class, (t_method)hslider_lin, gensym("lin"), 0); + class_addmethod(hslider_class, (t_method)hslider_init, gensym("init"), A_FLOAT, 0); + class_addmethod(hslider_class, (t_method)hslider_steady, gensym("steady"), A_FLOAT, 0); + hslider_widgetbehavior.w_getrectfn = hslider_getrect; + hslider_widgetbehavior.w_displacefn = iemgui_displace; + hslider_widgetbehavior.w_selectfn = iemgui_select; + hslider_widgetbehavior.w_activatefn = NULL; + hslider_widgetbehavior.w_deletefn = iemgui_delete; + hslider_widgetbehavior.w_visfn = iemgui_vis; + hslider_widgetbehavior.w_clickfn = hslider_newclick; + class_setwidget(hslider_class, &hslider_widgetbehavior); + class_sethelpsymbol(hslider_class, gensym("hslider")); + class_setsavefn(hslider_class, hslider_save); + class_setpropertiesfn(hslider_class, hslider_properties); +} diff --git a/apps/plugins/pdbox/PDa/src/g_io.c b/apps/plugins/pdbox/PDa/src/g_io.c new file mode 100644 index 0000000..39788d2 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_io.c @@ -0,0 +1,1224 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* graphical inlets and outlets, both for control and signals. */ + +/* This code is highly inefficient; messages actually have to be forwarded +by inlets and outlets. The outlet is in even worse shape than the inlet; +in order to avoid having a "signal" method in the class, the oulet actually +sprouts an inlet, which forwards the message to the "outlet" object, which +sends it on to the outlet proper. Another way to do it would be to have +separate classes for "signal" and "control" outlets, but this would complicate +life elsewhere. */ + + +/* hacked to run subpatches with different samplerates + * + * mfg.gfd.uil + * IOhannes + * + * edited lines are marked with "IOhannes" + * + */ + +#include "m_pd.h" +#include "g_canvas.h" +#include +void signal_setborrowed(t_signal *sig, t_signal *sig2); +void signal_makereusable(t_signal *sig); + +/* ------------------------- vinlet -------------------------- */ +t_class *vinlet_class; + +typedef struct _vinlet +{ + t_object x_obj; + t_canvas *x_canvas; + t_inlet *x_inlet; + int x_bufsize; + t_float *x_buf; /* signal buffer; zero if not a signal */ + t_float *x_endbuf; + t_float *x_fill; + t_float *x_read; + int x_hop; + /* if not reblocking, the next slot communicates the parent's inlet + signal from the prolog to the DSP routine: */ + t_signal *x_directsignal; + + t_resample x_updown; /* IOhannes */ +} t_vinlet; + +static void *vinlet_new(t_symbol *s) +{ + t_vinlet *x = (t_vinlet *)pd_new(vinlet_class); + x->x_canvas = canvas_getcurrent(); + x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, 0); + x->x_bufsize = 0; + x->x_buf = 0; + outlet_new(&x->x_obj, 0); + return (x); +} + +static void vinlet_bang(t_vinlet *x) +{ + outlet_bang(x->x_obj.ob_outlet); +} + +static void vinlet_pointer(t_vinlet *x, t_gpointer *gp) +{ + outlet_pointer(x->x_obj.ob_outlet, gp); +} + +static void vinlet_float(t_vinlet *x, t_float f) +{ + outlet_float(x->x_obj.ob_outlet, f); +} + +static void vinlet_symbol(t_vinlet *x, t_symbol *s) +{ + outlet_symbol(x->x_obj.ob_outlet, s); +} + +static void vinlet_list(t_vinlet *x, t_symbol *s, int argc, t_atom *argv) +{ + outlet_list(x->x_obj.ob_outlet, s, argc, argv); +} + +static void vinlet_anything(t_vinlet *x, t_symbol *s, int argc, t_atom *argv) +{ + outlet_anything(x->x_obj.ob_outlet, s, argc, argv); +} + +static void vinlet_free(t_vinlet *x) +{ + canvas_rminlet(x->x_canvas, x->x_inlet); + resample_free(&x->x_updown); +} + +t_inlet *vinlet_getit(t_pd *x) +{ + if (pd_class(x) != vinlet_class) bug("vinlet_getit"); + return (((t_vinlet *)x)->x_inlet); +} + +/* ------------------------- signal inlet -------------------------- */ +int vinlet_issignal(t_vinlet *x) +{ + return (x->x_buf != 0); +} + +static int tot; + +t_int *vinlet_perform(t_int *w) +{ + t_vinlet *x = (t_vinlet *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + t_float *in = x->x_read; +#if 0 + if (tot < 5) post("-in %x out %x n %d", in, out, n); + if (tot < 5) post("-buf %x endbuf %x", x->x_buf, x->x_endbuf); + if (tot < 5) post("in[0] %f in[1] %f in[2] %f", in[0], in[1], in[2]); +#endif + while (n--) *out++ = *in++; + if (in == x->x_endbuf) in = x->x_buf; + x->x_read = in; + return (w+4); +} + +static void vinlet_dsp(t_vinlet *x, t_signal **sp) +{ + t_signal *outsig; + /* no buffer means we're not a signal inlet */ + if (!x->x_buf) + return; + outsig = sp[0]; + if (x->x_directsignal) + { + signal_setborrowed(sp[0], x->x_directsignal); + } + else + { + dsp_add(vinlet_perform, 3, x, outsig->s_vec, outsig->s_n); + x->x_read = x->x_buf; + } +} + + /* prolog code: loads buffer from parent patch */ +t_int *vinlet_doprolog(t_int *w) +{ + t_vinlet *x = (t_vinlet *)(w[1]); + t_float *in = (t_float *)(w[2]); + int n = (int)(w[3]); + t_float *out = x->x_fill; + if (out == x->x_endbuf) + { + t_float *f1 = x->x_buf, *f2 = x->x_buf + x->x_hop; + int nshift = x->x_bufsize - x->x_hop; + out -= x->x_hop; + while (nshift--) *f1++ = *f2++; + } +#if 0 + if (tot < 5) post("in %x out %x n %x", in, out, n), tot++; + if (tot < 5) post("in[0] %f in[1] %f in[2] %f", in[0], in[1], in[2]); +#endif + + while (n--) *out++ = *in++; + x->x_fill = out; + return (w+4); +} + +int inlet_getsignalindex(t_inlet *x); + + /* set up prolog DSP code */ +void vinlet_dspprolog(t_vinlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample/* IOhannes */, int reblock, + int switched) +{ + t_signal *insig, *outsig; + x->x_updown.downsample = downsample; + x->x_updown.upsample = upsample; + + /* if the "reblock" flag is set, arrange to copy data in from the + parent. */ + if (reblock) + { + int parentvecsize, bufsize, oldbufsize, prologphase; + int re_parentvecsize; /* resampled parentvectorsize: IOhannes */ + /* this should never happen: */ + if (!x->x_buf) return; + + /* the prolog code counts from 0 to period-1; the + phase is backed up by one so that AFTER the prolog code + runs, the "x_fill" phase is in sync with the "x_read" phase. */ + prologphase = (phase - 1) & (period - 1); + if (parentsigs) + { + insig = parentsigs[inlet_getsignalindex(x->x_inlet)]; + parentvecsize = insig->s_n; + re_parentvecsize = parentvecsize * upsample / downsample; + } + else + { + insig = 0; + parentvecsize = 1; + re_parentvecsize = 1; + } + + bufsize = re_parentvecsize; + if (bufsize < myvecsize) bufsize = myvecsize; + if (bufsize != (oldbufsize = x->x_bufsize)) + { + t_float *buf = x->x_buf; + t_freebytes(buf, oldbufsize * sizeof(*buf)); + buf = (t_float *)t_getbytes(bufsize * sizeof(*buf)); + memset((char *)buf, 0, bufsize * sizeof(*buf)); + x->x_bufsize = bufsize; + x->x_endbuf = buf + bufsize; + x->x_buf = buf; + } + if (parentsigs) + { + /* IOhannes { */ + x->x_hop = period * re_parentvecsize; + + x->x_fill = x->x_endbuf - + (x->x_hop - prologphase * re_parentvecsize); + + if (upsample * downsample == 1) + dsp_add(vinlet_doprolog, 3, x, insig->s_vec, re_parentvecsize); + else { + resamplefrom_dsp(&x->x_updown, insig->s_vec, parentvecsize, re_parentvecsize, x->x_updown.method); + dsp_add(vinlet_doprolog, 3, x, x->x_updown.s_vec, re_parentvecsize); + } + + /* } IOhannes */ + /* if the input signal's reference count is zero, we have + to free it here because we didn't in ugen_doit(). */ + if (!insig->s_refcount) + signal_makereusable(insig); + } + else memset((char *)(x->x_buf), 0, bufsize * sizeof(*x->x_buf)); + x->x_directsignal = 0; + } + else + { + /* no reblocking; in this case our output signal is "borrowed" + and merely needs to be pointed to the real one. */ + x->x_directsignal = parentsigs[inlet_getsignalindex(x->x_inlet)]; + } +} + +//static void *vinlet_newsig(void) +static void *vinlet_newsig(t_symbol *s) +{ + t_vinlet *x = (t_vinlet *)pd_new(vinlet_class); + x->x_canvas = canvas_getcurrent(); + x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, &s_signal); + x->x_endbuf = x->x_buf = (t_float *)getbytes(0); + x->x_bufsize = 0; + x->x_directsignal = 0; + outlet_new(&x->x_obj, &s_signal); + + resample_init(&x->x_updown); + + /* this should be though over: + * it might prove hard to provide consistency between labeled up- & downsampling methods + * maybe indeces would be better... + * + * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !) + */ + if (s == gensym("hold"))x->x_updown.method=1; /* up: sample and hold */ + else if (s == gensym("lin"))x->x_updown.method=2; /* up: linear interpolation */ + else x->x_updown.method=0; /* up: zero-padding */ + + return (x); +} + +static void vinlet_setup(void) +{ + vinlet_class = class_new(gensym("inlet"), (t_newmethod)vinlet_new, + (t_method)vinlet_free, sizeof(t_vinlet), CLASS_NOINLET, A_DEFSYM, 0); + class_addcreator((t_newmethod)vinlet_newsig, gensym("inlet~"), A_DEFSYM, 0); + class_addbang(vinlet_class, vinlet_bang); + class_addpointer(vinlet_class, vinlet_pointer); + class_addfloat(vinlet_class, vinlet_float); + class_addsymbol(vinlet_class, vinlet_symbol); + class_addlist(vinlet_class, vinlet_list); + class_addanything(vinlet_class, vinlet_anything); + class_addmethod(vinlet_class, (t_method)vinlet_dsp, gensym("dsp"), 0); + class_sethelpsymbol(vinlet_class, gensym("pd")); +} + +/* ------------------------- voutlet -------------------------- */ + +t_class *voutlet_class; + +typedef struct _voutlet +{ + t_object x_obj; + t_canvas *x_canvas; + t_outlet *x_parentoutlet; + int x_bufsize; + t_sample *x_buf; /* signal buffer; zero if not a signal */ + t_sample *x_endbuf; + t_sample *x_empty; /* next to read out of buffer in epilog code */ + t_sample *x_write; /* next to write in to buffer */ + int x_hop; /* hopsize */ + /* vice versa from the inlet, if we don't block, this holds the + parent's outlet signal, valid between the prolog and the dsp setup + routines. */ + t_signal *x_directsignal; + /* and here's a flag indicating that we aren't blocked but have to + do a copy (because we're switched). */ + char x_justcopyout; + t_resample x_updown; /* IOhannes */ +} t_voutlet; + +static void *voutlet_new(t_symbol *s) +{ + t_voutlet *x = (t_voutlet *)pd_new(voutlet_class); + x->x_canvas = canvas_getcurrent(); + x->x_parentoutlet = canvas_addoutlet(x->x_canvas, &x->x_obj.ob_pd, 0); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, 0, 0); + x->x_bufsize = 0; + x->x_buf = 0; + return (x); +} + +static void voutlet_bang(t_voutlet *x) +{ + outlet_bang(x->x_parentoutlet); +} + +static void voutlet_pointer(t_voutlet *x, t_gpointer *gp) +{ + outlet_pointer(x->x_parentoutlet, gp); +} + +static void voutlet_float(t_voutlet *x, t_float f) +{ + outlet_float(x->x_parentoutlet, f); +} + +static void voutlet_symbol(t_voutlet *x, t_symbol *s) +{ + outlet_symbol(x->x_parentoutlet, s); +} + +static void voutlet_list(t_voutlet *x, t_symbol *s, int argc, t_atom *argv) +{ + outlet_list(x->x_parentoutlet, s, argc, argv); +} + +static void voutlet_anything(t_voutlet *x, t_symbol *s, int argc, t_atom *argv) +{ + outlet_anything(x->x_parentoutlet, s, argc, argv); +} + +static void voutlet_free(t_voutlet *x) +{ + canvas_rmoutlet(x->x_canvas, x->x_parentoutlet); + resample_free(&x->x_updown); +} + +t_outlet *voutlet_getit(t_pd *x) +{ + if (pd_class(x) != voutlet_class) bug("voutlet_getit"); + return (((t_voutlet *)x)->x_parentoutlet); +} + +/* ------------------------- signal outlet -------------------------- */ + +int voutlet_issignal(t_voutlet *x) +{ + return (x->x_buf != 0); +} + + /* LATER optimize for non-overlapped case where the "+=" isn't needed */ +t_int *voutlet_perform(t_int *w) +{ + t_voutlet *x = (t_voutlet *)(w[1]); + t_sample *in = (t_sample *)(w[2]); + int n = (int)(w[3]); + t_sample *out = x->x_write, *outwas = out; +#if 0 + if (tot < 5) post("-in %x out %x n %d", in, out, n); + if (tot < 5) post("-buf %x endbuf %x", x->x_buf, x->x_endbuf); +#endif + while (n--) + { + *out++ += *in++; + if (out == x->x_endbuf) out = x->x_buf; + } + outwas += x->x_hop; + if (outwas >= x->x_endbuf) outwas = x->x_buf; + x->x_write = outwas; + return (w+4); +} + + /* epilog code for blocking: write buffer to parent patch */ +static t_int *voutlet_doepilog(t_int *w) +{ + t_voutlet *x = (t_voutlet *)(w[1]); + t_sample *out = (t_sample *)(w[2]); /* IOhannes */ + + int n = (int)(w[3]); + t_sample *in = x->x_empty; + if (x->x_updown.downsample != x->x_updown.upsample) out = x->x_updown.s_vec; /* IOhannes */ + +#if 0 + if (tot < 5) post("outlet in %x out %x n %x", in, out, n), tot++; +#endif + for (; n--; in++) *out++ = *in, *in = 0; + if (in == x->x_endbuf) in = x->x_buf; + x->x_empty = in; + return (w+4); +} + +/* IOhannes { */ +static t_int *voutlet_doepilog_resampling(t_int *w) +{ + t_voutlet *x = (t_voutlet *)(w[1]); + // t_float *dummy = (t_float *)(w[2]); + int n = (int)(w[2]); + t_sample *in = x->x_empty; + t_sample *out = x->x_updown.s_vec; /* IOhannes */ + +#if 0 + if (tot < 5) post("outlet in %x out %x n %x", in, out, n), tot++; +#endif + for (; n--; in++) *out++ = *in, *in = 0; + if (in == x->x_endbuf) in = x->x_buf; + x->x_empty = in; + return (w+3); +} +/* } IOhannes */ +int outlet_getsignalindex(t_outlet *x); + + /* prolog for outlets -- store pointer to the outlet on the + parent, which, if "reblock" is false, will want to refer + back to whatever we see on our input during the "dsp" method + called later. */ +void voutlet_dspprolog(t_voutlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, + int switched) +{ + x->x_updown.downsample=downsample; x->x_updown.upsample=upsample; /* IOhannes */ + x->x_justcopyout = (switched && !reblock); + if (reblock) + { + x->x_directsignal = 0; + } + else + { + if (!parentsigs) bug("voutlet_dspprolog"); + x->x_directsignal = + parentsigs[outlet_getsignalindex(x->x_parentoutlet)]; + } +} + +static void voutlet_dsp(t_voutlet *x, t_signal **sp) +{ + t_signal *insig; + if (!x->x_buf) return; + insig = sp[0]; + if (x->x_justcopyout) + dsp_add_copy(insig->s_vec, x->x_directsignal->s_vec, insig->s_n); + else if (x->x_directsignal) + { + /* if we're just going to make the signal available on the + parent patch, hand it off to the parent signal. */ + /* this is done elsewhere--> sp[0]->s_refcount++; */ + signal_setborrowed(x->x_directsignal, sp[0]); + } + else + dsp_add(voutlet_perform, 3, x, insig->s_vec, insig->s_n); +} + + /* set up epilog DSP code. If we're reblocking, this is the + time to copy the samples out to the containing object's outlets. + If we aren't reblocking, there's nothing to do here. */ +void voutlet_dspepilog(t_voutlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, + int switched) +{ + if (!x->x_buf) return; /* this shouldn't be necesssary... */ + x->x_updown.downsample=downsample; x->x_updown.upsample=upsample; /* IOhannes */ + if (reblock) + { + t_signal *insig, *outsig; + int parentvecsize, bufsize, oldbufsize; + int re_parentvecsize; /* IOhannes */ + int bigperiod, epilogphase, blockphase; + if (parentsigs) + { + outsig = parentsigs[outlet_getsignalindex(x->x_parentoutlet)]; + parentvecsize = outsig->s_n; + re_parentvecsize = parentvecsize * upsample / downsample; + } + else + { + outsig = 0; + parentvecsize = 1; + re_parentvecsize = 1; + } + // bigperiod = (downsample * myvecsize)/(upsample * parentvecsize); /* IOhannes */ + bigperiod = myvecsize/re_parentvecsize; /* IOhannes */ + if (!bigperiod) bigperiod = 1; + epilogphase = phase & (bigperiod - 1); + blockphase = (phase + period - 1) & (bigperiod - 1) & (- period); + // bufsize = parentvecsize * upsample; /* IOhannes */ + bufsize = re_parentvecsize; /* IOhannes */ + if (bufsize < myvecsize) bufsize = myvecsize; + if (bufsize != (oldbufsize = x->x_bufsize)) + { + t_sample *buf = x->x_buf; + t_freebytes(buf, oldbufsize * sizeof(*buf)); + buf = (t_sample *)t_getbytes(bufsize * sizeof(*buf)); + memset((char *)buf, 0, bufsize * sizeof(*buf)); + x->x_bufsize = bufsize; + x->x_endbuf = buf + bufsize; + x->x_buf = buf; + } + /* IOhannes: { */ + if (re_parentvecsize * period > bufsize) bug("voutlet_dspepilog"); + x->x_write = x->x_buf + re_parentvecsize * blockphase; + if (x->x_write == x->x_endbuf) x->x_write = x->x_buf; + if (period == 1 && frequency > 1) + x->x_hop = re_parentvecsize / frequency; + else x->x_hop = period * re_parentvecsize; + /* } IOhannes */ + /* post("phase %d, block %d, parent %d", phase & 63, + parentvecsize * blockphase, parentvecsize * epilogphase); */ + if (parentsigs) + { + /* set epilog pointer and schedule it */ + /* IOhannes { */ + x->x_empty = x->x_buf + re_parentvecsize * epilogphase; + if (upsample * downsample == 1) + dsp_add(voutlet_doepilog, 3, x, outsig->s_vec, re_parentvecsize); + else { + dsp_add(voutlet_doepilog_resampling, 2, x, re_parentvecsize); + resampleto_dsp(&x->x_updown, outsig->s_vec, re_parentvecsize, parentvecsize, x->x_updown.method); + } + /* } IOhannes */ + } + } + /* if we aren't blocked but we are switched, the epilog code just + copies zeros to the output. In this case the blocking code actually + jumps over the epilog if the block is running. */ + else if (switched) + { + if (parentsigs) + { + t_signal *outsig = + parentsigs[outlet_getsignalindex(x->x_parentoutlet)]; + dsp_add_zero(outsig->s_vec, outsig->s_n); + } + } +} + +static void *voutlet_newsig(t_symbol *s) +{ + t_voutlet *x = (t_voutlet *)pd_new(voutlet_class); + x->x_canvas = canvas_getcurrent(); + x->x_parentoutlet = canvas_addoutlet(x->x_canvas, + &x->x_obj.ob_pd, &s_signal); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + x->x_endbuf = x->x_buf = (t_sample *)getbytes(0); + x->x_bufsize = 0; + + resample_init(&x->x_updown); + + /* this should be though over: + * it might prove hard to provide consistency between labeled up- & downsampling methods + * maybe indeces would be better... + * + * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !) + */ + if (s == gensym("hold"))x->x_updown.method=1; /* up: sample and hold */ + else if (s == gensym("lin"))x->x_updown.method=2; /* up: linear interpolation */ + else if (s == gensym("linear"))x->x_updown.method=2; /* up: linear interpolation */ + else x->x_updown.method=0; /* up: zero-padding; down: ignore samples inbetween */ + + return (x); +} + + +static void voutlet_setup(void) +{ + voutlet_class = class_new(gensym("outlet"), (t_newmethod)voutlet_new, + (t_method)voutlet_free, sizeof(t_voutlet), CLASS_NOINLET, A_DEFSYM, 0); + class_addcreator((t_newmethod)voutlet_newsig, gensym("outlet~"), A_DEFSYM, 0); + class_addbang(voutlet_class, voutlet_bang); + class_addpointer(voutlet_class, voutlet_pointer); + class_addfloat(voutlet_class, (t_method)voutlet_float); + class_addsymbol(voutlet_class, voutlet_symbol); + class_addlist(voutlet_class, voutlet_list); + class_addanything(voutlet_class, voutlet_anything); + class_addmethod(voutlet_class, (t_method)voutlet_dsp, gensym("dsp"), 0); + class_sethelpsymbol(voutlet_class, gensym("pd")); +} + + +/* ---------------------------- overall setup ----------------------------- */ + +void g_io_setup(void) +{ + vinlet_setup(); + voutlet_setup(); +} +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* graphical inlets and outlets, both for control and signals. */ + +/* This code is highly inefficient; messages actually have to be forwarded +by inlets and outlets. The outlet is in even worse shape than the inlet; +in order to avoid having a "signal" method in the class, the oulet actually +sprouts an inlet, which forwards the message to the "outlet" object, which +sends it on to the outlet proper. Another way to do it would be to have +separate classes for "signal" and "control" outlets, but this would complicate +life elsewhere. */ + + +/* hacked to run subpatches with different samplerates + * + * mfg.gfd.uil + * IOhannes + * + * edited lines are marked with "IOhannes" + * + */ + +#include "m_pd.h" +#include "g_canvas.h" +#include +void signal_setborrowed(t_signal *sig, t_signal *sig2); +void signal_makereusable(t_signal *sig); + +/* ------------------------- vinlet -------------------------- */ +t_class *vinlet_class; + +typedef struct _vinlet +{ + t_object x_obj; + t_canvas *x_canvas; + t_inlet *x_inlet; + int x_bufsize; + t_float *x_buf; /* signal buffer; zero if not a signal */ + t_float *x_endbuf; + t_float *x_fill; + t_float *x_read; + int x_hop; + /* if not reblocking, the next slot communicates the parent's inlet + signal from the prolog to the DSP routine: */ + t_signal *x_directsignal; + + t_resample x_updown; /* IOhannes */ +} t_vinlet; + +static void *vinlet_new(t_symbol *s) +{ + t_vinlet *x = (t_vinlet *)pd_new(vinlet_class); + x->x_canvas = canvas_getcurrent(); + x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, 0); + x->x_bufsize = 0; + x->x_buf = 0; + outlet_new(&x->x_obj, 0); + return (x); +} + +static void vinlet_bang(t_vinlet *x) +{ + outlet_bang(x->x_obj.ob_outlet); +} + +static void vinlet_pointer(t_vinlet *x, t_gpointer *gp) +{ + outlet_pointer(x->x_obj.ob_outlet, gp); +} + +static void vinlet_float(t_vinlet *x, t_float f) +{ + outlet_float(x->x_obj.ob_outlet, f); +} + +static void vinlet_symbol(t_vinlet *x, t_symbol *s) +{ + outlet_symbol(x->x_obj.ob_outlet, s); +} + +static void vinlet_list(t_vinlet *x, t_symbol *s, int argc, t_atom *argv) +{ + outlet_list(x->x_obj.ob_outlet, s, argc, argv); +} + +static void vinlet_anything(t_vinlet *x, t_symbol *s, int argc, t_atom *argv) +{ + outlet_anything(x->x_obj.ob_outlet, s, argc, argv); +} + +static void vinlet_free(t_vinlet *x) +{ + canvas_rminlet(x->x_canvas, x->x_inlet); + resample_free(&x->x_updown); +} + +t_inlet *vinlet_getit(t_pd *x) +{ + if (pd_class(x) != vinlet_class) bug("vinlet_getit"); + return (((t_vinlet *)x)->x_inlet); +} + +/* ------------------------- signal inlet -------------------------- */ +int vinlet_issignal(t_vinlet *x) +{ + return (x->x_buf != 0); +} + +static int tot; + +t_int *vinlet_perform(t_int *w) +{ + t_vinlet *x = (t_vinlet *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + t_float *in = x->x_read; +#if 0 + if (tot < 5) post("-in %x out %x n %d", in, out, n); + if (tot < 5) post("-buf %x endbuf %x", x->x_buf, x->x_endbuf); + if (tot < 5) post("in[0] %f in[1] %f in[2] %f", in[0], in[1], in[2]); +#endif + while (n--) *out++ = *in++; + if (in == x->x_endbuf) in = x->x_buf; + x->x_read = in; + return (w+4); +} + +static void vinlet_dsp(t_vinlet *x, t_signal **sp) +{ + t_signal *outsig; + /* no buffer means we're not a signal inlet */ + if (!x->x_buf) + return; + outsig = sp[0]; + if (x->x_directsignal) + { + signal_setborrowed(sp[0], x->x_directsignal); + } + else + { + dsp_add(vinlet_perform, 3, x, outsig->s_vec, outsig->s_n); + x->x_read = x->x_buf; + } +} + + /* prolog code: loads buffer from parent patch */ +t_int *vinlet_doprolog(t_int *w) +{ + t_vinlet *x = (t_vinlet *)(w[1]); + t_float *in = (t_float *)(w[2]); + int n = (int)(w[3]); + t_float *out = x->x_fill; + if (out == x->x_endbuf) + { + t_float *f1 = x->x_buf, *f2 = x->x_buf + x->x_hop; + int nshift = x->x_bufsize - x->x_hop; + out -= x->x_hop; + while (nshift--) *f1++ = *f2++; + } +#if 0 + if (tot < 5) post("in %x out %x n %x", in, out, n), tot++; + if (tot < 5) post("in[0] %f in[1] %f in[2] %f", in[0], in[1], in[2]); +#endif + + while (n--) *out++ = *in++; + x->x_fill = out; + return (w+4); +} + +int inlet_getsignalindex(t_inlet *x); + + /* set up prolog DSP code */ +void vinlet_dspprolog(t_vinlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample/* IOhannes */, int reblock, + int switched) +{ + t_signal *insig, *outsig; + x->x_updown.downsample = downsample; + x->x_updown.upsample = upsample; + + /* if the "reblock" flag is set, arrange to copy data in from the + parent. */ + if (reblock) + { + int parentvecsize, bufsize, oldbufsize, prologphase; + int re_parentvecsize; /* resampled parentvectorsize: IOhannes */ + /* this should never happen: */ + if (!x->x_buf) return; + + /* the prolog code counts from 0 to period-1; the + phase is backed up by one so that AFTER the prolog code + runs, the "x_fill" phase is in sync with the "x_read" phase. */ + prologphase = (phase - 1) & (period - 1); + if (parentsigs) + { + insig = parentsigs[inlet_getsignalindex(x->x_inlet)]; + parentvecsize = insig->s_n; + re_parentvecsize = parentvecsize * upsample / downsample; + } + else + { + insig = 0; + parentvecsize = 1; + re_parentvecsize = 1; + } + + bufsize = re_parentvecsize; + if (bufsize < myvecsize) bufsize = myvecsize; + if (bufsize != (oldbufsize = x->x_bufsize)) + { + t_float *buf = x->x_buf; + t_freebytes(buf, oldbufsize * sizeof(*buf)); + buf = (t_float *)t_getbytes(bufsize * sizeof(*buf)); + memset((char *)buf, 0, bufsize * sizeof(*buf)); + x->x_bufsize = bufsize; + x->x_endbuf = buf + bufsize; + x->x_buf = buf; + } + if (parentsigs) + { + /* IOhannes { */ + x->x_hop = period * re_parentvecsize; + + x->x_fill = x->x_endbuf - + (x->x_hop - prologphase * re_parentvecsize); + + if (upsample * downsample == 1) + dsp_add(vinlet_doprolog, 3, x, insig->s_vec, re_parentvecsize); + else { + resamplefrom_dsp(&x->x_updown, insig->s_vec, parentvecsize, re_parentvecsize, x->x_updown.method); + dsp_add(vinlet_doprolog, 3, x, x->x_updown.s_vec, re_parentvecsize); + } + + /* } IOhannes */ + /* if the input signal's reference count is zero, we have + to free it here because we didn't in ugen_doit(). */ + if (!insig->s_refcount) + signal_makereusable(insig); + } + else memset((char *)(x->x_buf), 0, bufsize * sizeof(*x->x_buf)); + x->x_directsignal = 0; + } + else + { + /* no reblocking; in this case our output signal is "borrowed" + and merely needs to be pointed to the real one. */ + x->x_directsignal = parentsigs[inlet_getsignalindex(x->x_inlet)]; + } +} + +//static void *vinlet_newsig(void) +static void *vinlet_newsig(t_symbol *s) +{ + t_vinlet *x = (t_vinlet *)pd_new(vinlet_class); + x->x_canvas = canvas_getcurrent(); + x->x_inlet = canvas_addinlet(x->x_canvas, &x->x_obj.ob_pd, &s_signal); + x->x_endbuf = x->x_buf = (t_float *)getbytes(0); + x->x_bufsize = 0; + x->x_directsignal = 0; + outlet_new(&x->x_obj, &s_signal); + + resample_init(&x->x_updown); + + /* this should be though over: + * it might prove hard to provide consistency between labeled up- & downsampling methods + * maybe indeces would be better... + * + * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !) + */ + if (s == gensym("hold"))x->x_updown.method=1; /* up: sample and hold */ + else if (s == gensym("lin"))x->x_updown.method=2; /* up: linear interpolation */ + else x->x_updown.method=0; /* up: zero-padding */ + + return (x); +} + +static void vinlet_setup(void) +{ + vinlet_class = class_new(gensym("inlet"), (t_newmethod)vinlet_new, + (t_method)vinlet_free, sizeof(t_vinlet), CLASS_NOINLET, A_DEFSYM, 0); + class_addcreator((t_newmethod)vinlet_newsig, gensym("inlet~"), A_DEFSYM, 0); + class_addbang(vinlet_class, vinlet_bang); + class_addpointer(vinlet_class, vinlet_pointer); + class_addfloat(vinlet_class, vinlet_float); + class_addsymbol(vinlet_class, vinlet_symbol); + class_addlist(vinlet_class, vinlet_list); + class_addanything(vinlet_class, vinlet_anything); + class_addmethod(vinlet_class, (t_method)vinlet_dsp, gensym("dsp"), 0); + class_sethelpsymbol(vinlet_class, gensym("pd")); +} + +/* ------------------------- voutlet -------------------------- */ + +t_class *voutlet_class; + +typedef struct _voutlet +{ + t_object x_obj; + t_canvas *x_canvas; + t_outlet *x_parentoutlet; + int x_bufsize; + t_sample *x_buf; /* signal buffer; zero if not a signal */ + t_sample *x_endbuf; + t_sample *x_empty; /* next to read out of buffer in epilog code */ + t_sample *x_write; /* next to write in to buffer */ + int x_hop; /* hopsize */ + /* vice versa from the inlet, if we don't block, this holds the + parent's outlet signal, valid between the prolog and the dsp setup + routines. */ + t_signal *x_directsignal; + /* and here's a flag indicating that we aren't blocked but have to + do a copy (because we're switched). */ + char x_justcopyout; + t_resample x_updown; /* IOhannes */ +} t_voutlet; + +static void *voutlet_new(t_symbol *s) +{ + t_voutlet *x = (t_voutlet *)pd_new(voutlet_class); + x->x_canvas = canvas_getcurrent(); + x->x_parentoutlet = canvas_addoutlet(x->x_canvas, &x->x_obj.ob_pd, 0); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, 0, 0); + x->x_bufsize = 0; + x->x_buf = 0; + return (x); +} + +static void voutlet_bang(t_voutlet *x) +{ + outlet_bang(x->x_parentoutlet); +} + +static void voutlet_pointer(t_voutlet *x, t_gpointer *gp) +{ + outlet_pointer(x->x_parentoutlet, gp); +} + +static void voutlet_float(t_voutlet *x, t_float f) +{ + outlet_float(x->x_parentoutlet, f); +} + +static void voutlet_symbol(t_voutlet *x, t_symbol *s) +{ + outlet_symbol(x->x_parentoutlet, s); +} + +static void voutlet_list(t_voutlet *x, t_symbol *s, int argc, t_atom *argv) +{ + outlet_list(x->x_parentoutlet, s, argc, argv); +} + +static void voutlet_anything(t_voutlet *x, t_symbol *s, int argc, t_atom *argv) +{ + outlet_anything(x->x_parentoutlet, s, argc, argv); +} + +static void voutlet_free(t_voutlet *x) +{ + canvas_rmoutlet(x->x_canvas, x->x_parentoutlet); + resample_free(&x->x_updown); +} + +t_outlet *voutlet_getit(t_pd *x) +{ + if (pd_class(x) != voutlet_class) bug("voutlet_getit"); + return (((t_voutlet *)x)->x_parentoutlet); +} + +/* ------------------------- signal outlet -------------------------- */ + +int voutlet_issignal(t_voutlet *x) +{ + return (x->x_buf != 0); +} + + /* LATER optimize for non-overlapped case where the "+=" isn't needed */ +t_int *voutlet_perform(t_int *w) +{ + t_voutlet *x = (t_voutlet *)(w[1]); + t_sample *in = (t_sample *)(w[2]); + int n = (int)(w[3]); + t_sample *out = x->x_write, *outwas = out; +#if 0 + if (tot < 5) post("-in %x out %x n %d", in, out, n); + if (tot < 5) post("-buf %x endbuf %x", x->x_buf, x->x_endbuf); +#endif + while (n--) + { + *out++ += *in++; + if (out == x->x_endbuf) out = x->x_buf; + } + outwas += x->x_hop; + if (outwas >= x->x_endbuf) outwas = x->x_buf; + x->x_write = outwas; + return (w+4); +} + + /* epilog code for blocking: write buffer to parent patch */ +static t_int *voutlet_doepilog(t_int *w) +{ + t_voutlet *x = (t_voutlet *)(w[1]); + t_sample *out = (t_sample *)(w[2]); /* IOhannes */ + + int n = (int)(w[3]); + t_sample *in = x->x_empty; + if (x->x_updown.downsample != x->x_updown.upsample) out = x->x_updown.s_vec; /* IOhannes */ + +#if 0 + if (tot < 5) post("outlet in %x out %x n %x", in, out, n), tot++; +#endif + for (; n--; in++) *out++ = *in, *in = 0; + if (in == x->x_endbuf) in = x->x_buf; + x->x_empty = in; + return (w+4); +} + +/* IOhannes { */ +static t_int *voutlet_doepilog_resampling(t_int *w) +{ + t_voutlet *x = (t_voutlet *)(w[1]); + // t_float *dummy = (t_float *)(w[2]); + int n = (int)(w[2]); + t_sample *in = x->x_empty; + t_sample *out = x->x_updown.s_vec; /* IOhannes */ + +#if 0 + if (tot < 5) post("outlet in %x out %x n %x", in, out, n), tot++; +#endif + for (; n--; in++) *out++ = *in, *in = 0; + if (in == x->x_endbuf) in = x->x_buf; + x->x_empty = in; + return (w+3); +} +/* } IOhannes */ +int outlet_getsignalindex(t_outlet *x); + + /* prolog for outlets -- store pointer to the outlet on the + parent, which, if "reblock" is false, will want to refer + back to whatever we see on our input during the "dsp" method + called later. */ +void voutlet_dspprolog(t_voutlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, + int switched) +{ + x->x_updown.downsample=downsample; x->x_updown.upsample=upsample; /* IOhannes */ + x->x_justcopyout = (switched && !reblock); + if (reblock) + { + x->x_directsignal = 0; + } + else + { + if (!parentsigs) bug("voutlet_dspprolog"); + x->x_directsignal = + parentsigs[outlet_getsignalindex(x->x_parentoutlet)]; + } +} + +static void voutlet_dsp(t_voutlet *x, t_signal **sp) +{ + t_signal *insig; + if (!x->x_buf) return; + insig = sp[0]; + if (x->x_justcopyout) + dsp_add_copy(insig->s_vec, x->x_directsignal->s_vec, insig->s_n); + else if (x->x_directsignal) + { + /* if we're just going to make the signal available on the + parent patch, hand it off to the parent signal. */ + /* this is done elsewhere--> sp[0]->s_refcount++; */ + signal_setborrowed(x->x_directsignal, sp[0]); + } + else + dsp_add(voutlet_perform, 3, x, insig->s_vec, insig->s_n); +} + + /* set up epilog DSP code. If we're reblocking, this is the + time to copy the samples out to the containing object's outlets. + If we aren't reblocking, there's nothing to do here. */ +void voutlet_dspepilog(t_voutlet *x, t_signal **parentsigs, + int myvecsize, int phase, int period, int frequency, int downsample, int upsample /* IOhannes */, int reblock, + int switched) +{ + if (!x->x_buf) return; /* this shouldn't be necesssary... */ + x->x_updown.downsample=downsample; x->x_updown.upsample=upsample; /* IOhannes */ + if (reblock) + { + t_signal *insig, *outsig; + int parentvecsize, bufsize, oldbufsize; + int re_parentvecsize; /* IOhannes */ + int bigperiod, epilogphase, blockphase; + if (parentsigs) + { + outsig = parentsigs[outlet_getsignalindex(x->x_parentoutlet)]; + parentvecsize = outsig->s_n; + re_parentvecsize = parentvecsize * upsample / downsample; + } + else + { + outsig = 0; + parentvecsize = 1; + re_parentvecsize = 1; + } + // bigperiod = (downsample * myvecsize)/(upsample * parentvecsize); /* IOhannes */ + bigperiod = myvecsize/re_parentvecsize; /* IOhannes */ + if (!bigperiod) bigperiod = 1; + epilogphase = phase & (bigperiod - 1); + blockphase = (phase + period - 1) & (bigperiod - 1) & (- period); + // bufsize = parentvecsize * upsample; /* IOhannes */ + bufsize = re_parentvecsize; /* IOhannes */ + if (bufsize < myvecsize) bufsize = myvecsize; + if (bufsize != (oldbufsize = x->x_bufsize)) + { + t_sample *buf = x->x_buf; + t_freebytes(buf, oldbufsize * sizeof(*buf)); + buf = (t_sample *)t_getbytes(bufsize * sizeof(*buf)); + memset((char *)buf, 0, bufsize * sizeof(*buf)); + x->x_bufsize = bufsize; + x->x_endbuf = buf + bufsize; + x->x_buf = buf; + } + /* IOhannes: { */ + if (re_parentvecsize * period > bufsize) bug("voutlet_dspepilog"); + x->x_write = x->x_buf + re_parentvecsize * blockphase; + if (x->x_write == x->x_endbuf) x->x_write = x->x_buf; + if (period == 1 && frequency > 1) + x->x_hop = re_parentvecsize / frequency; + else x->x_hop = period * re_parentvecsize; + /* } IOhannes */ + /* post("phase %d, block %d, parent %d", phase & 63, + parentvecsize * blockphase, parentvecsize * epilogphase); */ + if (parentsigs) + { + /* set epilog pointer and schedule it */ + /* IOhannes { */ + x->x_empty = x->x_buf + re_parentvecsize * epilogphase; + if (upsample * downsample == 1) + dsp_add(voutlet_doepilog, 3, x, outsig->s_vec, re_parentvecsize); + else { + dsp_add(voutlet_doepilog_resampling, 2, x, re_parentvecsize); + resampleto_dsp(&x->x_updown, outsig->s_vec, re_parentvecsize, parentvecsize, x->x_updown.method); + } + /* } IOhannes */ + } + } + /* if we aren't blocked but we are switched, the epilog code just + copies zeros to the output. In this case the blocking code actually + jumps over the epilog if the block is running. */ + else if (switched) + { + if (parentsigs) + { + t_signal *outsig = + parentsigs[outlet_getsignalindex(x->x_parentoutlet)]; + dsp_add_zero(outsig->s_vec, outsig->s_n); + } + } +} + +static void *voutlet_newsig(t_symbol *s) +{ + t_voutlet *x = (t_voutlet *)pd_new(voutlet_class); + x->x_canvas = canvas_getcurrent(); + x->x_parentoutlet = canvas_addoutlet(x->x_canvas, + &x->x_obj.ob_pd, &s_signal); + inlet_new(&x->x_obj, &x->x_obj.ob_pd, &s_signal, &s_signal); + x->x_endbuf = x->x_buf = (t_sample *)getbytes(0); + x->x_bufsize = 0; + + resample_init(&x->x_updown); + + /* this should be though over: + * it might prove hard to provide consistency between labeled up- & downsampling methods + * maybe indeces would be better... + * + * up till now we provide several upsampling methods and 1 single downsampling method (no filtering !) + */ + if (s == gensym("hold"))x->x_updown.method=1; /* up: sample and hold */ + else if (s == gensym("lin"))x->x_updown.method=2; /* up: linear interpolation */ + else if (s == gensym("linear"))x->x_updown.method=2; /* up: linear interpolation */ + else x->x_updown.method=0; /* up: zero-padding; down: ignore samples inbetween */ + + return (x); +} + + +static void voutlet_setup(void) +{ + voutlet_class = class_new(gensym("outlet"), (t_newmethod)voutlet_new, + (t_method)voutlet_free, sizeof(t_voutlet), CLASS_NOINLET, A_DEFSYM, 0); + class_addcreator((t_newmethod)voutlet_newsig, gensym("outlet~"), A_DEFSYM, 0); + class_addbang(voutlet_class, voutlet_bang); + class_addpointer(voutlet_class, voutlet_pointer); + class_addfloat(voutlet_class, (t_method)voutlet_float); + class_addsymbol(voutlet_class, voutlet_symbol); + class_addlist(voutlet_class, voutlet_list); + class_addanything(voutlet_class, voutlet_anything); + class_addmethod(voutlet_class, (t_method)voutlet_dsp, gensym("dsp"), 0); + class_sethelpsymbol(voutlet_class, gensym("pd")); +} + + +/* ---------------------------- overall setup ----------------------------- */ + +void g_io_setup(void) +{ + vinlet_setup(); + voutlet_setup(); +} diff --git a/apps/plugins/pdbox/PDa/src/g_mycanvas.c b/apps/plugins/pdbox/PDa/src/g_mycanvas.c new file mode 100644 index 0000000..24dfbb2 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_mycanvas.c @@ -0,0 +1,770 @@ +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + +/* ---------- cnv my gui-canvas for a window ---------------- */ + +t_widgetbehavior my_canvas_widgetbehavior; +static t_class *my_canvas_class; + +/* widget helper functions */ + +void my_canvas_draw_new(t_my_canvas *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -outline #%6.6x -tags %xRECT\n", + canvas, xpos, ypos, + xpos + x->x_vis_w, ypos + x->x_vis_h, + x->x_gui.x_bcol, x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create rectangle %d %d %d %d -outline #%6.6x -tags %xBASE\n", + canvas, xpos, ypos, + xpos + x->x_gui.x_w, ypos + x->x_gui.x_h, + x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x); +} + +void my_canvas_draw_move(t_my_canvas *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c coords %xRECT %d %d %d %d\n", + canvas, x, xpos, ypos, xpos + x->x_vis_w, + ypos + x->x_vis_h); + sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n", + canvas, x, xpos, ypos, + xpos + x->x_gui.x_w, ypos + x->x_gui.x_h); + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + canvas, x, xpos+x->x_gui.x_ldx, + ypos+x->x_gui.x_ldy); +} + +void my_canvas_draw_erase(t_my_canvas* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c delete %xBASE\n", canvas, x); + sys_vgui(".x%x.c delete %xRECT\n", canvas, x); + sys_vgui(".x%x.c delete %xLABEL\n", canvas, x); +} + +void my_canvas_draw_config(t_my_canvas* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xRECT -fill #%6.6x -outline #%6.6x\n", canvas, x, + x->x_gui.x_bcol, x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); +} + +void my_canvas_draw_select(t_my_canvas* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + if(x->x_gui.x_fsf.x_selected) + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, x->x_gui.x_bcol); + } +} + +void my_canvas_draw(t_my_canvas *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_MOVE) + my_canvas_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + my_canvas_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + my_canvas_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + my_canvas_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + my_canvas_draw_config(x, glist); +} + +/* ------------------------ cnv widgetbehaviour----------------------------- */ + +static void my_canvas_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_my_canvas *x = (t_my_canvas *)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist); + *yp1 = text_ypix(&x->x_gui.x_obj, glist); + *xp2 = *xp1 + x->x_gui.x_w; + *yp2 = *yp1 + x->x_gui.x_h; +} + +static void my_canvas_save(t_gobj *z, t_binbuf *b) +{ + t_my_canvas *x = (t_my_canvas *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiiisssiiiiiii", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix, + gensym("cnv"), x->x_gui.x_w, x->x_vis_w, x->x_vis_h, + srl[0], srl[1], srl[2], x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[2], iem_symargstoint(&x->x_gui.x_isa)); + binbuf_addv(b, ";"); +} + +static void my_canvas_properties(t_gobj *z, t_glist *owner) +{ + t_my_canvas *x = (t_my_canvas *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + sprintf(buf, "pdtk_iemgui_dialog %%s MY_CANVAS \ + ------selectable_dimensions(pix):------ %d %d size: 0.0 0.0 empty \ + ------visible_rectangle(pix)(pix):------ %d width: %d height: %d \ + %d empty empty %d %d empty %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, 1, + x->x_vis_w, x->x_vis_h, 0,/*no_schedule*/ + -1, -1, -1, -1,/*no linlog, no init, no multi*/ + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, -1/*no frontcolor*/, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void my_canvas_get_pos(t_my_canvas *x) +{ + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + { + x->x_at[0].a_w.w_float = text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist); + x->x_at[1].a_w.w_float = text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist); + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } +} + +static void my_canvas_dialog(t_my_canvas *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int a = (int)atom_getintarg(0, argc, argv); + int w = (int)atom_getintarg(2, argc, argv); + int h = (int)atom_getintarg(3, argc, argv); + int sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + + x->x_gui.x_isa.x_loadinit = 0; + if(a < 1) + a = 1; + x->x_gui.x_w = a; + x->x_gui.x_h = x->x_gui.x_w; + if(w < 1) + w = 1; + x->x_vis_w = w; + if(h < 1) + h = 1; + x->x_vis_h = h; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); +} + +static void my_canvas_size(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{ + int i = (int)atom_getintarg(0, ac, av); + + if(i < 1) + i = 1; + x->x_gui.x_w = i; + x->x_gui.x_h = i; + iemgui_size((void *)x, &x->x_gui); +} + +static void my_canvas_delta(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void my_canvas_pos(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void my_canvas_vis_size(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{ + int i; + + i = (int)atom_getintarg(0, ac, av); + if(i < 1) + i = 1; + x->x_vis_w = i; + if(ac > 1) + { + i = (int)atom_getintarg(1, ac, av); + if(i < 1) + i = 1; + } + x->x_vis_h = i; + if(glist_isvisible(x->x_gui.x_glist)) + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); +} + +static void my_canvas_color(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void my_canvas_send(t_my_canvas *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void my_canvas_receive(t_my_canvas *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void my_canvas_label(t_my_canvas *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void my_canvas_label_pos(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void my_canvas_label_font(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void *my_canvas_new(t_symbol *s, int argc, t_atom *argv) +{ + t_my_canvas *x = (t_my_canvas *)pd_new(my_canvas_class); + int bflcol[]={-233017, -1, -66577}; + int a=IEM_GUI_DEFAULTSIZE, w=100, h=60; + int ldx=20, ldy=12, f=2, i=0; + int fs=14; + char str[144]; + + iem_inttosymargs(&x->x_gui.x_isa, 0); + iem_inttofstyle(&x->x_gui.x_fsf, 0); + + if(((argc >= 10)&&(argc <= 13)) + &&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2)) + { + a = (int)atom_getintarg(0, argc, argv); + w = (int)atom_getintarg(1, argc, argv); + h = (int)atom_getintarg(2, argc, argv); + } + if((argc >= 12)&&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3))&&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4))) + { + i = 2; + iemgui_new_getnames(&x->x_gui, 3, argv); + } + else if((argc == 11)&&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3))) + { + i = 1; + iemgui_new_getnames(&x->x_gui, 3, argv); + } + else iemgui_new_getnames(&x->x_gui, 3, 0); + + if(((argc >= 10)&&(argc <= 13)) + &&(IS_A_SYMBOL(argv,i+3)||IS_A_FLOAT(argv,i+3))&&IS_A_FLOAT(argv,i+4) + &&IS_A_FLOAT(argv,i+5)&&IS_A_FLOAT(argv,i+6) + &&IS_A_FLOAT(argv,i+7)&&IS_A_FLOAT(argv,i+8) + &&IS_A_FLOAT(argv,i+9)) + { + /* disastrously, the "label" sits in a different part of the + message. So we have to track its location separately (in + the slot x_labelbindex) and initialize it specially here. */ + iemgui_new_dogetname(&x->x_gui, i+3, argv); + x->x_gui.x_labelbindex = i+4; + ldx = (int)atom_getintarg(i+4, argc, argv); + ldy = (int)atom_getintarg(i+5, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(i+6, argc, argv)); + fs = (int)atom_getintarg(i+7, argc, argv); + bflcol[0] = (int)atom_getintarg(i+8, argc, argv); + bflcol[2] = (int)atom_getintarg(i+9, argc, argv); + } + if((argc == 13)&&IS_A_FLOAT(argv,i+10)) + { + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(i+10, argc, argv)); + } + x->x_gui.x_draw = (t_iemfunptr)my_canvas_draw; + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if (!strcmp(x->x_gui.x_snd->s_name, "empty")) + x->x_gui.x_fsf.x_snd_able = 0; + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if(a < 1) + a = 1; + x->x_gui.x_w = a; + x->x_gui.x_h = x->x_gui.x_w; + if(w < 1) + w = 1; + x->x_vis_w = w; + if(h < 1) + h = 1; + x->x_vis_h = h; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + if (x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + iemgui_all_colfromload(&x->x_gui, bflcol); + x->x_at[0].a_type = A_FLOAT; + x->x_at[1].a_type = A_FLOAT; + iemgui_verify_snd_ne_rcv(&x->x_gui); + return (x); +} + +static void my_canvas_ff(t_my_canvas *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + gfxstub_deleteforkey(x); +} + +void g_mycanvas_setup(void) +{ + my_canvas_class = class_new(gensym("cnv"), (t_newmethod)my_canvas_new, + (t_method)my_canvas_ff, sizeof(t_my_canvas), CLASS_NOINLET, A_GIMME, 0); + class_addcreator((t_newmethod)my_canvas_new, gensym("my_canvas"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_dialog, gensym("dialog"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_size, gensym("size"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_vis_size, gensym("vis_size"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_color, gensym("color"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_send, gensym("send"), A_DEFSYM, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_label_font, gensym("label_font"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_get_pos, gensym("get_pos"), 0); + + my_canvas_widgetbehavior.w_getrectfn = my_canvas_getrect; + my_canvas_widgetbehavior.w_displacefn = iemgui_displace; + my_canvas_widgetbehavior.w_selectfn = iemgui_select; + my_canvas_widgetbehavior.w_activatefn = NULL; + my_canvas_widgetbehavior.w_deletefn = iemgui_delete; + my_canvas_widgetbehavior.w_visfn = iemgui_vis; + my_canvas_widgetbehavior.w_clickfn = NULL; + class_setwidget(my_canvas_class, &my_canvas_widgetbehavior); + class_sethelpsymbol(my_canvas_class, gensym("my_canvas")); + class_setsavefn(my_canvas_class, my_canvas_save); + class_setpropertiesfn(my_canvas_class, my_canvas_properties); +} +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + +/* ---------- cnv my gui-canvas for a window ---------------- */ + +t_widgetbehavior my_canvas_widgetbehavior; +static t_class *my_canvas_class; + +/* widget helper functions */ + +void my_canvas_draw_new(t_my_canvas *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -outline #%6.6x -tags %xRECT\n", + canvas, xpos, ypos, + xpos + x->x_vis_w, ypos + x->x_vis_h, + x->x_gui.x_bcol, x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create rectangle %d %d %d %d -outline #%6.6x -tags %xBASE\n", + canvas, xpos, ypos, + xpos + x->x_gui.x_w, ypos + x->x_gui.x_h, + x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x); +} + +void my_canvas_draw_move(t_my_canvas *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c coords %xRECT %d %d %d %d\n", + canvas, x, xpos, ypos, xpos + x->x_vis_w, + ypos + x->x_vis_h); + sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n", + canvas, x, xpos, ypos, + xpos + x->x_gui.x_w, ypos + x->x_gui.x_h); + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + canvas, x, xpos+x->x_gui.x_ldx, + ypos+x->x_gui.x_ldy); +} + +void my_canvas_draw_erase(t_my_canvas* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c delete %xBASE\n", canvas, x); + sys_vgui(".x%x.c delete %xRECT\n", canvas, x); + sys_vgui(".x%x.c delete %xLABEL\n", canvas, x); +} + +void my_canvas_draw_config(t_my_canvas* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xRECT -fill #%6.6x -outline #%6.6x\n", canvas, x, + x->x_gui.x_bcol, x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); +} + +void my_canvas_draw_select(t_my_canvas* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + if(x->x_gui.x_fsf.x_selected) + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, x->x_gui.x_bcol); + } +} + +void my_canvas_draw(t_my_canvas *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_MOVE) + my_canvas_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + my_canvas_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + my_canvas_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + my_canvas_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + my_canvas_draw_config(x, glist); +} + +/* ------------------------ cnv widgetbehaviour----------------------------- */ + +static void my_canvas_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_my_canvas *x = (t_my_canvas *)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist); + *yp1 = text_ypix(&x->x_gui.x_obj, glist); + *xp2 = *xp1 + x->x_gui.x_w; + *yp2 = *yp1 + x->x_gui.x_h; +} + +static void my_canvas_save(t_gobj *z, t_binbuf *b) +{ + t_my_canvas *x = (t_my_canvas *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiiisssiiiiiii", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix, + gensym("cnv"), x->x_gui.x_w, x->x_vis_w, x->x_vis_h, + srl[0], srl[1], srl[2], x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[2], iem_symargstoint(&x->x_gui.x_isa)); + binbuf_addv(b, ";"); +} + +static void my_canvas_properties(t_gobj *z, t_glist *owner) +{ + t_my_canvas *x = (t_my_canvas *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + sprintf(buf, "pdtk_iemgui_dialog %%s MY_CANVAS \ + ------selectable_dimensions(pix):------ %d %d size: 0.0 0.0 empty \ + ------visible_rectangle(pix)(pix):------ %d width: %d height: %d \ + %d empty empty %d %d empty %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, 1, + x->x_vis_w, x->x_vis_h, 0,/*no_schedule*/ + -1, -1, -1, -1,/*no linlog, no init, no multi*/ + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, -1/*no frontcolor*/, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void my_canvas_get_pos(t_my_canvas *x) +{ + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + { + x->x_at[0].a_w.w_float = text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist); + x->x_at[1].a_w.w_float = text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist); + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } +} + +static void my_canvas_dialog(t_my_canvas *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int a = (int)atom_getintarg(0, argc, argv); + int w = (int)atom_getintarg(2, argc, argv); + int h = (int)atom_getintarg(3, argc, argv); + int sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + + x->x_gui.x_isa.x_loadinit = 0; + if(a < 1) + a = 1; + x->x_gui.x_w = a; + x->x_gui.x_h = x->x_gui.x_w; + if(w < 1) + w = 1; + x->x_vis_w = w; + if(h < 1) + h = 1; + x->x_vis_h = h; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); +} + +static void my_canvas_size(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{ + int i = (int)atom_getintarg(0, ac, av); + + if(i < 1) + i = 1; + x->x_gui.x_w = i; + x->x_gui.x_h = i; + iemgui_size((void *)x, &x->x_gui); +} + +static void my_canvas_delta(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void my_canvas_pos(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void my_canvas_vis_size(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{ + int i; + + i = (int)atom_getintarg(0, ac, av); + if(i < 1) + i = 1; + x->x_vis_w = i; + if(ac > 1) + { + i = (int)atom_getintarg(1, ac, av); + if(i < 1) + i = 1; + } + x->x_vis_h = i; + if(glist_isvisible(x->x_gui.x_glist)) + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); +} + +static void my_canvas_color(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void my_canvas_send(t_my_canvas *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void my_canvas_receive(t_my_canvas *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void my_canvas_label(t_my_canvas *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void my_canvas_label_pos(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void my_canvas_label_font(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void *my_canvas_new(t_symbol *s, int argc, t_atom *argv) +{ + t_my_canvas *x = (t_my_canvas *)pd_new(my_canvas_class); + int bflcol[]={-233017, -1, -66577}; + int a=IEM_GUI_DEFAULTSIZE, w=100, h=60; + int ldx=20, ldy=12, f=2, i=0; + int fs=14; + char str[144]; + + iem_inttosymargs(&x->x_gui.x_isa, 0); + iem_inttofstyle(&x->x_gui.x_fsf, 0); + + if(((argc >= 10)&&(argc <= 13)) + &&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2)) + { + a = (int)atom_getintarg(0, argc, argv); + w = (int)atom_getintarg(1, argc, argv); + h = (int)atom_getintarg(2, argc, argv); + } + if((argc >= 12)&&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3))&&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4))) + { + i = 2; + iemgui_new_getnames(&x->x_gui, 3, argv); + } + else if((argc == 11)&&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3))) + { + i = 1; + iemgui_new_getnames(&x->x_gui, 3, argv); + } + else iemgui_new_getnames(&x->x_gui, 3, 0); + + if(((argc >= 10)&&(argc <= 13)) + &&(IS_A_SYMBOL(argv,i+3)||IS_A_FLOAT(argv,i+3))&&IS_A_FLOAT(argv,i+4) + &&IS_A_FLOAT(argv,i+5)&&IS_A_FLOAT(argv,i+6) + &&IS_A_FLOAT(argv,i+7)&&IS_A_FLOAT(argv,i+8) + &&IS_A_FLOAT(argv,i+9)) + { + /* disastrously, the "label" sits in a different part of the + message. So we have to track its location separately (in + the slot x_labelbindex) and initialize it specially here. */ + iemgui_new_dogetname(&x->x_gui, i+3, argv); + x->x_gui.x_labelbindex = i+4; + ldx = (int)atom_getintarg(i+4, argc, argv); + ldy = (int)atom_getintarg(i+5, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(i+6, argc, argv)); + fs = (int)atom_getintarg(i+7, argc, argv); + bflcol[0] = (int)atom_getintarg(i+8, argc, argv); + bflcol[2] = (int)atom_getintarg(i+9, argc, argv); + } + if((argc == 13)&&IS_A_FLOAT(argv,i+10)) + { + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(i+10, argc, argv)); + } + x->x_gui.x_draw = (t_iemfunptr)my_canvas_draw; + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if (!strcmp(x->x_gui.x_snd->s_name, "empty")) + x->x_gui.x_fsf.x_snd_able = 0; + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if(a < 1) + a = 1; + x->x_gui.x_w = a; + x->x_gui.x_h = x->x_gui.x_w; + if(w < 1) + w = 1; + x->x_vis_w = w; + if(h < 1) + h = 1; + x->x_vis_h = h; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + if (x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + iemgui_all_colfromload(&x->x_gui, bflcol); + x->x_at[0].a_type = A_FLOAT; + x->x_at[1].a_type = A_FLOAT; + iemgui_verify_snd_ne_rcv(&x->x_gui); + return (x); +} + +static void my_canvas_ff(t_my_canvas *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + gfxstub_deleteforkey(x); +} + +void g_mycanvas_setup(void) +{ + my_canvas_class = class_new(gensym("cnv"), (t_newmethod)my_canvas_new, + (t_method)my_canvas_ff, sizeof(t_my_canvas), CLASS_NOINLET, A_GIMME, 0); + class_addcreator((t_newmethod)my_canvas_new, gensym("my_canvas"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_dialog, gensym("dialog"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_size, gensym("size"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_vis_size, gensym("vis_size"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_color, gensym("color"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_send, gensym("send"), A_DEFSYM, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_label_font, gensym("label_font"), A_GIMME, 0); + class_addmethod(my_canvas_class, (t_method)my_canvas_get_pos, gensym("get_pos"), 0); + + my_canvas_widgetbehavior.w_getrectfn = my_canvas_getrect; + my_canvas_widgetbehavior.w_displacefn = iemgui_displace; + my_canvas_widgetbehavior.w_selectfn = iemgui_select; + my_canvas_widgetbehavior.w_activatefn = NULL; + my_canvas_widgetbehavior.w_deletefn = iemgui_delete; + my_canvas_widgetbehavior.w_visfn = iemgui_vis; + my_canvas_widgetbehavior.w_clickfn = NULL; + class_setwidget(my_canvas_class, &my_canvas_widgetbehavior); + class_sethelpsymbol(my_canvas_class, gensym("my_canvas")); + class_setsavefn(my_canvas_class, my_canvas_save); + class_setpropertiesfn(my_canvas_class, my_canvas_properties); +} diff --git a/apps/plugins/pdbox/PDa/src/g_numbox.c b/apps/plugins/pdbox/PDa/src/g_numbox.c new file mode 100644 index 0000000..e742120 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_numbox.c @@ -0,0 +1,1814 @@ +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* my_numbox.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + +/*------------------ global varaibles -------------------------*/ + + +/*------------------ global functions -------------------------*/ + +static void my_numbox_key(void *z, t_floatarg fkey); + +/* ------------ nmx gui-my number box ----------------------- */ + +t_widgetbehavior my_numbox_widgetbehavior; +static t_class *my_numbox_class; + +/* widget helper functions */ + +static void my_numbox_tick_reset(t_my_numbox *x) +{ + if(x->x_gui.x_fsf.x_change) + { + x->x_gui.x_fsf.x_change = 0; + glist_grab(x->x_gui.x_glist, 0, 0, 0, 0, 0); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } +} + +static void my_numbox_tick_wait(t_my_numbox *x) +{ + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); +} + +void my_numbox_clip(t_my_numbox *x) +{ + if(x->x_val < x->x_min) + x->x_val = x->x_min; + if(x->x_val > x->x_max) + x->x_val = x->x_max; +} + +void my_numbox_calc_fontwidth(t_my_numbox *x) +{ + int w, f=31; + + if(x->x_gui.x_fsf.x_font_style == 1) + f = 27; + else if(x->x_gui.x_fsf.x_font_style == 2) + f = 25; + + w = x->x_gui.x_fontsize * f * x->x_gui.x_w; + w /= 36; + x->x_numwidth = w + (x->x_gui.x_h / 2) + 4; +} + +void my_numbox_ftoa(t_my_numbox *x) +{ + double f=x->x_val; + int bufsize, is_exp=0, i, idecimal; + + sprintf(x->x_buf, "%g", f); + bufsize = strlen(x->x_buf); + if(bufsize >= 5)/* if it is in exponential mode */ + { + i = bufsize - 4; + if((x->x_buf[i] == 'e') || (x->x_buf[i] == 'E')) + is_exp = 1; + } + if(bufsize > x->x_gui.x_w)/* if to reduce */ + { + if(is_exp) + { + if(x->x_gui.x_w <= 5) + { + x->x_buf[0] = (f < 0.0 ? '-' : '+'); + x->x_buf[1] = 0; + } + i = bufsize - 4; + for(idecimal=0; idecimal < i; idecimal++) + if(x->x_buf[idecimal] == '.') + break; + if(idecimal > (x->x_gui.x_w - 4)) + { + x->x_buf[0] = (f < 0.0 ? '-' : '+'); + x->x_buf[1] = 0; + } + else + { + int new_exp_index=x->x_gui.x_w-4, old_exp_index=bufsize-4; + + for(i=0; i < 4; i++, new_exp_index++, old_exp_index++) + x->x_buf[new_exp_index] = x->x_buf[old_exp_index]; + x->x_buf[x->x_gui.x_w] = 0; + } + + } + else + { + for(idecimal=0; idecimal < bufsize; idecimal++) + if(x->x_buf[idecimal] == '.') + break; + if(idecimal > x->x_gui.x_w) + { + x->x_buf[0] = (f < 0.0 ? '-' : '+'); + x->x_buf[1] = 0; + } + else + x->x_buf[x->x_gui.x_w] = 0; + } + } +} + +static void my_numbox_draw_update(t_my_numbox *x, t_glist *glist) +{ + if (glist_isvisible(glist)) + { + if(x->x_gui.x_fsf.x_change) + { + if(x->x_buf[0]) + { + char *cp=x->x_buf; + int sl = strlen(x->x_buf); + + x->x_buf[sl] = '>'; + x->x_buf[sl+1] = 0; + if(sl >= x->x_gui.x_w) + cp += sl - x->x_gui.x_w + 1; + sys_vgui( + ".x%x.c itemconfigure %xNUMBER -fill #%6.6x -text {%s} \n", + glist_getcanvas(glist), x, IEM_GUI_COLOR_EDITED, cp); + x->x_buf[sl] = 0; + } + else + { + my_numbox_ftoa(x); + sys_vgui( + ".x%x.c itemconfigure %xNUMBER -fill #%6.6x -text {%s} \n", + glist_getcanvas(glist), x, IEM_GUI_COLOR_EDITED, x->x_buf); + x->x_buf[0] = 0; + } + } + else + { + my_numbox_ftoa(x); + sys_vgui( + ".x%x.c itemconfigure %xNUMBER -fill #%6.6x -text {%s} \n", + glist_getcanvas(glist), x, + x->x_gui.x_fsf.x_selected? + IEM_GUI_COLOR_SELECTED:x->x_gui.x_fcol, + x->x_buf); + x->x_buf[0] = 0; + } + } +} + +static void my_numbox_draw_new(t_my_numbox *x, t_glist *glist) +{ + int half=x->x_gui.x_h/2, d=1+x->x_gui.x_h/34; + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui( +".x%x.c create polygon %d %d %d %d %d %d %d %d %d %d -outline #%6.6x \ +-fill #%6.6x -tags %xBASE1\n", + canvas, xpos, ypos, + xpos + x->x_numwidth-4, ypos, + xpos + x->x_numwidth, ypos+4, + xpos + x->x_numwidth, ypos + x->x_gui.x_h, + xpos, ypos + x->x_gui.x_h, + IEM_GUI_COLOR_NORMAL, x->x_gui.x_bcol, x); + sys_vgui( + ".x%x.c create line %d %d %d %d %d %d -fill #%6.6x -tags %xBASE2\n", + canvas, xpos, ypos, + xpos + half, ypos + half, + xpos, ypos + x->x_gui.x_h, + x->x_gui.x_fcol, x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x); + my_numbox_ftoa(x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xNUMBER\n", + canvas, xpos+half+2, ypos+half+d, + x->x_buf, x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_fcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos, ypos + x->x_gui.x_h-1, + xpos+IOWIDTH, ypos + x->x_gui.x_h, + x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos, ypos, + xpos+IOWIDTH, ypos+1, + x, 0); +} + +static void my_numbox_draw_move(t_my_numbox *x, t_glist *glist) +{ + int half = x->x_gui.x_h/2, d=1+x->x_gui.x_h/34; + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c coords %xBASE1 %d %d %d %d %d %d %d %d %d %d\n", + canvas, x, xpos, ypos, + xpos + x->x_numwidth-4, ypos, + xpos + x->x_numwidth, ypos+4, + xpos + x->x_numwidth, ypos + x->x_gui.x_h, + xpos, ypos + x->x_gui.x_h); + sys_vgui(".x%x.c coords %xBASE2 %d %d %d %d %d %d\n", + canvas, x, xpos, ypos, + xpos + half, ypos + half, + xpos, ypos + x->x_gui.x_h); + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + canvas, x, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy); + sys_vgui(".x%x.c coords %xNUMBER %d %d\n", + canvas, x, xpos+half+2, ypos+half+d); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, + xpos, ypos + x->x_gui.x_h-1, + xpos+IOWIDTH, ypos + x->x_gui.x_h); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, + xpos, ypos, + xpos+IOWIDTH, ypos+1); +} + +static void my_numbox_draw_erase(t_my_numbox* x,t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c delete %xBASE1\n", canvas, x); + sys_vgui(".x%x.c delete %xBASE2\n", canvas, x); + sys_vgui(".x%x.c delete %xLABEL\n", canvas, x); + sys_vgui(".x%x.c delete %xNUMBER\n", canvas, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +static void my_numbox_draw_config(t_my_numbox* x,t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + sys_vgui(".x%x.c itemconfigure %xNUMBER -font {%s %d bold} -fill #%6.6x \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_fcol); + sys_vgui(".x%x.c itemconfigure %xBASE1 -fill #%6.6x\n", canvas, + x, x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xBASE2 -fill #%6.6x\n", canvas, + x, x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_fcol); +} + +static void my_numbox_draw_io(t_my_numbox* x,t_glist* glist, int old_snd_rcv_flags) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos, ypos + x->x_gui.x_h-1, + xpos+IOWIDTH, ypos + x->x_gui.x_h, + x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos, ypos, + xpos+IOWIDTH, ypos+1, + x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +static void my_numbox_draw_select(t_my_numbox *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + if(x->x_gui.x_fsf.x_selected) + { + if(x->x_gui.x_fsf.x_change) + { + x->x_gui.x_fsf.x_change = 0; + clock_unset(x->x_clock_reset); + x->x_buf[0] = 0; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } + sys_vgui(".x%x.c itemconfigure %xBASE1 -outline #%6.6x\n", + canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xBASE2 -fill #%6.6x\n", + canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", + canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xNUMBER -fill #%6.6x\n", + canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + sys_vgui(".x%x.c itemconfigure %xBASE1 -outline #%6.6x\n", + canvas, x, IEM_GUI_COLOR_NORMAL); + sys_vgui(".x%x.c itemconfigure %xBASE2 -fill #%6.6x\n", + canvas, x, x->x_gui.x_fcol); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", + canvas, x, x->x_gui.x_lcol); + sys_vgui(".x%x.c itemconfigure %xNUMBER -fill #%6.6x\n", + canvas, x, x->x_gui.x_fcol); + } +} + +void my_numbox_draw(t_my_numbox *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + my_numbox_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + my_numbox_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + my_numbox_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + my_numbox_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + my_numbox_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + my_numbox_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + my_numbox_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ nbx widgetbehaviour----------------------------- */ + + +static void my_numbox_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_my_numbox* x = (t_my_numbox*)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist); + *yp1 = text_ypix(&x->x_gui.x_obj, glist); + *xp2 = *xp1 + x->x_numwidth; + *yp2 = *yp1 + x->x_gui.x_h; +} + +static void my_numbox_save(t_gobj *z, t_binbuf *b) +{ + t_my_numbox *x = (t_my_numbox *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + if(x->x_gui.x_fsf.x_change) + { + x->x_gui.x_fsf.x_change = 0; + clock_unset(x->x_clock_reset); + glist_grab(x->x_gui.x_glist, 0, 0, 0, 0, 0); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + + } + binbuf_addv(b, "ssiisiiffiisssiiiiiiifi", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix, + gensym("nbx"), x->x_gui.x_w, x->x_gui.x_h, + (float)x->x_min, (float)x->x_max, + x->x_lin0_log1, iem_symargstoint(&x->x_gui.x_isa), + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2], + x->x_val, x->x_log_height); + binbuf_addv(b, ";"); +} + +int my_numbox_check_minmax(t_my_numbox *x, double min, double max) +{ + int ret=0; + + if(x->x_lin0_log1) + { + if((min == 0.0)&&(max == 0.0)) + max = 1.0; + if(max > 0.0) + { + if(min <= 0.0) + min = 0.01*max; + } + else + { + if(min > 0.0) + max = 0.01*min; + } + } + x->x_min = min; + x->x_max = max; + if(x->x_val < x->x_min) + { + x->x_val = x->x_min; + ret = 1; + } + if(x->x_val > x->x_max) + { + x->x_val = x->x_max; + ret = 1; + } + if(x->x_lin0_log1) + x->x_k = exp(log(x->x_max/x->x_min)/(double)(x->x_log_height)); + else + x->x_k = 1.0; + return(ret); +} + +static void my_numbox_properties(t_gobj *z, t_glist *owner) +{ + t_my_numbox *x = (t_my_numbox *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + if(x->x_gui.x_fsf.x_change) + { + x->x_gui.x_fsf.x_change = 0; + clock_unset(x->x_clock_reset); + glist_grab(x->x_gui.x_glist, 0, 0, 0, 0, 0); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + + } + sprintf(buf, "pdtk_iemgui_dialog %%s NUMBERBOX \ + -------dimensions(digits)(pix):------- %d %d width: %d %d height: \ + -----------output-range:----------- %g min: %g max: %d \ + %d lin log %d %d log-height: %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, 1, x->x_gui.x_h, 8, + x->x_min, x->x_max, 0,/*no_schedule*/ + x->x_lin0_log1, x->x_gui.x_isa.x_loadinit, -1, + x->x_log_height, /*no multi, but iem-characteristic*/ + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, + 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void my_numbox_bang(t_my_numbox *x) +{ + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_val); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_val); +} + +static void my_numbox_dialog(t_my_numbox *x, t_symbol *s, int argc, + t_atom *argv) +{ + t_symbol *srl[3]; + int w = (int)atom_getintarg(0, argc, argv); + int h = (int)atom_getintarg(1, argc, argv); + double min = (double)atom_getfloatarg(2, argc, argv); + double max = (double)atom_getfloatarg(3, argc, argv); + int lilo = (int)atom_getintarg(4, argc, argv); + int log_height = (int)atom_getintarg(6, argc, argv); + int sr_flags; + + if(lilo != 0) lilo = 1; + x->x_lin0_log1 = lilo; + sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + if(w < 1) + w = 1; + x->x_gui.x_w = w; + if(h < 8) + h = 8; + x->x_gui.x_h = h; + if(log_height < 10) + log_height = 10; + x->x_log_height = log_height; + my_numbox_calc_fontwidth(x); + /*if(my_numbox_check_minmax(x, min, max)) + my_numbox_bang(x);*/ + my_numbox_check_minmax(x, min, max); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); +} + +static void my_numbox_motion(t_my_numbox *x, t_floatarg dx, t_floatarg dy) +{ + double k2=1.0; + + if(x->x_gui.x_fsf.x_finemoved) + k2 = 0.01; + if(x->x_lin0_log1) + x->x_val *= pow(x->x_k, -k2*dy); + else + x->x_val -= k2*dy; + my_numbox_clip(x); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + my_numbox_bang(x); + clock_unset(x->x_clock_reset); +} + +static void my_numbox_click(t_my_numbox *x, t_floatarg xpos, t_floatarg ypos, + t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.te_g, + (t_glistmotionfn)my_numbox_motion, my_numbox_key, xpos, ypos); +} + +static int my_numbox_newclick(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_my_numbox* x = (t_my_numbox *)z; + + if(doit) + { + my_numbox_click( x, (t_floatarg)xpix, (t_floatarg)ypix, + (t_floatarg)shift, 0, (t_floatarg)alt); + if(shift) + x->x_gui.x_fsf.x_finemoved = 1; + else + x->x_gui.x_fsf.x_finemoved = 0; + if(!x->x_gui.x_fsf.x_change) + { + clock_delay(x->x_clock_wait, 50); + x->x_gui.x_fsf.x_change = 1; + clock_delay(x->x_clock_reset, 3000); + /* glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.ob_g, + 0, my_numbox_key, 0, 0); */ + + x->x_buf[0] = 0; + } + else + { + x->x_gui.x_fsf.x_change = 0; + clock_unset(x->x_clock_reset); + glist_grab(x->x_gui.x_glist, 0, 0, 0, 0, 0); + x->x_buf[0] = 0; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } + } + return (1); +} + +static void my_numbox_set(t_my_numbox *x, t_floatarg f) +{ + x->x_val = f; + my_numbox_clip(x); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); +} + +static void my_numbox_log_height(t_my_numbox *x, t_floatarg lh) +{ + if(lh < 10.0) + lh = 10.0; + x->x_log_height = (int)lh; + if(x->x_lin0_log1) + x->x_k = exp(log(x->x_max/x->x_min)/(double)(x->x_log_height)); + else + x->x_k = 1.0; + +} + +static void my_numbox_float(t_my_numbox *x, t_floatarg f) +{ + my_numbox_set(x, f); + if(x->x_gui.x_fsf.x_put_in2out) + my_numbox_bang(x); +} + +static void my_numbox_size(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{ + int h, w; + + w = (int)atom_getintarg(0, ac, av); + if(w < 1) + w = 1; + x->x_gui.x_w = w; + if(ac > 1) + { + h = (int)atom_getintarg(1, ac, av); + if(h < 8) + h = 8; + x->x_gui.x_h = h; + } + my_numbox_calc_fontwidth(x); + iemgui_size((void *)x, &x->x_gui); +} + +static void my_numbox_delta(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void my_numbox_pos(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void my_numbox_range(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{ + if(my_numbox_check_minmax(x, (double)atom_getfloatarg(0, ac, av), + (double)atom_getfloatarg(1, ac, av))) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + /*my_numbox_bang(x);*/ + } +} + +static void my_numbox_color(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void my_numbox_send(t_my_numbox *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void my_numbox_receive(t_my_numbox *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void my_numbox_label(t_my_numbox *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void my_numbox_label_pos(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void my_numbox_label_font(t_my_numbox *x, + t_symbol *s, int ac, t_atom *av) +{ + int f = (int)atom_getintarg(1, ac, av); + + if(f < 4) + f = 4; + x->x_gui.x_fontsize = f; + f = (int)atom_getintarg(0, ac, av); + if((f < 0) || (f > 2)) + f = 0; + x->x_gui.x_fsf.x_font_style = f; + my_numbox_calc_fontwidth(x); + iemgui_label_font((void *)x, &x->x_gui, s, ac, av); +} + +static void my_numbox_log(t_my_numbox *x) +{ + x->x_lin0_log1 = 1; + if(my_numbox_check_minmax(x, x->x_min, x->x_max)) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + /*my_numbox_bang(x);*/ + } +} + +static void my_numbox_lin(t_my_numbox *x) +{ + x->x_lin0_log1 = 0; +} + +static void my_numbox_init(t_my_numbox *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void my_numbox_loadbang(t_my_numbox *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + my_numbox_bang(x); + } +} + +static void my_numbox_key(void *z, t_floatarg fkey) +{ + t_my_numbox *x = z; + char c=fkey; + char buf[3]; + buf[1] = 0; + + if (c == 0) + { + x->x_gui.x_fsf.x_change = 0; + clock_unset(x->x_clock_reset); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + return; + } + if(((c>='0')&&(c<='9'))||(c=='.')||(c=='-')|| + (c=='e')||(c=='+')||(c=='E')) + { + if(strlen(x->x_buf) < (IEMGUI_MAX_NUM_LEN-2)) + { + buf[0] = c; + strcat(x->x_buf, buf); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } + } + else if((c=='\b')||(c==127)) + { + int sl=strlen(x->x_buf)-1; + + if(sl < 0) + sl = 0; + x->x_buf[sl] = 0; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } + else if((c=='\n')||(c==13)) + { + x->x_val = atof(x->x_buf); + x->x_buf[0] = 0; + x->x_gui.x_fsf.x_change = 0; + clock_unset(x->x_clock_reset); + my_numbox_clip(x); + my_numbox_bang(x); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } + clock_delay(x->x_clock_reset, 3000); +} + +static void my_numbox_list(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{ + if (IS_A_FLOAT(av,0)) + { + my_numbox_set(x, atom_getfloatarg(0, ac, av)); + my_numbox_bang(x); + } +} + +static void *my_numbox_new(t_symbol *s, int argc, t_atom *argv) +{ + t_my_numbox *x = (t_my_numbox *)pd_new(my_numbox_class); + int bflcol[]={-262144, -1, -1}; + int w=5, h=14; + int lilo=0, f=0, ldx=0, ldy=-6; + int fs=10; + int log_height=256; + double min=-1.0e+37, max=1.0e+37,v=0.0; + char str[144]; + + if((argc >= 17)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1) + &&IS_A_FLOAT(argv,2)&&IS_A_FLOAT(argv,3) + &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5) + &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) + &&(IS_A_SYMBOL(argv,7)||IS_A_FLOAT(argv,7)) + &&(IS_A_SYMBOL(argv,8)||IS_A_FLOAT(argv,8)) + &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10) + &&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13) + &&IS_A_FLOAT(argv,14)&&IS_A_FLOAT(argv,15)&&IS_A_FLOAT(argv,16)) + { + w = (int)atom_getintarg(0, argc, argv); + h = (int)atom_getintarg(1, argc, argv); + min = (double)atom_getfloatarg(2, argc, argv); + max = (double)atom_getfloatarg(3, argc, argv); + lilo = (int)atom_getintarg(4, argc, argv); + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(5, argc, argv)); + iemgui_new_getnames(&x->x_gui, 6, argv); + ldx = (int)atom_getintarg(9, argc, argv); + ldy = (int)atom_getintarg(10, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(11, argc, argv)); + fs = (int)atom_getintarg(12, argc, argv); + bflcol[0] = (int)atom_getintarg(13, argc, argv); + bflcol[1] = (int)atom_getintarg(14, argc, argv); + bflcol[2] = (int)atom_getintarg(15, argc, argv); + v = atom_getfloatarg(16, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 6, 0); + if((argc == 18)&&IS_A_FLOAT(argv,17)) + { + log_height = (int)atom_getintarg(17, argc, argv); + } + x->x_gui.x_draw = (t_iemfunptr)my_numbox_draw; + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if(x->x_gui.x_isa.x_loadinit) + x->x_val = v; + else + x->x_val = 0.0; + if(lilo != 0) lilo = 1; + x->x_lin0_log1 = lilo; + if(log_height < 10) + log_height = 10; + x->x_log_height = log_height; + if (!strcmp(x->x_gui.x_snd->s_name, "empty")) + x->x_gui.x_fsf.x_snd_able = 0; + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + if (x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + if(w < 1) + w = 1; + x->x_gui.x_w = w; + if(h < 8) + h = 8; + x->x_gui.x_h = h; + x->x_buf[0] = 0; + my_numbox_calc_fontwidth(x); + my_numbox_check_minmax(x, min, max); + iemgui_all_colfromload(&x->x_gui, bflcol); + iemgui_verify_snd_ne_rcv(&x->x_gui); + x->x_clock_reset = clock_new(x, (t_method)my_numbox_tick_reset); + x->x_clock_wait = clock_new(x, (t_method)my_numbox_tick_wait); + x->x_gui.x_fsf.x_change = 0; + outlet_new(&x->x_gui.x_obj, &s_float); + return (x); +} + +static void my_numbox_free(t_my_numbox *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + clock_free(x->x_clock_reset); + clock_free(x->x_clock_wait); + gfxstub_deleteforkey(x); +} + +void g_numbox_setup(void) +{ + my_numbox_class = class_new(gensym("nbx"), (t_newmethod)my_numbox_new, + (t_method)my_numbox_free, sizeof(t_my_numbox), 0, A_GIMME, 0); + class_addcreator((t_newmethod)my_numbox_new, gensym("my_numbox"), + A_GIMME, 0); + class_addbang(my_numbox_class,my_numbox_bang); + class_addfloat(my_numbox_class,my_numbox_float); + class_addlist(my_numbox_class, my_numbox_list); + class_addmethod(my_numbox_class, (t_method)my_numbox_click, + gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_motion, + gensym("motion"), A_FLOAT, A_FLOAT, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_dialog, + gensym("dialog"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_loadbang, + gensym("loadbang"), 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_set, + gensym("set"), A_FLOAT, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_size, + gensym("size"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_delta, + gensym("delta"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_pos, + gensym("pos"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_range, + gensym("range"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_color, + gensym("color"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_send, + gensym("send"), A_DEFSYM, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_receive, + gensym("receive"), A_DEFSYM, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_label, + gensym("label"), A_DEFSYM, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_label_pos, + gensym("label_pos"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_label_font, + gensym("label_font"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_log, + gensym("log"), 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_lin, + gensym("lin"), 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_init, + gensym("init"), A_FLOAT, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_log_height, + gensym("log_height"), A_FLOAT, 0); + my_numbox_widgetbehavior.w_getrectfn = my_numbox_getrect; + my_numbox_widgetbehavior.w_displacefn = iemgui_displace; + my_numbox_widgetbehavior.w_selectfn = iemgui_select; + my_numbox_widgetbehavior.w_activatefn = NULL; + my_numbox_widgetbehavior.w_deletefn = iemgui_delete; + my_numbox_widgetbehavior.w_visfn = iemgui_vis; + my_numbox_widgetbehavior.w_clickfn = my_numbox_newclick; + class_setwidget(my_numbox_class, &my_numbox_widgetbehavior); + class_sethelpsymbol(my_numbox_class, gensym("numbox2")); + class_setsavefn(my_numbox_class, my_numbox_save); + class_setpropertiesfn(my_numbox_class, my_numbox_properties); +} +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* my_numbox.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + +/*------------------ global varaibles -------------------------*/ + + +/*------------------ global functions -------------------------*/ + +static void my_numbox_key(void *z, t_floatarg fkey); + +/* ------------ nmx gui-my number box ----------------------- */ + +t_widgetbehavior my_numbox_widgetbehavior; +static t_class *my_numbox_class; + +/* widget helper functions */ + +static void my_numbox_tick_reset(t_my_numbox *x) +{ + if(x->x_gui.x_fsf.x_change) + { + x->x_gui.x_fsf.x_change = 0; + glist_grab(x->x_gui.x_glist, 0, 0, 0, 0, 0); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } +} + +static void my_numbox_tick_wait(t_my_numbox *x) +{ + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); +} + +void my_numbox_clip(t_my_numbox *x) +{ + if(x->x_val < x->x_min) + x->x_val = x->x_min; + if(x->x_val > x->x_max) + x->x_val = x->x_max; +} + +void my_numbox_calc_fontwidth(t_my_numbox *x) +{ + int w, f=31; + + if(x->x_gui.x_fsf.x_font_style == 1) + f = 27; + else if(x->x_gui.x_fsf.x_font_style == 2) + f = 25; + + w = x->x_gui.x_fontsize * f * x->x_gui.x_w; + w /= 36; + x->x_numwidth = w + (x->x_gui.x_h / 2) + 4; +} + +void my_numbox_ftoa(t_my_numbox *x) +{ + double f=x->x_val; + int bufsize, is_exp=0, i, idecimal; + + sprintf(x->x_buf, "%g", f); + bufsize = strlen(x->x_buf); + if(bufsize >= 5)/* if it is in exponential mode */ + { + i = bufsize - 4; + if((x->x_buf[i] == 'e') || (x->x_buf[i] == 'E')) + is_exp = 1; + } + if(bufsize > x->x_gui.x_w)/* if to reduce */ + { + if(is_exp) + { + if(x->x_gui.x_w <= 5) + { + x->x_buf[0] = (f < 0.0 ? '-' : '+'); + x->x_buf[1] = 0; + } + i = bufsize - 4; + for(idecimal=0; idecimal < i; idecimal++) + if(x->x_buf[idecimal] == '.') + break; + if(idecimal > (x->x_gui.x_w - 4)) + { + x->x_buf[0] = (f < 0.0 ? '-' : '+'); + x->x_buf[1] = 0; + } + else + { + int new_exp_index=x->x_gui.x_w-4, old_exp_index=bufsize-4; + + for(i=0; i < 4; i++, new_exp_index++, old_exp_index++) + x->x_buf[new_exp_index] = x->x_buf[old_exp_index]; + x->x_buf[x->x_gui.x_w] = 0; + } + + } + else + { + for(idecimal=0; idecimal < bufsize; idecimal++) + if(x->x_buf[idecimal] == '.') + break; + if(idecimal > x->x_gui.x_w) + { + x->x_buf[0] = (f < 0.0 ? '-' : '+'); + x->x_buf[1] = 0; + } + else + x->x_buf[x->x_gui.x_w] = 0; + } + } +} + +static void my_numbox_draw_update(t_my_numbox *x, t_glist *glist) +{ + if (glist_isvisible(glist)) + { + if(x->x_gui.x_fsf.x_change) + { + if(x->x_buf[0]) + { + char *cp=x->x_buf; + int sl = strlen(x->x_buf); + + x->x_buf[sl] = '>'; + x->x_buf[sl+1] = 0; + if(sl >= x->x_gui.x_w) + cp += sl - x->x_gui.x_w + 1; + sys_vgui( + ".x%x.c itemconfigure %xNUMBER -fill #%6.6x -text {%s} \n", + glist_getcanvas(glist), x, IEM_GUI_COLOR_EDITED, cp); + x->x_buf[sl] = 0; + } + else + { + my_numbox_ftoa(x); + sys_vgui( + ".x%x.c itemconfigure %xNUMBER -fill #%6.6x -text {%s} \n", + glist_getcanvas(glist), x, IEM_GUI_COLOR_EDITED, x->x_buf); + x->x_buf[0] = 0; + } + } + else + { + my_numbox_ftoa(x); + sys_vgui( + ".x%x.c itemconfigure %xNUMBER -fill #%6.6x -text {%s} \n", + glist_getcanvas(glist), x, + x->x_gui.x_fsf.x_selected? + IEM_GUI_COLOR_SELECTED:x->x_gui.x_fcol, + x->x_buf); + x->x_buf[0] = 0; + } + } +} + +static void my_numbox_draw_new(t_my_numbox *x, t_glist *glist) +{ + int half=x->x_gui.x_h/2, d=1+x->x_gui.x_h/34; + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui( +".x%x.c create polygon %d %d %d %d %d %d %d %d %d %d -outline #%6.6x \ +-fill #%6.6x -tags %xBASE1\n", + canvas, xpos, ypos, + xpos + x->x_numwidth-4, ypos, + xpos + x->x_numwidth, ypos+4, + xpos + x->x_numwidth, ypos + x->x_gui.x_h, + xpos, ypos + x->x_gui.x_h, + IEM_GUI_COLOR_NORMAL, x->x_gui.x_bcol, x); + sys_vgui( + ".x%x.c create line %d %d %d %d %d %d -fill #%6.6x -tags %xBASE2\n", + canvas, xpos, ypos, + xpos + half, ypos + half, + xpos, ypos + x->x_gui.x_h, + x->x_gui.x_fcol, x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x); + my_numbox_ftoa(x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xNUMBER\n", + canvas, xpos+half+2, ypos+half+d, + x->x_buf, x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_fcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos, ypos + x->x_gui.x_h-1, + xpos+IOWIDTH, ypos + x->x_gui.x_h, + x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos, ypos, + xpos+IOWIDTH, ypos+1, + x, 0); +} + +static void my_numbox_draw_move(t_my_numbox *x, t_glist *glist) +{ + int half = x->x_gui.x_h/2, d=1+x->x_gui.x_h/34; + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c coords %xBASE1 %d %d %d %d %d %d %d %d %d %d\n", + canvas, x, xpos, ypos, + xpos + x->x_numwidth-4, ypos, + xpos + x->x_numwidth, ypos+4, + xpos + x->x_numwidth, ypos + x->x_gui.x_h, + xpos, ypos + x->x_gui.x_h); + sys_vgui(".x%x.c coords %xBASE2 %d %d %d %d %d %d\n", + canvas, x, xpos, ypos, + xpos + half, ypos + half, + xpos, ypos + x->x_gui.x_h); + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + canvas, x, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy); + sys_vgui(".x%x.c coords %xNUMBER %d %d\n", + canvas, x, xpos+half+2, ypos+half+d); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, + xpos, ypos + x->x_gui.x_h-1, + xpos+IOWIDTH, ypos + x->x_gui.x_h); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, + xpos, ypos, + xpos+IOWIDTH, ypos+1); +} + +static void my_numbox_draw_erase(t_my_numbox* x,t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c delete %xBASE1\n", canvas, x); + sys_vgui(".x%x.c delete %xBASE2\n", canvas, x); + sys_vgui(".x%x.c delete %xLABEL\n", canvas, x); + sys_vgui(".x%x.c delete %xNUMBER\n", canvas, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +static void my_numbox_draw_config(t_my_numbox* x,t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + sys_vgui(".x%x.c itemconfigure %xNUMBER -font {%s %d bold} -fill #%6.6x \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_fcol); + sys_vgui(".x%x.c itemconfigure %xBASE1 -fill #%6.6x\n", canvas, + x, x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xBASE2 -fill #%6.6x\n", canvas, + x, x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_fcol); +} + +static void my_numbox_draw_io(t_my_numbox* x,t_glist* glist, int old_snd_rcv_flags) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos, ypos + x->x_gui.x_h-1, + xpos+IOWIDTH, ypos + x->x_gui.x_h, + x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos, ypos, + xpos+IOWIDTH, ypos+1, + x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +static void my_numbox_draw_select(t_my_numbox *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + if(x->x_gui.x_fsf.x_selected) + { + if(x->x_gui.x_fsf.x_change) + { + x->x_gui.x_fsf.x_change = 0; + clock_unset(x->x_clock_reset); + x->x_buf[0] = 0; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } + sys_vgui(".x%x.c itemconfigure %xBASE1 -outline #%6.6x\n", + canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xBASE2 -fill #%6.6x\n", + canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", + canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xNUMBER -fill #%6.6x\n", + canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + sys_vgui(".x%x.c itemconfigure %xBASE1 -outline #%6.6x\n", + canvas, x, IEM_GUI_COLOR_NORMAL); + sys_vgui(".x%x.c itemconfigure %xBASE2 -fill #%6.6x\n", + canvas, x, x->x_gui.x_fcol); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", + canvas, x, x->x_gui.x_lcol); + sys_vgui(".x%x.c itemconfigure %xNUMBER -fill #%6.6x\n", + canvas, x, x->x_gui.x_fcol); + } +} + +void my_numbox_draw(t_my_numbox *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + my_numbox_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + my_numbox_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + my_numbox_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + my_numbox_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + my_numbox_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + my_numbox_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + my_numbox_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ nbx widgetbehaviour----------------------------- */ + + +static void my_numbox_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_my_numbox* x = (t_my_numbox*)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist); + *yp1 = text_ypix(&x->x_gui.x_obj, glist); + *xp2 = *xp1 + x->x_numwidth; + *yp2 = *yp1 + x->x_gui.x_h; +} + +static void my_numbox_save(t_gobj *z, t_binbuf *b) +{ + t_my_numbox *x = (t_my_numbox *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + if(x->x_gui.x_fsf.x_change) + { + x->x_gui.x_fsf.x_change = 0; + clock_unset(x->x_clock_reset); + glist_grab(x->x_gui.x_glist, 0, 0, 0, 0, 0); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + + } + binbuf_addv(b, "ssiisiiffiisssiiiiiiifi", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix, + gensym("nbx"), x->x_gui.x_w, x->x_gui.x_h, + (float)x->x_min, (float)x->x_max, + x->x_lin0_log1, iem_symargstoint(&x->x_gui.x_isa), + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2], + x->x_val, x->x_log_height); + binbuf_addv(b, ";"); +} + +int my_numbox_check_minmax(t_my_numbox *x, double min, double max) +{ + int ret=0; + + if(x->x_lin0_log1) + { + if((min == 0.0)&&(max == 0.0)) + max = 1.0; + if(max > 0.0) + { + if(min <= 0.0) + min = 0.01*max; + } + else + { + if(min > 0.0) + max = 0.01*min; + } + } + x->x_min = min; + x->x_max = max; + if(x->x_val < x->x_min) + { + x->x_val = x->x_min; + ret = 1; + } + if(x->x_val > x->x_max) + { + x->x_val = x->x_max; + ret = 1; + } + if(x->x_lin0_log1) + x->x_k = exp(log(x->x_max/x->x_min)/(double)(x->x_log_height)); + else + x->x_k = 1.0; + return(ret); +} + +static void my_numbox_properties(t_gobj *z, t_glist *owner) +{ + t_my_numbox *x = (t_my_numbox *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + if(x->x_gui.x_fsf.x_change) + { + x->x_gui.x_fsf.x_change = 0; + clock_unset(x->x_clock_reset); + glist_grab(x->x_gui.x_glist, 0, 0, 0, 0, 0); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + + } + sprintf(buf, "pdtk_iemgui_dialog %%s NUMBERBOX \ + -------dimensions(digits)(pix):------- %d %d width: %d %d height: \ + -----------output-range:----------- %g min: %g max: %d \ + %d lin log %d %d log-height: %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, 1, x->x_gui.x_h, 8, + x->x_min, x->x_max, 0,/*no_schedule*/ + x->x_lin0_log1, x->x_gui.x_isa.x_loadinit, -1, + x->x_log_height, /*no multi, but iem-characteristic*/ + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, + 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void my_numbox_bang(t_my_numbox *x) +{ + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_val); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_val); +} + +static void my_numbox_dialog(t_my_numbox *x, t_symbol *s, int argc, + t_atom *argv) +{ + t_symbol *srl[3]; + int w = (int)atom_getintarg(0, argc, argv); + int h = (int)atom_getintarg(1, argc, argv); + double min = (double)atom_getfloatarg(2, argc, argv); + double max = (double)atom_getfloatarg(3, argc, argv); + int lilo = (int)atom_getintarg(4, argc, argv); + int log_height = (int)atom_getintarg(6, argc, argv); + int sr_flags; + + if(lilo != 0) lilo = 1; + x->x_lin0_log1 = lilo; + sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + if(w < 1) + w = 1; + x->x_gui.x_w = w; + if(h < 8) + h = 8; + x->x_gui.x_h = h; + if(log_height < 10) + log_height = 10; + x->x_log_height = log_height; + my_numbox_calc_fontwidth(x); + /*if(my_numbox_check_minmax(x, min, max)) + my_numbox_bang(x);*/ + my_numbox_check_minmax(x, min, max); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); +} + +static void my_numbox_motion(t_my_numbox *x, t_floatarg dx, t_floatarg dy) +{ + double k2=1.0; + + if(x->x_gui.x_fsf.x_finemoved) + k2 = 0.01; + if(x->x_lin0_log1) + x->x_val *= pow(x->x_k, -k2*dy); + else + x->x_val -= k2*dy; + my_numbox_clip(x); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + my_numbox_bang(x); + clock_unset(x->x_clock_reset); +} + +static void my_numbox_click(t_my_numbox *x, t_floatarg xpos, t_floatarg ypos, + t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.te_g, + (t_glistmotionfn)my_numbox_motion, my_numbox_key, xpos, ypos); +} + +static int my_numbox_newclick(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_my_numbox* x = (t_my_numbox *)z; + + if(doit) + { + my_numbox_click( x, (t_floatarg)xpix, (t_floatarg)ypix, + (t_floatarg)shift, 0, (t_floatarg)alt); + if(shift) + x->x_gui.x_fsf.x_finemoved = 1; + else + x->x_gui.x_fsf.x_finemoved = 0; + if(!x->x_gui.x_fsf.x_change) + { + clock_delay(x->x_clock_wait, 50); + x->x_gui.x_fsf.x_change = 1; + clock_delay(x->x_clock_reset, 3000); + /* glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.ob_g, + 0, my_numbox_key, 0, 0); */ + + x->x_buf[0] = 0; + } + else + { + x->x_gui.x_fsf.x_change = 0; + clock_unset(x->x_clock_reset); + glist_grab(x->x_gui.x_glist, 0, 0, 0, 0, 0); + x->x_buf[0] = 0; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } + } + return (1); +} + +static void my_numbox_set(t_my_numbox *x, t_floatarg f) +{ + x->x_val = f; + my_numbox_clip(x); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); +} + +static void my_numbox_log_height(t_my_numbox *x, t_floatarg lh) +{ + if(lh < 10.0) + lh = 10.0; + x->x_log_height = (int)lh; + if(x->x_lin0_log1) + x->x_k = exp(log(x->x_max/x->x_min)/(double)(x->x_log_height)); + else + x->x_k = 1.0; + +} + +static void my_numbox_float(t_my_numbox *x, t_floatarg f) +{ + my_numbox_set(x, f); + if(x->x_gui.x_fsf.x_put_in2out) + my_numbox_bang(x); +} + +static void my_numbox_size(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{ + int h, w; + + w = (int)atom_getintarg(0, ac, av); + if(w < 1) + w = 1; + x->x_gui.x_w = w; + if(ac > 1) + { + h = (int)atom_getintarg(1, ac, av); + if(h < 8) + h = 8; + x->x_gui.x_h = h; + } + my_numbox_calc_fontwidth(x); + iemgui_size((void *)x, &x->x_gui); +} + +static void my_numbox_delta(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void my_numbox_pos(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void my_numbox_range(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{ + if(my_numbox_check_minmax(x, (double)atom_getfloatarg(0, ac, av), + (double)atom_getfloatarg(1, ac, av))) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + /*my_numbox_bang(x);*/ + } +} + +static void my_numbox_color(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void my_numbox_send(t_my_numbox *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void my_numbox_receive(t_my_numbox *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void my_numbox_label(t_my_numbox *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void my_numbox_label_pos(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void my_numbox_label_font(t_my_numbox *x, + t_symbol *s, int ac, t_atom *av) +{ + int f = (int)atom_getintarg(1, ac, av); + + if(f < 4) + f = 4; + x->x_gui.x_fontsize = f; + f = (int)atom_getintarg(0, ac, av); + if((f < 0) || (f > 2)) + f = 0; + x->x_gui.x_fsf.x_font_style = f; + my_numbox_calc_fontwidth(x); + iemgui_label_font((void *)x, &x->x_gui, s, ac, av); +} + +static void my_numbox_log(t_my_numbox *x) +{ + x->x_lin0_log1 = 1; + if(my_numbox_check_minmax(x, x->x_min, x->x_max)) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + /*my_numbox_bang(x);*/ + } +} + +static void my_numbox_lin(t_my_numbox *x) +{ + x->x_lin0_log1 = 0; +} + +static void my_numbox_init(t_my_numbox *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void my_numbox_loadbang(t_my_numbox *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + my_numbox_bang(x); + } +} + +static void my_numbox_key(void *z, t_floatarg fkey) +{ + t_my_numbox *x = z; + char c=fkey; + char buf[3]; + buf[1] = 0; + + if (c == 0) + { + x->x_gui.x_fsf.x_change = 0; + clock_unset(x->x_clock_reset); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + return; + } + if(((c>='0')&&(c<='9'))||(c=='.')||(c=='-')|| + (c=='e')||(c=='+')||(c=='E')) + { + if(strlen(x->x_buf) < (IEMGUI_MAX_NUM_LEN-2)) + { + buf[0] = c; + strcat(x->x_buf, buf); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } + } + else if((c=='\b')||(c==127)) + { + int sl=strlen(x->x_buf)-1; + + if(sl < 0) + sl = 0; + x->x_buf[sl] = 0; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } + else if((c=='\n')||(c==13)) + { + x->x_val = atof(x->x_buf); + x->x_buf[0] = 0; + x->x_gui.x_fsf.x_change = 0; + clock_unset(x->x_clock_reset); + my_numbox_clip(x); + my_numbox_bang(x); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } + clock_delay(x->x_clock_reset, 3000); +} + +static void my_numbox_list(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{ + if (IS_A_FLOAT(av,0)) + { + my_numbox_set(x, atom_getfloatarg(0, ac, av)); + my_numbox_bang(x); + } +} + +static void *my_numbox_new(t_symbol *s, int argc, t_atom *argv) +{ + t_my_numbox *x = (t_my_numbox *)pd_new(my_numbox_class); + int bflcol[]={-262144, -1, -1}; + int w=5, h=14; + int lilo=0, f=0, ldx=0, ldy=-6; + int fs=10; + int log_height=256; + double min=-1.0e+37, max=1.0e+37,v=0.0; + char str[144]; + + if((argc >= 17)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1) + &&IS_A_FLOAT(argv,2)&&IS_A_FLOAT(argv,3) + &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5) + &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) + &&(IS_A_SYMBOL(argv,7)||IS_A_FLOAT(argv,7)) + &&(IS_A_SYMBOL(argv,8)||IS_A_FLOAT(argv,8)) + &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10) + &&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13) + &&IS_A_FLOAT(argv,14)&&IS_A_FLOAT(argv,15)&&IS_A_FLOAT(argv,16)) + { + w = (int)atom_getintarg(0, argc, argv); + h = (int)atom_getintarg(1, argc, argv); + min = (double)atom_getfloatarg(2, argc, argv); + max = (double)atom_getfloatarg(3, argc, argv); + lilo = (int)atom_getintarg(4, argc, argv); + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(5, argc, argv)); + iemgui_new_getnames(&x->x_gui, 6, argv); + ldx = (int)atom_getintarg(9, argc, argv); + ldy = (int)atom_getintarg(10, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(11, argc, argv)); + fs = (int)atom_getintarg(12, argc, argv); + bflcol[0] = (int)atom_getintarg(13, argc, argv); + bflcol[1] = (int)atom_getintarg(14, argc, argv); + bflcol[2] = (int)atom_getintarg(15, argc, argv); + v = atom_getfloatarg(16, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 6, 0); + if((argc == 18)&&IS_A_FLOAT(argv,17)) + { + log_height = (int)atom_getintarg(17, argc, argv); + } + x->x_gui.x_draw = (t_iemfunptr)my_numbox_draw; + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if(x->x_gui.x_isa.x_loadinit) + x->x_val = v; + else + x->x_val = 0.0; + if(lilo != 0) lilo = 1; + x->x_lin0_log1 = lilo; + if(log_height < 10) + log_height = 10; + x->x_log_height = log_height; + if (!strcmp(x->x_gui.x_snd->s_name, "empty")) + x->x_gui.x_fsf.x_snd_able = 0; + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + if (x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + if(w < 1) + w = 1; + x->x_gui.x_w = w; + if(h < 8) + h = 8; + x->x_gui.x_h = h; + x->x_buf[0] = 0; + my_numbox_calc_fontwidth(x); + my_numbox_check_minmax(x, min, max); + iemgui_all_colfromload(&x->x_gui, bflcol); + iemgui_verify_snd_ne_rcv(&x->x_gui); + x->x_clock_reset = clock_new(x, (t_method)my_numbox_tick_reset); + x->x_clock_wait = clock_new(x, (t_method)my_numbox_tick_wait); + x->x_gui.x_fsf.x_change = 0; + outlet_new(&x->x_gui.x_obj, &s_float); + return (x); +} + +static void my_numbox_free(t_my_numbox *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + clock_free(x->x_clock_reset); + clock_free(x->x_clock_wait); + gfxstub_deleteforkey(x); +} + +void g_numbox_setup(void) +{ + my_numbox_class = class_new(gensym("nbx"), (t_newmethod)my_numbox_new, + (t_method)my_numbox_free, sizeof(t_my_numbox), 0, A_GIMME, 0); + class_addcreator((t_newmethod)my_numbox_new, gensym("my_numbox"), + A_GIMME, 0); + class_addbang(my_numbox_class,my_numbox_bang); + class_addfloat(my_numbox_class,my_numbox_float); + class_addlist(my_numbox_class, my_numbox_list); + class_addmethod(my_numbox_class, (t_method)my_numbox_click, + gensym("click"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_motion, + gensym("motion"), A_FLOAT, A_FLOAT, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_dialog, + gensym("dialog"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_loadbang, + gensym("loadbang"), 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_set, + gensym("set"), A_FLOAT, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_size, + gensym("size"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_delta, + gensym("delta"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_pos, + gensym("pos"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_range, + gensym("range"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_color, + gensym("color"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_send, + gensym("send"), A_DEFSYM, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_receive, + gensym("receive"), A_DEFSYM, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_label, + gensym("label"), A_DEFSYM, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_label_pos, + gensym("label_pos"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_label_font, + gensym("label_font"), A_GIMME, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_log, + gensym("log"), 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_lin, + gensym("lin"), 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_init, + gensym("init"), A_FLOAT, 0); + class_addmethod(my_numbox_class, (t_method)my_numbox_log_height, + gensym("log_height"), A_FLOAT, 0); + my_numbox_widgetbehavior.w_getrectfn = my_numbox_getrect; + my_numbox_widgetbehavior.w_displacefn = iemgui_displace; + my_numbox_widgetbehavior.w_selectfn = iemgui_select; + my_numbox_widgetbehavior.w_activatefn = NULL; + my_numbox_widgetbehavior.w_deletefn = iemgui_delete; + my_numbox_widgetbehavior.w_visfn = iemgui_vis; + my_numbox_widgetbehavior.w_clickfn = my_numbox_newclick; + class_setwidget(my_numbox_class, &my_numbox_widgetbehavior); + class_sethelpsymbol(my_numbox_class, gensym("numbox2")); + class_setsavefn(my_numbox_class, my_numbox_save); + class_setpropertiesfn(my_numbox_class, my_numbox_properties); +} diff --git a/apps/plugins/pdbox/PDa/src/g_readwrite.c b/apps/plugins/pdbox/PDa/src/g_readwrite.c new file mode 100644 index 0000000..a25b60c --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_readwrite.c @@ -0,0 +1,1446 @@ +/* Copyright (c) 1997-2002 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* this file reads and writes the "data" portions of a canvas to a file. +See also canvas_saveto(), etc., in g_editor.c. The data portion is a +collection of "scalar" objects. Routines here can save collections of +scalars into a file and reload them; also, support is included here for +*/ + +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include + + /* the following routines read "scalars" from a file into a canvas. */ + +static int canvas_scanbinbuf(int natoms, t_atom *vec, int *p_indexout, + int *p_next) +{ + int i, j; + int indexwas = *p_next; + *p_indexout = indexwas; + if (indexwas >= natoms) + return (0); + for (i = indexwas; i < natoms && vec[i].a_type != A_SEMI; i++) + ; + if (i >= natoms) + *p_next = i; + else *p_next = i + 1; + return (i - indexwas); +} + +int glist_readscalar(t_glist *x, int natoms, t_atom *vec, + int *p_nextmsg, int selectit); + +static void canvas_readerror(int natoms, t_atom *vec, int message, + int nline, char *s) +{ + error(s); + startpost("line was:"); + postatom(nline, vec + message); + endpost(); +} + + /* fill in the contents of the scalar into the vector w. */ + +static void glist_readatoms(t_glist *x, int natoms, t_atom *vec, + int *p_nextmsg, t_symbol *templatesym, t_word *w, int argc, t_atom *argv) +{ + int message, nline, n, i; + + t_template *template = template_findbyname(templatesym); + if (!template) + { + error("%s: no such template", templatesym->s_name); + *p_nextmsg = natoms; + return; + } + word_restore(w, template, argc, argv); + n = template->t_n; + for (i = 0; i < n; i++) + { + if (template->t_vec[i].ds_type == DT_ARRAY) + { + int j; + t_array *a = w[i].w_array; + int elemsize = a->a_elemsize, nitems = 0; + t_symbol *arraytemplatesym = template->t_vec[i].ds_arraytemplate; + t_template *arraytemplate = + template_findbyname(arraytemplatesym); + if (!arraytemplate) + { + error("%s: no such template", arraytemplatesym->s_name); + } + else while (1) + { + t_word *element; + int nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg); + /* empty line terminates array */ + if (!nline) + break; + array_resize(a, arraytemplate, nitems + 1); + element = (t_word *)(((char *)a->a_vec) + + nitems * elemsize); + glist_readatoms(x, natoms, vec, p_nextmsg, arraytemplatesym, + element, nline, vec + message); + nitems++; + } + } + else if (template->t_vec[i].ds_type == DT_LIST) + { + while (1) + { + if (!glist_readscalar(w->w_list, natoms, vec, + p_nextmsg, 0)) + break; + } + } + } +} + +int glist_readscalar(t_glist *x, int natoms, t_atom *vec, + int *p_nextmsg, int selectit) +{ + int message, i, j, nline; + t_template *template; + t_symbol *templatesym; + t_scalar *sc; + int nextmsg = *p_nextmsg; + int wasvis = glist_isvisible(x); + + if (nextmsg >= natoms || vec[nextmsg].a_type != A_SYMBOL) + { + if (nextmsg < natoms) + post("stopping early: type %d", vec[nextmsg].a_type); + *p_nextmsg = natoms; + return (0); + } + templatesym = canvas_makebindsym(vec[nextmsg].a_w.w_symbol); + *p_nextmsg = nextmsg + 1; + + if (!(template = template_findbyname(templatesym))) + { + error("canvas_read: %s: no such template", templatesym->s_name); + *p_nextmsg = natoms; + return (0); + } + sc = scalar_new(x, templatesym); + if (!sc) + { + error("couldn't create scalar \"%s\"", templatesym->s_name); + *p_nextmsg = natoms; + return (0); + } + if (wasvis) + { + /* temporarily lie about vis flag while this is built */ + glist_getcanvas(x)->gl_mapped = 0; + } + glist_add(x, &sc->sc_gobj); + + nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg); + glist_readatoms(x, natoms, vec, p_nextmsg, templatesym, sc->sc_vec, + nline, vec + message); + if (wasvis) + { + /* reset vis flag as before */ + glist_getcanvas(x)->gl_mapped = 1; + gobj_vis(&sc->sc_gobj, x, 1); + } + if (selectit) + { + glist_select(x, &sc->sc_gobj); + } + return (1); +} + +void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename, int selectem) +{ + t_canvas *canvas = glist_getcanvas(x); + int cr = 0, natoms, nline, message, nextmsg = 0, i, j, nitems; + t_atom *vec; + t_gobj *gobj; + + natoms = binbuf_getnatom(b); + vec = binbuf_getvec(b); + + + /* check for file type */ + nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); + if (nline != 1 && vec[message].a_type != A_SYMBOL && + strcmp(vec[message].a_w.w_symbol->s_name, "data")) + { + pd_error(x, "%s: file apparently of wrong type", filename); + binbuf_free(b); + return; + } + /* read in templates and check for consistency */ + while (1) + { + t_template *newtemplate, *existtemplate; + t_symbol *templatesym; + t_atom *templateargs = getbytes(0); + int ntemplateargs = 0, newnargs; + nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); + if (nline < 2) + break; + else if (nline > 2) + canvas_readerror(natoms, vec, message, nline, + "extra items ignored"); + else if (vec[message].a_type != A_SYMBOL || + strcmp(vec[message].a_w.w_symbol->s_name, "template") || + vec[message + 1].a_type != A_SYMBOL) + { + canvas_readerror(natoms, vec, message, nline, + "bad template header"); + continue; + } + templatesym = canvas_makebindsym(vec[message + 1].a_w.w_symbol); + while (1) + { + nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); + if (nline != 2 && nline != 3) + break; + newnargs = ntemplateargs + nline; + templateargs = (t_atom *)t_resizebytes(templateargs, + sizeof(*templateargs) * ntemplateargs, + sizeof(*templateargs) * newnargs); + templateargs[ntemplateargs] = vec[message]; + templateargs[ntemplateargs + 1] = vec[message + 1]; + if (nline == 3) + templateargs[ntemplateargs + 2] = vec[message + 2]; + ntemplateargs = newnargs; + } + newtemplate = template_new(templatesym, ntemplateargs, templateargs); + t_freebytes(templateargs, sizeof (*templateargs) * ntemplateargs); + if (!(existtemplate = template_findbyname(templatesym))) + { + error("%s: template not found in current patch", + templatesym->s_name); + template_free(newtemplate); + return; + } + if (!template_match(existtemplate, newtemplate)) + { + error("%s: template doesn't match current one", + templatesym->s_name); + template_free(newtemplate); + return; + } + template_free(newtemplate); + } + while (nextmsg < natoms) + { + glist_readscalar(x, natoms, vec, &nextmsg, selectem); + } +} + +static void glist_doread(t_glist *x, t_symbol *filename, t_symbol *format, + int clearme) +{ + t_binbuf *b = binbuf_new(); + t_canvas *canvas = glist_getcanvas(x); + int wasvis = glist_isvisible(canvas); + int cr = 0, natoms, nline, message, nextmsg = 0, i, j; + t_atom *vec; + + if (!strcmp(format->s_name, "cr")) + cr = 1; + else if (*format->s_name) + error("qlist_read: unknown flag: %s", format->s_name); + + if (binbuf_read_via_path(b, filename->s_name, + canvas_getdir(canvas)->s_name, cr)) + { + pd_error(x, "read failed"); + binbuf_free(b); + return; + } + if (wasvis) + canvas_vis(canvas, 0); + if (clearme) + glist_clear(x); + glist_readfrombinbuf(x, b, filename->s_name, 0); + if (wasvis) + canvas_vis(canvas, 1); + binbuf_free(b); +} + +void glist_read(t_glist *x, t_symbol *filename, t_symbol *format) +{ + glist_doread(x, filename, format, 1); +} + +void glist_mergefile(t_glist *x, t_symbol *filename, t_symbol *format) +{ + glist_doread(x, filename, format, 0); +} + + /* read text from a "properties" window, called from a gfxstub set + up in scalar_properties(). We try to restore the object; if successful + we delete the scalar and put the new thing in its place on the list. */ +void canvas_dataproperties(t_canvas *x, t_scalar *sc, t_binbuf *b) +{ + int ntotal, nnew, scindex; + t_gobj *y, *y2 = 0, *newone, *oldone = 0; + for (y = x->gl_list, ntotal = 0, scindex = -1; y; y = y->g_next) + { + if (y == &sc->sc_gobj) + scindex = ntotal, oldone = y; + ntotal++; + } + + if (scindex == -1) + bug("data_properties: scalar disappeared"); + glist_readfrombinbuf(x, b, "properties dialog", 0); + newone = 0; + if (scindex >= 0) + { + /* take the new object off the list */ + if (ntotal) + { + for (y = x->gl_list, nnew = 1; y2 = y->g_next; + y = y2, nnew++) + if (nnew == ntotal) + { + newone = y2; + y->g_next = y2->g_next; + break; + } + } + else newone = x->gl_list, x->gl_list = newone->g_next; + } + if (!newone) + error("couldn't update properties (perhaps a format problem?)"); + else if (!oldone) + bug("data_properties: couldn't find old element"); + else + { + glist_delete(x, oldone); + if (scindex > 0) + { + for (y = x->gl_list, nnew = 1; y; + y = y->g_next, nnew++) + if (nnew == scindex || !y->g_next) + { + newone->g_next = y->g_next; + y->g_next = newone; + goto didit; + } + bug("data_properties: can't reinsert"); + } + else newone->g_next = x->gl_list, x->gl_list = newone; + } +didit: + ; +} + + /* ----------- routines to write data to a binbuf ----------- */ + +void canvas_doaddtemplate(t_symbol *templatesym, + int *p_ntemplates, t_symbol ***p_templatevec) +{ + int n = *p_ntemplates, i; + t_symbol **templatevec = *p_templatevec; + for (i = 0; i < n; i++) + if (templatevec[i] == templatesym) + return; + templatevec = (t_symbol **)t_resizebytes(templatevec, + n * sizeof(*templatevec), (n+1) * sizeof(*templatevec)); + templatevec[n] = templatesym; + *p_templatevec = templatevec; + *p_ntemplates = n+1; +} + +static void glist_writelist(t_gobj *y, t_binbuf *b); + +void canvas_writescalar(t_symbol *templatesym, t_word *w, t_binbuf *b, + int amarrayelement) +{ + t_dataslot *ds; + t_template *template = template_findbyname(templatesym); + t_atom *a = (t_atom *)t_getbytes(0); + int i, n = template->t_n, natom = 0; + if (!amarrayelement) + { + t_atom templatename; + SETSYMBOL(&templatename, gensym(templatesym->s_name + 3)); + binbuf_add(b, 1, &templatename); + } + if (!template) + bug("canvas_writescalar"); + /* write the atoms (floats and symbols) */ + for (i = 0; i < n; i++) + { + if (template->t_vec[i].ds_type == DT_FLOAT || + template->t_vec[i].ds_type == DT_SYMBOL) + { + a = (t_atom *)t_resizebytes(a, + natom * sizeof(*a), (natom + 1) * sizeof (*a)); + if (template->t_vec[i].ds_type == DT_FLOAT) + SETFLOAT(a + natom, w[i].w_float); + else SETSYMBOL(a + natom, w[i].w_symbol); + natom++; + } + } + /* array elements have to have at least something */ + if (natom == 0 && amarrayelement) + SETSYMBOL(a + natom, &s_bang), natom++; + binbuf_add(b, natom, a); + binbuf_addsemi(b); + t_freebytes(a, natom * sizeof(*a)); + for (i = 0; i < n; i++) + { + if (template->t_vec[i].ds_type == DT_ARRAY) + { + int j; + t_array *a = w[i].w_array; + int elemsize = a->a_elemsize, nitems = a->a_n; + t_symbol *arraytemplatesym = template->t_vec[i].ds_arraytemplate; + for (j = 0; j < nitems; j++) + canvas_writescalar(arraytemplatesym, + (t_word *)(((char *)a->a_vec) + elemsize * j), b, 1); + binbuf_addsemi(b); + } + else if (template->t_vec[i].ds_type == DT_LIST) + { + glist_writelist(w->w_list->gl_list, b); + binbuf_addsemi(b); + } + } +} + +static void glist_writelist(t_gobj *y, t_binbuf *b) +{ + for (; y; y = y->g_next) + { + if (pd_class(&y->g_pd) == scalar_class) + { + canvas_writescalar(((t_scalar *)y)->sc_template, + ((t_scalar *)y)->sc_vec, b, 0); + } + } +} + + /* ------------ routines to write out templates for data ------- */ + +static void canvas_addtemplatesforlist(t_gobj *y, + int *p_ntemplates, t_symbol ***p_templatevec); + +static void canvas_addtemplatesforscalar(t_symbol *templatesym, + t_word *w, int *p_ntemplates, t_symbol ***p_templatevec) +{ + t_dataslot *ds; + int i; + t_template *template = template_findbyname(templatesym); + canvas_doaddtemplate(templatesym, p_ntemplates, p_templatevec); + if (!template) + bug("canvas_addtemplatesforscalar"); + else for (ds = template->t_vec, i = template->t_n; i--; ds++, w++) + { + if (ds->ds_type == DT_ARRAY) + { + int j; + t_array *a = w->w_array; + int elemsize = a->a_elemsize, nitems = a->a_n; + t_symbol *arraytemplatesym = ds->ds_arraytemplate; + canvas_doaddtemplate(arraytemplatesym, p_ntemplates, p_templatevec); + for (j = 0; j < nitems; j++) + canvas_addtemplatesforscalar(arraytemplatesym, + (t_word *)(((char *)a->a_vec) + elemsize * j), + p_ntemplates, p_templatevec); + } + else if (ds->ds_type == DT_LIST) + canvas_addtemplatesforlist(w->w_list->gl_list, + p_ntemplates, p_templatevec); + } +} + +static void canvas_addtemplatesforlist(t_gobj *y, + int *p_ntemplates, t_symbol ***p_templatevec) +{ + for (; y; y = y->g_next) + { + if (pd_class(&y->g_pd) == scalar_class) + { + canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template, + ((t_scalar *)y)->sc_vec, p_ntemplates, p_templatevec); + } + } +} + + /* write all "scalars" in a glist to a binbuf. */ +t_binbuf *glist_writetobinbuf(t_glist *x, int wholething) +{ + int i; + t_symbol **templatevec = getbytes(0); + int ntemplates = 0; + t_gobj *y; + t_binbuf *b = binbuf_new(); + + for (y = x->gl_list; y; y = y->g_next) + { + if ((pd_class(&y->g_pd) == scalar_class) && + (wholething || glist_isselected(x, y))) + { + canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template, + ((t_scalar *)y)->sc_vec, &ntemplates, &templatevec); + } + } + binbuf_addv(b, "s;", gensym("data")); + for (i = 0; i < ntemplates; i++) + { + t_template *template = template_findbyname(templatevec[i]); + int j, m = template->t_n; + /* drop "pd-" prefix from template symbol to print it: */ + binbuf_addv(b, "ss;", gensym("template"), + gensym(templatevec[i]->s_name + 3)); + for (j = 0; j < m; j++) + { + t_symbol *type; + switch (template->t_vec[j].ds_type) + { + case DT_FLOAT: type = &s_float; break; + case DT_SYMBOL: type = &s_symbol; break; + case DT_ARRAY: type = gensym("array"); break; + case DT_LIST: type = &s_list; break; + default: type = &s_float; bug("canvas_write"); + } + if (template->t_vec[j].ds_type == DT_ARRAY) + binbuf_addv(b, "sss;", type, template->t_vec[j].ds_name, + gensym(template->t_vec[j].ds_arraytemplate->s_name + 3)); + else binbuf_addv(b, "ss;", type, template->t_vec[j].ds_name); + } + binbuf_addsemi(b); + } + binbuf_addsemi(b); + /* now write out the objects themselves */ + for (y = x->gl_list; y; y = y->g_next) + { + if ((pd_class(&y->g_pd) == scalar_class) && + (wholething || glist_isselected(x, y))) + { + canvas_writescalar(((t_scalar *)y)->sc_template, + ((t_scalar *)y)->sc_vec, b, 0); + } + } + return (b); +} + +static void glist_write(t_glist *x, t_symbol *filename, t_symbol *format) +{ + int cr = 0, i; + t_binbuf *b; + char buf[MAXPDSTRING]; + t_symbol **templatevec = getbytes(0); + int ntemplates = 0; + t_gobj *y; + t_canvas *canvas = glist_getcanvas(x); + canvas_makefilename(canvas, filename->s_name, buf, MAXPDSTRING); + if (!strcmp(format->s_name, "cr")) + cr = 1; + else if (*format->s_name) + error("qlist_read: unknown flag: %s", format->s_name); + + b = glist_writetobinbuf(x, 1); + if (b) + { + if (binbuf_write(b, buf, "", cr)) + error("%s: write failed", filename->s_name); + binbuf_free(b); + } +} + +/* ------ routines to save and restore canvases (patches) recursively. ----*/ + + /* save to a binbuf, called recursively; cf. canvas_savetofile() which + saves the document, and is only called on root canvases. */ +static void canvas_saveto(t_canvas *x, t_binbuf *b) +{ + t_gobj *y; + t_linetraverser t; + t_outconnect *oc; + /* subpatch */ + if (x->gl_owner && !x->gl_env) + { + binbuf_addv(b, "ssiiiisi;", gensym("#N"), gensym("canvas"), + (t_int)(x->gl_screenx1), + (t_int)(x->gl_screeny1), + (t_int)(x->gl_screenx2 - x->gl_screenx1), + (t_int)(x->gl_screeny2 - x->gl_screeny1), + (*x->gl_name->s_name ? x->gl_name: gensym("(subpatch)")), + x->gl_mapped); + } + /* root or abstraction */ + else binbuf_addv(b, "ssiiiii;", gensym("#N"), gensym("canvas"), + (t_int)(x->gl_screenx1), + (t_int)(x->gl_screeny1), + (t_int)(x->gl_screenx2 - x->gl_screenx1), + (t_int)(x->gl_screeny2 - x->gl_screeny1), + x->gl_font); + + for (y = x->gl_list; y; y = y->g_next) + gobj_save(y, b); + + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + int srcno = canvas_getindex(x, &t.tr_ob->ob_g); + int sinkno = canvas_getindex(x, &t.tr_ob2->ob_g); + binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"), + srcno, t.tr_outno, sinkno, t.tr_inno); + } + /* unless everything is the default (as in ordinary subpatches) + print out a "coords" message to set up the coordinate systems */ + if (x->gl_isgraph || x->gl_x1 || x->gl_y1 || + x->gl_x2 != 1 || x->gl_y2 != 1 || x->gl_pixwidth || x->gl_pixheight) + binbuf_addv(b, "ssfffffff;", gensym("#X"), gensym("coords"), + x->gl_x1, x->gl_y1, + x->gl_x2, x->gl_y2, + (float)x->gl_pixwidth, (float)x->gl_pixheight, + (float)x->gl_isgraph); +} + + /* call this recursively to collect all the template names for + a canvas or for the selection. */ +static void canvas_collecttemplatesfor(t_canvas *x, int *ntemplatesp, + t_symbol ***templatevecp, int wholething) +{ + t_gobj *y; + + for (y = x->gl_list; y; y = y->g_next) + { + if ((pd_class(&y->g_pd) == scalar_class) && + (wholething || glist_isselected(x, y))) + canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template, + ((t_scalar *)y)->sc_vec, ntemplatesp, templatevecp); + else if ((pd_class(&y->g_pd) == canvas_class) && + (wholething || glist_isselected(x, y))) + canvas_collecttemplatesfor((t_canvas *)y, + ntemplatesp, templatevecp, 1); + } +} + + /* save the templates needed by a canvas to a binbuf. */ +static void canvas_savetemplatesto(t_canvas *x, t_binbuf *b, int wholething) +{ + t_symbol **templatevec = getbytes(0); + int i, ntemplates = 0; + t_gobj *y; + canvas_collecttemplatesfor(x, &ntemplates, &templatevec, wholething); + for (i = 0; i < ntemplates; i++) + { + t_template *template = template_findbyname(templatevec[i]); + int j, m = template->t_n; + if (!template) + { + bug("canvas_savetemplatesto"); + continue; + } + /* drop "pd-" prefix from template symbol to print */ + binbuf_addv(b, "sss", &s__N, gensym("struct"), + gensym(templatevec[i]->s_name + 3)); + for (j = 0; j < m; j++) + { + t_symbol *type; + switch (template->t_vec[j].ds_type) + { + case DT_FLOAT: type = &s_float; break; + case DT_SYMBOL: type = &s_symbol; break; + case DT_ARRAY: type = gensym("array"); break; + case DT_LIST: type = &s_list; break; + default: type = &s_float; bug("canvas_write"); + } + if (template->t_vec[j].ds_type == DT_ARRAY) + binbuf_addv(b, "sss", type, template->t_vec[j].ds_name, + gensym(template->t_vec[j].ds_arraytemplate->s_name + 3)); + else binbuf_addv(b, "ss", type, template->t_vec[j].ds_name); + } + binbuf_addsemi(b); + } +} + +void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except); + + /* save a "root" canvas to a file; cf. canvas_saveto() which saves the + body (and which is called recursively.) */ +static void canvas_savetofile(t_canvas *x, t_symbol *filename, t_symbol *dir) +{ + t_binbuf *b = binbuf_new(); + canvas_savetemplatesto(x, b, 1); + canvas_saveto(x, b); + if (binbuf_write(b, filename->s_name, dir->s_name, 0)) sys_ouch(); + else + { + /* if not an abstraction, reset title bar and directory */ + if (!x->gl_owner) + canvas_rename(x, filename, dir); + post("saved to: %s/%s", dir->s_name, filename->s_name); + canvas_dirty(x, 0); + canvas_reload(filename, dir, &x->gl_gobj); + } + binbuf_free(b); +} + +static void canvas_menusaveas(t_canvas *x) +{ + t_canvas *x2 = canvas_getrootfor(x); + sys_vgui("pdtk_canvas_saveas .x%x \"%s\" \"%s\"\n", x2, + x2->gl_name->s_name, canvas_getdir(x2)->s_name); +} + +static void canvas_menusave(t_canvas *x) +{ + t_canvas *x2 = canvas_getrootfor(x); + char *name = x2->gl_name->s_name; + if (*name && strncmp(name, "Untitled", 8) + && (strlen(name) < 4 || strcmp(name + strlen(name)-4, ".pat"))) + canvas_savetofile(x2, x2->gl_name, canvas_getdir(x2)); + else canvas_menusaveas(x2); +} + + +void g_readwrite_setup(void) +{ + class_addmethod(canvas_class, (t_method)glist_write, + gensym("write"), A_SYMBOL, A_DEFSYM, A_NULL); + class_addmethod(canvas_class, (t_method)glist_read, + gensym("read"), A_SYMBOL, A_DEFSYM, A_NULL); + class_addmethod(canvas_class, (t_method)glist_mergefile, + gensym("mergefile"), A_SYMBOL, A_DEFSYM, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_savetofile, + gensym("savetofile"), A_SYMBOL, A_SYMBOL, 0); + class_addmethod(canvas_class, (t_method)canvas_saveto, + gensym("saveto"), A_CANT, 0); +/* ------------------ from the menu ------------------------- */ + class_addmethod(canvas_class, (t_method)canvas_menusave, + gensym("menusave"), 0); + class_addmethod(canvas_class, (t_method)canvas_menusaveas, + gensym("menusaveas"), 0); +} +/* Copyright (c) 1997-2002 Miller Puckette and others. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* this file reads and writes the "data" portions of a canvas to a file. +See also canvas_saveto(), etc., in g_editor.c. The data portion is a +collection of "scalar" objects. Routines here can save collections of +scalars into a file and reload them; also, support is included here for +*/ + +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include + + /* the following routines read "scalars" from a file into a canvas. */ + +static int canvas_scanbinbuf(int natoms, t_atom *vec, int *p_indexout, + int *p_next) +{ + int i, j; + int indexwas = *p_next; + *p_indexout = indexwas; + if (indexwas >= natoms) + return (0); + for (i = indexwas; i < natoms && vec[i].a_type != A_SEMI; i++) + ; + if (i >= natoms) + *p_next = i; + else *p_next = i + 1; + return (i - indexwas); +} + +int glist_readscalar(t_glist *x, int natoms, t_atom *vec, + int *p_nextmsg, int selectit); + +static void canvas_readerror(int natoms, t_atom *vec, int message, + int nline, char *s) +{ + error(s); + startpost("line was:"); + postatom(nline, vec + message); + endpost(); +} + + /* fill in the contents of the scalar into the vector w. */ + +static void glist_readatoms(t_glist *x, int natoms, t_atom *vec, + int *p_nextmsg, t_symbol *templatesym, t_word *w, int argc, t_atom *argv) +{ + int message, nline, n, i; + + t_template *template = template_findbyname(templatesym); + if (!template) + { + error("%s: no such template", templatesym->s_name); + *p_nextmsg = natoms; + return; + } + word_restore(w, template, argc, argv); + n = template->t_n; + for (i = 0; i < n; i++) + { + if (template->t_vec[i].ds_type == DT_ARRAY) + { + int j; + t_array *a = w[i].w_array; + int elemsize = a->a_elemsize, nitems = 0; + t_symbol *arraytemplatesym = template->t_vec[i].ds_arraytemplate; + t_template *arraytemplate = + template_findbyname(arraytemplatesym); + if (!arraytemplate) + { + error("%s: no such template", arraytemplatesym->s_name); + } + else while (1) + { + t_word *element; + int nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg); + /* empty line terminates array */ + if (!nline) + break; + array_resize(a, arraytemplate, nitems + 1); + element = (t_word *)(((char *)a->a_vec) + + nitems * elemsize); + glist_readatoms(x, natoms, vec, p_nextmsg, arraytemplatesym, + element, nline, vec + message); + nitems++; + } + } + else if (template->t_vec[i].ds_type == DT_LIST) + { + while (1) + { + if (!glist_readscalar(w->w_list, natoms, vec, + p_nextmsg, 0)) + break; + } + } + } +} + +int glist_readscalar(t_glist *x, int natoms, t_atom *vec, + int *p_nextmsg, int selectit) +{ + int message, i, j, nline; + t_template *template; + t_symbol *templatesym; + t_scalar *sc; + int nextmsg = *p_nextmsg; + int wasvis = glist_isvisible(x); + + if (nextmsg >= natoms || vec[nextmsg].a_type != A_SYMBOL) + { + if (nextmsg < natoms) + post("stopping early: type %d", vec[nextmsg].a_type); + *p_nextmsg = natoms; + return (0); + } + templatesym = canvas_makebindsym(vec[nextmsg].a_w.w_symbol); + *p_nextmsg = nextmsg + 1; + + if (!(template = template_findbyname(templatesym))) + { + error("canvas_read: %s: no such template", templatesym->s_name); + *p_nextmsg = natoms; + return (0); + } + sc = scalar_new(x, templatesym); + if (!sc) + { + error("couldn't create scalar \"%s\"", templatesym->s_name); + *p_nextmsg = natoms; + return (0); + } + if (wasvis) + { + /* temporarily lie about vis flag while this is built */ + glist_getcanvas(x)->gl_mapped = 0; + } + glist_add(x, &sc->sc_gobj); + + nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg); + glist_readatoms(x, natoms, vec, p_nextmsg, templatesym, sc->sc_vec, + nline, vec + message); + if (wasvis) + { + /* reset vis flag as before */ + glist_getcanvas(x)->gl_mapped = 1; + gobj_vis(&sc->sc_gobj, x, 1); + } + if (selectit) + { + glist_select(x, &sc->sc_gobj); + } + return (1); +} + +void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename, int selectem) +{ + t_canvas *canvas = glist_getcanvas(x); + int cr = 0, natoms, nline, message, nextmsg = 0, i, j, nitems; + t_atom *vec; + t_gobj *gobj; + + natoms = binbuf_getnatom(b); + vec = binbuf_getvec(b); + + + /* check for file type */ + nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); + if (nline != 1 && vec[message].a_type != A_SYMBOL && + strcmp(vec[message].a_w.w_symbol->s_name, "data")) + { + pd_error(x, "%s: file apparently of wrong type", filename); + binbuf_free(b); + return; + } + /* read in templates and check for consistency */ + while (1) + { + t_template *newtemplate, *existtemplate; + t_symbol *templatesym; + t_atom *templateargs = getbytes(0); + int ntemplateargs = 0, newnargs; + nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); + if (nline < 2) + break; + else if (nline > 2) + canvas_readerror(natoms, vec, message, nline, + "extra items ignored"); + else if (vec[message].a_type != A_SYMBOL || + strcmp(vec[message].a_w.w_symbol->s_name, "template") || + vec[message + 1].a_type != A_SYMBOL) + { + canvas_readerror(natoms, vec, message, nline, + "bad template header"); + continue; + } + templatesym = canvas_makebindsym(vec[message + 1].a_w.w_symbol); + while (1) + { + nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); + if (nline != 2 && nline != 3) + break; + newnargs = ntemplateargs + nline; + templateargs = (t_atom *)t_resizebytes(templateargs, + sizeof(*templateargs) * ntemplateargs, + sizeof(*templateargs) * newnargs); + templateargs[ntemplateargs] = vec[message]; + templateargs[ntemplateargs + 1] = vec[message + 1]; + if (nline == 3) + templateargs[ntemplateargs + 2] = vec[message + 2]; + ntemplateargs = newnargs; + } + newtemplate = template_new(templatesym, ntemplateargs, templateargs); + t_freebytes(templateargs, sizeof (*templateargs) * ntemplateargs); + if (!(existtemplate = template_findbyname(templatesym))) + { + error("%s: template not found in current patch", + templatesym->s_name); + template_free(newtemplate); + return; + } + if (!template_match(existtemplate, newtemplate)) + { + error("%s: template doesn't match current one", + templatesym->s_name); + template_free(newtemplate); + return; + } + template_free(newtemplate); + } + while (nextmsg < natoms) + { + glist_readscalar(x, natoms, vec, &nextmsg, selectem); + } +} + +static void glist_doread(t_glist *x, t_symbol *filename, t_symbol *format, + int clearme) +{ + t_binbuf *b = binbuf_new(); + t_canvas *canvas = glist_getcanvas(x); + int wasvis = glist_isvisible(canvas); + int cr = 0, natoms, nline, message, nextmsg = 0, i, j; + t_atom *vec; + + if (!strcmp(format->s_name, "cr")) + cr = 1; + else if (*format->s_name) + error("qlist_read: unknown flag: %s", format->s_name); + + if (binbuf_read_via_path(b, filename->s_name, + canvas_getdir(canvas)->s_name, cr)) + { + pd_error(x, "read failed"); + binbuf_free(b); + return; + } + if (wasvis) + canvas_vis(canvas, 0); + if (clearme) + glist_clear(x); + glist_readfrombinbuf(x, b, filename->s_name, 0); + if (wasvis) + canvas_vis(canvas, 1); + binbuf_free(b); +} + +void glist_read(t_glist *x, t_symbol *filename, t_symbol *format) +{ + glist_doread(x, filename, format, 1); +} + +void glist_mergefile(t_glist *x, t_symbol *filename, t_symbol *format) +{ + glist_doread(x, filename, format, 0); +} + + /* read text from a "properties" window, called from a gfxstub set + up in scalar_properties(). We try to restore the object; if successful + we delete the scalar and put the new thing in its place on the list. */ +void canvas_dataproperties(t_canvas *x, t_scalar *sc, t_binbuf *b) +{ + int ntotal, nnew, scindex; + t_gobj *y, *y2 = 0, *newone, *oldone = 0; + for (y = x->gl_list, ntotal = 0, scindex = -1; y; y = y->g_next) + { + if (y == &sc->sc_gobj) + scindex = ntotal, oldone = y; + ntotal++; + } + + if (scindex == -1) + bug("data_properties: scalar disappeared"); + glist_readfrombinbuf(x, b, "properties dialog", 0); + newone = 0; + if (scindex >= 0) + { + /* take the new object off the list */ + if (ntotal) + { + for (y = x->gl_list, nnew = 1; y2 = y->g_next; + y = y2, nnew++) + if (nnew == ntotal) + { + newone = y2; + y->g_next = y2->g_next; + break; + } + } + else newone = x->gl_list, x->gl_list = newone->g_next; + } + if (!newone) + error("couldn't update properties (perhaps a format problem?)"); + else if (!oldone) + bug("data_properties: couldn't find old element"); + else + { + glist_delete(x, oldone); + if (scindex > 0) + { + for (y = x->gl_list, nnew = 1; y; + y = y->g_next, nnew++) + if (nnew == scindex || !y->g_next) + { + newone->g_next = y->g_next; + y->g_next = newone; + goto didit; + } + bug("data_properties: can't reinsert"); + } + else newone->g_next = x->gl_list, x->gl_list = newone; + } +didit: + ; +} + + /* ----------- routines to write data to a binbuf ----------- */ + +void canvas_doaddtemplate(t_symbol *templatesym, + int *p_ntemplates, t_symbol ***p_templatevec) +{ + int n = *p_ntemplates, i; + t_symbol **templatevec = *p_templatevec; + for (i = 0; i < n; i++) + if (templatevec[i] == templatesym) + return; + templatevec = (t_symbol **)t_resizebytes(templatevec, + n * sizeof(*templatevec), (n+1) * sizeof(*templatevec)); + templatevec[n] = templatesym; + *p_templatevec = templatevec; + *p_ntemplates = n+1; +} + +static void glist_writelist(t_gobj *y, t_binbuf *b); + +void canvas_writescalar(t_symbol *templatesym, t_word *w, t_binbuf *b, + int amarrayelement) +{ + t_dataslot *ds; + t_template *template = template_findbyname(templatesym); + t_atom *a = (t_atom *)t_getbytes(0); + int i, n = template->t_n, natom = 0; + if (!amarrayelement) + { + t_atom templatename; + SETSYMBOL(&templatename, gensym(templatesym->s_name + 3)); + binbuf_add(b, 1, &templatename); + } + if (!template) + bug("canvas_writescalar"); + /* write the atoms (floats and symbols) */ + for (i = 0; i < n; i++) + { + if (template->t_vec[i].ds_type == DT_FLOAT || + template->t_vec[i].ds_type == DT_SYMBOL) + { + a = (t_atom *)t_resizebytes(a, + natom * sizeof(*a), (natom + 1) * sizeof (*a)); + if (template->t_vec[i].ds_type == DT_FLOAT) + SETFLOAT(a + natom, w[i].w_float); + else SETSYMBOL(a + natom, w[i].w_symbol); + natom++; + } + } + /* array elements have to have at least something */ + if (natom == 0 && amarrayelement) + SETSYMBOL(a + natom, &s_bang), natom++; + binbuf_add(b, natom, a); + binbuf_addsemi(b); + t_freebytes(a, natom * sizeof(*a)); + for (i = 0; i < n; i++) + { + if (template->t_vec[i].ds_type == DT_ARRAY) + { + int j; + t_array *a = w[i].w_array; + int elemsize = a->a_elemsize, nitems = a->a_n; + t_symbol *arraytemplatesym = template->t_vec[i].ds_arraytemplate; + for (j = 0; j < nitems; j++) + canvas_writescalar(arraytemplatesym, + (t_word *)(((char *)a->a_vec) + elemsize * j), b, 1); + binbuf_addsemi(b); + } + else if (template->t_vec[i].ds_type == DT_LIST) + { + glist_writelist(w->w_list->gl_list, b); + binbuf_addsemi(b); + } + } +} + +static void glist_writelist(t_gobj *y, t_binbuf *b) +{ + for (; y; y = y->g_next) + { + if (pd_class(&y->g_pd) == scalar_class) + { + canvas_writescalar(((t_scalar *)y)->sc_template, + ((t_scalar *)y)->sc_vec, b, 0); + } + } +} + + /* ------------ routines to write out templates for data ------- */ + +static void canvas_addtemplatesforlist(t_gobj *y, + int *p_ntemplates, t_symbol ***p_templatevec); + +static void canvas_addtemplatesforscalar(t_symbol *templatesym, + t_word *w, int *p_ntemplates, t_symbol ***p_templatevec) +{ + t_dataslot *ds; + int i; + t_template *template = template_findbyname(templatesym); + canvas_doaddtemplate(templatesym, p_ntemplates, p_templatevec); + if (!template) + bug("canvas_addtemplatesforscalar"); + else for (ds = template->t_vec, i = template->t_n; i--; ds++, w++) + { + if (ds->ds_type == DT_ARRAY) + { + int j; + t_array *a = w->w_array; + int elemsize = a->a_elemsize, nitems = a->a_n; + t_symbol *arraytemplatesym = ds->ds_arraytemplate; + canvas_doaddtemplate(arraytemplatesym, p_ntemplates, p_templatevec); + for (j = 0; j < nitems; j++) + canvas_addtemplatesforscalar(arraytemplatesym, + (t_word *)(((char *)a->a_vec) + elemsize * j), + p_ntemplates, p_templatevec); + } + else if (ds->ds_type == DT_LIST) + canvas_addtemplatesforlist(w->w_list->gl_list, + p_ntemplates, p_templatevec); + } +} + +static void canvas_addtemplatesforlist(t_gobj *y, + int *p_ntemplates, t_symbol ***p_templatevec) +{ + for (; y; y = y->g_next) + { + if (pd_class(&y->g_pd) == scalar_class) + { + canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template, + ((t_scalar *)y)->sc_vec, p_ntemplates, p_templatevec); + } + } +} + + /* write all "scalars" in a glist to a binbuf. */ +t_binbuf *glist_writetobinbuf(t_glist *x, int wholething) +{ + int i; + t_symbol **templatevec = getbytes(0); + int ntemplates = 0; + t_gobj *y; + t_binbuf *b = binbuf_new(); + + for (y = x->gl_list; y; y = y->g_next) + { + if ((pd_class(&y->g_pd) == scalar_class) && + (wholething || glist_isselected(x, y))) + { + canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template, + ((t_scalar *)y)->sc_vec, &ntemplates, &templatevec); + } + } + binbuf_addv(b, "s;", gensym("data")); + for (i = 0; i < ntemplates; i++) + { + t_template *template = template_findbyname(templatevec[i]); + int j, m = template->t_n; + /* drop "pd-" prefix from template symbol to print it: */ + binbuf_addv(b, "ss;", gensym("template"), + gensym(templatevec[i]->s_name + 3)); + for (j = 0; j < m; j++) + { + t_symbol *type; + switch (template->t_vec[j].ds_type) + { + case DT_FLOAT: type = &s_float; break; + case DT_SYMBOL: type = &s_symbol; break; + case DT_ARRAY: type = gensym("array"); break; + case DT_LIST: type = &s_list; break; + default: type = &s_float; bug("canvas_write"); + } + if (template->t_vec[j].ds_type == DT_ARRAY) + binbuf_addv(b, "sss;", type, template->t_vec[j].ds_name, + gensym(template->t_vec[j].ds_arraytemplate->s_name + 3)); + else binbuf_addv(b, "ss;", type, template->t_vec[j].ds_name); + } + binbuf_addsemi(b); + } + binbuf_addsemi(b); + /* now write out the objects themselves */ + for (y = x->gl_list; y; y = y->g_next) + { + if ((pd_class(&y->g_pd) == scalar_class) && + (wholething || glist_isselected(x, y))) + { + canvas_writescalar(((t_scalar *)y)->sc_template, + ((t_scalar *)y)->sc_vec, b, 0); + } + } + return (b); +} + +static void glist_write(t_glist *x, t_symbol *filename, t_symbol *format) +{ + int cr = 0, i; + t_binbuf *b; + char buf[MAXPDSTRING]; + t_symbol **templatevec = getbytes(0); + int ntemplates = 0; + t_gobj *y; + t_canvas *canvas = glist_getcanvas(x); + canvas_makefilename(canvas, filename->s_name, buf, MAXPDSTRING); + if (!strcmp(format->s_name, "cr")) + cr = 1; + else if (*format->s_name) + error("qlist_read: unknown flag: %s", format->s_name); + + b = glist_writetobinbuf(x, 1); + if (b) + { + if (binbuf_write(b, buf, "", cr)) + error("%s: write failed", filename->s_name); + binbuf_free(b); + } +} + +/* ------ routines to save and restore canvases (patches) recursively. ----*/ + + /* save to a binbuf, called recursively; cf. canvas_savetofile() which + saves the document, and is only called on root canvases. */ +static void canvas_saveto(t_canvas *x, t_binbuf *b) +{ + t_gobj *y; + t_linetraverser t; + t_outconnect *oc; + /* subpatch */ + if (x->gl_owner && !x->gl_env) + { + binbuf_addv(b, "ssiiiisi;", gensym("#N"), gensym("canvas"), + (t_int)(x->gl_screenx1), + (t_int)(x->gl_screeny1), + (t_int)(x->gl_screenx2 - x->gl_screenx1), + (t_int)(x->gl_screeny2 - x->gl_screeny1), + (*x->gl_name->s_name ? x->gl_name: gensym("(subpatch)")), + x->gl_mapped); + } + /* root or abstraction */ + else binbuf_addv(b, "ssiiiii;", gensym("#N"), gensym("canvas"), + (t_int)(x->gl_screenx1), + (t_int)(x->gl_screeny1), + (t_int)(x->gl_screenx2 - x->gl_screenx1), + (t_int)(x->gl_screeny2 - x->gl_screeny1), + x->gl_font); + + for (y = x->gl_list; y; y = y->g_next) + gobj_save(y, b); + + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + int srcno = canvas_getindex(x, &t.tr_ob->ob_g); + int sinkno = canvas_getindex(x, &t.tr_ob2->ob_g); + binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"), + srcno, t.tr_outno, sinkno, t.tr_inno); + } + /* unless everything is the default (as in ordinary subpatches) + print out a "coords" message to set up the coordinate systems */ + if (x->gl_isgraph || x->gl_x1 || x->gl_y1 || + x->gl_x2 != 1 || x->gl_y2 != 1 || x->gl_pixwidth || x->gl_pixheight) + binbuf_addv(b, "ssfffffff;", gensym("#X"), gensym("coords"), + x->gl_x1, x->gl_y1, + x->gl_x2, x->gl_y2, + (float)x->gl_pixwidth, (float)x->gl_pixheight, + (float)x->gl_isgraph); +} + + /* call this recursively to collect all the template names for + a canvas or for the selection. */ +static void canvas_collecttemplatesfor(t_canvas *x, int *ntemplatesp, + t_symbol ***templatevecp, int wholething) +{ + t_gobj *y; + + for (y = x->gl_list; y; y = y->g_next) + { + if ((pd_class(&y->g_pd) == scalar_class) && + (wholething || glist_isselected(x, y))) + canvas_addtemplatesforscalar(((t_scalar *)y)->sc_template, + ((t_scalar *)y)->sc_vec, ntemplatesp, templatevecp); + else if ((pd_class(&y->g_pd) == canvas_class) && + (wholething || glist_isselected(x, y))) + canvas_collecttemplatesfor((t_canvas *)y, + ntemplatesp, templatevecp, 1); + } +} + + /* save the templates needed by a canvas to a binbuf. */ +static void canvas_savetemplatesto(t_canvas *x, t_binbuf *b, int wholething) +{ + t_symbol **templatevec = getbytes(0); + int i, ntemplates = 0; + t_gobj *y; + canvas_collecttemplatesfor(x, &ntemplates, &templatevec, wholething); + for (i = 0; i < ntemplates; i++) + { + t_template *template = template_findbyname(templatevec[i]); + int j, m = template->t_n; + if (!template) + { + bug("canvas_savetemplatesto"); + continue; + } + /* drop "pd-" prefix from template symbol to print */ + binbuf_addv(b, "sss", &s__N, gensym("struct"), + gensym(templatevec[i]->s_name + 3)); + for (j = 0; j < m; j++) + { + t_symbol *type; + switch (template->t_vec[j].ds_type) + { + case DT_FLOAT: type = &s_float; break; + case DT_SYMBOL: type = &s_symbol; break; + case DT_ARRAY: type = gensym("array"); break; + case DT_LIST: type = &s_list; break; + default: type = &s_float; bug("canvas_write"); + } + if (template->t_vec[j].ds_type == DT_ARRAY) + binbuf_addv(b, "sss", type, template->t_vec[j].ds_name, + gensym(template->t_vec[j].ds_arraytemplate->s_name + 3)); + else binbuf_addv(b, "ss", type, template->t_vec[j].ds_name); + } + binbuf_addsemi(b); + } +} + +void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except); + + /* save a "root" canvas to a file; cf. canvas_saveto() which saves the + body (and which is called recursively.) */ +static void canvas_savetofile(t_canvas *x, t_symbol *filename, t_symbol *dir) +{ + t_binbuf *b = binbuf_new(); + canvas_savetemplatesto(x, b, 1); + canvas_saveto(x, b); + if (binbuf_write(b, filename->s_name, dir->s_name, 0)) sys_ouch(); + else + { + /* if not an abstraction, reset title bar and directory */ + if (!x->gl_owner) + canvas_rename(x, filename, dir); + post("saved to: %s/%s", dir->s_name, filename->s_name); + canvas_dirty(x, 0); + canvas_reload(filename, dir, &x->gl_gobj); + } + binbuf_free(b); +} + +static void canvas_menusaveas(t_canvas *x) +{ + t_canvas *x2 = canvas_getrootfor(x); + sys_vgui("pdtk_canvas_saveas .x%x \"%s\" \"%s\"\n", x2, + x2->gl_name->s_name, canvas_getdir(x2)->s_name); +} + +static void canvas_menusave(t_canvas *x) +{ + t_canvas *x2 = canvas_getrootfor(x); + char *name = x2->gl_name->s_name; + if (*name && strncmp(name, "Untitled", 8) + && (strlen(name) < 4 || strcmp(name + strlen(name)-4, ".pat"))) + canvas_savetofile(x2, x2->gl_name, canvas_getdir(x2)); + else canvas_menusaveas(x2); +} + + +void g_readwrite_setup(void) +{ + class_addmethod(canvas_class, (t_method)glist_write, + gensym("write"), A_SYMBOL, A_DEFSYM, A_NULL); + class_addmethod(canvas_class, (t_method)glist_read, + gensym("read"), A_SYMBOL, A_DEFSYM, A_NULL); + class_addmethod(canvas_class, (t_method)glist_mergefile, + gensym("mergefile"), A_SYMBOL, A_DEFSYM, A_NULL); + class_addmethod(canvas_class, (t_method)canvas_savetofile, + gensym("savetofile"), A_SYMBOL, A_SYMBOL, 0); + class_addmethod(canvas_class, (t_method)canvas_saveto, + gensym("saveto"), A_CANT, 0); +/* ------------------ from the menu ------------------------- */ + class_addmethod(canvas_class, (t_method)canvas_menusave, + gensym("menusave"), 0); + class_addmethod(canvas_class, (t_method)canvas_menusaveas, + gensym("menusaveas"), 0); +} diff --git a/apps/plugins/pdbox/PDa/src/g_rtext.c b/apps/plugins/pdbox/PDa/src/g_rtext.c new file mode 100644 index 0000000..8ffc8f4 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_rtext.c @@ -0,0 +1,972 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* changes by Thomas Musil IEM KUG Graz Austria 2001 */ +/* have to insert gui-objects into editor-list */ +/* all changes are labeled with iemlib */ + +#include +#include +#include +#include +#include "m_pd.h" +#include "s_stuff.h" +#include "g_canvas.h" +#include "t_tk.h" + +#define LMARGIN 1 +#define RMARGIN 1 +#define TMARGIN 2 +#define BMARGIN 2 + +#define SEND_FIRST 1 +#define SEND_UPDATE 2 +#define SEND_CHECK 0 + +struct _rtext +{ + char *x_buf; + int x_bufsize; + int x_selstart; + int x_selend; + int x_active; + int x_dragfrom; + int x_height; + int x_drawnwidth; + int x_drawnheight; + t_text *x_text; + t_glist *x_glist; + char x_tag[50]; + struct _rtext *x_next; +}; + +t_rtext *rtext_new(t_glist *glist, t_text *who) +{ + t_rtext *x = (t_rtext *)getbytes(sizeof *x); + int w = 0, h = 0, indx; + x->x_height = -1; + x->x_text = who; + x->x_glist = glist; + x->x_next = glist->gl_editor->e_rtext; + x->x_selstart = x->x_selend = x->x_active = + x->x_drawnwidth = x->x_drawnheight = 0; + binbuf_gettext(who->te_binbuf, &x->x_buf, &x->x_bufsize); + glist->gl_editor->e_rtext = x; + sprintf(x->x_tag, ".x%x.t%x", (t_int)glist_getcanvas(x->x_glist), + (t_int)x); + return (x); +} + +static t_rtext *rtext_entered; + +void rtext_free(t_rtext *x) +{ + if (x->x_glist->gl_editor->e_textedfor == x) + x->x_glist->gl_editor->e_textedfor = 0; + if (x->x_glist->gl_editor->e_rtext == x) + x->x_glist->gl_editor->e_rtext = x->x_next; + else + { + t_rtext *e2; + for (e2 = x->x_glist->gl_editor->e_rtext; e2; e2 = e2->x_next) + if (e2->x_next == x) + { + e2->x_next = x->x_next; + break; + } + } + if (rtext_entered == x) rtext_entered = 0; + freebytes(x->x_buf, x->x_bufsize); + freebytes(x, sizeof *x); +} + +char *rtext_gettag(t_rtext *x) +{ + return (x->x_tag); +} + +void rtext_gettext(t_rtext *x, char **buf, int *bufsize) +{ + *buf = x->x_buf; + *bufsize = x->x_bufsize; +} + + +/* LATER deal with tcl-significant characters */ + +static int firstone(char *s, int c, int n) +{ + char *s2 = s + n; + int i = 0; + while (s != s2) + { + if (*s == c) return (i); + i++; + s++; + } + return (-1); +} + +static int lastone(char *s, int c, int n) +{ + char *s2 = s + n; + while (s2 != s) + { + s2--; + n--; + if (*s2 == c) return (n); + } + return (-1); +} + + /* the following routine computes line breaks and carries out + some action which could be: + SEND_FIRST - draw the box for the first time + SEND_UPDATE - redraw the updated box + otherwise - don't draw, just calculate. + Called with *widthp and *heightpas coordinates of + a test point, the routine reports the index of the character found + there in *indexp. *widthp and *heightp are set to the width and height + of the entire text in pixels. + */ + + /* LATER get this and sys_vgui to work together properly, + breaking up messages as needed. As of now, there's + a limit of 1950 characters, imposed by sys_vgui(). */ +#define UPBUFSIZE 4000 +#define BOXWIDTH 60 + +/* Older (pre-8.3.4) TCL versions handle text selection differently; this +flag is set from the GUI if this happens. LATER take this out: early 2006? */ + +extern int sys_oldtclversion; + +static void rtext_senditup(t_rtext *x, int action, int *widthp, int *heightp, + int *indexp) +{ + float dispx, dispy; + char tempbuf[UPBUFSIZE], *tp = tempbuf, *bp = x->x_buf; + int outchars, inchars = x->x_bufsize, nlines = 0, ncolumns = 0, + pixwide, pixhigh; + int font = glist_getfont(x->x_glist); + int fontwidth = sys_fontwidth(font), fontheight = sys_fontheight(font); + int findx = (*widthp + (fontwidth/2)) / fontwidth, + findy = *heightp / fontheight; + int reportedindex = 0; + t_canvas *canvas = glist_getcanvas(x->x_glist); + int widthspec = x->x_text->te_width; + int widthlimit = (widthspec ? widthspec : BOXWIDTH); + while (inchars) + { + int maxindex = (inchars > widthlimit ? widthlimit : inchars); + int eatchar = 1; + int foundit = firstone(bp, '\n', maxindex); + if (foundit < 0) + { + if (inchars > widthlimit) + { + foundit = lastone(bp, ' ', maxindex); + if (foundit < 0) + { + foundit = maxindex; + eatchar = 0; + } + } + else + { + foundit = inchars; + eatchar = 0; + } + } + if (nlines == findy) + { + int actualx = (findx < 0 ? 0 : + (findx > foundit ? foundit : findx)); + *indexp = (bp - x->x_buf) + actualx; + reportedindex = 1; + } + strncpy(tp, bp, foundit); + tp += foundit; + bp += (foundit + eatchar); + inchars -= (foundit + eatchar); + if (inchars) *tp++ = '\n'; + if (foundit > ncolumns) ncolumns = foundit; + nlines++; + } + outchars = tp - tempbuf; + if (outchars > 1950) outchars = 1950; + if (!reportedindex) + *indexp = outchars; + dispx = text_xpix(x->x_text, x->x_glist); + dispy = text_ypix(x->x_text, x->x_glist); + if (nlines < 1) nlines = 1; + if (!widthspec) + { + while (ncolumns < 3) + { + tempbuf[outchars++] = ' '; + ncolumns++; + } + } + else ncolumns = widthspec; + pixwide = ncolumns * fontwidth + (LMARGIN + RMARGIN); + pixhigh = nlines * fontheight + (TMARGIN + BMARGIN); + + if (action == SEND_FIRST) + sys_vgui("pdtk_text_new .x%x.c %s %f %f {%.*s} %d %s\n", + canvas, x->x_tag, + dispx + LMARGIN, dispy + TMARGIN, + outchars, tempbuf, sys_hostfontsize(font), + (glist_isselected(x->x_glist, + &x->x_glist->gl_gobj)? "blue" : "black")); + else if (action == SEND_UPDATE) + { + sys_vgui("pdtk_text_set .x%x.c %s {%.*s}\n", + canvas, x->x_tag, outchars, tempbuf); + if (pixwide != x->x_drawnwidth || pixhigh != x->x_drawnheight) + text_drawborder(x->x_text, x->x_glist, x->x_tag, + pixwide, pixhigh, 0); + if (x->x_active) + { + if (x->x_selend > x->x_selstart) + { + sys_vgui(".x%x.c select from %s %d\n", canvas, + x->x_tag, x->x_selstart); + sys_vgui(".x%x.c select to %s %d\n", canvas, + x->x_tag, x->x_selend + (sys_oldtclversion ? 0 : -1)); + sys_vgui(".x%x.c focus \"\"\n", canvas); + } + else + { + sys_vgui(".x%x.c select clear\n", canvas); + sys_vgui(".x%x.c icursor %s %d\n", canvas, x->x_tag, + x->x_selstart); + sys_vgui(".x%x.c focus %s\n", canvas, x->x_tag); + } + } + } + x->x_drawnwidth = pixwide; + x->x_drawnheight = pixhigh; + + *widthp = pixwide; + *heightp = pixhigh; +} + +void rtext_retext(t_rtext *x) +{ + int w = 0, h = 0, indx; + t_text *text = x->x_text; + t_freebytes(x->x_buf, x->x_bufsize); + binbuf_gettext(text->te_binbuf, &x->x_buf, &x->x_bufsize); + /* special case: for number boxes, try to pare the number down + to the specified width of the box. */ + if (text->te_width > 0 && text->te_type == T_ATOM && + x->x_bufsize > text->te_width) + { + t_atom *atomp = binbuf_getvec(text->te_binbuf); + int natom = binbuf_getnatom(text->te_binbuf); + int bufsize = x->x_bufsize; + if (natom == 1 && atomp->a_type == A_FLOAT) + { + /* try to reduce size by dropping decimal digits */ + int wantreduce = bufsize - text->te_width; + char *decimal = 0, *nextchar, *ebuf = x->x_buf + bufsize, + *s1, *s2; + int ndecimals; + for (decimal = x->x_buf; decimal < ebuf; decimal++) + if (*decimal == '.') + break; + if (decimal >= ebuf) + goto giveup; + for (nextchar = decimal + 1; nextchar < ebuf; nextchar++) + if (*nextchar < '0' || *nextchar > '9') + break; + if (nextchar - decimal - 1 < wantreduce) + goto giveup; + for (s1 = nextchar - wantreduce, s2 = s1 + wantreduce; + s2 < ebuf; s1++, s2++) + *s1 = *s2; + t_resizebytes(x->x_buf, bufsize, text->te_width); + bufsize = text->te_width; + goto done; + giveup: + /* give up and bash it to "+" or "-" */ + x->x_buf[0] = (atomp->a_w.w_float < 0 ? '-' : '+'); + t_resizebytes(x->x_buf, bufsize, 1); + bufsize = 1; + } + else if (bufsize > text->te_width) + { + x->x_buf[text->te_width - 1] = '>'; + t_resizebytes(x->x_buf, bufsize, text->te_width); + bufsize = text->te_width; + } + done: + x->x_bufsize = bufsize; + } + rtext_senditup(x, SEND_UPDATE, &w, &h, &indx); +} + +/* find the rtext that goes with a text item */ +t_rtext *glist_findrtext(t_glist *gl, t_text *who) +{ + t_rtext *x = gl->gl_editor->e_rtext; + while (x && x->x_text != who) x = x->x_next; + if (!x) bug("glist_findrtext"); + return (x); +} + +int rtext_width(t_rtext *x) +{ + int w = 0, h = 0, indx; + rtext_senditup(x, SEND_CHECK, &w, &h, &indx); + return (w); +} + +int rtext_height(t_rtext *x) +{ + int w = 0, h = 0, indx; + rtext_senditup(x, SEND_CHECK, &w, &h, &indx); + return (h); +} + +void rtext_draw(t_rtext *x) +{ + int w = 0, h = 0, indx; + rtext_senditup(x, SEND_FIRST, &w, &h, &indx); +} + +void rtext_erase(t_rtext *x) +{ + sys_vgui(".x%x.c delete %s\n", glist_getcanvas(x->x_glist), x->x_tag); +} + +void rtext_displace(t_rtext *x, int dx, int dy) +{ + sys_vgui(".x%x.c move %s %d %d\n", glist_getcanvas(x->x_glist), + x->x_tag, dx, dy); +} + +void rtext_select(t_rtext *x, int state) +{ + t_glist *glist = x->x_glist; + t_canvas *canvas = glist_getcanvas(glist); + sys_vgui(".x%x.c itemconfigure %s -fill %s\n", canvas, + x->x_tag, (state? "blue" : "black")); + canvas_editing = canvas; +} + +void rtext_activate(t_rtext *x, int state) +{ + int w = 0, h = 0, indx; + t_glist *glist = x->x_glist; + t_canvas *canvas = glist_getcanvas(glist); + if (state) + { + sys_vgui(".x%x.c focus %s\n", canvas, x->x_tag); + glist->gl_editor->e_textedfor = x; + glist->gl_editor->e_textdirty = 0; + x->x_dragfrom = x->x_selstart = 0; + x->x_selend = x->x_bufsize; + x->x_active = 1; + } + else + { + sys_vgui("selection clear .x%x.c\n", canvas); + sys_vgui(".x%x.c focus \"\"\n", canvas); + if (glist->gl_editor->e_textedfor == x) + glist->gl_editor->e_textedfor = 0; + x->x_active = 0; + } + rtext_senditup(x, SEND_UPDATE, &w, &h, &indx); +} + +void rtext_key(t_rtext *x, int keynum, t_symbol *keysym) +{ + int w = 0, h = 0, indx, i, newsize, ndel; + char *s1, *s2; + if (keynum) + { + int n = keynum; + if (n == '\r') n = '\n'; + if (n == '\b') /* backspace */ + { + /* LATER delete the box if all text is selected... + this causes reentrancy problems now. */ + /* if ((!x->x_selstart) && (x->x_selend == x->x_bufsize)) + { + .... + } */ + if (x->x_selstart && (x->x_selstart == x->x_selend)) + x->x_selstart--; + } + else if (n == 127) /* delete */ + { + if (x->x_selend < x->x_bufsize && (x->x_selstart == x->x_selend)) + x->x_selend++; + } + + ndel = x->x_selend - x->x_selstart; + for (i = x->x_selend; i < x->x_bufsize; i++) + x->x_buf[i- ndel] = x->x_buf[i]; + newsize = x->x_bufsize - ndel; + x->x_buf = resizebytes(x->x_buf, x->x_bufsize, newsize); + x->x_bufsize = newsize; + + if (n == '\n' || isprint(n)) + { + newsize = x->x_bufsize+1; + x->x_buf = resizebytes(x->x_buf, x->x_bufsize, newsize); + for (i = x->x_bufsize; i > x->x_selstart; i--) + x->x_buf[i] = x->x_buf[i-1]; + x->x_buf[x->x_selstart] = n; + x->x_bufsize = newsize; + x->x_selstart = x->x_selstart + 1; + } + x->x_selend = x->x_selstart; + x->x_glist->gl_editor->e_textdirty = 1; + } + else if (!strcmp(keysym->s_name, "Right")) + { + if (x->x_selend == x->x_selstart && x->x_selstart < x->x_bufsize) + x->x_selend = x->x_selstart = x->x_selstart + 1; + else + x->x_selstart = x->x_selend; + } + else if (!strcmp(keysym->s_name, "Left")) + { + if (x->x_selend == x->x_selstart && x->x_selstart > 0) + x->x_selend = x->x_selstart = x->x_selstart - 1; + else + x->x_selend = x->x_selstart; + } + /* this should be improved... life's too short */ + else if (!strcmp(keysym->s_name, "Up")) + { + if (x->x_selstart) + x->x_selstart--; + while (x->x_selstart > 0 && x->x_buf[x->x_selstart] != '\n') + x->x_selstart--; + x->x_selend = x->x_selstart; + } + else if (!strcmp(keysym->s_name, "Down")) + { + while (x->x_selend < x->x_bufsize && + x->x_buf[x->x_selend] != '\n') + x->x_selend++; + if (x->x_selend < x->x_bufsize) + x->x_selend++; + x->x_selstart = x->x_selend; + } + rtext_senditup(x, SEND_UPDATE, &w, &h, &indx); +} + +void rtext_mouse(t_rtext *x, int xval, int yval, int flag) +{ + int w = xval, h = yval, indx; + rtext_senditup(x, SEND_CHECK, &w, &h, &indx); + if (flag == RTEXT_DOWN) + { + x->x_dragfrom = x->x_selstart = x->x_selend = indx; + } + else if (flag == RTEXT_SHIFT) + { + if (indx * 2 > x->x_selstart + x->x_selend) + x->x_dragfrom = x->x_selstart, x->x_selend = indx; + else + x->x_dragfrom = x->x_selend, x->x_selstart = indx; + } + else if (flag == RTEXT_DRAG) + { + x->x_selstart = (x->x_dragfrom < indx ? x->x_dragfrom : indx); + x->x_selend = (x->x_dragfrom > indx ? x->x_dragfrom : indx); + } + rtext_senditup(x, SEND_UPDATE, &w, &h, &indx); +} +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* changes by Thomas Musil IEM KUG Graz Austria 2001 */ +/* have to insert gui-objects into editor-list */ +/* all changes are labeled with iemlib */ + +#include +#include +#include +#include +#include "m_pd.h" +#include "s_stuff.h" +#include "g_canvas.h" +#include "t_tk.h" + +#define LMARGIN 1 +#define RMARGIN 1 +#define TMARGIN 2 +#define BMARGIN 2 + +#define SEND_FIRST 1 +#define SEND_UPDATE 2 +#define SEND_CHECK 0 + +struct _rtext +{ + char *x_buf; + int x_bufsize; + int x_selstart; + int x_selend; + int x_active; + int x_dragfrom; + int x_height; + int x_drawnwidth; + int x_drawnheight; + t_text *x_text; + t_glist *x_glist; + char x_tag[50]; + struct _rtext *x_next; +}; + +t_rtext *rtext_new(t_glist *glist, t_text *who) +{ + t_rtext *x = (t_rtext *)getbytes(sizeof *x); + int w = 0, h = 0, indx; + x->x_height = -1; + x->x_text = who; + x->x_glist = glist; + x->x_next = glist->gl_editor->e_rtext; + x->x_selstart = x->x_selend = x->x_active = + x->x_drawnwidth = x->x_drawnheight = 0; + binbuf_gettext(who->te_binbuf, &x->x_buf, &x->x_bufsize); + glist->gl_editor->e_rtext = x; + sprintf(x->x_tag, ".x%x.t%x", (t_int)glist_getcanvas(x->x_glist), + (t_int)x); + return (x); +} + +static t_rtext *rtext_entered; + +void rtext_free(t_rtext *x) +{ + if (x->x_glist->gl_editor->e_textedfor == x) + x->x_glist->gl_editor->e_textedfor = 0; + if (x->x_glist->gl_editor->e_rtext == x) + x->x_glist->gl_editor->e_rtext = x->x_next; + else + { + t_rtext *e2; + for (e2 = x->x_glist->gl_editor->e_rtext; e2; e2 = e2->x_next) + if (e2->x_next == x) + { + e2->x_next = x->x_next; + break; + } + } + if (rtext_entered == x) rtext_entered = 0; + freebytes(x->x_buf, x->x_bufsize); + freebytes(x, sizeof *x); +} + +char *rtext_gettag(t_rtext *x) +{ + return (x->x_tag); +} + +void rtext_gettext(t_rtext *x, char **buf, int *bufsize) +{ + *buf = x->x_buf; + *bufsize = x->x_bufsize; +} + + +/* LATER deal with tcl-significant characters */ + +static int firstone(char *s, int c, int n) +{ + char *s2 = s + n; + int i = 0; + while (s != s2) + { + if (*s == c) return (i); + i++; + s++; + } + return (-1); +} + +static int lastone(char *s, int c, int n) +{ + char *s2 = s + n; + while (s2 != s) + { + s2--; + n--; + if (*s2 == c) return (n); + } + return (-1); +} + + /* the following routine computes line breaks and carries out + some action which could be: + SEND_FIRST - draw the box for the first time + SEND_UPDATE - redraw the updated box + otherwise - don't draw, just calculate. + Called with *widthp and *heightpas coordinates of + a test point, the routine reports the index of the character found + there in *indexp. *widthp and *heightp are set to the width and height + of the entire text in pixels. + */ + + /* LATER get this and sys_vgui to work together properly, + breaking up messages as needed. As of now, there's + a limit of 1950 characters, imposed by sys_vgui(). */ +#define UPBUFSIZE 4000 +#define BOXWIDTH 60 + +/* Older (pre-8.3.4) TCL versions handle text selection differently; this +flag is set from the GUI if this happens. LATER take this out: early 2006? */ + +extern int sys_oldtclversion; + +static void rtext_senditup(t_rtext *x, int action, int *widthp, int *heightp, + int *indexp) +{ + float dispx, dispy; + char tempbuf[UPBUFSIZE], *tp = tempbuf, *bp = x->x_buf; + int outchars, inchars = x->x_bufsize, nlines = 0, ncolumns = 0, + pixwide, pixhigh; + int font = glist_getfont(x->x_glist); + int fontwidth = sys_fontwidth(font), fontheight = sys_fontheight(font); + int findx = (*widthp + (fontwidth/2)) / fontwidth, + findy = *heightp / fontheight; + int reportedindex = 0; + t_canvas *canvas = glist_getcanvas(x->x_glist); + int widthspec = x->x_text->te_width; + int widthlimit = (widthspec ? widthspec : BOXWIDTH); + while (inchars) + { + int maxindex = (inchars > widthlimit ? widthlimit : inchars); + int eatchar = 1; + int foundit = firstone(bp, '\n', maxindex); + if (foundit < 0) + { + if (inchars > widthlimit) + { + foundit = lastone(bp, ' ', maxindex); + if (foundit < 0) + { + foundit = maxindex; + eatchar = 0; + } + } + else + { + foundit = inchars; + eatchar = 0; + } + } + if (nlines == findy) + { + int actualx = (findx < 0 ? 0 : + (findx > foundit ? foundit : findx)); + *indexp = (bp - x->x_buf) + actualx; + reportedindex = 1; + } + strncpy(tp, bp, foundit); + tp += foundit; + bp += (foundit + eatchar); + inchars -= (foundit + eatchar); + if (inchars) *tp++ = '\n'; + if (foundit > ncolumns) ncolumns = foundit; + nlines++; + } + outchars = tp - tempbuf; + if (outchars > 1950) outchars = 1950; + if (!reportedindex) + *indexp = outchars; + dispx = text_xpix(x->x_text, x->x_glist); + dispy = text_ypix(x->x_text, x->x_glist); + if (nlines < 1) nlines = 1; + if (!widthspec) + { + while (ncolumns < 3) + { + tempbuf[outchars++] = ' '; + ncolumns++; + } + } + else ncolumns = widthspec; + pixwide = ncolumns * fontwidth + (LMARGIN + RMARGIN); + pixhigh = nlines * fontheight + (TMARGIN + BMARGIN); + + if (action == SEND_FIRST) + sys_vgui("pdtk_text_new .x%x.c %s %f %f {%.*s} %d %s\n", + canvas, x->x_tag, + dispx + LMARGIN, dispy + TMARGIN, + outchars, tempbuf, sys_hostfontsize(font), + (glist_isselected(x->x_glist, + &x->x_glist->gl_gobj)? "blue" : "black")); + else if (action == SEND_UPDATE) + { + sys_vgui("pdtk_text_set .x%x.c %s {%.*s}\n", + canvas, x->x_tag, outchars, tempbuf); + if (pixwide != x->x_drawnwidth || pixhigh != x->x_drawnheight) + text_drawborder(x->x_text, x->x_glist, x->x_tag, + pixwide, pixhigh, 0); + if (x->x_active) + { + if (x->x_selend > x->x_selstart) + { + sys_vgui(".x%x.c select from %s %d\n", canvas, + x->x_tag, x->x_selstart); + sys_vgui(".x%x.c select to %s %d\n", canvas, + x->x_tag, x->x_selend + (sys_oldtclversion ? 0 : -1)); + sys_vgui(".x%x.c focus \"\"\n", canvas); + } + else + { + sys_vgui(".x%x.c select clear\n", canvas); + sys_vgui(".x%x.c icursor %s %d\n", canvas, x->x_tag, + x->x_selstart); + sys_vgui(".x%x.c focus %s\n", canvas, x->x_tag); + } + } + } + x->x_drawnwidth = pixwide; + x->x_drawnheight = pixhigh; + + *widthp = pixwide; + *heightp = pixhigh; +} + +void rtext_retext(t_rtext *x) +{ + int w = 0, h = 0, indx; + t_text *text = x->x_text; + t_freebytes(x->x_buf, x->x_bufsize); + binbuf_gettext(text->te_binbuf, &x->x_buf, &x->x_bufsize); + /* special case: for number boxes, try to pare the number down + to the specified width of the box. */ + if (text->te_width > 0 && text->te_type == T_ATOM && + x->x_bufsize > text->te_width) + { + t_atom *atomp = binbuf_getvec(text->te_binbuf); + int natom = binbuf_getnatom(text->te_binbuf); + int bufsize = x->x_bufsize; + if (natom == 1 && atomp->a_type == A_FLOAT) + { + /* try to reduce size by dropping decimal digits */ + int wantreduce = bufsize - text->te_width; + char *decimal = 0, *nextchar, *ebuf = x->x_buf + bufsize, + *s1, *s2; + int ndecimals; + for (decimal = x->x_buf; decimal < ebuf; decimal++) + if (*decimal == '.') + break; + if (decimal >= ebuf) + goto giveup; + for (nextchar = decimal + 1; nextchar < ebuf; nextchar++) + if (*nextchar < '0' || *nextchar > '9') + break; + if (nextchar - decimal - 1 < wantreduce) + goto giveup; + for (s1 = nextchar - wantreduce, s2 = s1 + wantreduce; + s2 < ebuf; s1++, s2++) + *s1 = *s2; + t_resizebytes(x->x_buf, bufsize, text->te_width); + bufsize = text->te_width; + goto done; + giveup: + /* give up and bash it to "+" or "-" */ + x->x_buf[0] = (atomp->a_w.w_float < 0 ? '-' : '+'); + t_resizebytes(x->x_buf, bufsize, 1); + bufsize = 1; + } + else if (bufsize > text->te_width) + { + x->x_buf[text->te_width - 1] = '>'; + t_resizebytes(x->x_buf, bufsize, text->te_width); + bufsize = text->te_width; + } + done: + x->x_bufsize = bufsize; + } + rtext_senditup(x, SEND_UPDATE, &w, &h, &indx); +} + +/* find the rtext that goes with a text item */ +t_rtext *glist_findrtext(t_glist *gl, t_text *who) +{ + t_rtext *x = gl->gl_editor->e_rtext; + while (x && x->x_text != who) x = x->x_next; + if (!x) bug("glist_findrtext"); + return (x); +} + +int rtext_width(t_rtext *x) +{ + int w = 0, h = 0, indx; + rtext_senditup(x, SEND_CHECK, &w, &h, &indx); + return (w); +} + +int rtext_height(t_rtext *x) +{ + int w = 0, h = 0, indx; + rtext_senditup(x, SEND_CHECK, &w, &h, &indx); + return (h); +} + +void rtext_draw(t_rtext *x) +{ + int w = 0, h = 0, indx; + rtext_senditup(x, SEND_FIRST, &w, &h, &indx); +} + +void rtext_erase(t_rtext *x) +{ + sys_vgui(".x%x.c delete %s\n", glist_getcanvas(x->x_glist), x->x_tag); +} + +void rtext_displace(t_rtext *x, int dx, int dy) +{ + sys_vgui(".x%x.c move %s %d %d\n", glist_getcanvas(x->x_glist), + x->x_tag, dx, dy); +} + +void rtext_select(t_rtext *x, int state) +{ + t_glist *glist = x->x_glist; + t_canvas *canvas = glist_getcanvas(glist); + sys_vgui(".x%x.c itemconfigure %s -fill %s\n", canvas, + x->x_tag, (state? "blue" : "black")); + canvas_editing = canvas; +} + +void rtext_activate(t_rtext *x, int state) +{ + int w = 0, h = 0, indx; + t_glist *glist = x->x_glist; + t_canvas *canvas = glist_getcanvas(glist); + if (state) + { + sys_vgui(".x%x.c focus %s\n", canvas, x->x_tag); + glist->gl_editor->e_textedfor = x; + glist->gl_editor->e_textdirty = 0; + x->x_dragfrom = x->x_selstart = 0; + x->x_selend = x->x_bufsize; + x->x_active = 1; + } + else + { + sys_vgui("selection clear .x%x.c\n", canvas); + sys_vgui(".x%x.c focus \"\"\n", canvas); + if (glist->gl_editor->e_textedfor == x) + glist->gl_editor->e_textedfor = 0; + x->x_active = 0; + } + rtext_senditup(x, SEND_UPDATE, &w, &h, &indx); +} + +void rtext_key(t_rtext *x, int keynum, t_symbol *keysym) +{ + int w = 0, h = 0, indx, i, newsize, ndel; + char *s1, *s2; + if (keynum) + { + int n = keynum; + if (n == '\r') n = '\n'; + if (n == '\b') /* backspace */ + { + /* LATER delete the box if all text is selected... + this causes reentrancy problems now. */ + /* if ((!x->x_selstart) && (x->x_selend == x->x_bufsize)) + { + .... + } */ + if (x->x_selstart && (x->x_selstart == x->x_selend)) + x->x_selstart--; + } + else if (n == 127) /* delete */ + { + if (x->x_selend < x->x_bufsize && (x->x_selstart == x->x_selend)) + x->x_selend++; + } + + ndel = x->x_selend - x->x_selstart; + for (i = x->x_selend; i < x->x_bufsize; i++) + x->x_buf[i- ndel] = x->x_buf[i]; + newsize = x->x_bufsize - ndel; + x->x_buf = resizebytes(x->x_buf, x->x_bufsize, newsize); + x->x_bufsize = newsize; + + if (n == '\n' || isprint(n)) + { + newsize = x->x_bufsize+1; + x->x_buf = resizebytes(x->x_buf, x->x_bufsize, newsize); + for (i = x->x_bufsize; i > x->x_selstart; i--) + x->x_buf[i] = x->x_buf[i-1]; + x->x_buf[x->x_selstart] = n; + x->x_bufsize = newsize; + x->x_selstart = x->x_selstart + 1; + } + x->x_selend = x->x_selstart; + x->x_glist->gl_editor->e_textdirty = 1; + } + else if (!strcmp(keysym->s_name, "Right")) + { + if (x->x_selend == x->x_selstart && x->x_selstart < x->x_bufsize) + x->x_selend = x->x_selstart = x->x_selstart + 1; + else + x->x_selstart = x->x_selend; + } + else if (!strcmp(keysym->s_name, "Left")) + { + if (x->x_selend == x->x_selstart && x->x_selstart > 0) + x->x_selend = x->x_selstart = x->x_selstart - 1; + else + x->x_selend = x->x_selstart; + } + /* this should be improved... life's too short */ + else if (!strcmp(keysym->s_name, "Up")) + { + if (x->x_selstart) + x->x_selstart--; + while (x->x_selstart > 0 && x->x_buf[x->x_selstart] != '\n') + x->x_selstart--; + x->x_selend = x->x_selstart; + } + else if (!strcmp(keysym->s_name, "Down")) + { + while (x->x_selend < x->x_bufsize && + x->x_buf[x->x_selend] != '\n') + x->x_selend++; + if (x->x_selend < x->x_bufsize) + x->x_selend++; + x->x_selstart = x->x_selend; + } + rtext_senditup(x, SEND_UPDATE, &w, &h, &indx); +} + +void rtext_mouse(t_rtext *x, int xval, int yval, int flag) +{ + int w = xval, h = yval, indx; + rtext_senditup(x, SEND_CHECK, &w, &h, &indx); + if (flag == RTEXT_DOWN) + { + x->x_dragfrom = x->x_selstart = x->x_selend = indx; + } + else if (flag == RTEXT_SHIFT) + { + if (indx * 2 > x->x_selstart + x->x_selend) + x->x_dragfrom = x->x_selstart, x->x_selend = indx; + else + x->x_dragfrom = x->x_selend, x->x_selstart = indx; + } + else if (flag == RTEXT_DRAG) + { + x->x_selstart = (x->x_dragfrom < indx ? x->x_dragfrom : indx); + x->x_selend = (x->x_dragfrom > indx ? x->x_dragfrom : indx); + } + rtext_senditup(x, SEND_UPDATE, &w, &h, &indx); +} diff --git a/apps/plugins/pdbox/PDa/src/g_scalar.c b/apps/plugins/pdbox/PDa/src/g_scalar.c new file mode 100644 index 0000000..b3c6d82 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_scalar.c @@ -0,0 +1,802 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* This file defines the "scalar" object, which is not a text object, just a +"gobj". Scalars have templates which describe their structures, which +can contain numbers, sublists, and arrays. + +Also, the "tscalar" object, an ordinary text object that owns a single "scalar" +and draws it on the parent. This is intended as a way that abstractions can +control their appearances by adding stuff to draw. +*/ + +/* IOhannes : + * changed the canvas_restore, so that it might accept $args as well (like "pd $0_test") + * so you can make multiple & distinguishable templates + * 1511:forum::für::umläute:2001 + * changes marked with IOhannes + * added Krzysztof Czajas fix to avoid crashing... + */ + +#include +#include +#include /* for read/write to files */ +#include "m_pd.h" +#include "g_canvas.h" + +t_class *scalar_class; + +void word_init(t_word *wp, t_template *template, t_gpointer *gp) +{ + int i, nitems = template->t_n; + t_dataslot *datatypes = template->t_vec; + for (i = 0; i < nitems; i++, datatypes++, wp++) + { + int type = datatypes->ds_type; + if (type == DT_FLOAT) + wp->w_float = 0; + else if (type == DT_SYMBOL) + wp->w_symbol = &s_symbol; + else if (type == DT_ARRAY) + { + wp->w_array = array_new(datatypes->ds_arraytemplate, gp); + } + else if (type == DT_LIST) + { + /* LATER test this and get it to work */ + wp->w_list = canvas_new(0, 0, 0, 0); + } + } +} + +void word_restore(t_word *wp, t_template *template, + int argc, t_atom *argv) +{ + int i, nitems = template->t_n; + t_dataslot *datatypes = template->t_vec; + for (i = 0; i < nitems; i++, datatypes++, wp++) + { + int type = datatypes->ds_type; + if (type == DT_FLOAT) + { + float f; + if (argc) + { + f = atom_getfloat(argv); + argv++, argc--; + } + else f = 0; + wp->w_float = f; + } + else if (type == DT_SYMBOL) + { + t_symbol *s; + if (argc) + { + s = atom_getsymbol(argv); + argv++, argc--; + } + else s = &s_; + wp->w_symbol = s; + } + } + if (argc) + post("warning: word_restore: extra arguments"); +} + +void word_free(t_word *wp, t_template *template) +{ + int i; + t_dataslot *dt; + for (dt = template->t_vec, i = 0; i < template->t_n; i++, dt++) + { + if (dt->ds_type == DT_ARRAY) + array_free(wp[i].w_array); + else if (dt->ds_type == DT_LIST) + canvas_free(wp[i].w_list); + } +} + + /* make a new scalar and add to the glist. We create a "gp" here which + will be used for array items to point back here. This gp doesn't do + reference counting or "validation" updates though; the parent won't go away + without the contained arrays going away too. The "gp" is copied out + by value in the word_init() routine so we can throw our copy away. */ + +t_scalar *scalar_new(t_glist *owner, t_symbol *templatesym) +{ + t_scalar *x; + t_template *template; + t_gpointer gp; + gpointer_init(&gp); + template = template_findbyname(templatesym); + if (!template) + { + error("scalar: couldn't find template %s", templatesym->s_name); + return (0); + } + x = (t_scalar *)getbytes(sizeof(t_scalar) + + (template->t_n - 1) * sizeof(*x->sc_vec)); + x->sc_gobj.g_pd = scalar_class; + x->sc_template = templatesym; + gpointer_setglist(&gp, owner, x); + word_init(x->sc_vec, template, &gp); + return (x); +} + + /* Pd method to create a new scalar, add it to a glist, and initialize + it from the message arguments. */ + +int glist_readscalar(t_glist *x, int natoms, t_atom *vec, + int *p_nextmsg, int selectit); + +void glist_scalar(t_glist *glist, + t_symbol *classname, t_int argc, t_atom *argv) +{ + t_symbol *templatesym = + canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + t_binbuf *b; + int natoms, nextmsg = 0; + t_atom *vec; + if (!template_findbyname(templatesym)) + { + pd_error(glist, "%s: no such template", + atom_getsymbolarg(0, argc, argv)->s_name); + return; + } + + b = binbuf_new(); + binbuf_restore(b, argc, argv); + natoms = binbuf_getnatom(b); + vec = binbuf_getvec(b); + + glist_readscalar(glist, natoms, vec, &nextmsg, 0); + binbuf_free(b); +} + +/* -------------------- widget behavior for scalar ------------ */ +void scalar_getbasexy(t_scalar *x, float *basex, float *basey) +{ + t_template *template = template_findbyname(x->sc_template); + *basex = template_getfloat(template, gensym("x"), x->sc_vec, 0); + *basey = template_getfloat(template, gensym("y"), x->sc_vec, 0); +} + +static void scalar_getrect(t_gobj *z, t_glist *owner, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_scalar *x = (t_scalar *)z; + int hit = 0; + t_template *template = template_findbyname(x->sc_template); + t_canvas *templatecanvas = template_findcanvas(template); + int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff; + t_gobj *y; + float basex, basey; + scalar_getbasexy(x, &basex, &basey); + /* if someone deleted the template canvas, we're just a point */ + if (!templatecanvas) + { + x1 = x2 = glist_xtopixels(owner, basex); + y1 = y2 = glist_ytopixels(owner, basey); + } + else + { + int hit = 0; + x1 = y1 = 0x7fffffff; + x2 = y2 = -0x7fffffff; + for (y = templatecanvas->gl_list; y; y = y->g_next) + { + t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); + int nx1, ny1, nx2, ny2; + if (!wb) continue; + (*wb->w_parentgetrectfn)(y, owner, + x->sc_vec, template, basex, basey, + &nx1, &ny1, &nx2, &ny2); + if (hit) + { + if (nx1 < x1) x1 = nx1; + if (ny1 < y1) y1 = ny1; + if (nx2 > x2) x2 = nx2; + if (ny2 > y2) y2 = ny2; + } + else x1 = nx1, y1 = ny1, x2 = nx2, y2 = ny2, hit = 1; + } + if (!hit) x1 = y1 = x2 = y2 = 0; + } + /* post("scalar x1 %d y1 %d x2 %d y2 %d", x1, y1, x2, y2); */ + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void scalar_select(t_gobj *z, t_glist *owner, int state) +{ + t_scalar *x = (t_scalar *)z; + /* post("scalar_select %d", state); */ + /* later */ + if (state) + { + int x1, y1, x2, y2; + scalar_getrect(z, owner, &x1, &y1, &x2, &y2); + x1--; x2++; y1--; y2++; + sys_vgui(".x%x.c create line %d %d %d %d %d %d %d %d %d %d \ + -width 0 -fill blue -tags select%x\n", + glist_getcanvas(owner), x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, + x); + } + else sys_vgui(".x%x.c delete select%x\n", glist_getcanvas(owner), x); +} + +static void scalar_displace(t_gobj *z, t_glist *glist, int dx, int dy) +{ + t_scalar *x = (t_scalar *)z; + t_symbol *templatesym = x->sc_template; + t_template *template = template_findbyname(templatesym); + t_symbol *zz; + int xonset, yonset, xtype, ytype, gotx, goty; + if (!template) + { + error("scalar: couldn't find template %s", templatesym->s_name); + return; + } + gotx = template_find_field(template, gensym("x"), &xonset, &xtype, &zz); + if (gotx && (xtype != DT_FLOAT)) + gotx = 0; + goty = template_find_field(template, gensym("y"), &yonset, &ytype, &zz); + if (goty && (ytype != DT_FLOAT)) + goty = 0; + if (gotx) + *(t_float *)(((char *)(x->sc_vec)) + xonset) += + dx * (glist_pixelstox(glist, 1) - glist_pixelstox(glist, 0)); + if (goty) + *(t_float *)(((char *)(x->sc_vec)) + yonset) += + dy * (glist_pixelstoy(glist, 1) - glist_pixelstoy(glist, 0)); + glist_redrawitem(glist, z); + if (glist_isselected(glist, z)) + { + scalar_select(z, glist, 0); + scalar_select(z, glist, 1); + } +} + +static void scalar_activate(t_gobj *z, t_glist *owner, int state) +{ + /* post("scalar_activate %d", state); */ + /* later */ +} + +static void scalar_delete(t_gobj *z, t_glist *glist) +{ + /* nothing to do */ +} + +static void scalar_vis(t_gobj *z, t_glist *owner, int vis) +{ + t_scalar *x = (t_scalar *)z; + t_template *template = template_findbyname(x->sc_template); + t_canvas *templatecanvas = template_findcanvas(template); + t_gobj *y; + float basex, basey; + scalar_getbasexy(x, &basex, &basey); + /* if we don't know how to draw it, make a small rectangle */ + if (!templatecanvas) + { + if (vis) + { + int x1 = glist_xtopixels(owner, basex); + int y1 = glist_ytopixels(owner, basey); + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags scalar%x\n", + glist_getcanvas(owner), x1-1, y1-1, x1+1, y1+1, x); + } + else sys_vgui(".x%x.c delete scalar%x\n", glist_getcanvas(owner), x); + return; + } + + for (y = templatecanvas->gl_list; y; y = y->g_next) + { + t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); + if (!wb) continue; + (*wb->w_parentvisfn)(y, owner, x->sc_vec, template, basex, basey, vis); + } +} + +static int scalar_click(t_gobj *z, struct _glist *owner, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_scalar *x = (t_scalar *)z; + int hit = 0; + t_template *template = template_findbyname(x->sc_template); + t_canvas *templatecanvas = template_findcanvas(template); + t_gobj *y; + float basex, basey; + scalar_getbasexy(x, &basex, &basey); + for (y = templatecanvas->gl_list; y; y = y->g_next) + { + t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); + if (!wb) continue; + if (hit = (*wb->w_parentclickfn)(y, owner, + x, template, basex, basey, + xpix, ypix, shift, alt, dbl, doit)) + return (hit); + } + return (0); +} + +void canvas_writescalar(t_symbol *templatesym, t_word *w, t_binbuf *b, + int amarrayelement); + +static void scalar_save(t_gobj *z, t_binbuf *b) +{ + t_scalar *x = (t_scalar *)z; + t_binbuf *b2 = binbuf_new(); + t_atom a, *argv; + int i, argc; + canvas_writescalar(x->sc_template, x->sc_vec, b2, 0); + binbuf_addv(b, "ss", &s__X, gensym("scalar")); + binbuf_addbinbuf(b, b2); + binbuf_addsemi(b); + binbuf_free(b2); +} + +static void scalar_properties(t_gobj *z, struct _glist *owner) +{ + t_scalar *x = (t_scalar *)z; + char *buf, buf2[80]; + int bufsize; + t_binbuf *b; + glist_noselect(owner); + glist_select(owner, z); + b = glist_writetobinbuf(owner, 0); + binbuf_gettext(b, &buf, &bufsize); + binbuf_free(b); + buf = t_resizebytes(buf, bufsize, bufsize+1); + buf[bufsize] = 0; + sprintf(buf2, "pdtk_data_dialog %%s {"); + gfxstub_new((t_pd *)owner, x, buf2); + sys_gui(buf); + sys_gui("}\n"); + t_freebytes(buf, bufsize+1); +} + +static t_widgetbehavior scalar_widgetbehavior = +{ + scalar_getrect, + scalar_displace, + scalar_select, + scalar_activate, + scalar_delete, + scalar_vis, + scalar_click, +}; + +static void scalar_free(t_scalar *x) +{ + int i; + t_dataslot *datatypes, *dt; + t_symbol *templatesym = x->sc_template; + t_template *template = template_findbyname(templatesym); + if (!template) + { + error("scalar: couldn't find template %s", templatesym->s_name); + return; + } + word_free(x->sc_vec, template); + gfxstub_deleteforkey(x); + /* the "size" field in the class is zero, so Pd doesn't try to free + us automatically (see pd_free()) */ + freebytes(x, sizeof(t_scalar) + (template->t_n - 1) * sizeof(*x->sc_vec)); +} + +/* ----------------- setup function ------------------- */ + +void g_scalar_setup(void) +{ + scalar_class = class_new(gensym("scalar"), 0, (t_method)scalar_free, 0, + CLASS_GOBJ, 0); + class_setwidget(scalar_class, &scalar_widgetbehavior); + class_setsavefn(scalar_class, scalar_save); + class_setpropertiesfn(scalar_class, scalar_properties); +} +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* This file defines the "scalar" object, which is not a text object, just a +"gobj". Scalars have templates which describe their structures, which +can contain numbers, sublists, and arrays. + +Also, the "tscalar" object, an ordinary text object that owns a single "scalar" +and draws it on the parent. This is intended as a way that abstractions can +control their appearances by adding stuff to draw. +*/ + +/* IOhannes : + * changed the canvas_restore, so that it might accept $args as well (like "pd $0_test") + * so you can make multiple & distinguishable templates + * 1511:forum::für::umläute:2001 + * changes marked with IOhannes + * added Krzysztof Czajas fix to avoid crashing... + */ + +#include +#include +#include /* for read/write to files */ +#include "m_pd.h" +#include "g_canvas.h" + +t_class *scalar_class; + +void word_init(t_word *wp, t_template *template, t_gpointer *gp) +{ + int i, nitems = template->t_n; + t_dataslot *datatypes = template->t_vec; + for (i = 0; i < nitems; i++, datatypes++, wp++) + { + int type = datatypes->ds_type; + if (type == DT_FLOAT) + wp->w_float = 0; + else if (type == DT_SYMBOL) + wp->w_symbol = &s_symbol; + else if (type == DT_ARRAY) + { + wp->w_array = array_new(datatypes->ds_arraytemplate, gp); + } + else if (type == DT_LIST) + { + /* LATER test this and get it to work */ + wp->w_list = canvas_new(0, 0, 0, 0); + } + } +} + +void word_restore(t_word *wp, t_template *template, + int argc, t_atom *argv) +{ + int i, nitems = template->t_n; + t_dataslot *datatypes = template->t_vec; + for (i = 0; i < nitems; i++, datatypes++, wp++) + { + int type = datatypes->ds_type; + if (type == DT_FLOAT) + { + float f; + if (argc) + { + f = atom_getfloat(argv); + argv++, argc--; + } + else f = 0; + wp->w_float = f; + } + else if (type == DT_SYMBOL) + { + t_symbol *s; + if (argc) + { + s = atom_getsymbol(argv); + argv++, argc--; + } + else s = &s_; + wp->w_symbol = s; + } + } + if (argc) + post("warning: word_restore: extra arguments"); +} + +void word_free(t_word *wp, t_template *template) +{ + int i; + t_dataslot *dt; + for (dt = template->t_vec, i = 0; i < template->t_n; i++, dt++) + { + if (dt->ds_type == DT_ARRAY) + array_free(wp[i].w_array); + else if (dt->ds_type == DT_LIST) + canvas_free(wp[i].w_list); + } +} + + /* make a new scalar and add to the glist. We create a "gp" here which + will be used for array items to point back here. This gp doesn't do + reference counting or "validation" updates though; the parent won't go away + without the contained arrays going away too. The "gp" is copied out + by value in the word_init() routine so we can throw our copy away. */ + +t_scalar *scalar_new(t_glist *owner, t_symbol *templatesym) +{ + t_scalar *x; + t_template *template; + t_gpointer gp; + gpointer_init(&gp); + template = template_findbyname(templatesym); + if (!template) + { + error("scalar: couldn't find template %s", templatesym->s_name); + return (0); + } + x = (t_scalar *)getbytes(sizeof(t_scalar) + + (template->t_n - 1) * sizeof(*x->sc_vec)); + x->sc_gobj.g_pd = scalar_class; + x->sc_template = templatesym; + gpointer_setglist(&gp, owner, x); + word_init(x->sc_vec, template, &gp); + return (x); +} + + /* Pd method to create a new scalar, add it to a glist, and initialize + it from the message arguments. */ + +int glist_readscalar(t_glist *x, int natoms, t_atom *vec, + int *p_nextmsg, int selectit); + +void glist_scalar(t_glist *glist, + t_symbol *classname, t_int argc, t_atom *argv) +{ + t_symbol *templatesym = + canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + t_binbuf *b; + int natoms, nextmsg = 0; + t_atom *vec; + if (!template_findbyname(templatesym)) + { + pd_error(glist, "%s: no such template", + atom_getsymbolarg(0, argc, argv)->s_name); + return; + } + + b = binbuf_new(); + binbuf_restore(b, argc, argv); + natoms = binbuf_getnatom(b); + vec = binbuf_getvec(b); + + glist_readscalar(glist, natoms, vec, &nextmsg, 0); + binbuf_free(b); +} + +/* -------------------- widget behavior for scalar ------------ */ +void scalar_getbasexy(t_scalar *x, float *basex, float *basey) +{ + t_template *template = template_findbyname(x->sc_template); + *basex = template_getfloat(template, gensym("x"), x->sc_vec, 0); + *basey = template_getfloat(template, gensym("y"), x->sc_vec, 0); +} + +static void scalar_getrect(t_gobj *z, t_glist *owner, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_scalar *x = (t_scalar *)z; + int hit = 0; + t_template *template = template_findbyname(x->sc_template); + t_canvas *templatecanvas = template_findcanvas(template); + int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff; + t_gobj *y; + float basex, basey; + scalar_getbasexy(x, &basex, &basey); + /* if someone deleted the template canvas, we're just a point */ + if (!templatecanvas) + { + x1 = x2 = glist_xtopixels(owner, basex); + y1 = y2 = glist_ytopixels(owner, basey); + } + else + { + int hit = 0; + x1 = y1 = 0x7fffffff; + x2 = y2 = -0x7fffffff; + for (y = templatecanvas->gl_list; y; y = y->g_next) + { + t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); + int nx1, ny1, nx2, ny2; + if (!wb) continue; + (*wb->w_parentgetrectfn)(y, owner, + x->sc_vec, template, basex, basey, + &nx1, &ny1, &nx2, &ny2); + if (hit) + { + if (nx1 < x1) x1 = nx1; + if (ny1 < y1) y1 = ny1; + if (nx2 > x2) x2 = nx2; + if (ny2 > y2) y2 = ny2; + } + else x1 = nx1, y1 = ny1, x2 = nx2, y2 = ny2, hit = 1; + } + if (!hit) x1 = y1 = x2 = y2 = 0; + } + /* post("scalar x1 %d y1 %d x2 %d y2 %d", x1, y1, x2, y2); */ + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void scalar_select(t_gobj *z, t_glist *owner, int state) +{ + t_scalar *x = (t_scalar *)z; + /* post("scalar_select %d", state); */ + /* later */ + if (state) + { + int x1, y1, x2, y2; + scalar_getrect(z, owner, &x1, &y1, &x2, &y2); + x1--; x2++; y1--; y2++; + sys_vgui(".x%x.c create line %d %d %d %d %d %d %d %d %d %d \ + -width 0 -fill blue -tags select%x\n", + glist_getcanvas(owner), x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, + x); + } + else sys_vgui(".x%x.c delete select%x\n", glist_getcanvas(owner), x); +} + +static void scalar_displace(t_gobj *z, t_glist *glist, int dx, int dy) +{ + t_scalar *x = (t_scalar *)z; + t_symbol *templatesym = x->sc_template; + t_template *template = template_findbyname(templatesym); + t_symbol *zz; + int xonset, yonset, xtype, ytype, gotx, goty; + if (!template) + { + error("scalar: couldn't find template %s", templatesym->s_name); + return; + } + gotx = template_find_field(template, gensym("x"), &xonset, &xtype, &zz); + if (gotx && (xtype != DT_FLOAT)) + gotx = 0; + goty = template_find_field(template, gensym("y"), &yonset, &ytype, &zz); + if (goty && (ytype != DT_FLOAT)) + goty = 0; + if (gotx) + *(t_float *)(((char *)(x->sc_vec)) + xonset) += + dx * (glist_pixelstox(glist, 1) - glist_pixelstox(glist, 0)); + if (goty) + *(t_float *)(((char *)(x->sc_vec)) + yonset) += + dy * (glist_pixelstoy(glist, 1) - glist_pixelstoy(glist, 0)); + glist_redrawitem(glist, z); + if (glist_isselected(glist, z)) + { + scalar_select(z, glist, 0); + scalar_select(z, glist, 1); + } +} + +static void scalar_activate(t_gobj *z, t_glist *owner, int state) +{ + /* post("scalar_activate %d", state); */ + /* later */ +} + +static void scalar_delete(t_gobj *z, t_glist *glist) +{ + /* nothing to do */ +} + +static void scalar_vis(t_gobj *z, t_glist *owner, int vis) +{ + t_scalar *x = (t_scalar *)z; + t_template *template = template_findbyname(x->sc_template); + t_canvas *templatecanvas = template_findcanvas(template); + t_gobj *y; + float basex, basey; + scalar_getbasexy(x, &basex, &basey); + /* if we don't know how to draw it, make a small rectangle */ + if (!templatecanvas) + { + if (vis) + { + int x1 = glist_xtopixels(owner, basex); + int y1 = glist_ytopixels(owner, basey); + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags scalar%x\n", + glist_getcanvas(owner), x1-1, y1-1, x1+1, y1+1, x); + } + else sys_vgui(".x%x.c delete scalar%x\n", glist_getcanvas(owner), x); + return; + } + + for (y = templatecanvas->gl_list; y; y = y->g_next) + { + t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); + if (!wb) continue; + (*wb->w_parentvisfn)(y, owner, x->sc_vec, template, basex, basey, vis); + } +} + +static int scalar_click(t_gobj *z, struct _glist *owner, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_scalar *x = (t_scalar *)z; + int hit = 0; + t_template *template = template_findbyname(x->sc_template); + t_canvas *templatecanvas = template_findcanvas(template); + t_gobj *y; + float basex, basey; + scalar_getbasexy(x, &basex, &basey); + for (y = templatecanvas->gl_list; y; y = y->g_next) + { + t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); + if (!wb) continue; + if (hit = (*wb->w_parentclickfn)(y, owner, + x, template, basex, basey, + xpix, ypix, shift, alt, dbl, doit)) + return (hit); + } + return (0); +} + +void canvas_writescalar(t_symbol *templatesym, t_word *w, t_binbuf *b, + int amarrayelement); + +static void scalar_save(t_gobj *z, t_binbuf *b) +{ + t_scalar *x = (t_scalar *)z; + t_binbuf *b2 = binbuf_new(); + t_atom a, *argv; + int i, argc; + canvas_writescalar(x->sc_template, x->sc_vec, b2, 0); + binbuf_addv(b, "ss", &s__X, gensym("scalar")); + binbuf_addbinbuf(b, b2); + binbuf_addsemi(b); + binbuf_free(b2); +} + +static void scalar_properties(t_gobj *z, struct _glist *owner) +{ + t_scalar *x = (t_scalar *)z; + char *buf, buf2[80]; + int bufsize; + t_binbuf *b; + glist_noselect(owner); + glist_select(owner, z); + b = glist_writetobinbuf(owner, 0); + binbuf_gettext(b, &buf, &bufsize); + binbuf_free(b); + buf = t_resizebytes(buf, bufsize, bufsize+1); + buf[bufsize] = 0; + sprintf(buf2, "pdtk_data_dialog %%s {"); + gfxstub_new((t_pd *)owner, x, buf2); + sys_gui(buf); + sys_gui("}\n"); + t_freebytes(buf, bufsize+1); +} + +static t_widgetbehavior scalar_widgetbehavior = +{ + scalar_getrect, + scalar_displace, + scalar_select, + scalar_activate, + scalar_delete, + scalar_vis, + scalar_click, +}; + +static void scalar_free(t_scalar *x) +{ + int i; + t_dataslot *datatypes, *dt; + t_symbol *templatesym = x->sc_template; + t_template *template = template_findbyname(templatesym); + if (!template) + { + error("scalar: couldn't find template %s", templatesym->s_name); + return; + } + word_free(x->sc_vec, template); + gfxstub_deleteforkey(x); + /* the "size" field in the class is zero, so Pd doesn't try to free + us automatically (see pd_free()) */ + freebytes(x, sizeof(t_scalar) + (template->t_n - 1) * sizeof(*x->sc_vec)); +} + +/* ----------------- setup function ------------------- */ + +void g_scalar_setup(void) +{ + scalar_class = class_new(gensym("scalar"), 0, (t_method)scalar_free, 0, + CLASS_GOBJ, 0); + class_setwidget(scalar_class, &scalar_widgetbehavior); + class_setsavefn(scalar_class, scalar_save); + class_setpropertiesfn(scalar_class, scalar_properties); +} diff --git a/apps/plugins/pdbox/PDa/src/g_template.c b/apps/plugins/pdbox/PDa/src/g_template.c new file mode 100644 index 0000000..c65613a --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_template.c @@ -0,0 +1,3358 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include +#include +#include + +#include "m_pd.h" +#include "s_stuff.h" /* for sys_hostfontsize */ +#include "g_canvas.h" + +/* +This file contains text objects you would put in a canvas to define a +template. Templates describe objects of type "array" (g_array.c) and +"scalar" (g_scalar.c). +*/ + +/* T.Grill - changed the _template.t_pd member to t_pdobj to avoid name clashes +with the t_pd type */ + + /* the structure of a "struct" object (also the obsolete "gtemplate" + you get when using the name "template" in a box.) */ + +struct _gtemplate +{ + t_object x_obj; + t_template *x_template; + t_canvas *x_owner; + t_symbol *x_sym; + struct _gtemplate *x_next; + int x_argc; + t_atom *x_argv; +}; + +/* ---------------- forward definitions ---------------- */ + +static void template_conformarray(t_template *tfrom, t_template *tto, + int *conformaction, t_array *a); +static void template_conformglist(t_template *tfrom, t_template *tto, + t_glist *glist, int *conformaction); + +/* ---------------------- storage ------------------------- */ + +static t_class *gtemplate_class; +static t_class *template_class; + +/* there's a pre-defined "float" template. LATER should we bind this +to a symbol such as "pd-float"??? */ + +static t_dataslot template_float_vec = +{ + DT_FLOAT, + &s_y, + &s_ +}; + +static t_template template_float = +{ + 0, /* class -- fill in in setup routine */ + 0, /* list of "struct"/t_gtemplate objects */ + &s_float, /* name */ + 1, /* number of items */ + &template_float_vec +}; + + /* return true if two dataslot definitions match */ +static int dataslot_matches(t_dataslot *ds1, t_dataslot *ds2, + int nametoo) +{ + return ((!nametoo || ds1->ds_name == ds2->ds_name) && + ds1->ds_type == ds2->ds_type && + (ds1->ds_type != DT_ARRAY || + ds1->ds_arraytemplate == ds2->ds_arraytemplate)); +} + +/* -- templates, the active ingredient in gtemplates defined below. ------- */ + +t_template *template_new(t_symbol *templatesym, int argc, t_atom *argv) +{ + t_template *x = (t_template *)pd_new(template_class); + x->t_n = 0; + x->t_vec = (t_dataslot *)t_getbytes(0); + while (argc > 0) + { + int newtype, oldn, newn; + t_symbol *newname, *newarraytemplate = &s_, *newtypesym; + if (argc < 2 || argv[0].a_type != A_SYMBOL || + argv[1].a_type != A_SYMBOL) + goto bad; + newtypesym = argv[0].a_w.w_symbol; + newname = argv[1].a_w.w_symbol; + if (newtypesym == &s_float) + newtype = DT_FLOAT; + else if (newtypesym == &s_symbol) + newtype = DT_SYMBOL; + else if (newtypesym == &s_list) + newtype = DT_LIST; + else if (newtypesym == gensym("array")) + { + if (argc < 3 || argv[2].a_type != A_SYMBOL) + { + pd_error(x, "array lacks element template or name"); + goto bad; + } + newarraytemplate = canvas_makebindsym(argv[2].a_w.w_symbol); + newtype = DT_ARRAY; + argc--; + argv++; + } + else + { + pd_error(x, "%s: no such type", newtypesym->s_name); + return (0); + } + newn = (oldn = x->t_n) + 1; + x->t_vec = (t_dataslot *)t_resizebytes(x->t_vec, + oldn * sizeof(*x->t_vec), newn * sizeof(*x->t_vec)); + x->t_n = newn; + x->t_vec[oldn].ds_type = newtype; + x->t_vec[oldn].ds_name = newname; + x->t_vec[oldn].ds_arraytemplate = newarraytemplate; + bad: + argc -= 2; argv += 2; + } + if (templatesym->s_name) + { + x->t_sym = templatesym; + pd_bind(&x->t_pdobj, x->t_sym); + } + else x->t_sym = templatesym; + return (x); +} + +int template_size(t_template *x) +{ + return (x->t_n * sizeof(t_word)); +} + +int template_find_field(t_template *x, t_symbol *name, int *p_onset, + int *p_type, t_symbol **p_arraytype) +{ + t_template *t; + int i, n; + if (!x) + { + bug("template_find_field"); + return (0); + } + n = x->t_n; + for (i = 0; i < n; i++) + if (x->t_vec[i].ds_name == name) + { + *p_onset = i * sizeof(t_word); + *p_type = x->t_vec[i].ds_type; + *p_arraytype = x->t_vec[i].ds_arraytemplate; + return (1); + } + return (0); +} + +t_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp, + int loud) +{ + int onset, type; + t_symbol *arraytype; + t_sample val = 0; + if (template_find_field(x, fieldname, &onset, &type, &arraytype)) + { + if (type == DT_FLOAT) + val = *(t_sample *)(((char *)wp) + onset); + else if (loud) error("%s.%s: not a number", + x->t_sym->s_name, fieldname->s_name); + } + else if (loud) error("%s.%s: no such field", + x->t_sym->s_name, fieldname->s_name); + return (fixtof(val)); +} + +void template_setfloat(t_template *x, t_symbol *fieldname, t_word *wp, + t_float f, int loud) +{ + int onset, type; + t_symbol *arraytype; + if (template_find_field(x, fieldname, &onset, &type, &arraytype)) + { + if (type == DT_FLOAT) + *(t_sample *)(((char *)wp) + onset) = ftofix(f); + else if (loud) error("%s.%s: not a number", + x->t_sym->s_name, fieldname->s_name); + } + else if (loud) error("%s.%s: no such field", + x->t_sym->s_name, fieldname->s_name); +} + +t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, t_word *wp, + int loud) +{ + int onset, type; + t_symbol *arraytype; + t_symbol *val = &s_; + if (template_find_field(x, fieldname, &onset, &type, &arraytype)) + { + if (type == DT_SYMBOL) + val = *(t_symbol **)(((char *)wp) + onset); + else if (loud) error("%s.%s: not a symbol", + x->t_sym->s_name, fieldname->s_name); + } + else if (loud) error("%s.%s: no such field", + x->t_sym->s_name, fieldname->s_name); + return (val); +} + +void template_setsymbol(t_template *x, t_symbol *fieldname, t_word *wp, + t_symbol *s, int loud) +{ + int onset, type; + t_symbol *arraytype; + if (template_find_field(x, fieldname, &onset, &type, &arraytype)) + { + if (type == DT_SYMBOL) + *(t_symbol **)(((char *)wp) + onset) = s; + else if (loud) error("%s.%s: not a symbol", + x->t_sym->s_name, fieldname->s_name); + } + else if (loud) error("%s.%s: no such field", + x->t_sym->s_name, fieldname->s_name); +} + + /* stringent check to see if a "saved" template, x2, matches the current + one (x1). It's OK if x1 has additional scalar elements but not (yet) + arrays or lists. This is used for reading in "data files". */ +int template_match(t_template *x1, t_template *x2) +{ + int i; + if (x1->t_n < x2->t_n) + return (0); + for (i = x2->t_n; i < x1->t_n; i++) + { + if (x1->t_vec[i].ds_type == DT_ARRAY || + x1->t_vec[i].ds_type == DT_LIST) + return (0); + } + if (x2->t_n > x1->t_n) + post("add elements..."); + for (i = 0; i < x2->t_n; i++) + if (!dataslot_matches(&x1->t_vec[i], &x2->t_vec[i], 1)) + return (0); + return (1); +} + +/* --------------- CONFORMING TO CHANGES IN A TEMPLATE ------------ */ + +/* the following routines handle updating scalars to agree with changes +in their template. The old template is assumed to be the "installed" one +so we can delete old items; but making new ones we have to avoid scalar_new +which would make an old one whereas we will want a new one (but whose array +elements might still be old ones. + LATER deal with graphics updates too... */ + + /* conform the word vector of a scalar to the new template */ +static void template_conformwords(t_template *tfrom, t_template *tto, + int *conformaction, t_word *wfrom, t_word *wto) +{ + int nfrom = tfrom->t_n, nto = tto->t_n, i; + for (i = 0; i < nto; i++) + { + if (conformaction[i] >= 0) + { + /* we swap the two, in case it's an array or list, so that + when "wfrom" is deleted the old one gets cleaned up. */ + t_word wwas = wto[i]; + wto[i] = wfrom[conformaction[i]]; + wfrom[conformaction[i]] = wwas; + } + } +} + + /* conform a scalar, recursively conforming sublists and arrays */ +static t_scalar *template_conformscalar(t_template *tfrom, t_template *tto, + int *conformaction, t_glist *glist, t_scalar *scfrom) +{ + t_scalar *x; + t_gpointer gp; + int nto = tto->t_n, nfrom = tfrom->t_n, i; + t_template *scalartemplate; + /* post("conform scalar"); */ + /* possibly replace the scalar */ + if (scfrom->sc_template == tfrom->t_sym) + { + /* see scalar_new() for comment about the gpointer. */ + gpointer_init(&gp); + x = (t_scalar *)getbytes(sizeof(t_scalar) + + (tto->t_n - 1) * sizeof(*x->sc_vec)); + x->sc_gobj.g_pd = scalar_class; + x->sc_template = tfrom->t_sym; + gpointer_setglist(&gp, glist, x); + /* Here we initialize to the new template, but array and list + elements will still belong to old template. */ + word_init(x->sc_vec, tto, &gp); + + template_conformwords(tfrom, tto, conformaction, + scfrom->sc_vec, x->sc_vec); + + /* replace the old one with the new one in the list */ + if (glist->gl_list == &scfrom->sc_gobj) + { + glist->gl_list = &x->sc_gobj; + x->sc_gobj.g_next = scfrom->sc_gobj.g_next; + } + else + { + t_gobj *y, *y2; + for (y = glist->gl_list; y2 = y->g_next; y = y2) + if (y2 == &scfrom->sc_gobj) + { + x->sc_gobj.g_next = y2->g_next; + y->g_next = &x->sc_gobj; + goto nobug; + } + bug("template_conformscalar"); + nobug: ; + } + /* burn the old one */ + pd_free(&scfrom->sc_gobj.g_pd); + } + else x = scfrom; + scalartemplate = template_findbyname(x->sc_template); + /* convert all array elements and sublists */ + for (i = 0; i < scalartemplate->t_n; i++) + { + t_dataslot *ds = scalartemplate->t_vec + i; + if (ds->ds_type == DT_LIST) + { + t_glist *gl2 = x->sc_vec[i].w_list; + template_conformglist(tfrom, tto, gl2, conformaction); + } + else if (ds->ds_type == DT_ARRAY) + { + template_conformarray(tfrom, tto, conformaction, + x->sc_vec[i].w_array); + } + } + return (x); +} + + /* conform an array, recursively conforming sublists and arrays */ +static void template_conformarray(t_template *tfrom, t_template *tto, + int *conformaction, t_array *a) +{ + int i; + if (a->a_templatesym == tfrom->t_sym) + { + /* the array elements must all be conformed */ + int oldelemsize = sizeof(t_word) * tfrom->t_n, + newelemsize = sizeof(t_word) * tto->t_n; + char *newarray = getbytes(sizeof(t_word) * tto->t_n * a->a_n); + char *oldarray = a->a_vec; + if (a->a_elemsize != oldelemsize) + bug("template_conformarray"); + for (i = 0; i < a->a_n; i++) + { + t_word *wp = (t_word *)(newarray + newelemsize * i); + word_init(wp, tto, &a->a_gp); + template_conformwords(tfrom, tto, conformaction, + (t_word *)(oldarray + oldelemsize * i), wp); + } + } + bug("template_conformarray: this part not written"); + /* go through item by item conforming subarrays and sublists... */ +} + + /* this routine searches for every scalar in the glist that belongs + to the "from" template and makes it belong to the "to" template. Descend + glists recursively. + We don't handle redrawing here; this is to be filled in LATER... */ + +static void template_conformglist(t_template *tfrom, t_template *tto, + t_glist *glist, int *conformaction) +{ + t_gobj *g; + /* post("conform glist %s", glist->gl_name->s_name); */ + for (g = glist->gl_list; g; g = g->g_next) + { + if (pd_class(&g->g_pd) == scalar_class) + g = &template_conformscalar(tfrom, tto, conformaction, + glist, (t_scalar *)g)->sc_gobj; + else if (pd_class(&g->g_pd) == canvas_class) + template_conformglist(tfrom, tto, (t_glist *)g, conformaction); + } +} + + /* globally conform all scalars from one template to another */ +void template_conform(t_template *tfrom, t_template *tto) +{ + int nto = tto->t_n, nfrom = tfrom->t_n, i, j, + *conformaction = (int *)getbytes(sizeof(int) * nto), + *conformedfrom = (int *)getbytes(sizeof(int) * nfrom), doit = 0; + for (i = 0; i < nto; i++) + conformaction[i] = -1; + for (i = 0; i < nfrom; i++) + conformedfrom[i] = 0; + for (i = 0; i < nto; i++) + { + t_dataslot *dataslot = &tto->t_vec[i]; + for (j = 0; j < nfrom; j++) + { + t_dataslot *dataslot2 = &tfrom->t_vec[j]; + if (dataslot_matches(dataslot, dataslot2, 1)) + { + conformaction[i] = j; + conformedfrom[j] = 1; + } + } + } + for (i = 0; i < nto; i++) + if (conformaction[i] < 0) + { + t_dataslot *dataslot = &tto->t_vec[i]; + for (j = 0; j < nfrom; j++) + if (!conformedfrom[j] && + dataslot_matches(dataslot, &tfrom->t_vec[j], 1)) + { + conformaction[i] = j; + conformedfrom[j] = 1; + } + } + if (nto != nfrom) + doit = 1; + else for (i = 0; i < nto; i++) + if (conformaction[i] != i) + doit = 1; + + if (doit) + { + t_glist *gl; + /* post("conforming template '%s' to new structure", + tfrom->t_sym->s_name); + for (i = 0; i < nto; i++) + post("... %d", conformaction[i]); */ + for (gl = canvas_list; gl; gl = gl->gl_next) + template_conformglist(tfrom, tto, gl, conformaction); + } + freebytes(conformaction, sizeof(int) * nto); + freebytes(conformedfrom, sizeof(int) * nfrom); +} + +t_template *template_findbyname(t_symbol *s) +{ + int i; + if (s == &s_float) + return (&template_float); + else return ((t_template *)pd_findbyclass(s, template_class)); +} + +t_canvas *template_findcanvas(t_template *template) +{ + t_gtemplate *gt; + if (!template) + bug("template_findcanvas"); + if (!(gt = template->t_list)) + return (0); + return (gt->x_owner); + /* return ((t_canvas *)pd_findbyclass(template->t_sym, canvas_class)); */ +} + + /* call this when reading a patch from a file to declare what templates + we'll need. If there's already a template, check if it matches. + If it doesn't it's still OK as long as there are no "struct" (gtemplate) + objects hanging from it; we just conform everyone to the new template. + If there are still struct objects belonging to the other template, we're + in trouble. LATER we'll figure out how to conform the new patch's objects + to the pre-existing struct. */ +static void *template_usetemplate(void *dummy, t_symbol *s, + int argc, t_atom *argv) +{ + t_template *x; + t_symbol *templatesym = + canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (!argc) + return (0); + argc--; argv++; + /* check if there's already a template by this name. */ + if ((x = (t_template *)pd_findbyclass(templatesym, template_class))) + { + t_template *y = template_new(&s_, argc, argv); + /* If the new template is the same as the old one, + there's nothing to do. */ + if (!template_match(x, y)) + { + /* Are there "struct" objects upholding this template? */ + if (x->t_list) + { + /* don't know what to do here! */ + error("%s: template mismatch", + templatesym->s_name); + } + else + { + /* conform everyone to the new template */ + template_conform(x, y); + pd_free(&x->t_pdobj); + template_new(templatesym, argc, argv); + } + } + pd_free(&y->t_pdobj); + } + /* otherwise, just make one. */ + else template_new(templatesym, argc, argv); + return (0); +} + + /* here we assume someone has already cleaned up all instances of this. */ +void template_free(t_template *x) +{ + if (*x->t_sym->s_name) + pd_unbind(&x->t_pdobj, x->t_sym); + t_freebytes(x->t_vec, x->t_n * sizeof(*x->t_vec)); +} + +static void template_setup(void) +{ + template_class = class_new(gensym("template"), 0, (t_method)template_free, + sizeof(t_template), CLASS_PD, 0); + class_addmethod(pd_canvasmaker, (t_method)template_usetemplate, + gensym("struct"), A_GIMME, 0); + +} + +/* ---------------- gtemplates. One per canvas. ----------- */ + +/* this is a "text" object that searches for, and if necessary creates, +a "template" (above). Other objects in the canvas then can give drawing +instructions for the template. The template doesn't go away when the +gtemplate is deleted, so that you can replace it with +another one to add new fields, for example. */ + +static void *gtemplate_donew(t_symbol *sym, int argc, t_atom *argv) +{ + t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class); + t_template *t = template_findbyname(sym); + int i; + t_symbol *sx = gensym("x"); + x->x_owner = canvas_getcurrent(); + x->x_next = 0; + x->x_sym = sym; + x->x_argc = argc; + x->x_argv = (t_atom *)getbytes(argc * sizeof(t_atom)); + for (i = 0; i < argc; i++) + x->x_argv[i] = argv[i]; + + /* already have a template by this name? */ + if (t) + { + x->x_template = t; + /* if it's already got a "struct" or "gtemplate" object we + just tack this one to the end of the list and leave it + there. */ + if (t->t_list) + { + t_gtemplate *x2, *x3; + for (x2 = x->x_template->t_list; x3 = x2->x_next; x2 = x3) + ; + x2->x_next = x; + post("template %s: warning: already exists.", sym->s_name); + } + else + { + /* if there's none, we just replace the template with + our own and conform it. */ + t_template *y = template_new(&s_, argc, argv); + /* Unless the new template is different from the old one, + there's nothing to do. */ + if (!template_match(t, y)) + { + /* conform everyone to the new template */ + template_conform(t, y); + pd_free(&t->t_pdobj); + t = template_new(sym, argc, argv); + } + pd_free(&y->t_pdobj); + t->t_list = x; + } + } + else + { + /* otherwise make a new one and we're the only struct on it. */ + x->x_template = t = template_new(sym, argc, argv); + t->t_list = x; + } + return (x); +} + +static void *gtemplate_new(t_symbol *s, int argc, t_atom *argv) +{ + t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class); + t_symbol *sym = atom_getsymbolarg(0, argc, argv); + if (argc >= 1) + argc--; argv++; + return (gtemplate_donew(canvas_makebindsym(sym), argc, argv)); +} + + /* old version (0.34) -- delete 2003 or so */ +static void *gtemplate_new_old(t_symbol *s, int argc, t_atom *argv) +{ + t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class); + t_symbol *sym = canvas_makebindsym(canvas_getcurrent()->gl_name); + static int warned; + if (!warned) + { + post("warning -- 'template' (%s) is obsolete; replace with 'struct'", + sym->s_name); + warned = 1; + } + return (gtemplate_donew(sym, argc, argv)); +} + +t_template *gtemplate_get(t_gtemplate *x) +{ + return (x->x_template); +} + +static void gtemplate_free(t_gtemplate *x) +{ + /* get off the template's list */ + t_template *t = x->x_template; + if (x == t->t_list) + { + if (x->x_next) + { + /* if we were first on the list, and there are others on + the list, make a new template corresponding to the new + first-on-list and replace teh existing template with it. */ + t_template *z = template_new(&s_, x->x_argc, x->x_argv); + template_conform(t, z); + pd_free(&t->t_pdobj); + pd_free(&z->t_pdobj); + z = template_new(x->x_sym, x->x_argc, x->x_argv); + z->t_list = x->x_next; + } + else t->t_list = 0; + } + else + { + t_gtemplate *x2, *x3; + for (x2 = t->t_list; x3 = x2->x_next; x2 = x3) + { + if (x == x3) + { + x2->x_next = x3->x_next; + break; + } + } + } + freebytes(x->x_argv, sizeof(t_atom) * x->x_argc); +} + +static void gtemplate_setup(void) +{ + gtemplate_class = class_new(gensym("struct"), + (t_newmethod)gtemplate_new, (t_method)gtemplate_free, + sizeof(t_gtemplate), CLASS_NOINLET, A_GIMME, 0); + class_addcreator((t_newmethod)gtemplate_new_old, gensym("template"), + A_GIMME, 0); +} + +/* --------------- FIELD DESCRIPTORS ---------------------- */ + +/* a field descriptor can hold a constant or a variable; if a variable, +it's the name of a field in the template we belong to. LATER, we might +want to cache the offset of the field so we don't have to search for it +every single time we draw the object. +*/ + +typedef struct _fielddesc +{ + char fd_type; /* LATER consider removing this? */ + char fd_var; + union + { + t_float fd_float; /* the field is a constant float */ + t_symbol *fd_symbol; /* the field is a constant symbol */ + t_symbol *fd_varsym; /* the field is variable and this is the name */ + } fd_un; +} t_fielddesc; + +#define FIELDDESC_SETFLOAT(x, f) \ + ((x)->fd_type = A_FLOAT, (x)->fd_var = 0, (x)->fd_un.fd_float = (f)) +#define FIELDDESC_SETSYMBOL(x, s) \ + ((x)->fd_type = A_SYMBOL, (x)->fd_var = 0, (x)->fd_un.fd_symbol = (s)) +#define FIELDDESC_SETVAR(x, s, type) \ + ((x)->fd_type = type, (x)->fd_var = 1, (x)->fd_un.fd_varsym = (s)) + +#define CLOSED 1 +#define BEZ 2 +#define A_ARRAY 55 /* LATER decide whether to enshrine this in m_pd.h */ + +static void fielddesc_setfloatarg(t_fielddesc *fd, int argc, t_atom *argv) +{ + if (argc <= 0) FIELDDESC_SETFLOAT(fd, 0); + else if (argv->a_type == A_SYMBOL) + FIELDDESC_SETVAR(fd, argv->a_w.w_symbol, A_FLOAT); + else FIELDDESC_SETFLOAT(fd, argv->a_w.w_float); +} + +static void fielddesc_setarrayarg(t_fielddesc *fd, int argc, t_atom *argv) +{ + if (argc <= 0) FIELDDESC_SETFLOAT(fd, 0); + else if (argv->a_type == A_SYMBOL) + FIELDDESC_SETVAR(fd, argv->a_w.w_symbol, A_ARRAY); + else FIELDDESC_SETFLOAT(fd, argv->a_w.w_float); +} + +static t_float fielddesc_getfloat(t_fielddesc *f, t_template *template, + t_word *wp, int loud) +{ + if (f->fd_type == A_FLOAT) + { + if (f->fd_var) + return (template_getfloat(template, f->fd_un.fd_varsym, wp, loud)); + else return (f->fd_un.fd_float); + } + else + { + if (loud) + error("symbolic data field used as number"); + return (0); + } +} + +static t_symbol *fielddesc_getsymbol(t_fielddesc *f, t_template *template, + t_word *wp, int loud) +{ + if (f->fd_type == A_SYMBOL) + { + if (f->fd_var) + return(template_getsymbol(template, f->fd_un.fd_varsym, wp, loud)); + else return (f->fd_un.fd_symbol); + } + else + { + if (loud) + error("numeric data field used as symbol"); + return (&s_); + } +} + +/* ---------------- curves and polygons (joined segments) ---------------- */ + +/* +curves belong to templates and describe how the data in the template are to +be drawn. The coordinates of the curve (and other display features) can +be attached to fields in the template. +*/ + +t_class *curve_class; + +typedef struct _curve +{ + t_object x_obj; + int x_flags; /* CLOSED and/or BEZ */ + t_fielddesc x_fillcolor; + t_fielddesc x_outlinecolor; + t_fielddesc x_width; + int x_npoints; + t_fielddesc *x_vec; +} t_curve; + +static void *curve_new(t_symbol *classsym, t_int argc, t_atom *argv) +{ + t_curve *x = (t_curve *)pd_new(curve_class); + char *classname = classsym->s_name; + int flags = 0; + int nxy, i; + t_fielddesc *fd; + if (classname[0] == 'f') + { + classname += 6; + flags |= CLOSED; + if (argc) fielddesc_setfloatarg(&x->x_fillcolor, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0); + } + else classname += 4; + if (classname[0] == 'c') flags |= BEZ; + x->x_flags = flags; + if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0); + if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_width, 1); + if (argc < 0) argc = 0; + nxy = (argc + (argc & 1)); + x->x_npoints = (nxy>>1); + x->x_vec = (t_fielddesc *)t_getbytes(nxy * sizeof(t_fielddesc)); + for (i = 0, fd = x->x_vec; i < argc; i++, fd++, argv++) + fielddesc_setfloatarg(fd, 1, argv); + if (argc & 1) FIELDDESC_SETFLOAT(fd, 0); + + return (x); +} + +/* -------------------- widget behavior for curve ------------ */ + +static void curve_getrect(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_curve *x = (t_curve *)z; + int i, n = x->x_npoints; + t_fielddesc *f = x->x_vec; + int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff; + for (i = 0, f = x->x_vec; i < n; i++, f += 2) + { + int xloc = glist_xtopixels(glist, + basex + fielddesc_getfloat(f, template, data, 0)); + int yloc = glist_ytopixels(glist, + basey + fielddesc_getfloat(f+1, template, data, 0)); + if (xloc < x1) x1 = xloc; + if (xloc > x2) x2 = xloc; + if (yloc < y1) y1 = yloc; + if (yloc > y2) y2 = yloc; + } + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void curve_displace(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int dx, int dy) +{ + /* refuse */ +} + +static void curve_select(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int state) +{ + /* fill in later */ +} + +static void curve_activate(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int state) +{ + /* fill in later */ +} + +static int rangecolor(int n) /* 0 to 9 in 5 steps */ +{ + int n2 = n/2; /* 0 to 4 */ + int ret = (n2 << 6); /* 0 to 256 in 5 steps */ + if (ret > 255) ret = 255; + return (ret); +} + +static void numbertocolor(int n, char *s) +{ + int red, blue, green; + if (n < 0) n = 0; + red = n / 100; + blue = ((n / 10) % 10); + green = n % 10; + sprintf(s, "#%2.2x%2.2x%2.2x", rangecolor(red), rangecolor(blue), + rangecolor(green)); +} + +static void curve_vis(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int vis) +{ + t_curve *x = (t_curve *)z; + int i, n = x->x_npoints; + t_fielddesc *f = x->x_vec; + + if (vis) + { + if (n > 1) + { + int flags = x->x_flags, closed = (flags & CLOSED); + float width = fielddesc_getfloat(&x->x_width, template, data, 1); + char outline[20], fill[20]; + if (width < 1) width = 1; + numbertocolor( + fielddesc_getfloat(&x->x_outlinecolor, template, data, 1), + outline); + if (flags & CLOSED) + { + numbertocolor( + fielddesc_getfloat(&x->x_fillcolor, template, data, 1), + fill); + sys_vgui(".x%x.c create polygon\\\n", + glist_getcanvas(glist)); + } + else sys_vgui(".x%x.c create line\\\n", + glist_getcanvas(glist)); + for (i = 0, f = x->x_vec; i < n; i++, f += 2) + { + float xloc = glist_xtopixels(glist, + basex + fielddesc_getfloat(f, template, data, 1)); + float yloc = glist_ytopixels(glist, + basey + fielddesc_getfloat(f+1, template, data, 1)); + sys_vgui("%d %d\\\n", (int)xloc, (int)yloc); + } + sys_vgui("-width %f\\\n", + fielddesc_getfloat(&x->x_width, template, data, 1)); + if (flags & CLOSED) sys_vgui("-fill %s -outline %s\\\n", + fill, outline); + else sys_vgui("-fill %s\\\n", outline); + if (flags & BEZ) sys_vgui("-smooth 1\\\n"); + sys_vgui("-tags curve%x\n", data); + } + else post("warning: curves need at least two points to be graphed"); + } + else + { + if (n > 1) sys_vgui(".x%x.c delete curve%x\n", + glist_getcanvas(glist), data); + } +} + +static int curve_motion_field; +static float curve_motion_xcumulative; +static float curve_motion_xbase; +static float curve_motion_xper; +static float curve_motion_ycumulative; +static float curve_motion_ybase; +static float curve_motion_yper; +static t_glist *curve_motion_glist; +static t_gobj *curve_motion_gobj; +static t_word *curve_motion_wp; +static t_template *curve_motion_template; + + /* LATER protect against the template changing or the scalar disappearing + probably by attaching a gpointer here ... */ + +static void curve_motion(void *z, t_floatarg dx, t_floatarg dy) +{ + t_curve *x = (t_curve *)z; + t_fielddesc *f = x->x_vec + curve_motion_field; + curve_motion_xcumulative += dx; + curve_motion_ycumulative += dy; + if (f->fd_var) + { + template_setfloat(curve_motion_template, + f->fd_un.fd_varsym, + curve_motion_wp, + curve_motion_xbase + curve_motion_xcumulative * curve_motion_xper, + 1); + } + if ((f+1)->fd_var) + { + template_setfloat(curve_motion_template, + (f+1)->fd_un.fd_varsym, + curve_motion_wp, + curve_motion_ybase + curve_motion_ycumulative * curve_motion_yper, + 1); + } + glist_redrawitem(curve_motion_glist, curve_motion_gobj); +} + +static int curve_click(t_gobj *z, t_glist *glist, + t_scalar *sc, t_template *template, float basex, float basey, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_curve *x = (t_curve *)z; + int i, n = x->x_npoints; + int bestn = -1; + int besterror = 0x7fffffff; + t_fielddesc *f = x->x_vec; + t_word *data = sc->sc_vec; + for (i = 0, f = x->x_vec; i < n; i++, f += 2) + { + int xloc = glist_xtopixels(glist, + basex + fielddesc_getfloat(f, template, data, 0)); + int yloc = glist_ytopixels(glist, + basey + fielddesc_getfloat(f+1, template, data, 0)); + int xerr = xloc - xpix, yerr = yloc - ypix; + if (!f->fd_var && !(f+1)->fd_var) + continue; + if (xerr < 0) + xerr = -xerr; + if (yerr < 0) + yerr = -yerr; + if (yerr > xerr) + xerr = yerr; + if (xerr < besterror) + { + besterror = xerr; + bestn = i; + curve_motion_xbase = fielddesc_getfloat(f, template, data, 0); + curve_motion_ybase = fielddesc_getfloat(f+1, template, data, 0); + } + } + if (besterror > 10) + return (0); + if (doit) + { + curve_motion_xper = glist_pixelstox(glist, 1) + - glist_pixelstox(glist, 0); + curve_motion_yper = glist_pixelstoy(glist, 1) + - glist_pixelstoy(glist, 0); + curve_motion_xcumulative = curve_motion_ycumulative = 0; + curve_motion_glist = glist; + curve_motion_gobj = &sc->sc_gobj; + curve_motion_wp = data; + curve_motion_field = 2*bestn; + curve_motion_template = template; + glist_grab(glist, z, curve_motion, 0, xpix, ypix); + } + return (1); +} + +t_parentwidgetbehavior curve_widgetbehavior = +{ + curve_getrect, + curve_displace, + curve_select, + curve_activate, + curve_vis, + curve_click, +}; + +static void curve_free(t_curve *x) +{ + t_freebytes(x->x_vec, 2 * x->x_npoints * sizeof(*x->x_vec)); +} + +static void curve_setup(void) +{ + curve_class = class_new(gensym("drawpolygon"), (t_newmethod)curve_new, + (t_method)curve_free, sizeof(t_curve), CLASS_NOINLET, A_GIMME, 0); + class_setdrawcommand(curve_class); + class_addcreator((t_newmethod)curve_new, gensym("drawcurve"), + A_GIMME, 0); + class_addcreator((t_newmethod)curve_new, gensym("filledpolygon"), + A_GIMME, 0); + class_addcreator((t_newmethod)curve_new, gensym("filledcurve"), + A_GIMME, 0); + class_setparentwidget(curve_class, &curve_widgetbehavior); +} + +/* --------- plots for showing arrays --------------- */ + +t_class *plot_class; + +typedef struct _plot +{ + t_object x_obj; + int x_flags; + t_fielddesc x_outlinecolor; + t_fielddesc x_width; + t_fielddesc x_xloc; + t_fielddesc x_yloc; + t_fielddesc x_xinc; + t_fielddesc x_data; +} t_plot; + +static void *plot_new(t_symbol *classsym, t_int argc, t_atom *argv) +{ + t_plot *x = (t_plot *)pd_new(plot_class); + int flags = 0; + int nxy, i; + t_fielddesc *fd; + t_symbol *firstarg = atom_getsymbolarg(0, argc, argv); + if (!strcmp(firstarg->s_name, "curve")) + { + flags |= BEZ; + argc--, argv++; + } + if (argc) fielddesc_setarrayarg(&x->x_data, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_data, 1); + if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0); + if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_width, 1); + if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_xloc, 1); + if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_yloc, 1); + if (argc) fielddesc_setfloatarg(&x->x_xinc, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_xinc, 1); + x->x_flags = flags; + return (x); +} + +/* -------------------- widget behavior for plot ------------ */ + + + /* get everything we'll need from the owner template of the array being + plotted. Not used for garrays, but see below */ +static int plot_readownertemplate(t_plot *x, + t_word *data, t_template *ownertemplate, + t_symbol **elemtemplatesymp, t_array **arrayp, + float *linewidthp, float *xlocp, float *xincp, float *ylocp) +{ + int arrayonset, type; + t_symbol *elemtemplatesym; + t_array *array; + + /* find the data and verify it's an array */ + if (x->x_data.fd_type != A_ARRAY || !x->x_data.fd_var) + { + error("plot: needs an array field"); + return (-1); + } + if (!template_find_field(ownertemplate, x->x_data.fd_un.fd_varsym, + &arrayonset, &type, &elemtemplatesym)) + { + error("plot: %s: no such field", x->x_data.fd_un.fd_varsym->s_name); + return (-1); + } + if (type != DT_ARRAY) + { + error("plot: %s: not an array", x->x_data.fd_un.fd_varsym->s_name); + return (-1); + } + array = *(t_array **)(((char *)data) + arrayonset); + *linewidthp = fielddesc_getfloat(&x->x_width, ownertemplate, data, 1); + *xlocp = fielddesc_getfloat(&x->x_xloc, ownertemplate, data, 1); + *xincp = fielddesc_getfloat(&x->x_xinc, ownertemplate, data, 1); + *ylocp = fielddesc_getfloat(&x->x_yloc, ownertemplate, data, 1); + *elemtemplatesymp = elemtemplatesym; + *arrayp = array; + return (0); +} + + /* get everything else you could possibly need about a plot, + either for plot's own purposes or for plotting a "garray" */ +int array_getfields(t_symbol *elemtemplatesym, + t_canvas **elemtemplatecanvasp, + t_template **elemtemplatep, int *elemsizep, + int *xonsetp, int *yonsetp, int *wonsetp) +{ + int arrayonset, elemsize, yonset, wonset, xonset, type; + t_template *elemtemplate; + t_symbol *dummy; + t_canvas *elemtemplatecanvas = 0; + + /* the "float" template is special in not having to have a canvas; + template_findbyname is hardwired to return a predefined + template. */ + + if (!(elemtemplate = template_findbyname(elemtemplatesym))) + { + error("plot: %s: no such template", elemtemplatesym->s_name); + return (-1); + } + if (!((elemtemplatesym == &s_float) || + (elemtemplatecanvas = template_findcanvas(elemtemplate)))) + { + error("plot: %s: no canvas for this template", elemtemplatesym->s_name); + return (-1); + } + elemsize = elemtemplate->t_n * sizeof(t_word); + if (!template_find_field(elemtemplate, gensym("y"), &yonset, &type, &dummy) + || type != DT_FLOAT) + yonset = -1; + if (!template_find_field(elemtemplate, gensym("x"), &xonset, &type, &dummy) + || type != DT_FLOAT) + xonset = -1; + if (!template_find_field(elemtemplate, gensym("w"), &wonset, &type, &dummy) + || type != DT_FLOAT) + wonset = -1; + + /* fill in slots for return values */ + *elemtemplatecanvasp = elemtemplatecanvas; + *elemtemplatep = elemtemplate; + *elemsizep = elemsize; + *xonsetp = xonset; + *yonsetp = yonset; + *wonsetp = wonset; + return (0); +} + +static void plot_getrect(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_plot *x = (t_plot *)z; + int elemsize, yonset, wonset, xonset; + t_canvas *elemtemplatecanvas; + t_template *elemtemplate; + t_symbol *elemtemplatesym; + float linewidth, xloc, xinc, yloc; + t_array *array; + float x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff; + int i; + float xpix, ypix, wpix; + + if (!plot_readownertemplate(x, data, template, + &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc) && + !array_getfields(elemtemplatesym, &elemtemplatecanvas, + &elemtemplate, &elemsize, &xonset, &yonset, &wonset)) + { + for (i = 0; i < array->a_n; i++) + { + array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, + xonset, yonset, wonset, i, basex + xloc, basey + yloc, xinc, + &xpix, &ypix, &wpix); + if (xpix < x1) + x1 = xpix; + if (xpix > x2) + x2 = xpix; + if (ypix - wpix < y1) + y1 = ypix - wpix; + if (ypix + wpix > y2) + y2 = ypix + wpix; + } + } + + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void plot_displace(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int dx, int dy) +{ + /* not yet */ +} + +static void plot_select(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int state) +{ + /* not yet */ +} + +static void plot_activate(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int state) +{ + /* not yet */ +} + +static void plot_vis(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int vis) +{ + t_plot *x = (t_plot *)z; + int elemsize, yonset, wonset, xonset; + t_canvas *elemtemplatecanvas; + t_template *elemtemplate; + t_symbol *elemtemplatesym; + float linewidth, xloc, xinc, yloc; + t_array *array; + int nelem; + char *elem; + if (plot_readownertemplate(x, data, template, + &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc) || + array_getfields(elemtemplatesym, &elemtemplatecanvas, + &elemtemplate, &elemsize, &xonset, &yonset, &wonset)) + return; + nelem = array->a_n; + elem = (char *)array->a_vec; + if (vis) + { + char outline[20]; + int lastpixel = -1, ndrawn = 0; + float xsum, yval = 0, wval = 0, xpix; + int ixpix = 0, i; + + /* draw the trace */ + numbertocolor(fielddesc_getfloat(&x->x_outlinecolor, template, data, 1), + outline); + if (wonset >= 0) + { + /* found "w" field which controls linewidth. The trace is + a filled polygon with 2n points. */ + sys_vgui(".x%x.c create polygon \\\n", + glist_getcanvas(glist)); + + for (i = 0, xsum = xloc; i < nelem; i++) + { + float usexloc; + if (xonset >= 0) + usexloc = xloc + *(float *)((elem + elemsize * i) + xonset); + else usexloc = xsum, xsum += xinc; + if (yonset >= 0) + yval = *(float *)((elem + elemsize * i) + yonset); + else yval = 0; + wval = *(float *)((elem + elemsize * i) + wonset); + xpix = glist_xtopixels(glist, basex + usexloc); + ixpix = xpix + 0.5; + if (xonset >= 0 || ixpix != lastpixel) + { + sys_vgui("%d %f \\\n", ixpix, + glist_ytopixels(glist, + basey + yloc + yval - wval)); + ndrawn++; + } + lastpixel = ixpix; + if (ndrawn >= 1000) goto ouch; + } + lastpixel = -1; + for (i = nelem-1; i >= 0; i--) + { + float usexloc; + if (xonset >= 0) + usexloc = xloc + *(float *)((elem + elemsize * i) + xonset); + else xsum -= xinc, usexloc = xsum; + if (yonset >= 0) + yval = *(float *)((elem + elemsize * i) + yonset); + else yval = 0; + wval = *(float *)((elem + elemsize * i) + wonset); + xpix = glist_xtopixels(glist, basex + usexloc); + ixpix = xpix + 0.5; + if (xonset >= 0 || ixpix != lastpixel) + { + sys_vgui("%d %f \\\n", ixpix, glist_ytopixels(glist, + basey + yloc + yval + wval)); + ndrawn++; + } + lastpixel = ixpix; + if (ndrawn >= 1000) goto ouch; + } + /* TK will complain if there aren't at least 3 points. There + should be at least two already. */ + if (ndrawn < 4) + { + sys_vgui("%d %f \\\n", ixpix + 10, glist_ytopixels(glist, + basey + yloc + yval + wval)); + sys_vgui("%d %f \\\n", ixpix + 10, glist_ytopixels(glist, + basey + yloc + yval - wval)); + } + ouch: + sys_vgui(" -width 1 -fill %s -outline %s\\\n", outline, outline); + if (x->x_flags & BEZ) sys_vgui("-smooth 1\\\n"); + + sys_vgui("-tags plot%x\n", data); + } + else if (linewidth > 0) + { + /* no "w" field. If the linewidth is positive, draw a + segmented line with the requested width; otherwise don't + draw the trace at all. */ + sys_vgui(".x%x.c create line \\\n", glist_getcanvas(glist)); + + for (xsum = xloc, i = 0; i < nelem; i++) + { + float usexloc; + if (xonset >= 0) + usexloc = xloc + *(float *)((elem + elemsize * i) + xonset); + else usexloc = xsum, xsum += xinc; + if (yonset >= 0) + yval = *(float *)((elem + elemsize * i) + yonset); + else yval = 0; + xpix = glist_xtopixels(glist, basex + usexloc); + ixpix = xpix + 0.5; + if (xonset >= 0 || ixpix != lastpixel) + { + sys_vgui("%d %f \\\n", ixpix, + glist_ytopixels(glist, basey + yloc + yval)); + ndrawn++; + } + lastpixel = ixpix; + if (ndrawn >= 1000) break; + } + /* TK will complain if there aren't at least 2 points... */ + if (ndrawn == 0) sys_vgui("0 0 0 0 \\\n"); + else if (ndrawn == 1) sys_vgui("%d %f \\\n", ixpix + 10, + glist_ytopixels(glist, basey + yloc + yval)); + + sys_vgui("-width %f\\\n", linewidth); + sys_vgui("-fill %s\\\n", outline); + if (x->x_flags & BEZ) sys_vgui("-smooth 1\\\n"); + + sys_vgui("-tags plot%x\n", data); + } + /* We're done with the outline; now draw all the points. + This code is inefficient since the template has to be + searched for drawing instructions for every last point. */ + + for (xsum = xloc, i = 0; i < nelem; i++) + { + float usexloc, useyloc; + t_gobj *y; + if (xonset >= 0) + usexloc = basex + xloc + + *(float *)((elem + elemsize * i) + xonset); + else usexloc = basex + xsum, xsum += xinc; + if (yonset >= 0) + yval = *(float *)((elem + elemsize * i) + yonset); + else yval = 0; + useyloc = basey + yloc + yval; + for (y = elemtemplatecanvas->gl_list; y; y = y->g_next) + { + t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); + if (!wb) continue; + (*wb->w_parentvisfn)(y, glist, + (t_word *)(elem + elemsize * i), + elemtemplate, usexloc, useyloc, vis); + } + } + } + else + { + /* un-draw the individual points */ + int i; + for (i = 0; i < nelem; i++) + { + t_gobj *y; + for (y = elemtemplatecanvas->gl_list; y; y = y->g_next) + { + t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); + if (!wb) continue; + (*wb->w_parentvisfn)(y, glist, + (t_word *)(elem + elemsize * i), elemtemplate, + 0, 0, 0); + } + } + /* and then the trace */ + sys_vgui(".x%x.c delete plot%x\n", + glist_getcanvas(glist), data); + } +} + + +static int plot_click(t_gobj *z, t_glist *glist, + t_scalar *sc, t_template *template, float basex, float basey, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_plot *x = (t_plot *)z; + t_symbol *elemtemplatesym; + float linewidth, xloc, xinc, yloc; + t_array *array; + t_word *data = sc->sc_vec; + + if (!plot_readownertemplate(x, data, template, + &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc)) + { + return (array_doclick(array, glist, &sc->sc_gobj, + elemtemplatesym, + linewidth, basex + xloc, xinc, basey + yloc, + xpix, ypix, shift, alt, dbl, doit)); + } + else return (0); +} + +t_parentwidgetbehavior plot_widgetbehavior = +{ + plot_getrect, + plot_displace, + plot_select, + plot_activate, + plot_vis, + plot_click, +}; + +static void plot_setup(void) +{ + plot_class = class_new(gensym("plot"), (t_newmethod)plot_new, 0, + sizeof(t_plot), CLASS_NOINLET, A_GIMME, 0); + class_setdrawcommand(plot_class); + class_setparentwidget(plot_class, &plot_widgetbehavior); +} + +/* ---------------- drawnumber: draw a number ---------------- */ + +/* + drawnumbers draw numeric fields at controllable locations, with + controllable color and label . + invocation: (drawnumber|drawsymbol) variable x y color label +*/ + +t_class *drawnumber_class; + +#define DRAW_SYMBOL 1 + +typedef struct _drawnumber +{ + t_object x_obj; + t_fielddesc x_value; + t_fielddesc x_xloc; + t_fielddesc x_yloc; + t_fielddesc x_color; + t_symbol *x_label; + int x_flags; +} t_drawnumber; + +static void *drawnumber_new(t_symbol *classsym, t_int argc, t_atom *argv) +{ + t_drawnumber *x = (t_drawnumber *)pd_new(drawnumber_class); + char *classname = classsym->s_name; + int flags = 0; + if (classname[4] == 's') + flags |= DRAW_SYMBOL; + x->x_flags = flags; + if (argc) fielddesc_setfloatarg(&x->x_value, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_value, 0); + if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_xloc, 0); + if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_yloc, 0); + if (argc) fielddesc_setfloatarg(&x->x_color, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_color, 1); + if (argc) + x->x_label = atom_getsymbolarg(0, argc, argv); + else x->x_label = &s_; + + return (x); +} + +/* -------------------- widget behavior for drawnumber ------------ */ + +#define DRAWNUMBER_BUFSIZE 80 +static void drawnumber_sprintf(t_drawnumber *x, char *buf, t_atom *ap) +{ + int nchars; + strncpy(buf, x->x_label->s_name, DRAWNUMBER_BUFSIZE); + buf[DRAWNUMBER_BUFSIZE - 1] = 0; + nchars = strlen(buf); + atom_string(ap, buf + nchars, DRAWNUMBER_BUFSIZE - nchars); +} + +static void drawnumber_getrect(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_drawnumber *x = (t_drawnumber *)z; + t_atom at; + int xloc = glist_xtopixels(glist, + basex + fielddesc_getfloat(&x->x_xloc, template, data, 0)); + int yloc = glist_ytopixels(glist, + basey + fielddesc_getfloat(&x->x_yloc, template, data, 0)); + int font = glist_getfont(glist); + int fontwidth = sys_fontwidth(font), fontheight = sys_fontheight(font); + char buf[DRAWNUMBER_BUFSIZE]; + if (x->x_flags & DRAW_SYMBOL) + SETSYMBOL(&at, fielddesc_getsymbol(&x->x_value, template, data, 0)); + else SETFLOAT(&at, fielddesc_getfloat(&x->x_value, template, data, 0)); + drawnumber_sprintf(x, buf, &at); + *xp1 = xloc; + *yp1 = yloc; + *xp2 = xloc + fontwidth * strlen(buf); + *yp2 = yloc + fontheight; +} + +static void drawnumber_displace(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int dx, int dy) +{ + /* refuse */ +} + +static void drawnumber_select(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int state) +{ + post("drawnumber_select %d", state); + /* fill in later */ +} + +static void drawnumber_activate(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int state) +{ + post("drawnumber_activate %d", state); +} + +static void drawnumber_vis(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int vis) +{ + t_drawnumber *x = (t_drawnumber *)z; + + if (vis) + { + t_atom at; + int xloc = glist_xtopixels(glist, + basex + fielddesc_getfloat(&x->x_xloc, template, data, 0)); + int yloc = glist_ytopixels(glist, + basey + fielddesc_getfloat(&x->x_yloc, template, data, 0)); + char colorstring[20], buf[DRAWNUMBER_BUFSIZE]; + numbertocolor(fielddesc_getfloat(&x->x_color, template, data, 1), + colorstring); + if (x->x_flags & DRAW_SYMBOL) + SETSYMBOL(&at, fielddesc_getsymbol(&x->x_value, template, data, 0)); + else SETFLOAT(&at, fielddesc_getfloat(&x->x_value, template, data, 0)); + drawnumber_sprintf(x, buf, &at); + sys_vgui(".x%x.c create text %d %d -anchor nw -fill %s -text {%s}", + glist_getcanvas(glist), xloc, yloc, colorstring, buf); + sys_vgui(" -font -*-courier-bold--normal--%d-*", + sys_hostfontsize(glist_getfont(glist))); + sys_vgui(" -tags drawnumber%x\n", data); + } + else sys_vgui(".x%x.c delete drawnumber%x\n", glist_getcanvas(glist), data); +} + +static float drawnumber_motion_ycumulative; +static t_glist *drawnumber_motion_glist; +static t_gobj *drawnumber_motion_gobj; +static t_word *drawnumber_motion_wp; +static t_template *drawnumber_motion_template; + + /* LATER protect against the template changing or the scalar disappearing + probably by attaching a gpointer here ... */ + +static void drawnumber_motion(void *z, t_floatarg dx, t_floatarg dy) +{ + t_drawnumber *x = (t_drawnumber *)z; + t_fielddesc *f = &x->x_value; + drawnumber_motion_ycumulative -= dy; + template_setfloat(drawnumber_motion_template, + f->fd_un.fd_varsym, + drawnumber_motion_wp, + drawnumber_motion_ycumulative, + 1); + glist_redrawitem(drawnumber_motion_glist, drawnumber_motion_gobj); +} + +static int drawnumber_click(t_gobj *z, t_glist *glist, + t_scalar *sc, t_template *template, float basex, float basey, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_drawnumber *x = (t_drawnumber *)z; + int x1, y1, x2, y2; + t_word *data = sc->sc_vec; + drawnumber_getrect(z, glist, + sc->sc_vec, template, basex, basey, + &x1, &y1, &x2, &y2); + if (xpix >= x1 && xpix <= x2 && ypix >= y1 && ypix <= y2 + && x->x_value.fd_var) + { + if (doit) + { + drawnumber_motion_glist = glist; + drawnumber_motion_gobj = &sc->sc_gobj; + drawnumber_motion_wp = data; + drawnumber_motion_template = template; + drawnumber_motion_ycumulative = + fielddesc_getfloat(&x->x_value, template, data, 0); + glist_grab(glist, z, drawnumber_motion, 0, xpix, ypix); + } + return (1); + } + else return (0); +} + +t_parentwidgetbehavior drawnumber_widgetbehavior = +{ + drawnumber_getrect, + drawnumber_displace, + drawnumber_select, + drawnumber_activate, + drawnumber_vis, + drawnumber_click, +}; + +static void drawnumber_free(t_drawnumber *x) +{ +} + +static void drawnumber_setup(void) +{ + drawnumber_class = class_new(gensym("drawnumber"), + (t_newmethod)drawnumber_new, (t_method)drawnumber_free, + sizeof(t_drawnumber), CLASS_NOINLET, A_GIMME, 0); + class_setdrawcommand(drawnumber_class); + class_addcreator((t_newmethod)drawnumber_new, gensym("drawsymbol"), + A_GIMME, 0); + class_setparentwidget(drawnumber_class, &drawnumber_widgetbehavior); +} + +/* ---------------------- setup function ---------------------------- */ + +void g_template_setup(void) +{ + template_setup(); + gtemplate_setup(); + template_float.t_pdobj = template_class; + curve_setup(); + plot_setup(); + drawnumber_setup(); +} + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include +#include +#include + +#include "m_pd.h" +#include "s_stuff.h" /* for sys_hostfontsize */ +#include "g_canvas.h" + +/* +This file contains text objects you would put in a canvas to define a +template. Templates describe objects of type "array" (g_array.c) and +"scalar" (g_scalar.c). +*/ + +/* T.Grill - changed the _template.t_pd member to t_pdobj to avoid name clashes +with the t_pd type */ + + /* the structure of a "struct" object (also the obsolete "gtemplate" + you get when using the name "template" in a box.) */ + +struct _gtemplate +{ + t_object x_obj; + t_template *x_template; + t_canvas *x_owner; + t_symbol *x_sym; + struct _gtemplate *x_next; + int x_argc; + t_atom *x_argv; +}; + +/* ---------------- forward definitions ---------------- */ + +static void template_conformarray(t_template *tfrom, t_template *tto, + int *conformaction, t_array *a); +static void template_conformglist(t_template *tfrom, t_template *tto, + t_glist *glist, int *conformaction); + +/* ---------------------- storage ------------------------- */ + +static t_class *gtemplate_class; +static t_class *template_class; + +/* there's a pre-defined "float" template. LATER should we bind this +to a symbol such as "pd-float"??? */ + +static t_dataslot template_float_vec = +{ + DT_FLOAT, + &s_y, + &s_ +}; + +static t_template template_float = +{ + 0, /* class -- fill in in setup routine */ + 0, /* list of "struct"/t_gtemplate objects */ + &s_float, /* name */ + 1, /* number of items */ + &template_float_vec +}; + + /* return true if two dataslot definitions match */ +static int dataslot_matches(t_dataslot *ds1, t_dataslot *ds2, + int nametoo) +{ + return ((!nametoo || ds1->ds_name == ds2->ds_name) && + ds1->ds_type == ds2->ds_type && + (ds1->ds_type != DT_ARRAY || + ds1->ds_arraytemplate == ds2->ds_arraytemplate)); +} + +/* -- templates, the active ingredient in gtemplates defined below. ------- */ + +t_template *template_new(t_symbol *templatesym, int argc, t_atom *argv) +{ + t_template *x = (t_template *)pd_new(template_class); + x->t_n = 0; + x->t_vec = (t_dataslot *)t_getbytes(0); + while (argc > 0) + { + int newtype, oldn, newn; + t_symbol *newname, *newarraytemplate = &s_, *newtypesym; + if (argc < 2 || argv[0].a_type != A_SYMBOL || + argv[1].a_type != A_SYMBOL) + goto bad; + newtypesym = argv[0].a_w.w_symbol; + newname = argv[1].a_w.w_symbol; + if (newtypesym == &s_float) + newtype = DT_FLOAT; + else if (newtypesym == &s_symbol) + newtype = DT_SYMBOL; + else if (newtypesym == &s_list) + newtype = DT_LIST; + else if (newtypesym == gensym("array")) + { + if (argc < 3 || argv[2].a_type != A_SYMBOL) + { + pd_error(x, "array lacks element template or name"); + goto bad; + } + newarraytemplate = canvas_makebindsym(argv[2].a_w.w_symbol); + newtype = DT_ARRAY; + argc--; + argv++; + } + else + { + pd_error(x, "%s: no such type", newtypesym->s_name); + return (0); + } + newn = (oldn = x->t_n) + 1; + x->t_vec = (t_dataslot *)t_resizebytes(x->t_vec, + oldn * sizeof(*x->t_vec), newn * sizeof(*x->t_vec)); + x->t_n = newn; + x->t_vec[oldn].ds_type = newtype; + x->t_vec[oldn].ds_name = newname; + x->t_vec[oldn].ds_arraytemplate = newarraytemplate; + bad: + argc -= 2; argv += 2; + } + if (templatesym->s_name) + { + x->t_sym = templatesym; + pd_bind(&x->t_pdobj, x->t_sym); + } + else x->t_sym = templatesym; + return (x); +} + +int template_size(t_template *x) +{ + return (x->t_n * sizeof(t_word)); +} + +int template_find_field(t_template *x, t_symbol *name, int *p_onset, + int *p_type, t_symbol **p_arraytype) +{ + t_template *t; + int i, n; + if (!x) + { + bug("template_find_field"); + return (0); + } + n = x->t_n; + for (i = 0; i < n; i++) + if (x->t_vec[i].ds_name == name) + { + *p_onset = i * sizeof(t_word); + *p_type = x->t_vec[i].ds_type; + *p_arraytype = x->t_vec[i].ds_arraytemplate; + return (1); + } + return (0); +} + +t_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp, + int loud) +{ + int onset, type; + t_symbol *arraytype; + t_sample val = 0; + if (template_find_field(x, fieldname, &onset, &type, &arraytype)) + { + if (type == DT_FLOAT) + val = *(t_sample *)(((char *)wp) + onset); + else if (loud) error("%s.%s: not a number", + x->t_sym->s_name, fieldname->s_name); + } + else if (loud) error("%s.%s: no such field", + x->t_sym->s_name, fieldname->s_name); + return (fixtof(val)); +} + +void template_setfloat(t_template *x, t_symbol *fieldname, t_word *wp, + t_float f, int loud) +{ + int onset, type; + t_symbol *arraytype; + if (template_find_field(x, fieldname, &onset, &type, &arraytype)) + { + if (type == DT_FLOAT) + *(t_sample *)(((char *)wp) + onset) = ftofix(f); + else if (loud) error("%s.%s: not a number", + x->t_sym->s_name, fieldname->s_name); + } + else if (loud) error("%s.%s: no such field", + x->t_sym->s_name, fieldname->s_name); +} + +t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, t_word *wp, + int loud) +{ + int onset, type; + t_symbol *arraytype; + t_symbol *val = &s_; + if (template_find_field(x, fieldname, &onset, &type, &arraytype)) + { + if (type == DT_SYMBOL) + val = *(t_symbol **)(((char *)wp) + onset); + else if (loud) error("%s.%s: not a symbol", + x->t_sym->s_name, fieldname->s_name); + } + else if (loud) error("%s.%s: no such field", + x->t_sym->s_name, fieldname->s_name); + return (val); +} + +void template_setsymbol(t_template *x, t_symbol *fieldname, t_word *wp, + t_symbol *s, int loud) +{ + int onset, type; + t_symbol *arraytype; + if (template_find_field(x, fieldname, &onset, &type, &arraytype)) + { + if (type == DT_SYMBOL) + *(t_symbol **)(((char *)wp) + onset) = s; + else if (loud) error("%s.%s: not a symbol", + x->t_sym->s_name, fieldname->s_name); + } + else if (loud) error("%s.%s: no such field", + x->t_sym->s_name, fieldname->s_name); +} + + /* stringent check to see if a "saved" template, x2, matches the current + one (x1). It's OK if x1 has additional scalar elements but not (yet) + arrays or lists. This is used for reading in "data files". */ +int template_match(t_template *x1, t_template *x2) +{ + int i; + if (x1->t_n < x2->t_n) + return (0); + for (i = x2->t_n; i < x1->t_n; i++) + { + if (x1->t_vec[i].ds_type == DT_ARRAY || + x1->t_vec[i].ds_type == DT_LIST) + return (0); + } + if (x2->t_n > x1->t_n) + post("add elements..."); + for (i = 0; i < x2->t_n; i++) + if (!dataslot_matches(&x1->t_vec[i], &x2->t_vec[i], 1)) + return (0); + return (1); +} + +/* --------------- CONFORMING TO CHANGES IN A TEMPLATE ------------ */ + +/* the following routines handle updating scalars to agree with changes +in their template. The old template is assumed to be the "installed" one +so we can delete old items; but making new ones we have to avoid scalar_new +which would make an old one whereas we will want a new one (but whose array +elements might still be old ones. + LATER deal with graphics updates too... */ + + /* conform the word vector of a scalar to the new template */ +static void template_conformwords(t_template *tfrom, t_template *tto, + int *conformaction, t_word *wfrom, t_word *wto) +{ + int nfrom = tfrom->t_n, nto = tto->t_n, i; + for (i = 0; i < nto; i++) + { + if (conformaction[i] >= 0) + { + /* we swap the two, in case it's an array or list, so that + when "wfrom" is deleted the old one gets cleaned up. */ + t_word wwas = wto[i]; + wto[i] = wfrom[conformaction[i]]; + wfrom[conformaction[i]] = wwas; + } + } +} + + /* conform a scalar, recursively conforming sublists and arrays */ +static t_scalar *template_conformscalar(t_template *tfrom, t_template *tto, + int *conformaction, t_glist *glist, t_scalar *scfrom) +{ + t_scalar *x; + t_gpointer gp; + int nto = tto->t_n, nfrom = tfrom->t_n, i; + t_template *scalartemplate; + /* post("conform scalar"); */ + /* possibly replace the scalar */ + if (scfrom->sc_template == tfrom->t_sym) + { + /* see scalar_new() for comment about the gpointer. */ + gpointer_init(&gp); + x = (t_scalar *)getbytes(sizeof(t_scalar) + + (tto->t_n - 1) * sizeof(*x->sc_vec)); + x->sc_gobj.g_pd = scalar_class; + x->sc_template = tfrom->t_sym; + gpointer_setglist(&gp, glist, x); + /* Here we initialize to the new template, but array and list + elements will still belong to old template. */ + word_init(x->sc_vec, tto, &gp); + + template_conformwords(tfrom, tto, conformaction, + scfrom->sc_vec, x->sc_vec); + + /* replace the old one with the new one in the list */ + if (glist->gl_list == &scfrom->sc_gobj) + { + glist->gl_list = &x->sc_gobj; + x->sc_gobj.g_next = scfrom->sc_gobj.g_next; + } + else + { + t_gobj *y, *y2; + for (y = glist->gl_list; y2 = y->g_next; y = y2) + if (y2 == &scfrom->sc_gobj) + { + x->sc_gobj.g_next = y2->g_next; + y->g_next = &x->sc_gobj; + goto nobug; + } + bug("template_conformscalar"); + nobug: ; + } + /* burn the old one */ + pd_free(&scfrom->sc_gobj.g_pd); + } + else x = scfrom; + scalartemplate = template_findbyname(x->sc_template); + /* convert all array elements and sublists */ + for (i = 0; i < scalartemplate->t_n; i++) + { + t_dataslot *ds = scalartemplate->t_vec + i; + if (ds->ds_type == DT_LIST) + { + t_glist *gl2 = x->sc_vec[i].w_list; + template_conformglist(tfrom, tto, gl2, conformaction); + } + else if (ds->ds_type == DT_ARRAY) + { + template_conformarray(tfrom, tto, conformaction, + x->sc_vec[i].w_array); + } + } + return (x); +} + + /* conform an array, recursively conforming sublists and arrays */ +static void template_conformarray(t_template *tfrom, t_template *tto, + int *conformaction, t_array *a) +{ + int i; + if (a->a_templatesym == tfrom->t_sym) + { + /* the array elements must all be conformed */ + int oldelemsize = sizeof(t_word) * tfrom->t_n, + newelemsize = sizeof(t_word) * tto->t_n; + char *newarray = getbytes(sizeof(t_word) * tto->t_n * a->a_n); + char *oldarray = a->a_vec; + if (a->a_elemsize != oldelemsize) + bug("template_conformarray"); + for (i = 0; i < a->a_n; i++) + { + t_word *wp = (t_word *)(newarray + newelemsize * i); + word_init(wp, tto, &a->a_gp); + template_conformwords(tfrom, tto, conformaction, + (t_word *)(oldarray + oldelemsize * i), wp); + } + } + bug("template_conformarray: this part not written"); + /* go through item by item conforming subarrays and sublists... */ +} + + /* this routine searches for every scalar in the glist that belongs + to the "from" template and makes it belong to the "to" template. Descend + glists recursively. + We don't handle redrawing here; this is to be filled in LATER... */ + +static void template_conformglist(t_template *tfrom, t_template *tto, + t_glist *glist, int *conformaction) +{ + t_gobj *g; + /* post("conform glist %s", glist->gl_name->s_name); */ + for (g = glist->gl_list; g; g = g->g_next) + { + if (pd_class(&g->g_pd) == scalar_class) + g = &template_conformscalar(tfrom, tto, conformaction, + glist, (t_scalar *)g)->sc_gobj; + else if (pd_class(&g->g_pd) == canvas_class) + template_conformglist(tfrom, tto, (t_glist *)g, conformaction); + } +} + + /* globally conform all scalars from one template to another */ +void template_conform(t_template *tfrom, t_template *tto) +{ + int nto = tto->t_n, nfrom = tfrom->t_n, i, j, + *conformaction = (int *)getbytes(sizeof(int) * nto), + *conformedfrom = (int *)getbytes(sizeof(int) * nfrom), doit = 0; + for (i = 0; i < nto; i++) + conformaction[i] = -1; + for (i = 0; i < nfrom; i++) + conformedfrom[i] = 0; + for (i = 0; i < nto; i++) + { + t_dataslot *dataslot = &tto->t_vec[i]; + for (j = 0; j < nfrom; j++) + { + t_dataslot *dataslot2 = &tfrom->t_vec[j]; + if (dataslot_matches(dataslot, dataslot2, 1)) + { + conformaction[i] = j; + conformedfrom[j] = 1; + } + } + } + for (i = 0; i < nto; i++) + if (conformaction[i] < 0) + { + t_dataslot *dataslot = &tto->t_vec[i]; + for (j = 0; j < nfrom; j++) + if (!conformedfrom[j] && + dataslot_matches(dataslot, &tfrom->t_vec[j], 1)) + { + conformaction[i] = j; + conformedfrom[j] = 1; + } + } + if (nto != nfrom) + doit = 1; + else for (i = 0; i < nto; i++) + if (conformaction[i] != i) + doit = 1; + + if (doit) + { + t_glist *gl; + /* post("conforming template '%s' to new structure", + tfrom->t_sym->s_name); + for (i = 0; i < nto; i++) + post("... %d", conformaction[i]); */ + for (gl = canvas_list; gl; gl = gl->gl_next) + template_conformglist(tfrom, tto, gl, conformaction); + } + freebytes(conformaction, sizeof(int) * nto); + freebytes(conformedfrom, sizeof(int) * nfrom); +} + +t_template *template_findbyname(t_symbol *s) +{ + int i; + if (s == &s_float) + return (&template_float); + else return ((t_template *)pd_findbyclass(s, template_class)); +} + +t_canvas *template_findcanvas(t_template *template) +{ + t_gtemplate *gt; + if (!template) + bug("template_findcanvas"); + if (!(gt = template->t_list)) + return (0); + return (gt->x_owner); + /* return ((t_canvas *)pd_findbyclass(template->t_sym, canvas_class)); */ +} + + /* call this when reading a patch from a file to declare what templates + we'll need. If there's already a template, check if it matches. + If it doesn't it's still OK as long as there are no "struct" (gtemplate) + objects hanging from it; we just conform everyone to the new template. + If there are still struct objects belonging to the other template, we're + in trouble. LATER we'll figure out how to conform the new patch's objects + to the pre-existing struct. */ +static void *template_usetemplate(void *dummy, t_symbol *s, + int argc, t_atom *argv) +{ + t_template *x; + t_symbol *templatesym = + canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (!argc) + return (0); + argc--; argv++; + /* check if there's already a template by this name. */ + if ((x = (t_template *)pd_findbyclass(templatesym, template_class))) + { + t_template *y = template_new(&s_, argc, argv); + /* If the new template is the same as the old one, + there's nothing to do. */ + if (!template_match(x, y)) + { + /* Are there "struct" objects upholding this template? */ + if (x->t_list) + { + /* don't know what to do here! */ + error("%s: template mismatch", + templatesym->s_name); + } + else + { + /* conform everyone to the new template */ + template_conform(x, y); + pd_free(&x->t_pdobj); + template_new(templatesym, argc, argv); + } + } + pd_free(&y->t_pdobj); + } + /* otherwise, just make one. */ + else template_new(templatesym, argc, argv); + return (0); +} + + /* here we assume someone has already cleaned up all instances of this. */ +void template_free(t_template *x) +{ + if (*x->t_sym->s_name) + pd_unbind(&x->t_pdobj, x->t_sym); + t_freebytes(x->t_vec, x->t_n * sizeof(*x->t_vec)); +} + +static void template_setup(void) +{ + template_class = class_new(gensym("template"), 0, (t_method)template_free, + sizeof(t_template), CLASS_PD, 0); + class_addmethod(pd_canvasmaker, (t_method)template_usetemplate, + gensym("struct"), A_GIMME, 0); + +} + +/* ---------------- gtemplates. One per canvas. ----------- */ + +/* this is a "text" object that searches for, and if necessary creates, +a "template" (above). Other objects in the canvas then can give drawing +instructions for the template. The template doesn't go away when the +gtemplate is deleted, so that you can replace it with +another one to add new fields, for example. */ + +static void *gtemplate_donew(t_symbol *sym, int argc, t_atom *argv) +{ + t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class); + t_template *t = template_findbyname(sym); + int i; + t_symbol *sx = gensym("x"); + x->x_owner = canvas_getcurrent(); + x->x_next = 0; + x->x_sym = sym; + x->x_argc = argc; + x->x_argv = (t_atom *)getbytes(argc * sizeof(t_atom)); + for (i = 0; i < argc; i++) + x->x_argv[i] = argv[i]; + + /* already have a template by this name? */ + if (t) + { + x->x_template = t; + /* if it's already got a "struct" or "gtemplate" object we + just tack this one to the end of the list and leave it + there. */ + if (t->t_list) + { + t_gtemplate *x2, *x3; + for (x2 = x->x_template->t_list; x3 = x2->x_next; x2 = x3) + ; + x2->x_next = x; + post("template %s: warning: already exists.", sym->s_name); + } + else + { + /* if there's none, we just replace the template with + our own and conform it. */ + t_template *y = template_new(&s_, argc, argv); + /* Unless the new template is different from the old one, + there's nothing to do. */ + if (!template_match(t, y)) + { + /* conform everyone to the new template */ + template_conform(t, y); + pd_free(&t->t_pdobj); + t = template_new(sym, argc, argv); + } + pd_free(&y->t_pdobj); + t->t_list = x; + } + } + else + { + /* otherwise make a new one and we're the only struct on it. */ + x->x_template = t = template_new(sym, argc, argv); + t->t_list = x; + } + return (x); +} + +static void *gtemplate_new(t_symbol *s, int argc, t_atom *argv) +{ + t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class); + t_symbol *sym = atom_getsymbolarg(0, argc, argv); + if (argc >= 1) + argc--; argv++; + return (gtemplate_donew(canvas_makebindsym(sym), argc, argv)); +} + + /* old version (0.34) -- delete 2003 or so */ +static void *gtemplate_new_old(t_symbol *s, int argc, t_atom *argv) +{ + t_gtemplate *x = (t_gtemplate *)pd_new(gtemplate_class); + t_symbol *sym = canvas_makebindsym(canvas_getcurrent()->gl_name); + static int warned; + if (!warned) + { + post("warning -- 'template' (%s) is obsolete; replace with 'struct'", + sym->s_name); + warned = 1; + } + return (gtemplate_donew(sym, argc, argv)); +} + +t_template *gtemplate_get(t_gtemplate *x) +{ + return (x->x_template); +} + +static void gtemplate_free(t_gtemplate *x) +{ + /* get off the template's list */ + t_template *t = x->x_template; + if (x == t->t_list) + { + if (x->x_next) + { + /* if we were first on the list, and there are others on + the list, make a new template corresponding to the new + first-on-list and replace teh existing template with it. */ + t_template *z = template_new(&s_, x->x_argc, x->x_argv); + template_conform(t, z); + pd_free(&t->t_pdobj); + pd_free(&z->t_pdobj); + z = template_new(x->x_sym, x->x_argc, x->x_argv); + z->t_list = x->x_next; + } + else t->t_list = 0; + } + else + { + t_gtemplate *x2, *x3; + for (x2 = t->t_list; x3 = x2->x_next; x2 = x3) + { + if (x == x3) + { + x2->x_next = x3->x_next; + break; + } + } + } + freebytes(x->x_argv, sizeof(t_atom) * x->x_argc); +} + +static void gtemplate_setup(void) +{ + gtemplate_class = class_new(gensym("struct"), + (t_newmethod)gtemplate_new, (t_method)gtemplate_free, + sizeof(t_gtemplate), CLASS_NOINLET, A_GIMME, 0); + class_addcreator((t_newmethod)gtemplate_new_old, gensym("template"), + A_GIMME, 0); +} + +/* --------------- FIELD DESCRIPTORS ---------------------- */ + +/* a field descriptor can hold a constant or a variable; if a variable, +it's the name of a field in the template we belong to. LATER, we might +want to cache the offset of the field so we don't have to search for it +every single time we draw the object. +*/ + +typedef struct _fielddesc +{ + char fd_type; /* LATER consider removing this? */ + char fd_var; + union + { + t_float fd_float; /* the field is a constant float */ + t_symbol *fd_symbol; /* the field is a constant symbol */ + t_symbol *fd_varsym; /* the field is variable and this is the name */ + } fd_un; +} t_fielddesc; + +#define FIELDDESC_SETFLOAT(x, f) \ + ((x)->fd_type = A_FLOAT, (x)->fd_var = 0, (x)->fd_un.fd_float = (f)) +#define FIELDDESC_SETSYMBOL(x, s) \ + ((x)->fd_type = A_SYMBOL, (x)->fd_var = 0, (x)->fd_un.fd_symbol = (s)) +#define FIELDDESC_SETVAR(x, s, type) \ + ((x)->fd_type = type, (x)->fd_var = 1, (x)->fd_un.fd_varsym = (s)) + +#define CLOSED 1 +#define BEZ 2 +#define A_ARRAY 55 /* LATER decide whether to enshrine this in m_pd.h */ + +static void fielddesc_setfloatarg(t_fielddesc *fd, int argc, t_atom *argv) +{ + if (argc <= 0) FIELDDESC_SETFLOAT(fd, 0); + else if (argv->a_type == A_SYMBOL) + FIELDDESC_SETVAR(fd, argv->a_w.w_symbol, A_FLOAT); + else FIELDDESC_SETFLOAT(fd, argv->a_w.w_float); +} + +static void fielddesc_setarrayarg(t_fielddesc *fd, int argc, t_atom *argv) +{ + if (argc <= 0) FIELDDESC_SETFLOAT(fd, 0); + else if (argv->a_type == A_SYMBOL) + FIELDDESC_SETVAR(fd, argv->a_w.w_symbol, A_ARRAY); + else FIELDDESC_SETFLOAT(fd, argv->a_w.w_float); +} + +static t_float fielddesc_getfloat(t_fielddesc *f, t_template *template, + t_word *wp, int loud) +{ + if (f->fd_type == A_FLOAT) + { + if (f->fd_var) + return (template_getfloat(template, f->fd_un.fd_varsym, wp, loud)); + else return (f->fd_un.fd_float); + } + else + { + if (loud) + error("symbolic data field used as number"); + return (0); + } +} + +static t_symbol *fielddesc_getsymbol(t_fielddesc *f, t_template *template, + t_word *wp, int loud) +{ + if (f->fd_type == A_SYMBOL) + { + if (f->fd_var) + return(template_getsymbol(template, f->fd_un.fd_varsym, wp, loud)); + else return (f->fd_un.fd_symbol); + } + else + { + if (loud) + error("numeric data field used as symbol"); + return (&s_); + } +} + +/* ---------------- curves and polygons (joined segments) ---------------- */ + +/* +curves belong to templates and describe how the data in the template are to +be drawn. The coordinates of the curve (and other display features) can +be attached to fields in the template. +*/ + +t_class *curve_class; + +typedef struct _curve +{ + t_object x_obj; + int x_flags; /* CLOSED and/or BEZ */ + t_fielddesc x_fillcolor; + t_fielddesc x_outlinecolor; + t_fielddesc x_width; + int x_npoints; + t_fielddesc *x_vec; +} t_curve; + +static void *curve_new(t_symbol *classsym, t_int argc, t_atom *argv) +{ + t_curve *x = (t_curve *)pd_new(curve_class); + char *classname = classsym->s_name; + int flags = 0; + int nxy, i; + t_fielddesc *fd; + if (classname[0] == 'f') + { + classname += 6; + flags |= CLOSED; + if (argc) fielddesc_setfloatarg(&x->x_fillcolor, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0); + } + else classname += 4; + if (classname[0] == 'c') flags |= BEZ; + x->x_flags = flags; + if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0); + if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_width, 1); + if (argc < 0) argc = 0; + nxy = (argc + (argc & 1)); + x->x_npoints = (nxy>>1); + x->x_vec = (t_fielddesc *)t_getbytes(nxy * sizeof(t_fielddesc)); + for (i = 0, fd = x->x_vec; i < argc; i++, fd++, argv++) + fielddesc_setfloatarg(fd, 1, argv); + if (argc & 1) FIELDDESC_SETFLOAT(fd, 0); + + return (x); +} + +/* -------------------- widget behavior for curve ------------ */ + +static void curve_getrect(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_curve *x = (t_curve *)z; + int i, n = x->x_npoints; + t_fielddesc *f = x->x_vec; + int x1 = 0x7fffffff, x2 = -0x7fffffff, y1 = 0x7fffffff, y2 = -0x7fffffff; + for (i = 0, f = x->x_vec; i < n; i++, f += 2) + { + int xloc = glist_xtopixels(glist, + basex + fielddesc_getfloat(f, template, data, 0)); + int yloc = glist_ytopixels(glist, + basey + fielddesc_getfloat(f+1, template, data, 0)); + if (xloc < x1) x1 = xloc; + if (xloc > x2) x2 = xloc; + if (yloc < y1) y1 = yloc; + if (yloc > y2) y2 = yloc; + } + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void curve_displace(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int dx, int dy) +{ + /* refuse */ +} + +static void curve_select(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int state) +{ + /* fill in later */ +} + +static void curve_activate(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int state) +{ + /* fill in later */ +} + +static int rangecolor(int n) /* 0 to 9 in 5 steps */ +{ + int n2 = n/2; /* 0 to 4 */ + int ret = (n2 << 6); /* 0 to 256 in 5 steps */ + if (ret > 255) ret = 255; + return (ret); +} + +static void numbertocolor(int n, char *s) +{ + int red, blue, green; + if (n < 0) n = 0; + red = n / 100; + blue = ((n / 10) % 10); + green = n % 10; + sprintf(s, "#%2.2x%2.2x%2.2x", rangecolor(red), rangecolor(blue), + rangecolor(green)); +} + +static void curve_vis(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int vis) +{ + t_curve *x = (t_curve *)z; + int i, n = x->x_npoints; + t_fielddesc *f = x->x_vec; + + if (vis) + { + if (n > 1) + { + int flags = x->x_flags, closed = (flags & CLOSED); + float width = fielddesc_getfloat(&x->x_width, template, data, 1); + char outline[20], fill[20]; + if (width < 1) width = 1; + numbertocolor( + fielddesc_getfloat(&x->x_outlinecolor, template, data, 1), + outline); + if (flags & CLOSED) + { + numbertocolor( + fielddesc_getfloat(&x->x_fillcolor, template, data, 1), + fill); + sys_vgui(".x%x.c create polygon\\\n", + glist_getcanvas(glist)); + } + else sys_vgui(".x%x.c create line\\\n", + glist_getcanvas(glist)); + for (i = 0, f = x->x_vec; i < n; i++, f += 2) + { + float xloc = glist_xtopixels(glist, + basex + fielddesc_getfloat(f, template, data, 1)); + float yloc = glist_ytopixels(glist, + basey + fielddesc_getfloat(f+1, template, data, 1)); + sys_vgui("%d %d\\\n", (int)xloc, (int)yloc); + } + sys_vgui("-width %f\\\n", + fielddesc_getfloat(&x->x_width, template, data, 1)); + if (flags & CLOSED) sys_vgui("-fill %s -outline %s\\\n", + fill, outline); + else sys_vgui("-fill %s\\\n", outline); + if (flags & BEZ) sys_vgui("-smooth 1\\\n"); + sys_vgui("-tags curve%x\n", data); + } + else post("warning: curves need at least two points to be graphed"); + } + else + { + if (n > 1) sys_vgui(".x%x.c delete curve%x\n", + glist_getcanvas(glist), data); + } +} + +static int curve_motion_field; +static float curve_motion_xcumulative; +static float curve_motion_xbase; +static float curve_motion_xper; +static float curve_motion_ycumulative; +static float curve_motion_ybase; +static float curve_motion_yper; +static t_glist *curve_motion_glist; +static t_gobj *curve_motion_gobj; +static t_word *curve_motion_wp; +static t_template *curve_motion_template; + + /* LATER protect against the template changing or the scalar disappearing + probably by attaching a gpointer here ... */ + +static void curve_motion(void *z, t_floatarg dx, t_floatarg dy) +{ + t_curve *x = (t_curve *)z; + t_fielddesc *f = x->x_vec + curve_motion_field; + curve_motion_xcumulative += dx; + curve_motion_ycumulative += dy; + if (f->fd_var) + { + template_setfloat(curve_motion_template, + f->fd_un.fd_varsym, + curve_motion_wp, + curve_motion_xbase + curve_motion_xcumulative * curve_motion_xper, + 1); + } + if ((f+1)->fd_var) + { + template_setfloat(curve_motion_template, + (f+1)->fd_un.fd_varsym, + curve_motion_wp, + curve_motion_ybase + curve_motion_ycumulative * curve_motion_yper, + 1); + } + glist_redrawitem(curve_motion_glist, curve_motion_gobj); +} + +static int curve_click(t_gobj *z, t_glist *glist, + t_scalar *sc, t_template *template, float basex, float basey, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_curve *x = (t_curve *)z; + int i, n = x->x_npoints; + int bestn = -1; + int besterror = 0x7fffffff; + t_fielddesc *f = x->x_vec; + t_word *data = sc->sc_vec; + for (i = 0, f = x->x_vec; i < n; i++, f += 2) + { + int xloc = glist_xtopixels(glist, + basex + fielddesc_getfloat(f, template, data, 0)); + int yloc = glist_ytopixels(glist, + basey + fielddesc_getfloat(f+1, template, data, 0)); + int xerr = xloc - xpix, yerr = yloc - ypix; + if (!f->fd_var && !(f+1)->fd_var) + continue; + if (xerr < 0) + xerr = -xerr; + if (yerr < 0) + yerr = -yerr; + if (yerr > xerr) + xerr = yerr; + if (xerr < besterror) + { + besterror = xerr; + bestn = i; + curve_motion_xbase = fielddesc_getfloat(f, template, data, 0); + curve_motion_ybase = fielddesc_getfloat(f+1, template, data, 0); + } + } + if (besterror > 10) + return (0); + if (doit) + { + curve_motion_xper = glist_pixelstox(glist, 1) + - glist_pixelstox(glist, 0); + curve_motion_yper = glist_pixelstoy(glist, 1) + - glist_pixelstoy(glist, 0); + curve_motion_xcumulative = curve_motion_ycumulative = 0; + curve_motion_glist = glist; + curve_motion_gobj = &sc->sc_gobj; + curve_motion_wp = data; + curve_motion_field = 2*bestn; + curve_motion_template = template; + glist_grab(glist, z, curve_motion, 0, xpix, ypix); + } + return (1); +} + +t_parentwidgetbehavior curve_widgetbehavior = +{ + curve_getrect, + curve_displace, + curve_select, + curve_activate, + curve_vis, + curve_click, +}; + +static void curve_free(t_curve *x) +{ + t_freebytes(x->x_vec, 2 * x->x_npoints * sizeof(*x->x_vec)); +} + +static void curve_setup(void) +{ + curve_class = class_new(gensym("drawpolygon"), (t_newmethod)curve_new, + (t_method)curve_free, sizeof(t_curve), CLASS_NOINLET, A_GIMME, 0); + class_setdrawcommand(curve_class); + class_addcreator((t_newmethod)curve_new, gensym("drawcurve"), + A_GIMME, 0); + class_addcreator((t_newmethod)curve_new, gensym("filledpolygon"), + A_GIMME, 0); + class_addcreator((t_newmethod)curve_new, gensym("filledcurve"), + A_GIMME, 0); + class_setparentwidget(curve_class, &curve_widgetbehavior); +} + +/* --------- plots for showing arrays --------------- */ + +t_class *plot_class; + +typedef struct _plot +{ + t_object x_obj; + int x_flags; + t_fielddesc x_outlinecolor; + t_fielddesc x_width; + t_fielddesc x_xloc; + t_fielddesc x_yloc; + t_fielddesc x_xinc; + t_fielddesc x_data; +} t_plot; + +static void *plot_new(t_symbol *classsym, t_int argc, t_atom *argv) +{ + t_plot *x = (t_plot *)pd_new(plot_class); + int flags = 0; + int nxy, i; + t_fielddesc *fd; + t_symbol *firstarg = atom_getsymbolarg(0, argc, argv); + if (!strcmp(firstarg->s_name, "curve")) + { + flags |= BEZ; + argc--, argv++; + } + if (argc) fielddesc_setarrayarg(&x->x_data, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_data, 1); + if (argc) fielddesc_setfloatarg(&x->x_outlinecolor, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_outlinecolor, 0); + if (argc) fielddesc_setfloatarg(&x->x_width, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_width, 1); + if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_xloc, 1); + if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_yloc, 1); + if (argc) fielddesc_setfloatarg(&x->x_xinc, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_xinc, 1); + x->x_flags = flags; + return (x); +} + +/* -------------------- widget behavior for plot ------------ */ + + + /* get everything we'll need from the owner template of the array being + plotted. Not used for garrays, but see below */ +static int plot_readownertemplate(t_plot *x, + t_word *data, t_template *ownertemplate, + t_symbol **elemtemplatesymp, t_array **arrayp, + float *linewidthp, float *xlocp, float *xincp, float *ylocp) +{ + int arrayonset, type; + t_symbol *elemtemplatesym; + t_array *array; + + /* find the data and verify it's an array */ + if (x->x_data.fd_type != A_ARRAY || !x->x_data.fd_var) + { + error("plot: needs an array field"); + return (-1); + } + if (!template_find_field(ownertemplate, x->x_data.fd_un.fd_varsym, + &arrayonset, &type, &elemtemplatesym)) + { + error("plot: %s: no such field", x->x_data.fd_un.fd_varsym->s_name); + return (-1); + } + if (type != DT_ARRAY) + { + error("plot: %s: not an array", x->x_data.fd_un.fd_varsym->s_name); + return (-1); + } + array = *(t_array **)(((char *)data) + arrayonset); + *linewidthp = fielddesc_getfloat(&x->x_width, ownertemplate, data, 1); + *xlocp = fielddesc_getfloat(&x->x_xloc, ownertemplate, data, 1); + *xincp = fielddesc_getfloat(&x->x_xinc, ownertemplate, data, 1); + *ylocp = fielddesc_getfloat(&x->x_yloc, ownertemplate, data, 1); + *elemtemplatesymp = elemtemplatesym; + *arrayp = array; + return (0); +} + + /* get everything else you could possibly need about a plot, + either for plot's own purposes or for plotting a "garray" */ +int array_getfields(t_symbol *elemtemplatesym, + t_canvas **elemtemplatecanvasp, + t_template **elemtemplatep, int *elemsizep, + int *xonsetp, int *yonsetp, int *wonsetp) +{ + int arrayonset, elemsize, yonset, wonset, xonset, type; + t_template *elemtemplate; + t_symbol *dummy; + t_canvas *elemtemplatecanvas = 0; + + /* the "float" template is special in not having to have a canvas; + template_findbyname is hardwired to return a predefined + template. */ + + if (!(elemtemplate = template_findbyname(elemtemplatesym))) + { + error("plot: %s: no such template", elemtemplatesym->s_name); + return (-1); + } + if (!((elemtemplatesym == &s_float) || + (elemtemplatecanvas = template_findcanvas(elemtemplate)))) + { + error("plot: %s: no canvas for this template", elemtemplatesym->s_name); + return (-1); + } + elemsize = elemtemplate->t_n * sizeof(t_word); + if (!template_find_field(elemtemplate, gensym("y"), &yonset, &type, &dummy) + || type != DT_FLOAT) + yonset = -1; + if (!template_find_field(elemtemplate, gensym("x"), &xonset, &type, &dummy) + || type != DT_FLOAT) + xonset = -1; + if (!template_find_field(elemtemplate, gensym("w"), &wonset, &type, &dummy) + || type != DT_FLOAT) + wonset = -1; + + /* fill in slots for return values */ + *elemtemplatecanvasp = elemtemplatecanvas; + *elemtemplatep = elemtemplate; + *elemsizep = elemsize; + *xonsetp = xonset; + *yonsetp = yonset; + *wonsetp = wonset; + return (0); +} + +static void plot_getrect(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_plot *x = (t_plot *)z; + int elemsize, yonset, wonset, xonset; + t_canvas *elemtemplatecanvas; + t_template *elemtemplate; + t_symbol *elemtemplatesym; + float linewidth, xloc, xinc, yloc; + t_array *array; + float x1 = 0x7fffffff, y1 = 0x7fffffff, x2 = -0x7fffffff, y2 = -0x7fffffff; + int i; + float xpix, ypix, wpix; + + if (!plot_readownertemplate(x, data, template, + &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc) && + !array_getfields(elemtemplatesym, &elemtemplatecanvas, + &elemtemplate, &elemsize, &xonset, &yonset, &wonset)) + { + for (i = 0; i < array->a_n; i++) + { + array_getcoordinate(glist, (char *)(array->a_vec) + i * elemsize, + xonset, yonset, wonset, i, basex + xloc, basey + yloc, xinc, + &xpix, &ypix, &wpix); + if (xpix < x1) + x1 = xpix; + if (xpix > x2) + x2 = xpix; + if (ypix - wpix < y1) + y1 = ypix - wpix; + if (ypix + wpix > y2) + y2 = ypix + wpix; + } + } + + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void plot_displace(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int dx, int dy) +{ + /* not yet */ +} + +static void plot_select(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int state) +{ + /* not yet */ +} + +static void plot_activate(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int state) +{ + /* not yet */ +} + +static void plot_vis(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int vis) +{ + t_plot *x = (t_plot *)z; + int elemsize, yonset, wonset, xonset; + t_canvas *elemtemplatecanvas; + t_template *elemtemplate; + t_symbol *elemtemplatesym; + float linewidth, xloc, xinc, yloc; + t_array *array; + int nelem; + char *elem; + if (plot_readownertemplate(x, data, template, + &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc) || + array_getfields(elemtemplatesym, &elemtemplatecanvas, + &elemtemplate, &elemsize, &xonset, &yonset, &wonset)) + return; + nelem = array->a_n; + elem = (char *)array->a_vec; + if (vis) + { + char outline[20]; + int lastpixel = -1, ndrawn = 0; + float xsum, yval = 0, wval = 0, xpix; + int ixpix = 0, i; + + /* draw the trace */ + numbertocolor(fielddesc_getfloat(&x->x_outlinecolor, template, data, 1), + outline); + if (wonset >= 0) + { + /* found "w" field which controls linewidth. The trace is + a filled polygon with 2n points. */ + sys_vgui(".x%x.c create polygon \\\n", + glist_getcanvas(glist)); + + for (i = 0, xsum = xloc; i < nelem; i++) + { + float usexloc; + if (xonset >= 0) + usexloc = xloc + *(float *)((elem + elemsize * i) + xonset); + else usexloc = xsum, xsum += xinc; + if (yonset >= 0) + yval = *(float *)((elem + elemsize * i) + yonset); + else yval = 0; + wval = *(float *)((elem + elemsize * i) + wonset); + xpix = glist_xtopixels(glist, basex + usexloc); + ixpix = xpix + 0.5; + if (xonset >= 0 || ixpix != lastpixel) + { + sys_vgui("%d %f \\\n", ixpix, + glist_ytopixels(glist, + basey + yloc + yval - wval)); + ndrawn++; + } + lastpixel = ixpix; + if (ndrawn >= 1000) goto ouch; + } + lastpixel = -1; + for (i = nelem-1; i >= 0; i--) + { + float usexloc; + if (xonset >= 0) + usexloc = xloc + *(float *)((elem + elemsize * i) + xonset); + else xsum -= xinc, usexloc = xsum; + if (yonset >= 0) + yval = *(float *)((elem + elemsize * i) + yonset); + else yval = 0; + wval = *(float *)((elem + elemsize * i) + wonset); + xpix = glist_xtopixels(glist, basex + usexloc); + ixpix = xpix + 0.5; + if (xonset >= 0 || ixpix != lastpixel) + { + sys_vgui("%d %f \\\n", ixpix, glist_ytopixels(glist, + basey + yloc + yval + wval)); + ndrawn++; + } + lastpixel = ixpix; + if (ndrawn >= 1000) goto ouch; + } + /* TK will complain if there aren't at least 3 points. There + should be at least two already. */ + if (ndrawn < 4) + { + sys_vgui("%d %f \\\n", ixpix + 10, glist_ytopixels(glist, + basey + yloc + yval + wval)); + sys_vgui("%d %f \\\n", ixpix + 10, glist_ytopixels(glist, + basey + yloc + yval - wval)); + } + ouch: + sys_vgui(" -width 1 -fill %s -outline %s\\\n", outline, outline); + if (x->x_flags & BEZ) sys_vgui("-smooth 1\\\n"); + + sys_vgui("-tags plot%x\n", data); + } + else if (linewidth > 0) + { + /* no "w" field. If the linewidth is positive, draw a + segmented line with the requested width; otherwise don't + draw the trace at all. */ + sys_vgui(".x%x.c create line \\\n", glist_getcanvas(glist)); + + for (xsum = xloc, i = 0; i < nelem; i++) + { + float usexloc; + if (xonset >= 0) + usexloc = xloc + *(float *)((elem + elemsize * i) + xonset); + else usexloc = xsum, xsum += xinc; + if (yonset >= 0) + yval = *(float *)((elem + elemsize * i) + yonset); + else yval = 0; + xpix = glist_xtopixels(glist, basex + usexloc); + ixpix = xpix + 0.5; + if (xonset >= 0 || ixpix != lastpixel) + { + sys_vgui("%d %f \\\n", ixpix, + glist_ytopixels(glist, basey + yloc + yval)); + ndrawn++; + } + lastpixel = ixpix; + if (ndrawn >= 1000) break; + } + /* TK will complain if there aren't at least 2 points... */ + if (ndrawn == 0) sys_vgui("0 0 0 0 \\\n"); + else if (ndrawn == 1) sys_vgui("%d %f \\\n", ixpix + 10, + glist_ytopixels(glist, basey + yloc + yval)); + + sys_vgui("-width %f\\\n", linewidth); + sys_vgui("-fill %s\\\n", outline); + if (x->x_flags & BEZ) sys_vgui("-smooth 1\\\n"); + + sys_vgui("-tags plot%x\n", data); + } + /* We're done with the outline; now draw all the points. + This code is inefficient since the template has to be + searched for drawing instructions for every last point. */ + + for (xsum = xloc, i = 0; i < nelem; i++) + { + float usexloc, useyloc; + t_gobj *y; + if (xonset >= 0) + usexloc = basex + xloc + + *(float *)((elem + elemsize * i) + xonset); + else usexloc = basex + xsum, xsum += xinc; + if (yonset >= 0) + yval = *(float *)((elem + elemsize * i) + yonset); + else yval = 0; + useyloc = basey + yloc + yval; + for (y = elemtemplatecanvas->gl_list; y; y = y->g_next) + { + t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); + if (!wb) continue; + (*wb->w_parentvisfn)(y, glist, + (t_word *)(elem + elemsize * i), + elemtemplate, usexloc, useyloc, vis); + } + } + } + else + { + /* un-draw the individual points */ + int i; + for (i = 0; i < nelem; i++) + { + t_gobj *y; + for (y = elemtemplatecanvas->gl_list; y; y = y->g_next) + { + t_parentwidgetbehavior *wb = pd_getparentwidget(&y->g_pd); + if (!wb) continue; + (*wb->w_parentvisfn)(y, glist, + (t_word *)(elem + elemsize * i), elemtemplate, + 0, 0, 0); + } + } + /* and then the trace */ + sys_vgui(".x%x.c delete plot%x\n", + glist_getcanvas(glist), data); + } +} + + +static int plot_click(t_gobj *z, t_glist *glist, + t_scalar *sc, t_template *template, float basex, float basey, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_plot *x = (t_plot *)z; + t_symbol *elemtemplatesym; + float linewidth, xloc, xinc, yloc; + t_array *array; + t_word *data = sc->sc_vec; + + if (!plot_readownertemplate(x, data, template, + &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc)) + { + return (array_doclick(array, glist, &sc->sc_gobj, + elemtemplatesym, + linewidth, basex + xloc, xinc, basey + yloc, + xpix, ypix, shift, alt, dbl, doit)); + } + else return (0); +} + +t_parentwidgetbehavior plot_widgetbehavior = +{ + plot_getrect, + plot_displace, + plot_select, + plot_activate, + plot_vis, + plot_click, +}; + +static void plot_setup(void) +{ + plot_class = class_new(gensym("plot"), (t_newmethod)plot_new, 0, + sizeof(t_plot), CLASS_NOINLET, A_GIMME, 0); + class_setdrawcommand(plot_class); + class_setparentwidget(plot_class, &plot_widgetbehavior); +} + +/* ---------------- drawnumber: draw a number ---------------- */ + +/* + drawnumbers draw numeric fields at controllable locations, with + controllable color and label . + invocation: (drawnumber|drawsymbol) variable x y color label +*/ + +t_class *drawnumber_class; + +#define DRAW_SYMBOL 1 + +typedef struct _drawnumber +{ + t_object x_obj; + t_fielddesc x_value; + t_fielddesc x_xloc; + t_fielddesc x_yloc; + t_fielddesc x_color; + t_symbol *x_label; + int x_flags; +} t_drawnumber; + +static void *drawnumber_new(t_symbol *classsym, t_int argc, t_atom *argv) +{ + t_drawnumber *x = (t_drawnumber *)pd_new(drawnumber_class); + char *classname = classsym->s_name; + int flags = 0; + if (classname[4] == 's') + flags |= DRAW_SYMBOL; + x->x_flags = flags; + if (argc) fielddesc_setfloatarg(&x->x_value, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_value, 0); + if (argc) fielddesc_setfloatarg(&x->x_xloc, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_xloc, 0); + if (argc) fielddesc_setfloatarg(&x->x_yloc, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_yloc, 0); + if (argc) fielddesc_setfloatarg(&x->x_color, argc--, argv++); + else FIELDDESC_SETFLOAT(&x->x_color, 1); + if (argc) + x->x_label = atom_getsymbolarg(0, argc, argv); + else x->x_label = &s_; + + return (x); +} + +/* -------------------- widget behavior for drawnumber ------------ */ + +#define DRAWNUMBER_BUFSIZE 80 +static void drawnumber_sprintf(t_drawnumber *x, char *buf, t_atom *ap) +{ + int nchars; + strncpy(buf, x->x_label->s_name, DRAWNUMBER_BUFSIZE); + buf[DRAWNUMBER_BUFSIZE - 1] = 0; + nchars = strlen(buf); + atom_string(ap, buf + nchars, DRAWNUMBER_BUFSIZE - nchars); +} + +static void drawnumber_getrect(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_drawnumber *x = (t_drawnumber *)z; + t_atom at; + int xloc = glist_xtopixels(glist, + basex + fielddesc_getfloat(&x->x_xloc, template, data, 0)); + int yloc = glist_ytopixels(glist, + basey + fielddesc_getfloat(&x->x_yloc, template, data, 0)); + int font = glist_getfont(glist); + int fontwidth = sys_fontwidth(font), fontheight = sys_fontheight(font); + char buf[DRAWNUMBER_BUFSIZE]; + if (x->x_flags & DRAW_SYMBOL) + SETSYMBOL(&at, fielddesc_getsymbol(&x->x_value, template, data, 0)); + else SETFLOAT(&at, fielddesc_getfloat(&x->x_value, template, data, 0)); + drawnumber_sprintf(x, buf, &at); + *xp1 = xloc; + *yp1 = yloc; + *xp2 = xloc + fontwidth * strlen(buf); + *yp2 = yloc + fontheight; +} + +static void drawnumber_displace(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int dx, int dy) +{ + /* refuse */ +} + +static void drawnumber_select(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int state) +{ + post("drawnumber_select %d", state); + /* fill in later */ +} + +static void drawnumber_activate(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int state) +{ + post("drawnumber_activate %d", state); +} + +static void drawnumber_vis(t_gobj *z, t_glist *glist, + t_word *data, t_template *template, float basex, float basey, + int vis) +{ + t_drawnumber *x = (t_drawnumber *)z; + + if (vis) + { + t_atom at; + int xloc = glist_xtopixels(glist, + basex + fielddesc_getfloat(&x->x_xloc, template, data, 0)); + int yloc = glist_ytopixels(glist, + basey + fielddesc_getfloat(&x->x_yloc, template, data, 0)); + char colorstring[20], buf[DRAWNUMBER_BUFSIZE]; + numbertocolor(fielddesc_getfloat(&x->x_color, template, data, 1), + colorstring); + if (x->x_flags & DRAW_SYMBOL) + SETSYMBOL(&at, fielddesc_getsymbol(&x->x_value, template, data, 0)); + else SETFLOAT(&at, fielddesc_getfloat(&x->x_value, template, data, 0)); + drawnumber_sprintf(x, buf, &at); + sys_vgui(".x%x.c create text %d %d -anchor nw -fill %s -text {%s}", + glist_getcanvas(glist), xloc, yloc, colorstring, buf); + sys_vgui(" -font -*-courier-bold--normal--%d-*", + sys_hostfontsize(glist_getfont(glist))); + sys_vgui(" -tags drawnumber%x\n", data); + } + else sys_vgui(".x%x.c delete drawnumber%x\n", glist_getcanvas(glist), data); +} + +static float drawnumber_motion_ycumulative; +static t_glist *drawnumber_motion_glist; +static t_gobj *drawnumber_motion_gobj; +static t_word *drawnumber_motion_wp; +static t_template *drawnumber_motion_template; + + /* LATER protect against the template changing or the scalar disappearing + probably by attaching a gpointer here ... */ + +static void drawnumber_motion(void *z, t_floatarg dx, t_floatarg dy) +{ + t_drawnumber *x = (t_drawnumber *)z; + t_fielddesc *f = &x->x_value; + drawnumber_motion_ycumulative -= dy; + template_setfloat(drawnumber_motion_template, + f->fd_un.fd_varsym, + drawnumber_motion_wp, + drawnumber_motion_ycumulative, + 1); + glist_redrawitem(drawnumber_motion_glist, drawnumber_motion_gobj); +} + +static int drawnumber_click(t_gobj *z, t_glist *glist, + t_scalar *sc, t_template *template, float basex, float basey, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_drawnumber *x = (t_drawnumber *)z; + int x1, y1, x2, y2; + t_word *data = sc->sc_vec; + drawnumber_getrect(z, glist, + sc->sc_vec, template, basex, basey, + &x1, &y1, &x2, &y2); + if (xpix >= x1 && xpix <= x2 && ypix >= y1 && ypix <= y2 + && x->x_value.fd_var) + { + if (doit) + { + drawnumber_motion_glist = glist; + drawnumber_motion_gobj = &sc->sc_gobj; + drawnumber_motion_wp = data; + drawnumber_motion_template = template; + drawnumber_motion_ycumulative = + fielddesc_getfloat(&x->x_value, template, data, 0); + glist_grab(glist, z, drawnumber_motion, 0, xpix, ypix); + } + return (1); + } + else return (0); +} + +t_parentwidgetbehavior drawnumber_widgetbehavior = +{ + drawnumber_getrect, + drawnumber_displace, + drawnumber_select, + drawnumber_activate, + drawnumber_vis, + drawnumber_click, +}; + +static void drawnumber_free(t_drawnumber *x) +{ +} + +static void drawnumber_setup(void) +{ + drawnumber_class = class_new(gensym("drawnumber"), + (t_newmethod)drawnumber_new, (t_method)drawnumber_free, + sizeof(t_drawnumber), CLASS_NOINLET, A_GIMME, 0); + class_setdrawcommand(drawnumber_class); + class_addcreator((t_newmethod)drawnumber_new, gensym("drawsymbol"), + A_GIMME, 0); + class_setparentwidget(drawnumber_class, &drawnumber_widgetbehavior); +} + +/* ---------------------- setup function ---------------------------- */ + +void g_template_setup(void) +{ + template_setup(); + gtemplate_setup(); + template_float.t_pdobj = template_class; + curve_setup(); + plot_setup(); + drawnumber_setup(); +} + diff --git a/apps/plugins/pdbox/PDa/src/g_text.c b/apps/plugins/pdbox/PDa/src/g_text.c new file mode 100644 index 0000000..8cf1fe2 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_text.c @@ -0,0 +1,2632 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* changes by Thomas Musil IEM KUG Graz Austria 2001 */ +/* the methods for calling the gui-objects from menu are implemented */ +/* all changes are labeled with iemlib */ + +#include +#include "m_pd.h" +#include "m_imp.h" +#include "s_stuff.h" +#include "t_tk.h" +#include "g_canvas.h" +#include +#include +#include + +static t_class *text_class; +static t_class *message_class; +static t_class *gatom_class; +static void text_vis(t_gobj *z, t_glist *glist, int vis); +static void text_displace(t_gobj *z, t_glist *glist, + int dx, int dy); +static void text_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2); + +void canvas_startmotion(t_canvas *x); +t_widgetbehavior text_widgetbehavior; + +/* ----------------- the "text" object. ------------------ */ + + /* add a "text" object (comment) to a glist. While this one goes for any glist, + the other 3 below are for canvases only. (why?) This is called + without args if invoked from the GUI; otherwise at least x and y + are provided. */ + +void glist_text(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + t_text *x = (t_text *)pd_new(text_class); + t_atom at; + x->te_width = 0; /* don't know it yet. */ + x->te_type = T_TEXT; + x->te_binbuf = binbuf_new(); + if (argc > 1) + { + x->te_xpix = atom_getfloatarg(0, argc, argv); + x->te_ypix = atom_getfloatarg(1, argc, argv); + if (argc > 2) binbuf_restore(x->te_binbuf, argc-2, argv+2); + else + { + SETSYMBOL(&at, gensym("comment")); + binbuf_restore(x->te_binbuf, 1, &at); + } + glist_add(gl, &x->te_g); + } + else + { + int xpix, ypix; + pd_vmess((t_pd *)glist_getcanvas(gl), gensym("editmode"), "i", 1); + SETSYMBOL(&at, gensym("comment")); + glist_noselect(gl); + glist_getnextxy(gl, &xpix, &ypix); + x->te_xpix = glist_pixelstox(gl, xpix-3); + x->te_ypix = glist_pixelstoy(gl, ypix-3); + binbuf_restore(x->te_binbuf, 1, &at); + glist_add(gl, &x->te_g); + glist_noselect(gl); + glist_select(gl, &x->te_g); + /* it would be nice to "activate" here, but then the second, + "put-me-down" click changes the text selection, which is quite + irritating, so I took this back out. It's OK in messages + and objects though since there's no text in them at menu + creation. */ + /* gobj_activate(&x->te_g, gl, 1); */ + canvas_startmotion(glist_getcanvas(gl)); + } +} + +/* ----------------- the "object" object. ------------------ */ + +extern t_pd *newest; +void canvas_getargs(int *argcp, t_atom **argvp); + +static void canvas_objtext(t_glist *gl, int xpix, int ypix, int selected, + t_binbuf *b) +{ + t_text *x; + int argc; + t_atom *argv; + newest = 0; + canvas_setcurrent((t_canvas *)gl); + canvas_getargs(&argc, &argv); + binbuf_eval(b, &pd_objectmaker, argc, argv); + if (binbuf_getnatom(b)) + { + if (!newest) + { + binbuf_print(b); + post("... couldn't create"); + x = 0; + } + else if (!(x = pd_checkobject(newest))) + { + binbuf_print(b); + post("... didn't return a patchable object"); + } + } + else x = 0; + if (!x) + { + + /* LATER make the color reflect this */ + x = (t_text *)pd_new(text_class); + } + x->te_binbuf = b; + x->te_xpix = xpix; + x->te_ypix = ypix; + x->te_width = 0; + x->te_type = T_OBJECT; + glist_add(gl, &x->te_g); + if (selected) + { + /* this is called if we've been created from the menu. */ + glist_select(gl, &x->te_g); + gobj_activate(&x->te_g, gl, 1); + } + if (pd_class(&x->ob_pd) == vinlet_class) + canvas_resortinlets(glist_getcanvas(gl)); + if (pd_class(&x->ob_pd) == voutlet_class) + canvas_resortoutlets(glist_getcanvas(gl)); + canvas_unsetcurrent((t_canvas *)gl); +} + + /* object creation routine. These are called without any arguments if + they're invoked from the + gui; when pasting or restoring from a file, we get at least x and y. */ + +void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + t_text *x; + if (argc >= 2) + { + t_binbuf *b = binbuf_new(); + binbuf_restore(b, argc-2, argv+2); + canvas_objtext(gl, atom_getintarg(0, argc, argv), + atom_getintarg(1, argc, argv), 0, b); + } + else + { + t_binbuf *b = binbuf_new(); + int xpix, ypix; + pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1); + glist_noselect(gl); + glist_getnextxy(gl, &xpix, &ypix); + canvas_objtext(gl, xpix, ypix, 1, b); + canvas_startmotion(glist_getcanvas(gl)); + } +} + +/* make an object box for an object that's already there. */ + +/* iemlib */ +void canvas_iemguis(t_glist *gl, t_symbol *guiobjname) +{ + t_atom at; + t_binbuf *b = binbuf_new(); + int xpix, ypix; + + pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1); + glist_noselect(gl); + SETSYMBOL(&at, guiobjname); + binbuf_restore(b, 1, &at); + glist_getnextxy(gl, &xpix, &ypix); + canvas_objtext(gl, xpix, ypix, 1, b); + canvas_startmotion(glist_getcanvas(gl)); +} + +void canvas_bng(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("bng")); +} + +void canvas_toggle(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("tgl")); +} + +void canvas_vslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("vsl")); +} + +void canvas_hslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("hsl")); +} + +void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("hdl")); +} + +void canvas_vdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("vdl")); +} + +void canvas_hradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("hradio")); +} + +void canvas_vradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("vradio")); +} + +void canvas_vumeter(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("vu")); +} + +void canvas_mycnv(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("cnv")); +} + +void canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("nbx")); +} + +/* iemlib */ + +void canvas_objfor(t_glist *gl, t_text *x, int argc, t_atom *argv) +{ + x->te_width = 0; /* don't know it yet. */ + x->te_type = T_OBJECT; + x->te_binbuf = binbuf_new(); + x->te_xpix = atom_getfloatarg(0, argc, argv); + x->te_ypix = atom_getfloatarg(1, argc, argv); + if (argc > 2) binbuf_restore(x->te_binbuf, argc-2, argv+2); + glist_add(gl, &x->te_g); +} + +/* ---------------------- the "message" text item ------------------------ */ + +typedef struct _messresponder +{ + t_pd mr_pd; + t_outlet *mr_outlet; +} t_messresponder; + +typedef struct _message +{ + t_text m_text; + t_messresponder m_messresponder; + t_glist *m_glist; + t_clock *m_clock; +} t_message; + +static t_class *message_class, *messresponder_class; + +static void messresponder_bang(t_messresponder *x) +{ + outlet_bang(x->mr_outlet); +} + +static void messresponder_float(t_messresponder *x, t_float f) +{ + outlet_float(x->mr_outlet, f); +} + +static void messresponder_symbol(t_messresponder *x, t_symbol *s) +{ + outlet_symbol(x->mr_outlet, s); +} + +static void messresponder_list(t_messresponder *x, + t_symbol *s, int argc, t_atom *argv) +{ + outlet_list(x->mr_outlet, s, argc, argv); +} + +static void messresponder_anything(t_messresponder *x, + t_symbol *s, int argc, t_atom *argv) +{ + outlet_anything(x->mr_outlet, s, argc, argv); +} + +static void message_bang(t_message *x) +{ + binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 0, 0); +} + +static void message_float(t_message *x, t_float f) +{ + t_atom at; + SETFLOAT(&at, f); + binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 1, &at); +} + +static void message_symbol(t_message *x, t_symbol *s) +{ + t_atom at; + SETSYMBOL(&at, s); + binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 1, &at); +} + +static void message_list(t_message *x, t_symbol *s, int argc, t_atom *argv) +{ + binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, argc, argv); +} + +static void message_set(t_message *x, t_symbol *s, int argc, t_atom *argv) +{ + binbuf_clear(x->m_text.te_binbuf); + binbuf_add(x->m_text.te_binbuf, argc, argv); + glist_retext(x->m_glist, &x->m_text); +} + +static void message_add2(t_message *x, t_symbol *s, int argc, t_atom *argv) +{ + binbuf_add(x->m_text.te_binbuf, argc, argv); + glist_retext(x->m_glist, &x->m_text); +} + +static void message_add(t_message *x, t_symbol *s, int argc, t_atom *argv) +{ + binbuf_add(x->m_text.te_binbuf, argc, argv); + binbuf_addsemi(x->m_text.te_binbuf); + glist_retext(x->m_glist, &x->m_text); +} + +static void message_click(t_message *x, + t_floatarg xpos, t_floatarg ypos, t_floatarg shift, + t_floatarg ctrl, t_floatarg alt) +{ + message_float(x, 0); + if (glist_isvisible(x->m_glist)) + { + t_rtext *y = glist_findrtext(x->m_glist, &x->m_text); + sys_vgui(".x%x.c itemconfigure %sR -width 5\n", + glist_getcanvas(x->m_glist), rtext_gettag(y)); + clock_delay(x->m_clock, 120); + } +} + +static void message_tick(t_message *x) +{ + if (glist_isvisible(x->m_glist)) + { + t_rtext *y = glist_findrtext(x->m_glist, &x->m_text); + sys_vgui(".x%x.c itemconfigure %sR -width 1\n", + glist_getcanvas(x->m_glist), rtext_gettag(y)); + } +} + +static void message_free(t_message *x) +{ + clock_free(x->m_clock); +} + +void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + t_message *x = (t_message *)pd_new(message_class); + x->m_messresponder.mr_pd = messresponder_class; + x->m_messresponder.mr_outlet = outlet_new(&x->m_text, &s_float); + x->m_text.te_width = 0; /* don't know it yet. */ + x->m_text.te_type = T_MESSAGE; + x->m_text.te_binbuf = binbuf_new(); + x->m_glist = gl; + x->m_clock = clock_new(x, (t_method)message_tick); + if (argc > 1) + { + x->m_text.te_xpix = atom_getfloatarg(0, argc, argv); + x->m_text.te_ypix = atom_getfloatarg(1, argc, argv); + if (argc > 2) binbuf_restore(x->m_text.te_binbuf, argc-2, argv+2); + glist_add(gl, &x->m_text.te_g); + } + else + { + int xpix, ypix; + pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1); + glist_noselect(gl); + glist_getnextxy(gl, &xpix, &ypix); + x->m_text.te_xpix = xpix-3; + x->m_text.te_ypix = ypix-3; + glist_add(gl, &x->m_text.te_g); + glist_noselect(gl); + glist_select(gl, &x->m_text.te_g); + gobj_activate(&x->m_text.te_g, gl, 1); + canvas_startmotion(glist_getcanvas(gl)); + } +} + +/* ---------------------- the "atom" text item ------------------------ */ + +#define ATOMBUFSIZE 40 +#define ATOM_LABELLEFT 0 +#define ATOM_LABELRIGHT 1 +#define ATOM_LABELUP 2 +#define ATOM_LABELDOWN 3 + +typedef struct _gatom +{ + t_text a_text; + t_atom a_atom; /* this holds the value and the type */ + t_glist *a_glist; /* owning glist */ + t_float a_toggle; /* value to toggle to */ + t_float a_draghi; /* high end of drag range */ + t_float a_draglo; /* low end of drag range */ + t_symbol *a_label; /* symbol to show as label next to box */ + t_symbol *a_symfrom; /* "receive" name -- bind ourselvs to this */ + t_symbol *a_symto; /* "send" name -- send to this on output */ + char a_buf[ATOMBUFSIZE];/* string buffer for typing */ + char a_shift; /* was shift key down when dragging started? */ + char a_wherelabel; /* 0-3 for left, right, above, below */ + t_symbol *a_expanded_to; /* a_symto after $0, $1, ... expansion */ +} t_gatom; + + /* prepend "-" as necessary to avoid empty strings, so we can + use them in Pd messages. A more complete solution would be + to introduce some quoting mechanism; but then we'd be much more + complicated. */ +static t_symbol *gatom_escapit(t_symbol *s) +{ + if (!*s->s_name) + return (gensym("-")); + else if (*s->s_name == '-') + { + char shmo[100]; + shmo[0] = '-'; + strncpy(shmo+1, s->s_name, 99); + shmo[99] = 0; + return (gensym(shmo)); + } + else return (iemgui_dollar2raute(s)); +} + + /* undo previous operation: strip leading "-" if found. */ +static t_symbol *gatom_unescapit(t_symbol *s) +{ + if (*s->s_name == '-') + return (gensym(s->s_name+1)); + else return (iemgui_raute2dollar(s)); +} + +#if 0 /* ??? */ + /* expand leading $0, $1, etc. in the symbol */ +static t_symbol *gatom_realizedollar(t_gatom *x, t_symbol *s) +{ + return (canvas_realizedollar(x->a_glist, s)); +} +#endif + +static void gatom_retext(t_gatom *x, int senditup) +{ + binbuf_clear(x->a_text.te_binbuf); + binbuf_add(x->a_text.te_binbuf, 1, &x->a_atom); + if (senditup) + glist_retext(x->a_glist, &x->a_text); +} + +static void gatom_set(t_gatom *x, t_symbol *s, int argc, t_atom *argv) +{ + t_atom oldatom = x->a_atom; + int senditup = 0; + if (!argc) return; + if (x->a_atom.a_type == A_FLOAT) + x->a_atom.a_w.w_float = atom_getfloat(argv), + senditup = (x->a_atom.a_w.w_float != oldatom.a_w.w_float); + else if (x->a_atom.a_type == A_SYMBOL) + x->a_atom.a_w.w_symbol = atom_getsymbol(argv), + senditup = (x->a_atom.a_w.w_symbol != oldatom.a_w.w_symbol); + gatom_retext(x, senditup); + x->a_buf[0] = 0; +} + +static void gatom_bang(t_gatom *x) +{ + if (x->a_atom.a_type == A_FLOAT) + { + if (x->a_text.te_outlet) + outlet_float(x->a_text.te_outlet, x->a_atom.a_w.w_float); + if (*x->a_expanded_to->s_name && x->a_expanded_to->s_thing) + { + if (x->a_symto == x->a_symfrom) + pd_error(x, + "%s: atom with same send/receive name (infinite loop)", + x->a_symto->s_name); + else pd_float(x->a_expanded_to->s_thing, x->a_atom.a_w.w_float); + } + } + else if (x->a_atom.a_type == A_SYMBOL) + { + if (x->a_text.te_outlet) + outlet_symbol(x->a_text.te_outlet, x->a_atom.a_w.w_symbol); + if (*x->a_symto->s_name && x->a_expanded_to->s_thing) + { + if (x->a_symto == x->a_symfrom) + pd_error(x, + "%s: atom with same send/receive name (infinite loop)", + x->a_symto->s_name); + else pd_symbol(x->a_expanded_to->s_thing, x->a_atom.a_w.w_symbol); + } + } +} + +static void gatom_float(t_gatom *x, t_float f) +{ + t_atom at; + SETFLOAT(&at, f); + gatom_set(x, 0, 1, &at); + gatom_bang(x); +} + +static void gatom_clipfloat(t_gatom *x, t_float f) +{ + if (x->a_draglo != 0 || x->a_draghi != 0) + { + if (f < x->a_draglo) + f = x->a_draglo; + if (f > x->a_draghi) + f = x->a_draghi; + } + gatom_float(x, f); +} + +static void gatom_symbol(t_gatom *x, t_symbol *s) +{ + t_atom at; + SETSYMBOL(&at, s); + gatom_set(x, 0, 1, &at); + gatom_bang(x); +} + +static void gatom_motion(void *z, t_floatarg dx, t_floatarg dy) +{ + t_gatom *x = (t_gatom *)z; + if (dy == 0) return; + if (x->a_atom.a_type == A_FLOAT) + { + if (x->a_shift) + { + double nval = x->a_atom.a_w.w_float - 0.01 * dy; + double trunc = 0.01 * (floor(100. * nval + 0.5)); + if (trunc < nval + 0.0001 && trunc > nval - 0.0001) nval = trunc; + gatom_clipfloat(x, nval); + } + else + { + double nval = x->a_atom.a_w.w_float - dy; + double trunc = 0.01 * (floor(100. * nval + 0.5)); + if (trunc < nval + 0.0001 && trunc > nval - 0.0001) nval = trunc; + trunc = floor(nval + 0.5); + if (trunc < nval + 0.001 && trunc > nval - 0.001) nval = trunc; + gatom_clipfloat(x, nval); + } + } +} + +static void gatom_key(void *z, t_floatarg f) +{ + t_gatom *x = (t_gatom *)z; + int c = f; + int len = strlen(x->a_buf); + t_atom at; + char sbuf[ATOMBUFSIZE + 4]; + if (c == 0) + { + /* we're being notified that no more keys will come for this grab */ + if (x->a_buf[0]) + gatom_retext(x, 1); + return; + } + else if (c == ' ') return; + else if (c == '\b') + { + if (len > 0) + x->a_buf[len-1] = 0; + goto redraw; + } + else if (c == '\n') + { + if (x->a_atom.a_type == A_FLOAT) + x->a_atom.a_w.w_float = atof(x->a_buf); + else if (x->a_atom.a_type == A_SYMBOL) + x->a_atom.a_w.w_symbol = gensym(x->a_buf); + else bug("gatom_key"); + gatom_bang(x); + gatom_retext(x, 1); + x->a_buf[0] = 0; + } + else if (len < (ATOMBUFSIZE-1)) + { + /* for numbers, only let reasonable characters through */ + if ((x->a_atom.a_type == A_SYMBOL) || + (c >= '0' && c <= '9' || c == '.' || c == '-' + || c == 'e' || c == 'E')) + { + x->a_buf[len] = c; + x->a_buf[len+1] = 0; + goto redraw; + } + } + return; +redraw: + /* LATER figure out how to avoid creating all these symbols! */ + sprintf(sbuf, "%s...", x->a_buf); + SETSYMBOL(&at, gensym(sbuf)); + binbuf_clear(x->a_text.te_binbuf); + binbuf_add(x->a_text.te_binbuf, 1, &at); + glist_retext(x->a_glist, &x->a_text); +} + +static void gatom_click(t_gatom *x, + t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, + t_floatarg alt) +{ + if (x->a_text.te_width == 1) + { + if (x->a_atom.a_type == A_FLOAT) + gatom_float(x, (x->a_atom.a_w.w_float == 0)); + } + else + { + if (alt) + { + if (x->a_atom.a_type != A_FLOAT) return; + if (x->a_atom.a_w.w_float != 0) + { + x->a_toggle = x->a_atom.a_w.w_float; + gatom_float(x, 0); + return; + } + else gatom_float(x, x->a_toggle); + } + x->a_shift = shift; + x->a_buf[0] = 0; + glist_grab(x->a_glist, &x->a_text.te_g, gatom_motion, gatom_key, + xpos, ypos); + } +} + + /* message back from dialog window */ +static void gatom_param(t_gatom *x, t_symbol *sel, int argc, t_atom *argv) +{ + t_float width = atom_getfloatarg(0, argc, argv); + t_float draglo = atom_getfloatarg(1, argc, argv); + t_float draghi = atom_getfloatarg(2, argc, argv); + t_symbol *label = gatom_unescapit(atom_getsymbolarg(3, argc, argv)); + t_float wherelabel = atom_getfloatarg(4, argc, argv); + t_symbol *symfrom = gatom_unescapit(atom_getsymbolarg(5, argc, argv)); + t_symbol *symto = gatom_unescapit(atom_getsymbolarg(6, argc, argv)); + + gobj_vis(&x->a_text.te_g, x->a_glist, 0); + if (!*symfrom->s_name && *x->a_symfrom->s_name) + inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0); + else if (*symfrom->s_name && !*x->a_symfrom->s_name && x->a_text.te_inlet) + { + canvas_deletelinesforio(x->a_glist, &x->a_text, + x->a_text.te_inlet, 0); + inlet_free(x->a_text.te_inlet); + } + if (!*symto->s_name && *x->a_symto->s_name) + outlet_new(&x->a_text, 0); + else if (*symto->s_name && !*x->a_symto->s_name && x->a_text.te_outlet) + { + canvas_deletelinesforio(x->a_glist, &x->a_text, + 0, x->a_text.te_outlet); + outlet_free(x->a_text.te_outlet); + } + if (draglo >= draghi) + draglo = draghi = 0; + x->a_draglo = draglo; + x->a_draghi = draghi; + if (width < 0) + width = 4; + else if (width > 80) + width = 80; + x->a_text.te_width = width; + x->a_wherelabel = ((int)wherelabel & 3); + x->a_label = label; + if (*x->a_symfrom->s_name) + pd_unbind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); + x->a_symfrom = symfrom; + if (*x->a_symfrom->s_name) + pd_bind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); + x->a_symto = symto; + x->a_expanded_to = canvas_realizedollar(x->a_glist, x->a_symto); + gobj_vis(&x->a_text.te_g, x->a_glist, 1); + + /* glist_retext(x->a_glist, &x->a_text); */ +} + + /* ---------------- gatom-specific widget functions --------------- */ +static void gatom_getwherelabel(t_gatom *x, t_glist *glist, int *xp, int *yp) +{ + int x1, y1, x2, y2, width, height; + text_getrect(&x->a_text.te_g, glist, &x1, &y1, &x2, &y2); + width = x2 - x1; + height = y2 - y1; + if (x->a_wherelabel == ATOM_LABELLEFT) + { + *xp = x1 - 3 - + strlen(canvas_realizedollar(x->a_glist, x->a_label)->s_name) * + sys_fontwidth(glist_getfont(glist)); + *yp = y1 + 2; + } + else if (x->a_wherelabel == ATOM_LABELRIGHT) + { + *xp = x2 + 2; + *yp = y1 + 2; + } + else if (x->a_wherelabel == ATOM_LABELUP) + { + *xp = x1 - 1; + *yp = y1 - 1 - sys_fontheight(glist_getfont(glist));; + } + else + { + *xp = x1 - 1; + *yp = y2 + 3; + } +} + +static void gatom_displace(t_gobj *z, t_glist *glist, + int dx, int dy) +{ + t_gatom *x = (t_gatom*)z; + text_displace(z, glist, dx, dy); + sys_vgui(".x%x.c move %x.l %d %d\n", glist_getcanvas(glist), + x, dx, dy); +} + +static void gatom_vis(t_gobj *z, t_glist *glist, int vis) +{ + t_gatom *x = (t_gatom*)z; + text_vis(z, glist, vis); + if (*x->a_label->s_name) + { + if (vis) + { + int x1, y1; + gatom_getwherelabel(x, glist, &x1, &y1); + sys_vgui("pdtk_text_new .x%x.c %x.l %f %f {%s} %d %s\n", + glist_getcanvas(glist), x, + (double)x1, (double)y1, + canvas_realizedollar(x->a_glist, x->a_label)->s_name, + sys_hostfontsize(glist_getfont(glist)), + "black"); + } + else sys_vgui(".x%x.c delete %x.l\n", glist_getcanvas(glist), x); + } +} + +void canvas_atom(t_glist *gl, t_atomtype type, + t_symbol *s, int argc, t_atom *argv) +{ + t_gatom *x = (t_gatom *)pd_new(gatom_class); + t_atom at; + x->a_text.te_width = 0; /* don't know it yet. */ + x->a_text.te_type = T_ATOM; + x->a_text.te_binbuf = binbuf_new(); + x->a_glist = gl; + x->a_atom.a_type = type; + x->a_toggle = 1; + x->a_draglo = 0; + x->a_draghi = 0; + x->a_wherelabel = 0; + x->a_label = &s_; + x->a_symfrom = &s_; + x->a_symto = x->a_expanded_to = &s_; + if (type == A_FLOAT) + { + x->a_atom.a_w.w_float = 0; + x->a_text.te_width = 5; + SETFLOAT(&at, 0); + } + else + { + x->a_atom.a_w.w_symbol = &s_symbol; + x->a_text.te_width = 10; + SETSYMBOL(&at, &s_symbol); + } + binbuf_add(x->a_text.te_binbuf, 1, &at); + if (argc > 1) + /* create from file. x, y, width, low-range, high-range, flags, + label, receive-name, send-name */ + { + x->a_text.te_xpix = atom_getfloatarg(0, argc, argv); + x->a_text.te_ypix = atom_getfloatarg(1, argc, argv); + x->a_text.te_width = atom_getintarg(2, argc, argv); + /* sanity check because some very old patches have trash in this + field... remove this in 2003 or so: */ + if (x->a_text.te_width < 0 || x->a_text.te_width > 500) + x->a_text.te_width = 4; + x->a_draglo = atom_getfloatarg(3, argc, argv); + x->a_draghi = atom_getfloatarg(4, argc, argv); + x->a_wherelabel = (((int)atom_getfloatarg(5, argc, argv)) & 3); + x->a_label = gatom_unescapit(atom_getsymbolarg(6, argc, argv)); + x->a_symfrom = gatom_unescapit(atom_getsymbolarg(7, argc, argv)); + if (*x->a_symfrom->s_name) + pd_bind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); + + x->a_symto = gatom_unescapit(atom_getsymbolarg(8, argc, argv)); + x->a_expanded_to = canvas_realizedollar(x->a_glist, x->a_symto); + if (x->a_symto == &s_) + outlet_new(&x->a_text, + x->a_atom.a_type == A_FLOAT ? &s_float: &s_symbol); + if (x->a_symfrom == &s_) + inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0); + glist_add(gl, &x->a_text.te_g); + } + else + { + int xpix, ypix; + outlet_new(&x->a_text, + x->a_atom.a_type == A_FLOAT ? &s_float: &s_symbol); + inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0); + pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1); + glist_noselect(gl); + glist_getnextxy(gl, &xpix, &ypix); + x->a_text.te_xpix = xpix; + x->a_text.te_ypix = ypix; + glist_add(gl, &x->a_text.te_g); + glist_noselect(gl); + glist_select(gl, &x->a_text.te_g); + canvas_startmotion(glist_getcanvas(gl)); + } +} + +void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_atom(gl, A_FLOAT, s, argc, argv); +} + +void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_atom(gl, A_SYMBOL, s, argc, argv); +} + +static void gatom_free(t_gatom *x) +{ + if (*x->a_symfrom->s_name) + pd_unbind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); + gfxstub_deleteforkey(x); +} + +static void gatom_properties(t_gobj *z, t_glist *owner) +{ + t_gatom *x = (t_gatom *)z; + char buf[200]; + sprintf(buf, "pdtk_gatom_dialog %%s %d %g %g %d %s %s %s\n", + x->a_text.te_width, x->a_draglo, x->a_draghi, + x->a_wherelabel, gatom_escapit(x->a_label)->s_name, + gatom_escapit(x->a_symfrom)->s_name, + gatom_escapit(x->a_symto)->s_name); + gfxstub_new(&x->a_text.te_pd, x, buf); +} + + +/* -------------------- widget behavior for text objects ------------ */ + +static void text_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_text *x = (t_text *)z; + int width, height, iscomment = (x->te_type == T_TEXT); + float x1, y1, x2, y2; + + /* for number boxes, we know width and height a priori, and should + report them here so that graphs can get swelled to fit. */ + + if (x->te_type == T_ATOM && x->te_width > 0) + { + int font = glist_getfont(glist); + int fontwidth = sys_fontwidth(font), fontheight = sys_fontheight(font); + width = (x->te_width > 0 ? x->te_width : 6) * fontwidth + 2; + height = fontheight + 1; /* borrowed from TMARGIN, etc, in g_rtext.c */ + } + /* if we're invisible we don't know our size so we just lie about + it. This is called on invisible boxes to establish order of inlets + and possibly other reasons. + To find out if the box is visible we can't just check the "vis" + flag because we might be within the vis() routine and not have set + that yet. So we check directly whether the "rtext" list has been + built. LATER reconsider when "vis" flag should be on and off? */ + + else if (glist->gl_editor && glist->gl_editor->e_rtext) + { + t_rtext *y = glist_findrtext(glist, x); + width = rtext_width(y); + height = rtext_height(y) - (iscomment << 1); + } + else width = height = 10; + x1 = text_xpix(x, glist); + y1 = text_ypix(x, glist); + x2 = x1 + width; + y2 = y1 + height; + y1 += iscomment; + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void text_displace(t_gobj *z, t_glist *glist, + int dx, int dy) +{ + t_text *x = (t_text *)z; + x->te_xpix += dx; + x->te_ypix += dy; + if (glist_isvisible(glist)) + { + t_rtext *y = glist_findrtext(glist, x); + rtext_displace(y, dx, dy); + text_drawborder(x, glist, rtext_gettag(y), + rtext_width(y), rtext_height(y), 0); + canvas_fixlinesfor(glist_getcanvas(glist), x); + } +} + +static void text_select(t_gobj *z, t_glist *glist, int state) +{ + t_text *x = (t_text *)z; + t_rtext *y = glist_findrtext(glist, x); + rtext_select(y, state); + if (glist_isvisible(glist) && text_shouldvis(x, glist)) + sys_vgui(".x%x.c itemconfigure %sR -fill %s\n", glist, + rtext_gettag(y), (state? "blue" : "black")); +} + +static void text_activate(t_gobj *z, t_glist *glist, int state) +{ + t_text *x = (t_text *)z; + t_rtext *y = glist_findrtext(glist, x); + if (z->g_pd != gatom_class) rtext_activate(y, state); +} + +static void text_delete(t_gobj *z, t_glist *glist) +{ + t_text *x = (t_text *)z; + canvas_deletelinesfor(glist, x); +} + + /* return true if the text box should be drawn. + We don't show object boxes inside graphs. */ +int text_shouldvis(t_text *x, t_glist *glist) +{ + return (glist->gl_havewindow || + (x->te_pd != canvas_class && x->te_pd->c_wb != &text_widgetbehavior) || + (x->te_pd == canvas_class && (((t_glist *)x)->gl_isgraph))); +} + +static void text_vis(t_gobj *z, t_glist *glist, int vis) +{ + t_text *x = (t_text *)z; + if (vis) + { + if (text_shouldvis(x, glist)) + { + t_rtext *y = glist_findrtext(glist, x); + if (x->te_type == T_ATOM) + glist_retext(glist, x); + text_drawborder(x, glist, rtext_gettag(y), + rtext_width(y), rtext_height(y), 1); + rtext_draw(y); + } + } + else + { + t_rtext *y = glist_findrtext(glist, x); + if (text_shouldvis(x, glist)) + { + text_eraseborder(x, glist, rtext_gettag(y)); + rtext_erase(y); + } + } +} + +static int text_click(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_text *x = (t_text *)z; + if (x->te_type == T_OBJECT) + { + t_symbol *clicksym = gensym("click"); + if (zgetfn(&x->te_pd, clicksym)) + { + if (doit) + pd_vmess(&x->te_pd, clicksym, "fffff", + (double)xpix, (double)ypix, + (double)shift, 0, (double)alt); + return (1); + } + else return (0); + } + else if (x->te_type == T_ATOM) + { + if (doit) + gatom_click((t_gatom *)x, (t_floatarg)xpix, (t_floatarg)ypix, + (t_floatarg)shift, 0, (t_floatarg)alt); + return (1); + } + else if (x->te_type == T_MESSAGE) + { + if (doit) + message_click((t_message *)x, (t_floatarg)xpix, (t_floatarg)ypix, + (t_floatarg)shift, 0, (t_floatarg)alt); + return (1); + } + else return (0); +} + +void text_save(t_gobj *z, t_binbuf *b) +{ + t_text *x = (t_text *)z; + if (x->te_type == T_OBJECT) + { + /* if we have a "saveto" method, and if we don't happen to be + a canvas that's an abstraction, the saveto method does the work */ + if (zgetfn(&x->te_pd, gensym("saveto")) && + !((pd_class(&x->te_pd) == canvas_class) && + (canvas_isabstraction((t_canvas *)x) + || canvas_istable((t_canvas *)x)))) + { + mess1(&x->te_pd, gensym("saveto"), b); + binbuf_addv(b, "ssii", gensym("#X"), gensym("restore"), + (t_int)x->te_xpix, (t_int)x->te_ypix); + } + else /* otherwise just save the text */ + { + binbuf_addv(b, "ssii", gensym("#X"), gensym("obj"), + (t_int)x->te_xpix, (t_int)x->te_ypix); + } + binbuf_addbinbuf(b, x->te_binbuf); + binbuf_addv(b, ";"); + } + else if (x->te_type == T_MESSAGE) + { + binbuf_addv(b, "ssii", gensym("#X"), gensym("msg"), + (t_int)x->te_xpix, (t_int)x->te_ypix); + binbuf_addbinbuf(b, x->te_binbuf); + binbuf_addv(b, ";"); + } + else if (x->te_type == T_ATOM) + { + t_atomtype t = ((t_gatom *)x)->a_atom.a_type; + t_symbol *sel = (t == A_SYMBOL ? gensym("symbolatom") : + (t == A_FLOAT ? gensym("floatatom") : gensym("intatom"))); + t_symbol *label = gatom_escapit(((t_gatom *)x)->a_label); + t_symbol *symfrom = gatom_escapit(((t_gatom *)x)->a_symfrom); + t_symbol *symto = gatom_escapit(((t_gatom *)x)->a_symto); + binbuf_addv(b, "ssiiifffsss", gensym("#X"), sel, + (t_int)x->te_xpix, (t_int)x->te_ypix, (t_int)x->te_width, + (double)((t_gatom *)x)->a_draglo, + (double)((t_gatom *)x)->a_draghi, + (double)((t_gatom *)x)->a_wherelabel, + label, symfrom, symto); + binbuf_addv(b, ";"); + } + else + { + binbuf_addv(b, "ssii", gensym("#X"), gensym("text"), + (t_int)x->te_xpix, (t_int)x->te_ypix); + binbuf_addbinbuf(b, x->te_binbuf); + binbuf_addv(b, ";"); + } +} + + /* this one is for everyone but "gatoms"; it's imposed in m_class.c */ +t_widgetbehavior text_widgetbehavior = +{ + text_getrect, + text_displace, + text_select, + text_activate, + text_delete, + text_vis, + text_click, +}; + +static t_widgetbehavior gatom_widgetbehavior = +{ + text_getrect, + gatom_displace, + text_select, + text_activate, + text_delete, + gatom_vis, + text_click, +}; + +/* -------------------- the "text" class ------------ */ + +#ifdef MACOSX +#define EXTRAPIX 2 +#else +#define EXTRAPIX 1 +#endif + + /* draw inlets and outlets for a text object or for a graph. */ +void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime, + char *tag, int x1, int y1, int x2, int y2) +{ + int n = obj_noutlets(ob), nplus = (n == 1 ? 1 : n-1), i; + int width = x2 - x1; + for (i = 0; i < n; i++) + { + int onset = x1 + (width - IOWIDTH) * i / nplus; + if (firsttime) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %so%d\n", + glist_getcanvas(glist), + onset, y2 - 1, + onset + IOWIDTH, y2, + tag, i); + else + sys_vgui(".x%x.c coords %so%d %d %d %d %d\n", + glist_getcanvas(glist), tag, i, + onset, y2 - 1, + onset + IOWIDTH, y2); + } + n = obj_ninlets(ob); + nplus = (n == 1 ? 1 : n-1); + for (i = 0; i < n; i++) + { + int onset = x1 + (width - IOWIDTH) * i / nplus; + if (firsttime) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %si%d\n", + glist_getcanvas(glist), + onset, y1, + onset + IOWIDTH, y1 + EXTRAPIX, + tag, i); + else + sys_vgui(".x%x.c coords %si%d %d %d %d %d\n", + glist_getcanvas(glist), tag, i, + onset, y1, + onset + IOWIDTH, y1 + EXTRAPIX); + } +} + +void text_drawborder(t_text *x, t_glist *glist, + char *tag, int width2, int height2, int firsttime) +{ + t_object *ob; + int x1, y1, x2, y2, width, height; + text_getrect(&x->te_g, glist, &x1, &y1, &x2, &y2); + width = x2 - x1; + height = y2 - y1; + if (x->te_type == T_OBJECT) + { + if (firsttime) + sys_vgui(".x%x.c create line\ + %d %d %d %d %d %d %d %d %d %d -tags %sR\n", + glist_getcanvas(glist), + x1, y1, x2, y1, x2, y2, x1, y2, x1, y1, tag); + else + sys_vgui(".x%x.c coords %sR\ + %d %d %d %d %d %d %d %d %d %d\n", + glist_getcanvas(glist), tag, + x1, y1, x2, y1, x2, y2, x1, y2, x1, y1); + } + else if (x->te_type == T_MESSAGE) + { + if (firsttime) + sys_vgui(".x%x.c create line\ + %d %d %d %d %d %d %d %d %d %d %d %d %d %d -tags %sR\n", + glist_getcanvas(glist), + x1, y1, x2+4, y1, x2, y1+4, x2, y2-4, x2+4, y2, + x1, y2, x1, y1, + tag); + else + sys_vgui(".x%x.c coords %sR\ + %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", + glist_getcanvas(glist), tag, + x1, y1, x2+4, y1, x2, y1+4, x2, y2-4, x2+4, y2, + x1, y2, x1, y1); + } + else if (x->te_type == T_ATOM) + { + if (firsttime) + sys_vgui(".x%x.c create line\ + %d %d %d %d %d %d %d %d %d %d %d %d -tags %sR\n", + glist_getcanvas(glist), + x1, y1, x2-4, y1, x2, y1+4, x2, y2, x1, y2, x1, y1, + tag); + else + sys_vgui(".x%x.c coords %sR\ + %d %d %d %d %d %d %d %d %d %d %d %d\n", + glist_getcanvas(glist), tag, + x1, y1, x2-4, y1, x2, y1+4, x2, y2, x1, y2, x1, y1); + } + /* draw inlets/outlets */ + + if (ob = pd_checkobject(&x->te_pd)) + glist_drawiofor(glist, ob, firsttime, tag, x1, y1, x2, y2); +} + +void glist_eraseiofor(t_glist *glist, t_object *ob, char *tag) +{ + int i, n; + n = obj_noutlets(ob); + for (i = 0; i < n; i++) + sys_vgui(".x%x.c delete %so%d\n", + glist_getcanvas(glist), tag, i); + n = obj_ninlets(ob); + for (i = 0; i < n; i++) + sys_vgui(".x%x.c delete %si%d\n", + glist_getcanvas(glist), tag, i); +} + +void text_eraseborder(t_text *x, t_glist *glist, char *tag) +{ + if (x->te_type == T_TEXT) return; + sys_vgui(".x%x.c delete %sR\n", + glist_getcanvas(glist), tag); + glist_eraseiofor(glist, x, tag); +} + + /* change text; if T_OBJECT, remake it. LATER we'll have an undo buffer + which should be filled in here before making the change. */ + +void text_setto(t_text *x, t_glist *glist, char *buf, int bufsize) +{ + if (x->te_type == T_OBJECT) + { + t_binbuf *b = binbuf_new(); + int natom1, natom2; + t_atom *vec1, *vec2; + binbuf_text(b, buf, bufsize); + natom1 = binbuf_getnatom(x->te_binbuf); + vec1 = binbuf_getvec(x->te_binbuf); + natom2 = binbuf_getnatom(b); + vec2 = binbuf_getvec(b); + /* special case: if pd args change just pass the message on. */ + if (natom1 >= 1 && natom2 >= 1 && vec1[0].a_type == A_SYMBOL + && !strcmp(vec1[0].a_w.w_symbol->s_name, "pd") && + vec2[0].a_type == A_SYMBOL + && !strcmp(vec2[0].a_w.w_symbol->s_name, "pd")) + { + typedmess(&x->te_pd, gensym("rename"), natom2-1, vec2+1); + binbuf_free(x->te_binbuf); + x->te_binbuf = b; + } + else /* normally, just destroy the old one and make a new one. */ + { + int xwas = x->te_xpix, ywas = x->te_ypix; + glist_delete(glist, &x->te_g); + canvas_objtext(glist, xwas, ywas, 0, b); + /* if it's an abstraction loadbang it here */ + if (newest && pd_class(newest) == canvas_class) + canvas_loadbang((t_canvas *)newest); + canvas_restoreconnections(glist_getcanvas(glist)); + } + /* if we made a new "pd" or changed a window name, + update window list */ + if (natom2 >= 1 && vec2[0].a_type == A_SYMBOL + && !strcmp(vec2[0].a_w.w_symbol->s_name, "pd")) + canvas_updatewindowlist(); + } + else binbuf_text(x->te_binbuf, buf, bufsize); +} + +void g_text_setup(void) +{ + text_class = class_new(gensym("text"), 0, 0, sizeof(t_text), + CLASS_NOINLET | CLASS_PATCHABLE, 0); + + message_class = class_new(gensym("message"), 0, (t_method)message_free, + sizeof(t_message), CLASS_PATCHABLE, 0); + class_addbang(message_class, message_bang); + class_addfloat(message_class, message_float); + class_addsymbol(message_class, message_symbol); + class_addlist(message_class, message_list); + class_addanything(message_class, message_list); + + class_addmethod(message_class, (t_method)message_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(message_class, (t_method)message_set, gensym("set"), + A_GIMME, 0); + class_addmethod(message_class, (t_method)message_add, gensym("add"), + A_GIMME, 0); + class_addmethod(message_class, (t_method)message_add2, gensym("add2"), + A_GIMME, 0); + + messresponder_class = class_new(gensym("messresponder"), 0, 0, + sizeof(t_text), CLASS_PD, 0); + class_addbang(messresponder_class, messresponder_bang); + class_addfloat(messresponder_class, (t_method) messresponder_float); + class_addsymbol(messresponder_class, messresponder_symbol); + class_addlist(messresponder_class, messresponder_list); + class_addanything(messresponder_class, messresponder_anything); + + gatom_class = class_new(gensym("gatom"), 0, (t_method)gatom_free, + sizeof(t_gatom), CLASS_NOINLET | CLASS_PATCHABLE, 0); + class_addbang(gatom_class, gatom_bang); + class_addfloat(gatom_class, gatom_float); + class_addsymbol(gatom_class, gatom_symbol); + class_addmethod(gatom_class, (t_method)gatom_set, gensym("set"), + A_GIMME, 0); + class_addmethod(gatom_class, (t_method)gatom_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(gatom_class, (t_method)gatom_param, gensym("param"), + A_GIMME, 0); + class_setwidget(gatom_class, &gatom_widgetbehavior); + class_setpropertiesfn(gatom_class, gatom_properties); +} + + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* changes by Thomas Musil IEM KUG Graz Austria 2001 */ +/* the methods for calling the gui-objects from menu are implemented */ +/* all changes are labeled with iemlib */ + +#include +#include "m_pd.h" +#include "m_imp.h" +#include "s_stuff.h" +#include "t_tk.h" +#include "g_canvas.h" +#include +#include +#include + +static t_class *text_class; +static t_class *message_class; +static t_class *gatom_class; +static void text_vis(t_gobj *z, t_glist *glist, int vis); +static void text_displace(t_gobj *z, t_glist *glist, + int dx, int dy); +static void text_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2); + +void canvas_startmotion(t_canvas *x); +t_widgetbehavior text_widgetbehavior; + +/* ----------------- the "text" object. ------------------ */ + + /* add a "text" object (comment) to a glist. While this one goes for any glist, + the other 3 below are for canvases only. (why?) This is called + without args if invoked from the GUI; otherwise at least x and y + are provided. */ + +void glist_text(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + t_text *x = (t_text *)pd_new(text_class); + t_atom at; + x->te_width = 0; /* don't know it yet. */ + x->te_type = T_TEXT; + x->te_binbuf = binbuf_new(); + if (argc > 1) + { + x->te_xpix = atom_getfloatarg(0, argc, argv); + x->te_ypix = atom_getfloatarg(1, argc, argv); + if (argc > 2) binbuf_restore(x->te_binbuf, argc-2, argv+2); + else + { + SETSYMBOL(&at, gensym("comment")); + binbuf_restore(x->te_binbuf, 1, &at); + } + glist_add(gl, &x->te_g); + } + else + { + int xpix, ypix; + pd_vmess((t_pd *)glist_getcanvas(gl), gensym("editmode"), "i", 1); + SETSYMBOL(&at, gensym("comment")); + glist_noselect(gl); + glist_getnextxy(gl, &xpix, &ypix); + x->te_xpix = glist_pixelstox(gl, xpix-3); + x->te_ypix = glist_pixelstoy(gl, ypix-3); + binbuf_restore(x->te_binbuf, 1, &at); + glist_add(gl, &x->te_g); + glist_noselect(gl); + glist_select(gl, &x->te_g); + /* it would be nice to "activate" here, but then the second, + "put-me-down" click changes the text selection, which is quite + irritating, so I took this back out. It's OK in messages + and objects though since there's no text in them at menu + creation. */ + /* gobj_activate(&x->te_g, gl, 1); */ + canvas_startmotion(glist_getcanvas(gl)); + } +} + +/* ----------------- the "object" object. ------------------ */ + +extern t_pd *newest; +void canvas_getargs(int *argcp, t_atom **argvp); + +static void canvas_objtext(t_glist *gl, int xpix, int ypix, int selected, + t_binbuf *b) +{ + t_text *x; + int argc; + t_atom *argv; + newest = 0; + canvas_setcurrent((t_canvas *)gl); + canvas_getargs(&argc, &argv); + binbuf_eval(b, &pd_objectmaker, argc, argv); + if (binbuf_getnatom(b)) + { + if (!newest) + { + binbuf_print(b); + post("... couldn't create"); + x = 0; + } + else if (!(x = pd_checkobject(newest))) + { + binbuf_print(b); + post("... didn't return a patchable object"); + } + } + else x = 0; + if (!x) + { + + /* LATER make the color reflect this */ + x = (t_text *)pd_new(text_class); + } + x->te_binbuf = b; + x->te_xpix = xpix; + x->te_ypix = ypix; + x->te_width = 0; + x->te_type = T_OBJECT; + glist_add(gl, &x->te_g); + if (selected) + { + /* this is called if we've been created from the menu. */ + glist_select(gl, &x->te_g); + gobj_activate(&x->te_g, gl, 1); + } + if (pd_class(&x->ob_pd) == vinlet_class) + canvas_resortinlets(glist_getcanvas(gl)); + if (pd_class(&x->ob_pd) == voutlet_class) + canvas_resortoutlets(glist_getcanvas(gl)); + canvas_unsetcurrent((t_canvas *)gl); +} + + /* object creation routine. These are called without any arguments if + they're invoked from the + gui; when pasting or restoring from a file, we get at least x and y. */ + +void canvas_obj(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + t_text *x; + if (argc >= 2) + { + t_binbuf *b = binbuf_new(); + binbuf_restore(b, argc-2, argv+2); + canvas_objtext(gl, atom_getintarg(0, argc, argv), + atom_getintarg(1, argc, argv), 0, b); + } + else + { + t_binbuf *b = binbuf_new(); + int xpix, ypix; + pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1); + glist_noselect(gl); + glist_getnextxy(gl, &xpix, &ypix); + canvas_objtext(gl, xpix, ypix, 1, b); + canvas_startmotion(glist_getcanvas(gl)); + } +} + +/* make an object box for an object that's already there. */ + +/* iemlib */ +void canvas_iemguis(t_glist *gl, t_symbol *guiobjname) +{ + t_atom at; + t_binbuf *b = binbuf_new(); + int xpix, ypix; + + pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1); + glist_noselect(gl); + SETSYMBOL(&at, guiobjname); + binbuf_restore(b, 1, &at); + glist_getnextxy(gl, &xpix, &ypix); + canvas_objtext(gl, xpix, ypix, 1, b); + canvas_startmotion(glist_getcanvas(gl)); +} + +void canvas_bng(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("bng")); +} + +void canvas_toggle(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("tgl")); +} + +void canvas_vslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("vsl")); +} + +void canvas_hslider(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("hsl")); +} + +void canvas_hdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("hdl")); +} + +void canvas_vdial(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("vdl")); +} + +void canvas_hradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("hradio")); +} + +void canvas_vradio(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("vradio")); +} + +void canvas_vumeter(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("vu")); +} + +void canvas_mycnv(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("cnv")); +} + +void canvas_numbox(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_iemguis(gl, gensym("nbx")); +} + +/* iemlib */ + +void canvas_objfor(t_glist *gl, t_text *x, int argc, t_atom *argv) +{ + x->te_width = 0; /* don't know it yet. */ + x->te_type = T_OBJECT; + x->te_binbuf = binbuf_new(); + x->te_xpix = atom_getfloatarg(0, argc, argv); + x->te_ypix = atom_getfloatarg(1, argc, argv); + if (argc > 2) binbuf_restore(x->te_binbuf, argc-2, argv+2); + glist_add(gl, &x->te_g); +} + +/* ---------------------- the "message" text item ------------------------ */ + +typedef struct _messresponder +{ + t_pd mr_pd; + t_outlet *mr_outlet; +} t_messresponder; + +typedef struct _message +{ + t_text m_text; + t_messresponder m_messresponder; + t_glist *m_glist; + t_clock *m_clock; +} t_message; + +static t_class *message_class, *messresponder_class; + +static void messresponder_bang(t_messresponder *x) +{ + outlet_bang(x->mr_outlet); +} + +static void messresponder_float(t_messresponder *x, t_float f) +{ + outlet_float(x->mr_outlet, f); +} + +static void messresponder_symbol(t_messresponder *x, t_symbol *s) +{ + outlet_symbol(x->mr_outlet, s); +} + +static void messresponder_list(t_messresponder *x, + t_symbol *s, int argc, t_atom *argv) +{ + outlet_list(x->mr_outlet, s, argc, argv); +} + +static void messresponder_anything(t_messresponder *x, + t_symbol *s, int argc, t_atom *argv) +{ + outlet_anything(x->mr_outlet, s, argc, argv); +} + +static void message_bang(t_message *x) +{ + binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 0, 0); +} + +static void message_float(t_message *x, t_float f) +{ + t_atom at; + SETFLOAT(&at, f); + binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 1, &at); +} + +static void message_symbol(t_message *x, t_symbol *s) +{ + t_atom at; + SETSYMBOL(&at, s); + binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, 1, &at); +} + +static void message_list(t_message *x, t_symbol *s, int argc, t_atom *argv) +{ + binbuf_eval(x->m_text.te_binbuf, &x->m_messresponder.mr_pd, argc, argv); +} + +static void message_set(t_message *x, t_symbol *s, int argc, t_atom *argv) +{ + binbuf_clear(x->m_text.te_binbuf); + binbuf_add(x->m_text.te_binbuf, argc, argv); + glist_retext(x->m_glist, &x->m_text); +} + +static void message_add2(t_message *x, t_symbol *s, int argc, t_atom *argv) +{ + binbuf_add(x->m_text.te_binbuf, argc, argv); + glist_retext(x->m_glist, &x->m_text); +} + +static void message_add(t_message *x, t_symbol *s, int argc, t_atom *argv) +{ + binbuf_add(x->m_text.te_binbuf, argc, argv); + binbuf_addsemi(x->m_text.te_binbuf); + glist_retext(x->m_glist, &x->m_text); +} + +static void message_click(t_message *x, + t_floatarg xpos, t_floatarg ypos, t_floatarg shift, + t_floatarg ctrl, t_floatarg alt) +{ + message_float(x, 0); + if (glist_isvisible(x->m_glist)) + { + t_rtext *y = glist_findrtext(x->m_glist, &x->m_text); + sys_vgui(".x%x.c itemconfigure %sR -width 5\n", + glist_getcanvas(x->m_glist), rtext_gettag(y)); + clock_delay(x->m_clock, 120); + } +} + +static void message_tick(t_message *x) +{ + if (glist_isvisible(x->m_glist)) + { + t_rtext *y = glist_findrtext(x->m_glist, &x->m_text); + sys_vgui(".x%x.c itemconfigure %sR -width 1\n", + glist_getcanvas(x->m_glist), rtext_gettag(y)); + } +} + +static void message_free(t_message *x) +{ + clock_free(x->m_clock); +} + +void canvas_msg(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + t_message *x = (t_message *)pd_new(message_class); + x->m_messresponder.mr_pd = messresponder_class; + x->m_messresponder.mr_outlet = outlet_new(&x->m_text, &s_float); + x->m_text.te_width = 0; /* don't know it yet. */ + x->m_text.te_type = T_MESSAGE; + x->m_text.te_binbuf = binbuf_new(); + x->m_glist = gl; + x->m_clock = clock_new(x, (t_method)message_tick); + if (argc > 1) + { + x->m_text.te_xpix = atom_getfloatarg(0, argc, argv); + x->m_text.te_ypix = atom_getfloatarg(1, argc, argv); + if (argc > 2) binbuf_restore(x->m_text.te_binbuf, argc-2, argv+2); + glist_add(gl, &x->m_text.te_g); + } + else + { + int xpix, ypix; + pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1); + glist_noselect(gl); + glist_getnextxy(gl, &xpix, &ypix); + x->m_text.te_xpix = xpix-3; + x->m_text.te_ypix = ypix-3; + glist_add(gl, &x->m_text.te_g); + glist_noselect(gl); + glist_select(gl, &x->m_text.te_g); + gobj_activate(&x->m_text.te_g, gl, 1); + canvas_startmotion(glist_getcanvas(gl)); + } +} + +/* ---------------------- the "atom" text item ------------------------ */ + +#define ATOMBUFSIZE 40 +#define ATOM_LABELLEFT 0 +#define ATOM_LABELRIGHT 1 +#define ATOM_LABELUP 2 +#define ATOM_LABELDOWN 3 + +typedef struct _gatom +{ + t_text a_text; + t_atom a_atom; /* this holds the value and the type */ + t_glist *a_glist; /* owning glist */ + t_float a_toggle; /* value to toggle to */ + t_float a_draghi; /* high end of drag range */ + t_float a_draglo; /* low end of drag range */ + t_symbol *a_label; /* symbol to show as label next to box */ + t_symbol *a_symfrom; /* "receive" name -- bind ourselvs to this */ + t_symbol *a_symto; /* "send" name -- send to this on output */ + char a_buf[ATOMBUFSIZE];/* string buffer for typing */ + char a_shift; /* was shift key down when dragging started? */ + char a_wherelabel; /* 0-3 for left, right, above, below */ + t_symbol *a_expanded_to; /* a_symto after $0, $1, ... expansion */ +} t_gatom; + + /* prepend "-" as necessary to avoid empty strings, so we can + use them in Pd messages. A more complete solution would be + to introduce some quoting mechanism; but then we'd be much more + complicated. */ +static t_symbol *gatom_escapit(t_symbol *s) +{ + if (!*s->s_name) + return (gensym("-")); + else if (*s->s_name == '-') + { + char shmo[100]; + shmo[0] = '-'; + strncpy(shmo+1, s->s_name, 99); + shmo[99] = 0; + return (gensym(shmo)); + } + else return (iemgui_dollar2raute(s)); +} + + /* undo previous operation: strip leading "-" if found. */ +static t_symbol *gatom_unescapit(t_symbol *s) +{ + if (*s->s_name == '-') + return (gensym(s->s_name+1)); + else return (iemgui_raute2dollar(s)); +} + +#if 0 /* ??? */ + /* expand leading $0, $1, etc. in the symbol */ +static t_symbol *gatom_realizedollar(t_gatom *x, t_symbol *s) +{ + return (canvas_realizedollar(x->a_glist, s)); +} +#endif + +static void gatom_retext(t_gatom *x, int senditup) +{ + binbuf_clear(x->a_text.te_binbuf); + binbuf_add(x->a_text.te_binbuf, 1, &x->a_atom); + if (senditup) + glist_retext(x->a_glist, &x->a_text); +} + +static void gatom_set(t_gatom *x, t_symbol *s, int argc, t_atom *argv) +{ + t_atom oldatom = x->a_atom; + int senditup = 0; + if (!argc) return; + if (x->a_atom.a_type == A_FLOAT) + x->a_atom.a_w.w_float = atom_getfloat(argv), + senditup = (x->a_atom.a_w.w_float != oldatom.a_w.w_float); + else if (x->a_atom.a_type == A_SYMBOL) + x->a_atom.a_w.w_symbol = atom_getsymbol(argv), + senditup = (x->a_atom.a_w.w_symbol != oldatom.a_w.w_symbol); + gatom_retext(x, senditup); + x->a_buf[0] = 0; +} + +static void gatom_bang(t_gatom *x) +{ + if (x->a_atom.a_type == A_FLOAT) + { + if (x->a_text.te_outlet) + outlet_float(x->a_text.te_outlet, x->a_atom.a_w.w_float); + if (*x->a_expanded_to->s_name && x->a_expanded_to->s_thing) + { + if (x->a_symto == x->a_symfrom) + pd_error(x, + "%s: atom with same send/receive name (infinite loop)", + x->a_symto->s_name); + else pd_float(x->a_expanded_to->s_thing, x->a_atom.a_w.w_float); + } + } + else if (x->a_atom.a_type == A_SYMBOL) + { + if (x->a_text.te_outlet) + outlet_symbol(x->a_text.te_outlet, x->a_atom.a_w.w_symbol); + if (*x->a_symto->s_name && x->a_expanded_to->s_thing) + { + if (x->a_symto == x->a_symfrom) + pd_error(x, + "%s: atom with same send/receive name (infinite loop)", + x->a_symto->s_name); + else pd_symbol(x->a_expanded_to->s_thing, x->a_atom.a_w.w_symbol); + } + } +} + +static void gatom_float(t_gatom *x, t_float f) +{ + t_atom at; + SETFLOAT(&at, f); + gatom_set(x, 0, 1, &at); + gatom_bang(x); +} + +static void gatom_clipfloat(t_gatom *x, t_float f) +{ + if (x->a_draglo != 0 || x->a_draghi != 0) + { + if (f < x->a_draglo) + f = x->a_draglo; + if (f > x->a_draghi) + f = x->a_draghi; + } + gatom_float(x, f); +} + +static void gatom_symbol(t_gatom *x, t_symbol *s) +{ + t_atom at; + SETSYMBOL(&at, s); + gatom_set(x, 0, 1, &at); + gatom_bang(x); +} + +static void gatom_motion(void *z, t_floatarg dx, t_floatarg dy) +{ + t_gatom *x = (t_gatom *)z; + if (dy == 0) return; + if (x->a_atom.a_type == A_FLOAT) + { + if (x->a_shift) + { + double nval = x->a_atom.a_w.w_float - 0.01 * dy; + double trunc = 0.01 * (floor(100. * nval + 0.5)); + if (trunc < nval + 0.0001 && trunc > nval - 0.0001) nval = trunc; + gatom_clipfloat(x, nval); + } + else + { + double nval = x->a_atom.a_w.w_float - dy; + double trunc = 0.01 * (floor(100. * nval + 0.5)); + if (trunc < nval + 0.0001 && trunc > nval - 0.0001) nval = trunc; + trunc = floor(nval + 0.5); + if (trunc < nval + 0.001 && trunc > nval - 0.001) nval = trunc; + gatom_clipfloat(x, nval); + } + } +} + +static void gatom_key(void *z, t_floatarg f) +{ + t_gatom *x = (t_gatom *)z; + int c = f; + int len = strlen(x->a_buf); + t_atom at; + char sbuf[ATOMBUFSIZE + 4]; + if (c == 0) + { + /* we're being notified that no more keys will come for this grab */ + if (x->a_buf[0]) + gatom_retext(x, 1); + return; + } + else if (c == ' ') return; + else if (c == '\b') + { + if (len > 0) + x->a_buf[len-1] = 0; + goto redraw; + } + else if (c == '\n') + { + if (x->a_atom.a_type == A_FLOAT) + x->a_atom.a_w.w_float = atof(x->a_buf); + else if (x->a_atom.a_type == A_SYMBOL) + x->a_atom.a_w.w_symbol = gensym(x->a_buf); + else bug("gatom_key"); + gatom_bang(x); + gatom_retext(x, 1); + x->a_buf[0] = 0; + } + else if (len < (ATOMBUFSIZE-1)) + { + /* for numbers, only let reasonable characters through */ + if ((x->a_atom.a_type == A_SYMBOL) || + (c >= '0' && c <= '9' || c == '.' || c == '-' + || c == 'e' || c == 'E')) + { + x->a_buf[len] = c; + x->a_buf[len+1] = 0; + goto redraw; + } + } + return; +redraw: + /* LATER figure out how to avoid creating all these symbols! */ + sprintf(sbuf, "%s...", x->a_buf); + SETSYMBOL(&at, gensym(sbuf)); + binbuf_clear(x->a_text.te_binbuf); + binbuf_add(x->a_text.te_binbuf, 1, &at); + glist_retext(x->a_glist, &x->a_text); +} + +static void gatom_click(t_gatom *x, + t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, + t_floatarg alt) +{ + if (x->a_text.te_width == 1) + { + if (x->a_atom.a_type == A_FLOAT) + gatom_float(x, (x->a_atom.a_w.w_float == 0)); + } + else + { + if (alt) + { + if (x->a_atom.a_type != A_FLOAT) return; + if (x->a_atom.a_w.w_float != 0) + { + x->a_toggle = x->a_atom.a_w.w_float; + gatom_float(x, 0); + return; + } + else gatom_float(x, x->a_toggle); + } + x->a_shift = shift; + x->a_buf[0] = 0; + glist_grab(x->a_glist, &x->a_text.te_g, gatom_motion, gatom_key, + xpos, ypos); + } +} + + /* message back from dialog window */ +static void gatom_param(t_gatom *x, t_symbol *sel, int argc, t_atom *argv) +{ + t_float width = atom_getfloatarg(0, argc, argv); + t_float draglo = atom_getfloatarg(1, argc, argv); + t_float draghi = atom_getfloatarg(2, argc, argv); + t_symbol *label = gatom_unescapit(atom_getsymbolarg(3, argc, argv)); + t_float wherelabel = atom_getfloatarg(4, argc, argv); + t_symbol *symfrom = gatom_unescapit(atom_getsymbolarg(5, argc, argv)); + t_symbol *symto = gatom_unescapit(atom_getsymbolarg(6, argc, argv)); + + gobj_vis(&x->a_text.te_g, x->a_glist, 0); + if (!*symfrom->s_name && *x->a_symfrom->s_name) + inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0); + else if (*symfrom->s_name && !*x->a_symfrom->s_name && x->a_text.te_inlet) + { + canvas_deletelinesforio(x->a_glist, &x->a_text, + x->a_text.te_inlet, 0); + inlet_free(x->a_text.te_inlet); + } + if (!*symto->s_name && *x->a_symto->s_name) + outlet_new(&x->a_text, 0); + else if (*symto->s_name && !*x->a_symto->s_name && x->a_text.te_outlet) + { + canvas_deletelinesforio(x->a_glist, &x->a_text, + 0, x->a_text.te_outlet); + outlet_free(x->a_text.te_outlet); + } + if (draglo >= draghi) + draglo = draghi = 0; + x->a_draglo = draglo; + x->a_draghi = draghi; + if (width < 0) + width = 4; + else if (width > 80) + width = 80; + x->a_text.te_width = width; + x->a_wherelabel = ((int)wherelabel & 3); + x->a_label = label; + if (*x->a_symfrom->s_name) + pd_unbind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); + x->a_symfrom = symfrom; + if (*x->a_symfrom->s_name) + pd_bind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); + x->a_symto = symto; + x->a_expanded_to = canvas_realizedollar(x->a_glist, x->a_symto); + gobj_vis(&x->a_text.te_g, x->a_glist, 1); + + /* glist_retext(x->a_glist, &x->a_text); */ +} + + /* ---------------- gatom-specific widget functions --------------- */ +static void gatom_getwherelabel(t_gatom *x, t_glist *glist, int *xp, int *yp) +{ + int x1, y1, x2, y2, width, height; + text_getrect(&x->a_text.te_g, glist, &x1, &y1, &x2, &y2); + width = x2 - x1; + height = y2 - y1; + if (x->a_wherelabel == ATOM_LABELLEFT) + { + *xp = x1 - 3 - + strlen(canvas_realizedollar(x->a_glist, x->a_label)->s_name) * + sys_fontwidth(glist_getfont(glist)); + *yp = y1 + 2; + } + else if (x->a_wherelabel == ATOM_LABELRIGHT) + { + *xp = x2 + 2; + *yp = y1 + 2; + } + else if (x->a_wherelabel == ATOM_LABELUP) + { + *xp = x1 - 1; + *yp = y1 - 1 - sys_fontheight(glist_getfont(glist));; + } + else + { + *xp = x1 - 1; + *yp = y2 + 3; + } +} + +static void gatom_displace(t_gobj *z, t_glist *glist, + int dx, int dy) +{ + t_gatom *x = (t_gatom*)z; + text_displace(z, glist, dx, dy); + sys_vgui(".x%x.c move %x.l %d %d\n", glist_getcanvas(glist), + x, dx, dy); +} + +static void gatom_vis(t_gobj *z, t_glist *glist, int vis) +{ + t_gatom *x = (t_gatom*)z; + text_vis(z, glist, vis); + if (*x->a_label->s_name) + { + if (vis) + { + int x1, y1; + gatom_getwherelabel(x, glist, &x1, &y1); + sys_vgui("pdtk_text_new .x%x.c %x.l %f %f {%s} %d %s\n", + glist_getcanvas(glist), x, + (double)x1, (double)y1, + canvas_realizedollar(x->a_glist, x->a_label)->s_name, + sys_hostfontsize(glist_getfont(glist)), + "black"); + } + else sys_vgui(".x%x.c delete %x.l\n", glist_getcanvas(glist), x); + } +} + +void canvas_atom(t_glist *gl, t_atomtype type, + t_symbol *s, int argc, t_atom *argv) +{ + t_gatom *x = (t_gatom *)pd_new(gatom_class); + t_atom at; + x->a_text.te_width = 0; /* don't know it yet. */ + x->a_text.te_type = T_ATOM; + x->a_text.te_binbuf = binbuf_new(); + x->a_glist = gl; + x->a_atom.a_type = type; + x->a_toggle = 1; + x->a_draglo = 0; + x->a_draghi = 0; + x->a_wherelabel = 0; + x->a_label = &s_; + x->a_symfrom = &s_; + x->a_symto = x->a_expanded_to = &s_; + if (type == A_FLOAT) + { + x->a_atom.a_w.w_float = 0; + x->a_text.te_width = 5; + SETFLOAT(&at, 0); + } + else + { + x->a_atom.a_w.w_symbol = &s_symbol; + x->a_text.te_width = 10; + SETSYMBOL(&at, &s_symbol); + } + binbuf_add(x->a_text.te_binbuf, 1, &at); + if (argc > 1) + /* create from file. x, y, width, low-range, high-range, flags, + label, receive-name, send-name */ + { + x->a_text.te_xpix = atom_getfloatarg(0, argc, argv); + x->a_text.te_ypix = atom_getfloatarg(1, argc, argv); + x->a_text.te_width = atom_getintarg(2, argc, argv); + /* sanity check because some very old patches have trash in this + field... remove this in 2003 or so: */ + if (x->a_text.te_width < 0 || x->a_text.te_width > 500) + x->a_text.te_width = 4; + x->a_draglo = atom_getfloatarg(3, argc, argv); + x->a_draghi = atom_getfloatarg(4, argc, argv); + x->a_wherelabel = (((int)atom_getfloatarg(5, argc, argv)) & 3); + x->a_label = gatom_unescapit(atom_getsymbolarg(6, argc, argv)); + x->a_symfrom = gatom_unescapit(atom_getsymbolarg(7, argc, argv)); + if (*x->a_symfrom->s_name) + pd_bind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); + + x->a_symto = gatom_unescapit(atom_getsymbolarg(8, argc, argv)); + x->a_expanded_to = canvas_realizedollar(x->a_glist, x->a_symto); + if (x->a_symto == &s_) + outlet_new(&x->a_text, + x->a_atom.a_type == A_FLOAT ? &s_float: &s_symbol); + if (x->a_symfrom == &s_) + inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0); + glist_add(gl, &x->a_text.te_g); + } + else + { + int xpix, ypix; + outlet_new(&x->a_text, + x->a_atom.a_type == A_FLOAT ? &s_float: &s_symbol); + inlet_new(&x->a_text, &x->a_text.te_pd, 0, 0); + pd_vmess(&gl->gl_pd, gensym("editmode"), "i", 1); + glist_noselect(gl); + glist_getnextxy(gl, &xpix, &ypix); + x->a_text.te_xpix = xpix; + x->a_text.te_ypix = ypix; + glist_add(gl, &x->a_text.te_g); + glist_noselect(gl); + glist_select(gl, &x->a_text.te_g); + canvas_startmotion(glist_getcanvas(gl)); + } +} + +void canvas_floatatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_atom(gl, A_FLOAT, s, argc, argv); +} + +void canvas_symbolatom(t_glist *gl, t_symbol *s, int argc, t_atom *argv) +{ + canvas_atom(gl, A_SYMBOL, s, argc, argv); +} + +static void gatom_free(t_gatom *x) +{ + if (*x->a_symfrom->s_name) + pd_unbind(&x->a_text.te_pd, + canvas_realizedollar(x->a_glist, x->a_symfrom)); + gfxstub_deleteforkey(x); +} + +static void gatom_properties(t_gobj *z, t_glist *owner) +{ + t_gatom *x = (t_gatom *)z; + char buf[200]; + sprintf(buf, "pdtk_gatom_dialog %%s %d %g %g %d %s %s %s\n", + x->a_text.te_width, x->a_draglo, x->a_draghi, + x->a_wherelabel, gatom_escapit(x->a_label)->s_name, + gatom_escapit(x->a_symfrom)->s_name, + gatom_escapit(x->a_symto)->s_name); + gfxstub_new(&x->a_text.te_pd, x, buf); +} + + +/* -------------------- widget behavior for text objects ------------ */ + +static void text_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_text *x = (t_text *)z; + int width, height, iscomment = (x->te_type == T_TEXT); + float x1, y1, x2, y2; + + /* for number boxes, we know width and height a priori, and should + report them here so that graphs can get swelled to fit. */ + + if (x->te_type == T_ATOM && x->te_width > 0) + { + int font = glist_getfont(glist); + int fontwidth = sys_fontwidth(font), fontheight = sys_fontheight(font); + width = (x->te_width > 0 ? x->te_width : 6) * fontwidth + 2; + height = fontheight + 1; /* borrowed from TMARGIN, etc, in g_rtext.c */ + } + /* if we're invisible we don't know our size so we just lie about + it. This is called on invisible boxes to establish order of inlets + and possibly other reasons. + To find out if the box is visible we can't just check the "vis" + flag because we might be within the vis() routine and not have set + that yet. So we check directly whether the "rtext" list has been + built. LATER reconsider when "vis" flag should be on and off? */ + + else if (glist->gl_editor && glist->gl_editor->e_rtext) + { + t_rtext *y = glist_findrtext(glist, x); + width = rtext_width(y); + height = rtext_height(y) - (iscomment << 1); + } + else width = height = 10; + x1 = text_xpix(x, glist); + y1 = text_ypix(x, glist); + x2 = x1 + width; + y2 = y1 + height; + y1 += iscomment; + *xp1 = x1; + *yp1 = y1; + *xp2 = x2; + *yp2 = y2; +} + +static void text_displace(t_gobj *z, t_glist *glist, + int dx, int dy) +{ + t_text *x = (t_text *)z; + x->te_xpix += dx; + x->te_ypix += dy; + if (glist_isvisible(glist)) + { + t_rtext *y = glist_findrtext(glist, x); + rtext_displace(y, dx, dy); + text_drawborder(x, glist, rtext_gettag(y), + rtext_width(y), rtext_height(y), 0); + canvas_fixlinesfor(glist_getcanvas(glist), x); + } +} + +static void text_select(t_gobj *z, t_glist *glist, int state) +{ + t_text *x = (t_text *)z; + t_rtext *y = glist_findrtext(glist, x); + rtext_select(y, state); + if (glist_isvisible(glist) && text_shouldvis(x, glist)) + sys_vgui(".x%x.c itemconfigure %sR -fill %s\n", glist, + rtext_gettag(y), (state? "blue" : "black")); +} + +static void text_activate(t_gobj *z, t_glist *glist, int state) +{ + t_text *x = (t_text *)z; + t_rtext *y = glist_findrtext(glist, x); + if (z->g_pd != gatom_class) rtext_activate(y, state); +} + +static void text_delete(t_gobj *z, t_glist *glist) +{ + t_text *x = (t_text *)z; + canvas_deletelinesfor(glist, x); +} + + /* return true if the text box should be drawn. + We don't show object boxes inside graphs. */ +int text_shouldvis(t_text *x, t_glist *glist) +{ + return (glist->gl_havewindow || + (x->te_pd != canvas_class && x->te_pd->c_wb != &text_widgetbehavior) || + (x->te_pd == canvas_class && (((t_glist *)x)->gl_isgraph))); +} + +static void text_vis(t_gobj *z, t_glist *glist, int vis) +{ + t_text *x = (t_text *)z; + if (vis) + { + if (text_shouldvis(x, glist)) + { + t_rtext *y = glist_findrtext(glist, x); + if (x->te_type == T_ATOM) + glist_retext(glist, x); + text_drawborder(x, glist, rtext_gettag(y), + rtext_width(y), rtext_height(y), 1); + rtext_draw(y); + } + } + else + { + t_rtext *y = glist_findrtext(glist, x); + if (text_shouldvis(x, glist)) + { + text_eraseborder(x, glist, rtext_gettag(y)); + rtext_erase(y); + } + } +} + +static int text_click(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_text *x = (t_text *)z; + if (x->te_type == T_OBJECT) + { + t_symbol *clicksym = gensym("click"); + if (zgetfn(&x->te_pd, clicksym)) + { + if (doit) + pd_vmess(&x->te_pd, clicksym, "fffff", + (double)xpix, (double)ypix, + (double)shift, 0, (double)alt); + return (1); + } + else return (0); + } + else if (x->te_type == T_ATOM) + { + if (doit) + gatom_click((t_gatom *)x, (t_floatarg)xpix, (t_floatarg)ypix, + (t_floatarg)shift, 0, (t_floatarg)alt); + return (1); + } + else if (x->te_type == T_MESSAGE) + { + if (doit) + message_click((t_message *)x, (t_floatarg)xpix, (t_floatarg)ypix, + (t_floatarg)shift, 0, (t_floatarg)alt); + return (1); + } + else return (0); +} + +void text_save(t_gobj *z, t_binbuf *b) +{ + t_text *x = (t_text *)z; + if (x->te_type == T_OBJECT) + { + /* if we have a "saveto" method, and if we don't happen to be + a canvas that's an abstraction, the saveto method does the work */ + if (zgetfn(&x->te_pd, gensym("saveto")) && + !((pd_class(&x->te_pd) == canvas_class) && + (canvas_isabstraction((t_canvas *)x) + || canvas_istable((t_canvas *)x)))) + { + mess1(&x->te_pd, gensym("saveto"), b); + binbuf_addv(b, "ssii", gensym("#X"), gensym("restore"), + (t_int)x->te_xpix, (t_int)x->te_ypix); + } + else /* otherwise just save the text */ + { + binbuf_addv(b, "ssii", gensym("#X"), gensym("obj"), + (t_int)x->te_xpix, (t_int)x->te_ypix); + } + binbuf_addbinbuf(b, x->te_binbuf); + binbuf_addv(b, ";"); + } + else if (x->te_type == T_MESSAGE) + { + binbuf_addv(b, "ssii", gensym("#X"), gensym("msg"), + (t_int)x->te_xpix, (t_int)x->te_ypix); + binbuf_addbinbuf(b, x->te_binbuf); + binbuf_addv(b, ";"); + } + else if (x->te_type == T_ATOM) + { + t_atomtype t = ((t_gatom *)x)->a_atom.a_type; + t_symbol *sel = (t == A_SYMBOL ? gensym("symbolatom") : + (t == A_FLOAT ? gensym("floatatom") : gensym("intatom"))); + t_symbol *label = gatom_escapit(((t_gatom *)x)->a_label); + t_symbol *symfrom = gatom_escapit(((t_gatom *)x)->a_symfrom); + t_symbol *symto = gatom_escapit(((t_gatom *)x)->a_symto); + binbuf_addv(b, "ssiiifffsss", gensym("#X"), sel, + (t_int)x->te_xpix, (t_int)x->te_ypix, (t_int)x->te_width, + (double)((t_gatom *)x)->a_draglo, + (double)((t_gatom *)x)->a_draghi, + (double)((t_gatom *)x)->a_wherelabel, + label, symfrom, symto); + binbuf_addv(b, ";"); + } + else + { + binbuf_addv(b, "ssii", gensym("#X"), gensym("text"), + (t_int)x->te_xpix, (t_int)x->te_ypix); + binbuf_addbinbuf(b, x->te_binbuf); + binbuf_addv(b, ";"); + } +} + + /* this one is for everyone but "gatoms"; it's imposed in m_class.c */ +t_widgetbehavior text_widgetbehavior = +{ + text_getrect, + text_displace, + text_select, + text_activate, + text_delete, + text_vis, + text_click, +}; + +static t_widgetbehavior gatom_widgetbehavior = +{ + text_getrect, + gatom_displace, + text_select, + text_activate, + text_delete, + gatom_vis, + text_click, +}; + +/* -------------------- the "text" class ------------ */ + +#ifdef MACOSX +#define EXTRAPIX 2 +#else +#define EXTRAPIX 1 +#endif + + /* draw inlets and outlets for a text object or for a graph. */ +void glist_drawiofor(t_glist *glist, t_object *ob, int firsttime, + char *tag, int x1, int y1, int x2, int y2) +{ + int n = obj_noutlets(ob), nplus = (n == 1 ? 1 : n-1), i; + int width = x2 - x1; + for (i = 0; i < n; i++) + { + int onset = x1 + (width - IOWIDTH) * i / nplus; + if (firsttime) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %so%d\n", + glist_getcanvas(glist), + onset, y2 - 1, + onset + IOWIDTH, y2, + tag, i); + else + sys_vgui(".x%x.c coords %so%d %d %d %d %d\n", + glist_getcanvas(glist), tag, i, + onset, y2 - 1, + onset + IOWIDTH, y2); + } + n = obj_ninlets(ob); + nplus = (n == 1 ? 1 : n-1); + for (i = 0; i < n; i++) + { + int onset = x1 + (width - IOWIDTH) * i / nplus; + if (firsttime) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %si%d\n", + glist_getcanvas(glist), + onset, y1, + onset + IOWIDTH, y1 + EXTRAPIX, + tag, i); + else + sys_vgui(".x%x.c coords %si%d %d %d %d %d\n", + glist_getcanvas(glist), tag, i, + onset, y1, + onset + IOWIDTH, y1 + EXTRAPIX); + } +} + +void text_drawborder(t_text *x, t_glist *glist, + char *tag, int width2, int height2, int firsttime) +{ + t_object *ob; + int x1, y1, x2, y2, width, height; + text_getrect(&x->te_g, glist, &x1, &y1, &x2, &y2); + width = x2 - x1; + height = y2 - y1; + if (x->te_type == T_OBJECT) + { + if (firsttime) + sys_vgui(".x%x.c create line\ + %d %d %d %d %d %d %d %d %d %d -tags %sR\n", + glist_getcanvas(glist), + x1, y1, x2, y1, x2, y2, x1, y2, x1, y1, tag); + else + sys_vgui(".x%x.c coords %sR\ + %d %d %d %d %d %d %d %d %d %d\n", + glist_getcanvas(glist), tag, + x1, y1, x2, y1, x2, y2, x1, y2, x1, y1); + } + else if (x->te_type == T_MESSAGE) + { + if (firsttime) + sys_vgui(".x%x.c create line\ + %d %d %d %d %d %d %d %d %d %d %d %d %d %d -tags %sR\n", + glist_getcanvas(glist), + x1, y1, x2+4, y1, x2, y1+4, x2, y2-4, x2+4, y2, + x1, y2, x1, y1, + tag); + else + sys_vgui(".x%x.c coords %sR\ + %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", + glist_getcanvas(glist), tag, + x1, y1, x2+4, y1, x2, y1+4, x2, y2-4, x2+4, y2, + x1, y2, x1, y1); + } + else if (x->te_type == T_ATOM) + { + if (firsttime) + sys_vgui(".x%x.c create line\ + %d %d %d %d %d %d %d %d %d %d %d %d -tags %sR\n", + glist_getcanvas(glist), + x1, y1, x2-4, y1, x2, y1+4, x2, y2, x1, y2, x1, y1, + tag); + else + sys_vgui(".x%x.c coords %sR\ + %d %d %d %d %d %d %d %d %d %d %d %d\n", + glist_getcanvas(glist), tag, + x1, y1, x2-4, y1, x2, y1+4, x2, y2, x1, y2, x1, y1); + } + /* draw inlets/outlets */ + + if (ob = pd_checkobject(&x->te_pd)) + glist_drawiofor(glist, ob, firsttime, tag, x1, y1, x2, y2); +} + +void glist_eraseiofor(t_glist *glist, t_object *ob, char *tag) +{ + int i, n; + n = obj_noutlets(ob); + for (i = 0; i < n; i++) + sys_vgui(".x%x.c delete %so%d\n", + glist_getcanvas(glist), tag, i); + n = obj_ninlets(ob); + for (i = 0; i < n; i++) + sys_vgui(".x%x.c delete %si%d\n", + glist_getcanvas(glist), tag, i); +} + +void text_eraseborder(t_text *x, t_glist *glist, char *tag) +{ + if (x->te_type == T_TEXT) return; + sys_vgui(".x%x.c delete %sR\n", + glist_getcanvas(glist), tag); + glist_eraseiofor(glist, x, tag); +} + + /* change text; if T_OBJECT, remake it. LATER we'll have an undo buffer + which should be filled in here before making the change. */ + +void text_setto(t_text *x, t_glist *glist, char *buf, int bufsize) +{ + if (x->te_type == T_OBJECT) + { + t_binbuf *b = binbuf_new(); + int natom1, natom2; + t_atom *vec1, *vec2; + binbuf_text(b, buf, bufsize); + natom1 = binbuf_getnatom(x->te_binbuf); + vec1 = binbuf_getvec(x->te_binbuf); + natom2 = binbuf_getnatom(b); + vec2 = binbuf_getvec(b); + /* special case: if pd args change just pass the message on. */ + if (natom1 >= 1 && natom2 >= 1 && vec1[0].a_type == A_SYMBOL + && !strcmp(vec1[0].a_w.w_symbol->s_name, "pd") && + vec2[0].a_type == A_SYMBOL + && !strcmp(vec2[0].a_w.w_symbol->s_name, "pd")) + { + typedmess(&x->te_pd, gensym("rename"), natom2-1, vec2+1); + binbuf_free(x->te_binbuf); + x->te_binbuf = b; + } + else /* normally, just destroy the old one and make a new one. */ + { + int xwas = x->te_xpix, ywas = x->te_ypix; + glist_delete(glist, &x->te_g); + canvas_objtext(glist, xwas, ywas, 0, b); + /* if it's an abstraction loadbang it here */ + if (newest && pd_class(newest) == canvas_class) + canvas_loadbang((t_canvas *)newest); + canvas_restoreconnections(glist_getcanvas(glist)); + } + /* if we made a new "pd" or changed a window name, + update window list */ + if (natom2 >= 1 && vec2[0].a_type == A_SYMBOL + && !strcmp(vec2[0].a_w.w_symbol->s_name, "pd")) + canvas_updatewindowlist(); + } + else binbuf_text(x->te_binbuf, buf, bufsize); +} + +void g_text_setup(void) +{ + text_class = class_new(gensym("text"), 0, 0, sizeof(t_text), + CLASS_NOINLET | CLASS_PATCHABLE, 0); + + message_class = class_new(gensym("message"), 0, (t_method)message_free, + sizeof(t_message), CLASS_PATCHABLE, 0); + class_addbang(message_class, message_bang); + class_addfloat(message_class, message_float); + class_addsymbol(message_class, message_symbol); + class_addlist(message_class, message_list); + class_addanything(message_class, message_list); + + class_addmethod(message_class, (t_method)message_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(message_class, (t_method)message_set, gensym("set"), + A_GIMME, 0); + class_addmethod(message_class, (t_method)message_add, gensym("add"), + A_GIMME, 0); + class_addmethod(message_class, (t_method)message_add2, gensym("add2"), + A_GIMME, 0); + + messresponder_class = class_new(gensym("messresponder"), 0, 0, + sizeof(t_text), CLASS_PD, 0); + class_addbang(messresponder_class, messresponder_bang); + class_addfloat(messresponder_class, (t_method) messresponder_float); + class_addsymbol(messresponder_class, messresponder_symbol); + class_addlist(messresponder_class, messresponder_list); + class_addanything(messresponder_class, messresponder_anything); + + gatom_class = class_new(gensym("gatom"), 0, (t_method)gatom_free, + sizeof(t_gatom), CLASS_NOINLET | CLASS_PATCHABLE, 0); + class_addbang(gatom_class, gatom_bang); + class_addfloat(gatom_class, gatom_float); + class_addsymbol(gatom_class, gatom_symbol); + class_addmethod(gatom_class, (t_method)gatom_set, gensym("set"), + A_GIMME, 0); + class_addmethod(gatom_class, (t_method)gatom_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(gatom_class, (t_method)gatom_param, gensym("param"), + A_GIMME, 0); + class_setwidget(gatom_class, &gatom_widgetbehavior); + class_setpropertiesfn(gatom_class, gatom_properties); +} + + diff --git a/apps/plugins/pdbox/PDa/src/g_toggle.c b/apps/plugins/pdbox/PDa/src/g_toggle.c new file mode 100644 index 0000000..b6bb9f3 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_toggle.c @@ -0,0 +1,948 @@ +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + +/* --------------- tgl gui-toggle ------------------------- */ + +t_widgetbehavior toggle_widgetbehavior; +static t_class *toggle_class; + +/* widget helper functions */ + +void toggle_draw_update(t_toggle *x, t_glist *glist) +{ + if(glist_isvisible(glist)) + { + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xX1 -fill #%6.6x\n", canvas, x, + (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xX2 -fill #%6.6x\n", canvas, x, + (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol); + } +} + +void toggle_draw_new(t_toggle *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int w=1, xx=text_xpix(&x->x_gui.x_obj, glist), yy=text_ypix(&x->x_gui.x_obj, glist); + + if(x->x_gui.x_w >= 30) + w = 2; + if(x->x_gui.x_w >= 60) + w = 3; + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n", + canvas, xx, yy, xx + x->x_gui.x_w, yy + x->x_gui.x_h, + x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create line %d %d %d %d -width %d -fill #%6.6x -tags %xX1\n", + canvas, xx+w+1, yy+w+1, xx + x->x_gui.x_w-w, yy + x->x_gui.x_h-w, w, + (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create line %d %d %d %d -width %d -fill #%6.6x -tags %xX2\n", + canvas, xx+w+1, yy + x->x_gui.x_h-w-1, xx + x->x_gui.x_w-w, yy+w, w, + (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xx+x->x_gui.x_ldx, + yy+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xx, yy + x->x_gui.x_h-1, xx + IOWIDTH, yy + x->x_gui.x_h, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xx, yy, xx + IOWIDTH, yy+1, x, 0); +} + +void toggle_draw_move(t_toggle *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int w=1, xx=text_xpix(&x->x_gui.x_obj, glist), yy=text_ypix(&x->x_gui.x_obj, glist); + + if(x->x_gui.x_w >= 30) + w = 2; + + if(x->x_gui.x_w >= 60) + w = 3; + sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n", + canvas, x, xx, yy, xx + x->x_gui.x_w, yy + x->x_gui.x_h); + sys_vgui(".x%x.c itemconfigure %xX1 -width %d\n", canvas, x, w); + sys_vgui(".x%x.c coords %xX1 %d %d %d %d\n", + canvas, x, xx+w+1, yy+w+1, xx + x->x_gui.x_w-w, yy + x->x_gui.x_h-w); + sys_vgui(".x%x.c itemconfigure %xX2 -width %d\n", canvas, x, w); + sys_vgui(".x%x.c coords %xX2 %d %d %d %d\n", + canvas, x, xx+w+1, yy + x->x_gui.x_h-w-1, xx + x->x_gui.x_w-w, yy+w); + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + canvas, x, xx+x->x_gui.x_ldx, yy+x->x_gui.x_ldy); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, xx, yy + x->x_gui.x_h-1, xx + IOWIDTH, yy + x->x_gui.x_h); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, xx, yy, xx + IOWIDTH, yy+1); +} + +void toggle_draw_erase(t_toggle* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c delete %xBASE\n", canvas, x); + sys_vgui(".x%x.c delete %xX1\n", canvas, x); + sys_vgui(".x%x.c delete %xX2\n", canvas, x); + sys_vgui(".x%x.c delete %xLABEL\n", canvas, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void toggle_draw_config(t_toggle* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas, x, + x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xX1 -fill #%6.6x\n", canvas, x, + x->x_on?x->x_gui.x_fcol:x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xX2 -fill #%6.6x\n", canvas, x, + x->x_on?x->x_gui.x_fcol:x->x_gui.x_bcol); +} + +void toggle_draw_io(t_toggle* x, t_glist* glist, int old_snd_rcv_flags) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xpos, + ypos + x->x_gui.x_h-1, xpos + IOWIDTH, + ypos + x->x_gui.x_h, x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xpos, ypos, + xpos + IOWIDTH, ypos+1, x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void toggle_draw_select(t_toggle* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + if(x->x_gui.x_fsf.x_selected) + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol); + } +} + +void toggle_draw(t_toggle *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + toggle_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + toggle_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + toggle_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + toggle_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + toggle_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + toggle_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + toggle_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ tgl widgetbehaviour----------------------------- */ + +static void toggle_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_toggle *x = (t_toggle *)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist); + *yp1 = text_ypix(&x->x_gui.x_obj, glist); + *xp2 = *xp1 + x->x_gui.x_w; + *yp2 = *yp1 + x->x_gui.x_h; +} + +static void toggle_save(t_gobj *z, t_binbuf *b) +{ + t_toggle *x = (t_toggle *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiisssiiiiiiiff", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, + (t_int)x->x_gui.x_obj.te_ypix, + gensym("tgl"), x->x_gui.x_w, + iem_symargstoint(&x->x_gui.x_isa), + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2], x->x_on, x->x_nonzero); + binbuf_addv(b, ";"); +} + +static void toggle_properties(t_gobj *z, t_glist *owner) +{ + t_toggle *x = (t_toggle *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + sprintf(buf, "pdtk_iemgui_dialog %%s TOGGLE \ + ----------dimensions(pix):----------- %d %d size: 0 0 empty \ + -----------non-zero-value:----------- %g value: 0.0 empty %g \ + -1 lin log %d %d empty %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, IEM_GUI_MINSIZE, + x->x_nonzero, 1.0,/*non_zero-schedule*/ + x->x_gui.x_isa.x_loadinit, -1, -1,/*no multi*/ + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void toggle_bang(t_toggle *x) +{ + x->x_on = (x->x_on==0.0)?x->x_nonzero:0.0; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); +} + +static void toggle_dialog(t_toggle *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int a = (int)atom_getintarg(0, argc, argv); + float nonzero = (float)atom_getfloatarg(2, argc, argv); + int sr_flags; + + if(nonzero == 0.0) + nonzero = 1.0; + x->x_nonzero = nonzero; + if(x->x_on != 0.0) + x->x_on = x->x_nonzero; + sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); +} + +static void toggle_click(t_toggle *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{toggle_bang(x);} + +static int toggle_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + if(doit) + toggle_click((t_toggle *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt); + return (1); +} + +static void toggle_set(t_toggle *x, t_floatarg f) +{ + x->x_on = f; + if(f != 0.0) + x->x_nonzero = f; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); +} + +static void toggle_float(t_toggle *x, t_floatarg f) +{ + toggle_set(x, f); + if(x->x_gui.x_fsf.x_put_in2out) + { + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); + } +} + +static void toggle_fout(t_toggle *x, t_floatarg f) +{ + toggle_set(x, f); + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); +} + +static void toggle_loadbang(t_toggle *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + toggle_fout(x, (float)x->x_on); +} + +static void toggle_size(t_toggle *x, t_symbol *s, int ac, t_atom *av) +{ + x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av)); + x->x_gui.x_h = x->x_gui.x_w; + iemgui_size((void *)x, &x->x_gui); +} + +static void toggle_delta(t_toggle *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void toggle_pos(t_toggle *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void toggle_color(t_toggle *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void toggle_send(t_toggle *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void toggle_receive(t_toggle *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void toggle_label(t_toggle *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void toggle_label_font(t_toggle *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void toggle_label_pos(t_toggle *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void toggle_init(t_toggle *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void toggle_nonzero(t_toggle *x, t_floatarg f) +{ + if(f != 0.0) + x->x_nonzero = f; +} + +static void *toggle_new(t_symbol *s, int argc, t_atom *argv) +{ + t_toggle *x = (t_toggle *)pd_new(toggle_class); + int bflcol[]={-262144, -1, -1}; + int a=IEM_GUI_DEFAULTSIZE, f=0; + int ldx=0, ldy=-6; + int fs=8; + float on=0.0, nonzero=1.0; + char str[144]; + + iem_inttosymargs(&x->x_gui.x_isa, 0); + iem_inttofstyle(&x->x_gui.x_fsf, 0); + + if(((argc == 13)||(argc == 14))&&IS_A_FLOAT(argv,0) + &&IS_A_FLOAT(argv,1) + &&(IS_A_SYMBOL(argv,2)||IS_A_FLOAT(argv,2)) + &&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3)) + &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4)) + &&IS_A_FLOAT(argv,5)&&IS_A_FLOAT(argv,6) + &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8)&&IS_A_FLOAT(argv,9) + &&IS_A_FLOAT(argv,10)&&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)) + { + a = (int)atom_getintarg(0, argc, argv); + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(1, argc, argv)); + iemgui_new_getnames(&x->x_gui, 2, argv); + ldx = (int)atom_getintarg(5, argc, argv); + ldy = (int)atom_getintarg(6, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(7, argc, argv)); + fs = (int)atom_getintarg(8, argc, argv); + bflcol[0] = (int)atom_getintarg(9, argc, argv); + bflcol[1] = (int)atom_getintarg(10, argc, argv); + bflcol[2] = (int)atom_getintarg(11, argc, argv); + on = (float)atom_getfloatarg(12, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 2, 0); + if((argc == 14)&&IS_A_FLOAT(argv,13)) + nonzero = (float)atom_getfloatarg(13, argc, argv); + x->x_gui.x_draw = (t_iemfunptr)toggle_draw; + + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if (!strcmp(x->x_gui.x_snd->s_name, "empty")) + x->x_gui.x_fsf.x_snd_able = 0; + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + x->x_nonzero = (nonzero!=0.0)?nonzero:1.0; + if(x->x_gui.x_isa.x_loadinit) + x->x_on = (on!=0.0)?nonzero:0.0; + else + x->x_on = 0.0; + if (x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + iemgui_all_colfromload(&x->x_gui, bflcol); + iemgui_verify_snd_ne_rcv(&x->x_gui); + outlet_new(&x->x_gui.x_obj, &s_float); + return (x); +} + +static void toggle_ff(t_toggle *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + gfxstub_deleteforkey(x); +} + +void g_toggle_setup(void) +{ + toggle_class = class_new(gensym("tgl"), (t_newmethod)toggle_new, + (t_method)toggle_ff, sizeof(t_toggle), 0, A_GIMME, 0); + class_addcreator((t_newmethod)toggle_new, gensym("toggle"), A_GIMME, 0); + class_addbang(toggle_class, toggle_bang); + class_addfloat(toggle_class, toggle_float); + class_addmethod(toggle_class, (t_method)toggle_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(toggle_class, (t_method)toggle_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(toggle_class, (t_method)toggle_loadbang, gensym("loadbang"), 0); + class_addmethod(toggle_class, (t_method)toggle_set, gensym("set"), A_FLOAT, 0); + class_addmethod(toggle_class, (t_method)toggle_size, gensym("size"), A_GIMME, 0); + class_addmethod(toggle_class, (t_method)toggle_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(toggle_class, (t_method)toggle_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(toggle_class, (t_method)toggle_color, gensym("color"), A_GIMME, 0); + class_addmethod(toggle_class, (t_method)toggle_send, gensym("send"), A_DEFSYM, 0); + class_addmethod(toggle_class, (t_method)toggle_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(toggle_class, (t_method)toggle_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(toggle_class, (t_method)toggle_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(toggle_class, (t_method)toggle_label_font, gensym("label_font"), A_GIMME, 0); + class_addmethod(toggle_class, (t_method)toggle_init, gensym("init"), A_FLOAT, 0); + class_addmethod(toggle_class, (t_method)toggle_nonzero, gensym("nonzero"), A_FLOAT, 0); + toggle_widgetbehavior.w_getrectfn = toggle_getrect; + toggle_widgetbehavior.w_displacefn = iemgui_displace; + toggle_widgetbehavior.w_selectfn = iemgui_select; + toggle_widgetbehavior.w_activatefn = NULL; + toggle_widgetbehavior.w_deletefn = iemgui_delete; + toggle_widgetbehavior.w_visfn = iemgui_vis; + toggle_widgetbehavior.w_clickfn = toggle_newclick; + class_setwidget(toggle_class, &toggle_widgetbehavior); + class_sethelpsymbol(toggle_class, gensym("toggle")); + class_setsavefn(toggle_class, toggle_save); + class_setpropertiesfn(toggle_class, toggle_properties); +} +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + +/* --------------- tgl gui-toggle ------------------------- */ + +t_widgetbehavior toggle_widgetbehavior; +static t_class *toggle_class; + +/* widget helper functions */ + +void toggle_draw_update(t_toggle *x, t_glist *glist) +{ + if(glist_isvisible(glist)) + { + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xX1 -fill #%6.6x\n", canvas, x, + (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xX2 -fill #%6.6x\n", canvas, x, + (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol); + } +} + +void toggle_draw_new(t_toggle *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int w=1, xx=text_xpix(&x->x_gui.x_obj, glist), yy=text_ypix(&x->x_gui.x_obj, glist); + + if(x->x_gui.x_w >= 30) + w = 2; + if(x->x_gui.x_w >= 60) + w = 3; + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n", + canvas, xx, yy, xx + x->x_gui.x_w, yy + x->x_gui.x_h, + x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create line %d %d %d %d -width %d -fill #%6.6x -tags %xX1\n", + canvas, xx+w+1, yy+w+1, xx + x->x_gui.x_w-w, yy + x->x_gui.x_h-w, w, + (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create line %d %d %d %d -width %d -fill #%6.6x -tags %xX2\n", + canvas, xx+w+1, yy + x->x_gui.x_h-w-1, xx + x->x_gui.x_w-w, yy+w, w, + (x->x_on!=0.0)?x->x_gui.x_fcol:x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xx+x->x_gui.x_ldx, + yy+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xx, yy + x->x_gui.x_h-1, xx + IOWIDTH, yy + x->x_gui.x_h, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xx, yy, xx + IOWIDTH, yy+1, x, 0); +} + +void toggle_draw_move(t_toggle *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int w=1, xx=text_xpix(&x->x_gui.x_obj, glist), yy=text_ypix(&x->x_gui.x_obj, glist); + + if(x->x_gui.x_w >= 30) + w = 2; + + if(x->x_gui.x_w >= 60) + w = 3; + sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n", + canvas, x, xx, yy, xx + x->x_gui.x_w, yy + x->x_gui.x_h); + sys_vgui(".x%x.c itemconfigure %xX1 -width %d\n", canvas, x, w); + sys_vgui(".x%x.c coords %xX1 %d %d %d %d\n", + canvas, x, xx+w+1, yy+w+1, xx + x->x_gui.x_w-w, yy + x->x_gui.x_h-w); + sys_vgui(".x%x.c itemconfigure %xX2 -width %d\n", canvas, x, w); + sys_vgui(".x%x.c coords %xX2 %d %d %d %d\n", + canvas, x, xx+w+1, yy + x->x_gui.x_h-w-1, xx + x->x_gui.x_w-w, yy+w); + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + canvas, x, xx+x->x_gui.x_ldx, yy+x->x_gui.x_ldy); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, xx, yy + x->x_gui.x_h-1, xx + IOWIDTH, yy + x->x_gui.x_h); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, xx, yy, xx + IOWIDTH, yy+1); +} + +void toggle_draw_erase(t_toggle* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c delete %xBASE\n", canvas, x); + sys_vgui(".x%x.c delete %xX1\n", canvas, x); + sys_vgui(".x%x.c delete %xX2\n", canvas, x); + sys_vgui(".x%x.c delete %xLABEL\n", canvas, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void toggle_draw_config(t_toggle* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas, x, + x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xX1 -fill #%6.6x\n", canvas, x, + x->x_on?x->x_gui.x_fcol:x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xX2 -fill #%6.6x\n", canvas, x, + x->x_on?x->x_gui.x_fcol:x->x_gui.x_bcol); +} + +void toggle_draw_io(t_toggle* x, t_glist* glist, int old_snd_rcv_flags) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xpos, + ypos + x->x_gui.x_h-1, xpos + IOWIDTH, + ypos + x->x_gui.x_h, x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xpos, ypos, + xpos + IOWIDTH, ypos+1, x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void toggle_draw_select(t_toggle* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + if(x->x_gui.x_fsf.x_selected) + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol); + } +} + +void toggle_draw(t_toggle *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + toggle_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + toggle_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + toggle_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + toggle_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + toggle_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + toggle_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + toggle_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ tgl widgetbehaviour----------------------------- */ + +static void toggle_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_toggle *x = (t_toggle *)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist); + *yp1 = text_ypix(&x->x_gui.x_obj, glist); + *xp2 = *xp1 + x->x_gui.x_w; + *yp2 = *yp1 + x->x_gui.x_h; +} + +static void toggle_save(t_gobj *z, t_binbuf *b) +{ + t_toggle *x = (t_toggle *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiisssiiiiiiiff", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, + (t_int)x->x_gui.x_obj.te_ypix, + gensym("tgl"), x->x_gui.x_w, + iem_symargstoint(&x->x_gui.x_isa), + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2], x->x_on, x->x_nonzero); + binbuf_addv(b, ";"); +} + +static void toggle_properties(t_gobj *z, t_glist *owner) +{ + t_toggle *x = (t_toggle *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + sprintf(buf, "pdtk_iemgui_dialog %%s TOGGLE \ + ----------dimensions(pix):----------- %d %d size: 0 0 empty \ + -----------non-zero-value:----------- %g value: 0.0 empty %g \ + -1 lin log %d %d empty %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, IEM_GUI_MINSIZE, + x->x_nonzero, 1.0,/*non_zero-schedule*/ + x->x_gui.x_isa.x_loadinit, -1, -1,/*no multi*/ + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void toggle_bang(t_toggle *x) +{ + x->x_on = (x->x_on==0.0)?x->x_nonzero:0.0; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); +} + +static void toggle_dialog(t_toggle *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int a = (int)atom_getintarg(0, argc, argv); + float nonzero = (float)atom_getfloatarg(2, argc, argv); + int sr_flags; + + if(nonzero == 0.0) + nonzero = 1.0; + x->x_nonzero = nonzero; + if(x->x_on != 0.0) + x->x_on = x->x_nonzero; + sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); +} + +static void toggle_click(t_toggle *x, t_floatarg xpos, t_floatarg ypos, t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{toggle_bang(x);} + +static int toggle_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + if(doit) + toggle_click((t_toggle *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt); + return (1); +} + +static void toggle_set(t_toggle *x, t_floatarg f) +{ + x->x_on = f; + if(f != 0.0) + x->x_nonzero = f; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); +} + +static void toggle_float(t_toggle *x, t_floatarg f) +{ + toggle_set(x, f); + if(x->x_gui.x_fsf.x_put_in2out) + { + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); + } +} + +static void toggle_fout(t_toggle *x, t_floatarg f) +{ + toggle_set(x, f); + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); +} + +static void toggle_loadbang(t_toggle *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + toggle_fout(x, (float)x->x_on); +} + +static void toggle_size(t_toggle *x, t_symbol *s, int ac, t_atom *av) +{ + x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av)); + x->x_gui.x_h = x->x_gui.x_w; + iemgui_size((void *)x, &x->x_gui); +} + +static void toggle_delta(t_toggle *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void toggle_pos(t_toggle *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void toggle_color(t_toggle *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void toggle_send(t_toggle *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void toggle_receive(t_toggle *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void toggle_label(t_toggle *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void toggle_label_font(t_toggle *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void toggle_label_pos(t_toggle *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void toggle_init(t_toggle *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void toggle_nonzero(t_toggle *x, t_floatarg f) +{ + if(f != 0.0) + x->x_nonzero = f; +} + +static void *toggle_new(t_symbol *s, int argc, t_atom *argv) +{ + t_toggle *x = (t_toggle *)pd_new(toggle_class); + int bflcol[]={-262144, -1, -1}; + int a=IEM_GUI_DEFAULTSIZE, f=0; + int ldx=0, ldy=-6; + int fs=8; + float on=0.0, nonzero=1.0; + char str[144]; + + iem_inttosymargs(&x->x_gui.x_isa, 0); + iem_inttofstyle(&x->x_gui.x_fsf, 0); + + if(((argc == 13)||(argc == 14))&&IS_A_FLOAT(argv,0) + &&IS_A_FLOAT(argv,1) + &&(IS_A_SYMBOL(argv,2)||IS_A_FLOAT(argv,2)) + &&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3)) + &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4)) + &&IS_A_FLOAT(argv,5)&&IS_A_FLOAT(argv,6) + &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8)&&IS_A_FLOAT(argv,9) + &&IS_A_FLOAT(argv,10)&&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)) + { + a = (int)atom_getintarg(0, argc, argv); + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(1, argc, argv)); + iemgui_new_getnames(&x->x_gui, 2, argv); + ldx = (int)atom_getintarg(5, argc, argv); + ldy = (int)atom_getintarg(6, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(7, argc, argv)); + fs = (int)atom_getintarg(8, argc, argv); + bflcol[0] = (int)atom_getintarg(9, argc, argv); + bflcol[1] = (int)atom_getintarg(10, argc, argv); + bflcol[2] = (int)atom_getintarg(11, argc, argv); + on = (float)atom_getfloatarg(12, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 2, 0); + if((argc == 14)&&IS_A_FLOAT(argv,13)) + nonzero = (float)atom_getfloatarg(13, argc, argv); + x->x_gui.x_draw = (t_iemfunptr)toggle_draw; + + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if (!strcmp(x->x_gui.x_snd->s_name, "empty")) + x->x_gui.x_fsf.x_snd_able = 0; + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + x->x_nonzero = (nonzero!=0.0)?nonzero:1.0; + if(x->x_gui.x_isa.x_loadinit) + x->x_on = (on!=0.0)?nonzero:0.0; + else + x->x_on = 0.0; + if (x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + iemgui_all_colfromload(&x->x_gui, bflcol); + iemgui_verify_snd_ne_rcv(&x->x_gui); + outlet_new(&x->x_gui.x_obj, &s_float); + return (x); +} + +static void toggle_ff(t_toggle *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + gfxstub_deleteforkey(x); +} + +void g_toggle_setup(void) +{ + toggle_class = class_new(gensym("tgl"), (t_newmethod)toggle_new, + (t_method)toggle_ff, sizeof(t_toggle), 0, A_GIMME, 0); + class_addcreator((t_newmethod)toggle_new, gensym("toggle"), A_GIMME, 0); + class_addbang(toggle_class, toggle_bang); + class_addfloat(toggle_class, toggle_float); + class_addmethod(toggle_class, (t_method)toggle_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(toggle_class, (t_method)toggle_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(toggle_class, (t_method)toggle_loadbang, gensym("loadbang"), 0); + class_addmethod(toggle_class, (t_method)toggle_set, gensym("set"), A_FLOAT, 0); + class_addmethod(toggle_class, (t_method)toggle_size, gensym("size"), A_GIMME, 0); + class_addmethod(toggle_class, (t_method)toggle_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(toggle_class, (t_method)toggle_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(toggle_class, (t_method)toggle_color, gensym("color"), A_GIMME, 0); + class_addmethod(toggle_class, (t_method)toggle_send, gensym("send"), A_DEFSYM, 0); + class_addmethod(toggle_class, (t_method)toggle_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(toggle_class, (t_method)toggle_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(toggle_class, (t_method)toggle_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(toggle_class, (t_method)toggle_label_font, gensym("label_font"), A_GIMME, 0); + class_addmethod(toggle_class, (t_method)toggle_init, gensym("init"), A_FLOAT, 0); + class_addmethod(toggle_class, (t_method)toggle_nonzero, gensym("nonzero"), A_FLOAT, 0); + toggle_widgetbehavior.w_getrectfn = toggle_getrect; + toggle_widgetbehavior.w_displacefn = iemgui_displace; + toggle_widgetbehavior.w_selectfn = iemgui_select; + toggle_widgetbehavior.w_activatefn = NULL; + toggle_widgetbehavior.w_deletefn = iemgui_delete; + toggle_widgetbehavior.w_visfn = iemgui_vis; + toggle_widgetbehavior.w_clickfn = toggle_newclick; + class_setwidget(toggle_class, &toggle_widgetbehavior); + class_sethelpsymbol(toggle_class, gensym("toggle")); + class_setsavefn(toggle_class, toggle_save); + class_setpropertiesfn(toggle_class, toggle_properties); +} diff --git a/apps/plugins/pdbox/PDa/src/g_traversal.c b/apps/plugins/pdbox/PDa/src/g_traversal.c new file mode 100644 index 0000000..2a34f5c --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_traversal.c @@ -0,0 +1,2168 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* This file defines Text objects which traverse data contained in scalars +and arrays: + +pointer - point to an object belonging to a template +get - get numeric fields +set - change numeric fields +element - get an array element +getsize - get the size of an array +setsize - change the size of an array +append - add an element to a list +sublist - get a pointer into a list which is an element of another scalar + +*/ + +#include +#include +#include /* for read/write to files */ +#include "m_pd.h" +#include "g_canvas.h" + +/* ------------- gstubs and gpointers - safe pointing --------------- */ + +/* create a gstub which is "owned" by a glist (gl) or an array ("a"). */ + +t_gstub *gstub_new(t_glist *gl, t_array *a) +{ + t_gstub *gs = t_getbytes(sizeof(*gs)); + if (gl) + { + gs->gs_which = GP_GLIST; + gs->gs_un.gs_glist = gl; + } + else + { + gs->gs_which = GP_ARRAY; + gs->gs_un.gs_array = a; + } + gs->gs_refcount = 0; + return (gs); +} + +/* when a "gpointer" is set to point to this stub (so we can later chase +down the owner) we increase a reference count. The following routine is called +whenever a gpointer is unset from pointing here. If the owner is +gone and the refcount goes to zero, we can free the gstub safely. */ + +static void gstub_dis(t_gstub *gs) +{ + int refcount = --gs->gs_refcount; + if ((!refcount) && gs->gs_which == GP_NONE) + t_freebytes(gs, sizeof (*gs)); + else if (refcount < 0) bug("gstub_dis"); +} + +/* this routing is called by the owner to inform the gstub that it is +being deleted. If no gpointers are pointing here, we can free the gstub; +otherwise we wait for the last gstub_dis() to free it. */ + +void gstub_cutoff(t_gstub *gs) +{ + gs->gs_which = GP_NONE; + if (gs->gs_refcount < 0) bug("gstub_cutoff"); + if (!gs->gs_refcount) t_freebytes(gs, sizeof (*gs)); +} + +/* call this to verify that a pointer is fresh, i.e., that it either +points to real data or to the head of a list, and that in either case +the object hasn't disappeared since this pointer was generated. +Unless "headok" is set, the routine also fails for the head of a list. */ + +int gpointer_check(const t_gpointer *gp, int headok) +{ + t_gstub *gs = gp->gp_stub; + if (!gs) return (0); + if (gs->gs_which == GP_ARRAY) + { + if (gs->gs_un.gs_array->a_valid != gp->gp_valid) return (0); + else return (1); + } + else if (gs->gs_which == GP_GLIST) + { + if (!headok && !gp->gp_un.gp_scalar) return (0); + else if (gs->gs_un.gs_glist->gl_valid != gp->gp_valid) return (0); + else return (1); + } + else return (0); +} + +/* call this if you know the pointer is fresh but don't know if we're pointing +to the head of a list or to real data. Any pointer is known to be fresh +when it appears as the argument of a message, but if your "pointer" method +or inlet stores it and you use it later, call gpointer_check above. */ + +/* LATER reconsider the above... I no longer think it's true! */ + +static int gpointer_ishead(const t_gpointer *gp) +{ + return ((gp->gp_stub->gs_which == GP_GLIST) && !gp->gp_un.gp_scalar); +} + +/* get the template for the object pointer to. Assumes we've already checked +freshness. Returns 0 if head of list. */ + +static t_symbol *gpointer_gettemplatesym(const t_gpointer *gp) +{ + t_gstub *gs = gp->gp_stub; + if (gs->gs_which == GP_GLIST) + { + t_scalar *sc = gp->gp_un.gp_scalar; + if (sc) + return (sc->sc_template); + else return (0); + } + else + { + t_array *a = gs->gs_un.gs_array; + return (a->a_templatesym); + } +} + + /* copy a pointer to another, assuming the first one is fresh and + the second one hasn't yet been initialized. */ +void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto) +{ + *gpto = *gpfrom; + if (gpto->gp_stub) + gpto->gp_stub->gs_refcount++; + else bug("gpointer_copy"); +} + +void gpointer_unset(t_gpointer *gp) +{ + t_gstub *gs; + if (gs = gp->gp_stub) + { + gstub_dis(gs); + gp->gp_stub = 0; + } +} + +void gpointer_setglist(t_gpointer *gp, t_glist *glist, t_scalar *x) +{ + t_gstub *gs; + if (gs = gp->gp_stub) gstub_dis(gs); + gp->gp_stub = gs = glist->gl_stub; + gp->gp_valid = glist->gl_valid; + gp->gp_un.gp_scalar = x; + gs->gs_refcount++; +} + +static void gpointer_setarray(t_gpointer *gp, t_array *array, t_word *w) +{ + t_gstub *gs; + if (gs = gp->gp_stub) gstub_dis(gs); + gp->gp_stub = gs = array->a_stub; + gp->gp_valid = array->a_valid; + gp->gp_un.gp_w = w; + gs->gs_refcount++; +} + +void gpointer_init(t_gpointer *gp) +{ + gp->gp_stub = 0; + gp->gp_valid = 0; + gp->gp_un.gp_scalar = 0; +} + +/* ---------------------- pointers ----------------------------- */ + +static t_class *ptrobj_class; + +typedef struct +{ + t_symbol *to_type; + t_outlet *to_outlet; +} t_typedout; + +typedef struct _ptrobj +{ + t_object x_obj; + t_gpointer x_gp; + t_typedout *x_typedout; + int x_ntypedout; + t_outlet *x_otherout; + t_outlet *x_bangout; +} t_ptrobj; + +static void *ptrobj_new(t_symbol *classname, int argc, t_atom *argv) +{ + t_ptrobj *x = (t_ptrobj *)pd_new(ptrobj_class); + t_typedout *to; + int n; + gpointer_init(&x->x_gp); + x->x_typedout = to = (t_typedout *)getbytes(argc * sizeof (*to)); + x->x_ntypedout = n = argc; + for (; n--; to++) + { + to->to_outlet = outlet_new(&x->x_obj, &s_pointer); + to->to_type = canvas_makebindsym(atom_getsymbol(argv++)); + } + x->x_otherout = outlet_new(&x->x_obj, &s_pointer); + x->x_bangout = outlet_new(&x->x_obj, &s_bang); + pointerinlet_new(&x->x_obj, &x->x_gp); + return (x); +} + +static void ptrobj_traverse(t_ptrobj *x, t_symbol *s) +{ + t_glist *glist = (t_glist *)pd_findbyclass(s, canvas_class); + if (glist) gpointer_setglist(&x->x_gp, glist, 0); + else pd_error(x, "pointer: list '%s' not found", s->s_name); +} + +static void ptrobj_vnext(t_ptrobj *x, float f) +{ + t_gobj *gobj; + t_gpointer *gp = &x->x_gp; + t_gstub *gs = gp->gp_stub; + t_glist *glist; + int wantselected = (f != 0); + + if (!gs) + { + pd_error(x, "ptrobj_next: no current pointer"); + return; + } + if (gs->gs_which != GP_GLIST) + { + pd_error(x, "ptrobj_next: lists only, not arrays"); + return; + } + glist = gs->gs_un.gs_glist; + if (glist->gl_valid != gp->gp_valid) + { + pd_error(x, "ptrobj_next: stale pointer"); + return; + } + if (wantselected && !glist_isvisible(glist)) + { + pd_error(x, + "ptrobj_vnext: next-selected only works for a visible window"); + return; + } + gobj = &gp->gp_un.gp_scalar->sc_gobj; + + if (!gobj) gobj = glist->gl_list; + else gobj = gobj->g_next; + while (gobj && ((pd_class(&gobj->g_pd) != scalar_class) || + (wantselected && !glist_isselected(glist, gobj)))) + gobj = gobj->g_next; + + if (gobj) + { + t_typedout *to; + int n; + t_scalar *sc = (t_scalar *)gobj; + t_symbol *templatesym = sc->sc_template; + + gp->gp_un.gp_scalar = sc; + for (n = x->x_ntypedout, to = x->x_typedout; n--; to++) + { + if (to->to_type == templatesym) + { + outlet_pointer(to->to_outlet, &x->x_gp); + return; + } + } + outlet_pointer(x->x_otherout, &x->x_gp); + } + else + { + gpointer_unset(gp); + outlet_bang(x->x_bangout); + } +} + +static void ptrobj_next(t_ptrobj *x) +{ + ptrobj_vnext(x, 0); +} + +static void ptrobj_sendwindow(t_ptrobj *x, t_symbol *s, int argc, t_atom *argv) +{ + t_scalar *sc; + t_symbol *templatesym; + int n; + t_typedout *to; + t_glist *glist; + t_pd *canvas; + t_gstub *gs; + if (!gpointer_check(&x->x_gp, 1)) + { + pd_error(x, "ptrobj_bang: empty pointer"); + return; + } + gs = x->x_gp.gp_stub; + if (gs->gs_which == GP_GLIST) + glist = gs->gs_un.gs_glist; + else + { + t_array *owner_array = gs->gs_un.gs_array; + while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) + owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; + glist = owner_array->a_gp.gp_stub->gs_un.gs_glist; + } + canvas = (t_pd *)glist_getcanvas(glist); + if (argc && argv->a_type == A_SYMBOL) + pd_typedmess(canvas, argv->a_w.w_symbol, argc-1, argv+1); + else pd_error(x, "send-window: no message?"); +} + +static void ptrobj_bang(t_ptrobj *x) +{ + t_symbol *templatesym; + int n; + t_typedout *to; + if (!gpointer_check(&x->x_gp, 1)) + { + pd_error(x, "ptrobj_bang: empty pointer"); + return; + } + templatesym = gpointer_gettemplatesym(&x->x_gp); + for (n = x->x_ntypedout, to = x->x_typedout; n--; to++) + { + if (to->to_type == templatesym) + { + outlet_pointer(to->to_outlet, &x->x_gp); + return; + } + } + outlet_pointer(x->x_otherout, &x->x_gp); +} + + +static void ptrobj_pointer(t_ptrobj *x, t_gpointer *gp) +{ + gpointer_unset(&x->x_gp); + gpointer_copy(gp, &x->x_gp); + ptrobj_bang(x); +} + +static void ptrobj_free(t_ptrobj *x) +{ + freebytes(x->x_typedout, x->x_ntypedout * sizeof (*x->x_typedout)); + gpointer_unset(&x->x_gp); +} + +static void ptrobj_setup(void) +{ + ptrobj_class = class_new(gensym("pointer"), (t_newmethod)ptrobj_new, + (t_method)ptrobj_free, sizeof(t_ptrobj), 0, A_GIMME, 0); + class_addmethod(ptrobj_class, (t_method)ptrobj_traverse, gensym("traverse"), + A_SYMBOL, 0); + class_addmethod(ptrobj_class, (t_method)ptrobj_next, gensym("next"), 0); + class_addmethod(ptrobj_class, (t_method)ptrobj_vnext, gensym("vnext"), + A_DEFFLOAT, 0); + class_addmethod(ptrobj_class, (t_method)ptrobj_sendwindow, + gensym("send-window"), A_GIMME, 0); + class_addpointer(ptrobj_class, ptrobj_pointer); + class_addbang(ptrobj_class, ptrobj_bang); +} + +/* ---------------------- get ----------------------------- */ + +static t_class *get_class; + +typedef struct _getvariable +{ + t_symbol *gv_sym; + t_outlet *gv_outlet; +} t_getvariable; + +typedef struct _get +{ + t_object x_obj; + t_symbol *x_templatesym; + int x_nout; + t_getvariable *x_variables; +} t_get; + +static void *get_new(t_symbol *why, int argc, t_atom *argv) +{ + t_get *x = (t_get *)pd_new(get_class); + int i; + t_getvariable *sp; + x->x_templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (argc) argc--, argv++; + x->x_variables + = (t_getvariable *)getbytes(argc * sizeof (*x->x_variables)); + x->x_nout = argc; + for (i = 0, sp = x->x_variables; i < argc; i++, sp++) + { + sp->gv_sym = atom_getsymbolarg(i, argc, argv); + sp->gv_outlet = outlet_new(&x->x_obj, 0); + /* LATER connect with the template and set the outlet's type + correctly. We can't yet guarantee that the template is there + before we hit this routine. */ + } + return (x); +} + +static void get_pointer(t_get *x, t_gpointer *gp) +{ + int nitems = x->x_nout, i; + t_symbol *templatesym = x->x_templatesym; + t_template *template = template_findbyname(templatesym); + t_gstub *gs = gp->gp_stub; + t_word *vec; + t_getvariable *vp; + if (!template) + { + pd_error(x, "get: couldn't find template %s", templatesym->s_name); + return; + } + if (gpointer_ishead(gp)) + { + pd_error(x, "get: empty pointer"); + return; + } + if (gs->gs_which == GP_ARRAY) vec = gp->gp_un.gp_w; + else vec = gp->gp_un.gp_scalar->sc_vec; + for (i = nitems - 1, vp = x->x_variables + i; i >= 0; i--, vp--) + { + float f = template_getfloat(template, vp->gv_sym, vec, 1); + outlet_float(vp->gv_outlet, f); + /* LATER deal with other types. */ + } +} + +static void get_free(t_get *x) +{ + freebytes(x->x_variables, x->x_nout * sizeof (*x->x_variables)); +} + +static void get_setup(void) +{ + get_class = class_new(gensym("get"), (t_newmethod)get_new, + (t_method)get_free, sizeof(t_get), 0, A_GIMME, 0); + class_addpointer(get_class, get_pointer); +} + +/* ---------------------- set ----------------------------- */ + +static t_class *set_class; + +typedef struct _setvariable +{ + t_symbol *gv_sym; + t_float gv_f; /* LATER take other types */ +} t_setvariable; + +typedef struct _set +{ + t_object x_obj; + t_gpointer x_gp; + t_symbol *x_templatesym; + int x_nin; + t_setvariable *x_variables; +} t_set; + +static void *set_new(t_symbol *why, int argc, t_atom *argv) +{ + t_set *x = (t_set *)pd_new(set_class); + int i; + t_setvariable *sp; + x->x_templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (argc) argc--, argv++; + x->x_variables + = (t_setvariable *)getbytes(argc * sizeof (*x->x_variables)); + x->x_nin = argc; + if (argc) + { + for (i = 0, sp = x->x_variables; i < argc; i++, sp++) + { + sp->gv_sym = atom_getsymbolarg(i, argc, argv); + sp->gv_f = 0; + if (i) floatinlet_new(&x->x_obj, &sp->gv_f); + /* LATER figure out type as in "get" object. */ + } + } + pointerinlet_new(&x->x_obj, &x->x_gp); + gpointer_init(&x->x_gp); + return (x); +} + +static void set_float(t_set *x, t_float f) +{ + int nitems = x->x_nin, i; + t_symbol *templatesym = x->x_templatesym; + t_template *template = template_findbyname(templatesym); + t_setvariable *vp; + t_gpointer *gp = &x->x_gp; + t_gstub *gs = gp->gp_stub; + t_word *vec; + if (!template) + { + pd_error(x, "set: couldn't find template %s", templatesym->s_name); + return; + } + if (!gpointer_check(gp, 0)) + { + pd_error(x, "set: empty pointer"); + return; + } + if (gpointer_gettemplatesym(gp) != x->x_templatesym) + { + pd_error(x, "set %s: got wrong template (%s)", + x->x_templatesym->s_name, gpointer_gettemplatesym(gp)->s_name); + return; + } + if (!nitems) return; + x->x_variables[0].gv_f = f; + if (gs->gs_which == GP_ARRAY) vec = gp->gp_un.gp_w; + else vec = gp->gp_un.gp_scalar->sc_vec; + for (i = 0, vp = x->x_variables; i < nitems; i++, vp++) + { + template_setfloat(template, vp->gv_sym, vec, vp->gv_f, 1); + /* LATER deal with other types ala get_pointer. */ + } + if (gs->gs_which == GP_GLIST) + glist_redrawitem(gs->gs_un.gs_glist, (t_gobj *)(gp->gp_un.gp_scalar)); + else + { + t_array *owner_array = gs->gs_un.gs_array; + while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) + owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; + glist_redrawitem(owner_array->a_gp.gp_stub->gs_un.gs_glist, + (t_gobj *)(owner_array->a_gp.gp_un.gp_scalar)); + } +} + +static void set_free(t_set *x) +{ + freebytes(x->x_variables, x->x_nin * sizeof (*x->x_variables)); + gpointer_unset(&x->x_gp); +} + +static void set_setup(void) +{ + set_class = class_new(gensym("set"), (t_newmethod)set_new, + (t_method)set_free, sizeof(t_set), 0, A_GIMME, 0); + class_addfloat(set_class, set_float); +} + +/* ---------------------- elem ----------------------------- */ + +static t_class *elem_class; + +typedef struct _elem +{ + t_object x_obj; + t_symbol *x_templatesym; + t_symbol *x_fieldsym; + t_gpointer x_gp; + t_gpointer x_gparent; +} t_elem; + +static void *elem_new(t_symbol *templatesym, t_symbol *fieldsym) +{ + t_elem *x = (t_elem *)pd_new(elem_class); + x->x_templatesym = canvas_makebindsym(templatesym); + x->x_fieldsym = fieldsym; + gpointer_init(&x->x_gp); + gpointer_init(&x->x_gparent); + pointerinlet_new(&x->x_obj, &x->x_gparent); + outlet_new(&x->x_obj, &s_pointer); + return (x); +} + +static void elem_float(t_elem *x, t_float f) +{ + int indx = f, nitems, onset; + t_symbol *templatesym = x->x_templatesym, *fieldsym = x->x_fieldsym, + *elemtemplatesym; + t_template *template = template_findbyname(templatesym); + t_template *elemtemplate; + t_gpointer *gparent = &x->x_gparent; + t_word *w; + t_array *array; + int elemsize, type; + + if (!gpointer_check(gparent, 0)) + { + pd_error(x, "element: empty pointer"); + return; + } + if (gpointer_gettemplatesym(gparent) != x->x_templatesym) + { + pd_error(x, "element %s: got wrong template (%s)", + x->x_templatesym->s_name, gpointer_gettemplatesym(gparent)->s_name); + return; + } + if (gparent->gp_stub->gs_which == GP_ARRAY) w = gparent->gp_un.gp_w; + else w = gparent->gp_un.gp_scalar->sc_vec; + if (!template) + { + pd_error(x, "element: couldn't find template %s", templatesym->s_name); + return; + } + if (!template_find_field(template, fieldsym, + &onset, &type, &elemtemplatesym)) + { + pd_error(x, "element: couldn't find array field %s", fieldsym->s_name); + return; + } + if (type != DT_ARRAY) + { + pd_error(x, "element: field %s not of type array", fieldsym->s_name); + return; + } + if (!(elemtemplate = template_findbyname(elemtemplatesym))) + { + pd_error(x, "element: couldn't find field template %s", + elemtemplatesym->s_name); + return; + } + + elemsize = elemtemplate->t_n * sizeof(t_word); + + array = *(t_array **)(((char *)w) + onset); + + nitems = array->a_n; + if (indx < 0) indx = 0; + if (indx >= nitems) indx = nitems-1; + + gpointer_setarray(&x->x_gp, array, + (t_word *)((char *)(array->a_vec) + indx * elemsize)); + outlet_pointer(x->x_obj.ob_outlet, &x->x_gp); +} + +static void elem_free(t_elem *x, t_gpointer *gp) +{ + gpointer_unset(&x->x_gp); + gpointer_unset(&x->x_gparent); +} + +static void elem_setup(void) +{ + elem_class = class_new(gensym("element"), (t_newmethod)elem_new, + (t_method)elem_free, sizeof(t_elem), 0, A_DEFSYM, A_DEFSYM, 0); + class_addfloat(elem_class, elem_float); +} + +/* ---------------------- getsize ----------------------------- */ + +static t_class *getsize_class; + +typedef struct _getsize +{ + t_object x_obj; + t_symbol *x_templatesym; + t_symbol *x_fieldsym; +} t_getsize; + +static void *getsize_new(t_symbol *templatesym, t_symbol *fieldsym) +{ + t_getsize *x = (t_getsize *)pd_new(getsize_class); + x->x_templatesym = canvas_makebindsym(templatesym); + x->x_fieldsym = fieldsym; + outlet_new(&x->x_obj, &s_float); + return (x); +} + +static void getsize_pointer(t_getsize *x, t_gpointer *gp) +{ + int nitems, onset, type; + t_symbol *templatesym = x->x_templatesym, *fieldsym = x->x_fieldsym, + *elemtemplatesym; + t_template *template = template_findbyname(templatesym); + t_word *w; + t_array *array; + int elemsize; + t_gstub *gs = gp->gp_stub; + if (!template) + { + pd_error(x, "getsize: couldn't find template %s", templatesym->s_name); + return; + } + if (!template_find_field(template, fieldsym, + &onset, &type, &elemtemplatesym)) + { + pd_error(x, "getsize: couldn't find array field %s", fieldsym->s_name); + return; + } + if (type != DT_ARRAY) + { + pd_error(x, "getsize: field %s not of type array", fieldsym->s_name); + return; + } + if (gpointer_ishead(gp)) + { + pd_error(x, "getsize: empty pointer"); + return; + } + if (gpointer_gettemplatesym(gp) != x->x_templatesym) + { + pd_error(x, "getsize %s: got wrong template (%s)", + x->x_templatesym->s_name, gpointer_gettemplatesym(gp)->s_name); + return; + } + if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w; + else w = gp->gp_un.gp_scalar->sc_vec; + + array = *(t_array **)(((char *)w) + onset); + outlet_float(x->x_obj.ob_outlet, (float)(array->a_n)); +} + +static void getsize_setup(void) +{ + getsize_class = class_new(gensym("getsize"), (t_newmethod)getsize_new, 0, + sizeof(t_getsize), 0, A_DEFSYM, A_DEFSYM, 0); + class_addpointer(getsize_class, getsize_pointer); +} + +/* ---------------------- setsize ----------------------------- */ + +static t_class *setsize_class; + +typedef struct _setsize +{ + t_object x_obj; + t_symbol *x_templatesym; + t_symbol *x_fieldsym; + t_gpointer x_gp; +} t_setsize; + +static void *setsize_new(t_symbol *templatesym, t_symbol *fieldsym, + t_floatarg newsize) +{ + t_setsize *x = (t_setsize *)pd_new(setsize_class); + x->x_templatesym = canvas_makebindsym(templatesym); + x->x_fieldsym = fieldsym; + gpointer_init(&x->x_gp); + + pointerinlet_new(&x->x_obj, &x->x_gp); + return (x); +} + +static void setsize_float(t_setsize *x, t_float f) +{ + int nitems, onset, type; + t_symbol *templatesym = x->x_templatesym, *fieldsym = x->x_fieldsym, + *elemtemplatesym; + t_template *template = template_findbyname(templatesym); + t_template *elemtemplate; + t_word *w; + t_atom at; + t_array *array; + int elemsize; + int newsize = f; + t_gpointer *gp = &x->x_gp; + t_gstub *gs = gp->gp_stub; + if (!gpointer_check(&x->x_gp, 0)) + { + pd_error(x, "setsize: empty pointer"); + return; + } + if (gpointer_gettemplatesym(&x->x_gp) != x->x_templatesym) + { + pd_error(x, "setsize %s: got wrong template (%s)", + x->x_templatesym->s_name, + gpointer_gettemplatesym(&x->x_gp)->s_name); + return; + } + if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w; + else w = gp->gp_un.gp_scalar->sc_vec; + + if (!template) + { + pd_error(x,"setsize: couldn't find template %s", templatesym->s_name); + return; + } + if (!template_find_field(template, fieldsym, + &onset, &type, &elemtemplatesym)) + { + pd_error(x,"setsize: couldn't find array field %s", fieldsym->s_name); + return; + } + if (type != DT_ARRAY) + { + pd_error(x,"setsize: field %s not of type array", fieldsym->s_name); + return; + } + + if (!(elemtemplate = template_findbyname(elemtemplatesym))) + { + pd_error(x,"element: couldn't find field template %s", + elemtemplatesym->s_name); + return; + } + + elemsize = elemtemplate->t_n * sizeof(t_word); + + array = *(t_array **)(((char *)w) + onset); + + if (elemsize != array->a_elemsize) bug("setsize_gpointer"); + + nitems = array->a_n; + if (newsize < 1) newsize = 1; + if (newsize == nitems) return; + + /* erase the array before resizing it. If we belong to a + scalar it's easy, but if we belong to an element of another + array we have to search back until we get to a scalar to erase. + When graphics updates become queueable this may fall apart... */ + + + if (gs->gs_which == GP_GLIST) + { + if (glist_isvisible(gs->gs_un.gs_glist)) + gobj_vis((t_gobj *)(gp->gp_un.gp_scalar), gs->gs_un.gs_glist, 0); + } + else + { + t_array *owner_array = gs->gs_un.gs_array; + while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) + owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; + if (glist_isvisible(owner_array->a_gp.gp_stub->gs_un.gs_glist)) + gobj_vis((t_gobj *)(owner_array->a_gp.gp_un.gp_scalar), + owner_array->a_gp.gp_stub->gs_un.gs_glist, 0); + } + /* now do the resizing and, if growing, initialize new scalars */ + array->a_vec = (char *)resizebytes(array->a_vec, + elemsize * nitems, elemsize * newsize); + array->a_n = newsize; + if (newsize > nitems) + { + char *newelem = ((char *)array->a_vec) + nitems * elemsize; + int i = 0, nnew = newsize - nitems; + + while (nnew--) + { + word_init((t_word *)newelem, elemtemplate, gp); + newelem += elemsize; + /* post("new %x %x, ntypes %d", newelem, *(int *)newelem, ntypes); */ + } + } + + /* redraw again. */ + if (gs->gs_which == GP_GLIST) + { + if (glist_isvisible(gs->gs_un.gs_glist)) + gobj_vis((t_gobj *)(gp->gp_un.gp_scalar), gs->gs_un.gs_glist, 1); + } + else + { + t_array *owner_array = gs->gs_un.gs_array; + while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) + owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; + if (glist_isvisible(owner_array->a_gp.gp_stub->gs_un.gs_glist)) + gobj_vis((t_gobj *)(owner_array->a_gp.gp_un.gp_scalar), + owner_array->a_gp.gp_stub->gs_un.gs_glist, 1); + } +} + + +static void setsize_free(t_setsize *x) +{ + gpointer_unset(&x->x_gp); +} + +static void setsize_setup(void) +{ + setsize_class = class_new(gensym("setsize"), (t_newmethod)setsize_new, + (t_method)setsize_free, sizeof(t_setsize), 0, + A_DEFSYM, A_DEFSYM, A_DEFFLOAT, 0); + class_addfloat(setsize_class, setsize_float); +} + +/* ---------------------- append ----------------------------- */ + +static t_class *append_class; + +typedef struct _appendvariable +{ + t_symbol *gv_sym; + t_float gv_f; +} t_appendvariable; + +typedef struct _append +{ + t_object x_obj; + t_gpointer x_gp; + t_symbol *x_templatesym; + int x_nin; + t_appendvariable *x_variables; +} t_append; + +static void *append_new(t_symbol *why, int argc, t_atom *argv) +{ + t_append *x = (t_append *)pd_new(append_class); + int i; + t_appendvariable *sp; + x->x_templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (argc) argc--, argv++; + x->x_variables + = (t_appendvariable *)getbytes(argc * sizeof (*x->x_variables)); + x->x_nin = argc; + if (argc) + { + for (i = 0, sp = x->x_variables; i < argc; i++, sp++) + { + sp->gv_sym = atom_getsymbolarg(i, argc, argv); + sp->gv_f = 0; + if (i) floatinlet_new(&x->x_obj, &sp->gv_f); + } + } + pointerinlet_new(&x->x_obj, &x->x_gp); + outlet_new(&x->x_obj, &s_pointer); + gpointer_init(&x->x_gp); + return (x); +} + +static void append_float(t_append *x, t_float f) +{ + int nitems = x->x_nin, i; + t_symbol *templatesym = x->x_templatesym; + t_template *template = template_findbyname(templatesym); + t_appendvariable *vp; + t_gpointer *gp = &x->x_gp; + t_gstub *gs = gp->gp_stub; + t_word *vec; + t_scalar *sc, *oldsc; + t_glist *glist; + if (!template) + { + pd_error(x, "append: couldn't find template %s", templatesym->s_name); + return; + } + if (!gs) + { + pd_error(x, "append: no current pointer"); + return; + } + if (gs->gs_which != GP_GLIST) + { + pd_error(x, "append: lists only, not arrays"); + return; + } + glist = gs->gs_un.gs_glist; + if (glist->gl_valid != gp->gp_valid) + { + pd_error(x, "append: stale pointer"); + return; + } + if (!nitems) return; + x->x_variables[0].gv_f = f; + + sc = scalar_new(glist, templatesym); + if (!sc) + { + pd_error(x, "%s: couldn't create scalar", templatesym->s_name); + return; + } + oldsc = gp->gp_un.gp_scalar; + + if (oldsc) + { + sc->sc_gobj.g_next = oldsc->sc_gobj.g_next; + oldsc->sc_gobj.g_next = &sc->sc_gobj; + } + else + { + sc->sc_gobj.g_next = glist->gl_list; + glist->gl_list = &sc->sc_gobj; + } + if (glist_isvisible(glist_getcanvas(glist))) + gobj_vis(&sc->sc_gobj, glist, 1); + + gp->gp_un.gp_scalar = sc; + vec = sc->sc_vec; + for (i = 0, vp = x->x_variables; i < nitems; i++, vp++) + { + template_setfloat(template, vp->gv_sym, vec, vp->gv_f, 1); + } + + glist_redrawitem(glist, (t_gobj *)sc); + + outlet_pointer(x->x_obj.ob_outlet, gp); +} + +static void append_free(t_append *x) +{ + freebytes(x->x_variables, x->x_nin * sizeof (*x->x_variables)); + gpointer_unset(&x->x_gp); +} + +static void append_setup(void) +{ + append_class = class_new(gensym("append"), (t_newmethod)append_new, + (t_method)append_free, sizeof(t_append), 0, A_GIMME, 0); + class_addfloat(append_class, append_float); +} + +/* ---------------------- sublist ----------------------------- */ + +static t_class *sublist_class; + +typedef struct _sublist +{ + t_object x_obj; + t_symbol *x_templatesym; + t_symbol *x_fieldsym; + t_gpointer x_gp; +} t_sublist; + +static void *sublist_new(t_symbol *templatesym, t_symbol *fieldsym) +{ + t_sublist *x = (t_sublist *)pd_new(sublist_class); + x->x_templatesym = canvas_makebindsym(templatesym); + x->x_fieldsym = fieldsym; + gpointer_init(&x->x_gp); + outlet_new(&x->x_obj, &s_pointer); + return (x); +} + +static void sublist_pointer(t_sublist *x, t_gpointer *gp) +{ + t_symbol *templatesym = x->x_templatesym, *dummy; + t_template *template = template_findbyname(templatesym); + t_gstub *gs = gp->gp_stub; + t_word *vec; + t_getvariable *vp; + int onset, type; + t_word *w; + + if (!template) + { + pd_error(x, "sublist: couldn't find template %s", templatesym->s_name); + return; + } + if (gpointer_ishead(gp)) + { + pd_error(x, "sublist: empty pointer"); + return; + } + if (!template_find_field(template, x->x_fieldsym, + &onset, &type, &dummy)) + { + pd_error(x, "sublist: couldn't find field %s", x->x_fieldsym->s_name); + return; + } + if (type != DT_LIST) + { + pd_error(x, "sublist: field %s not of type list", x->x_fieldsym->s_name); + return; + } + if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w; + else w = gp->gp_un.gp_scalar->sc_vec; + + gpointer_setglist(&x->x_gp, *(t_glist **)(((char *)w) + onset), 0); + + outlet_pointer(x->x_obj.ob_outlet, &x->x_gp); +} + +static void sublist_free(t_sublist *x, t_gpointer *gp) +{ + gpointer_unset(&x->x_gp); +} + +static void sublist_setup(void) +{ + sublist_class = class_new(gensym("sublist"), (t_newmethod)sublist_new, + (t_method)sublist_free, sizeof(t_sublist), 0, A_DEFSYM, A_DEFSYM, 0); + class_addpointer(sublist_class, sublist_pointer); +} + +/* ----------------- setup function ------------------- */ + +void g_traversal_setup(void) +{ + ptrobj_setup(); + get_setup(); + set_setup(); + elem_setup(); + getsize_setup(); + setsize_setup(); + append_setup(); + sublist_setup(); +} +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* This file defines Text objects which traverse data contained in scalars +and arrays: + +pointer - point to an object belonging to a template +get - get numeric fields +set - change numeric fields +element - get an array element +getsize - get the size of an array +setsize - change the size of an array +append - add an element to a list +sublist - get a pointer into a list which is an element of another scalar + +*/ + +#include +#include +#include /* for read/write to files */ +#include "m_pd.h" +#include "g_canvas.h" + +/* ------------- gstubs and gpointers - safe pointing --------------- */ + +/* create a gstub which is "owned" by a glist (gl) or an array ("a"). */ + +t_gstub *gstub_new(t_glist *gl, t_array *a) +{ + t_gstub *gs = t_getbytes(sizeof(*gs)); + if (gl) + { + gs->gs_which = GP_GLIST; + gs->gs_un.gs_glist = gl; + } + else + { + gs->gs_which = GP_ARRAY; + gs->gs_un.gs_array = a; + } + gs->gs_refcount = 0; + return (gs); +} + +/* when a "gpointer" is set to point to this stub (so we can later chase +down the owner) we increase a reference count. The following routine is called +whenever a gpointer is unset from pointing here. If the owner is +gone and the refcount goes to zero, we can free the gstub safely. */ + +static void gstub_dis(t_gstub *gs) +{ + int refcount = --gs->gs_refcount; + if ((!refcount) && gs->gs_which == GP_NONE) + t_freebytes(gs, sizeof (*gs)); + else if (refcount < 0) bug("gstub_dis"); +} + +/* this routing is called by the owner to inform the gstub that it is +being deleted. If no gpointers are pointing here, we can free the gstub; +otherwise we wait for the last gstub_dis() to free it. */ + +void gstub_cutoff(t_gstub *gs) +{ + gs->gs_which = GP_NONE; + if (gs->gs_refcount < 0) bug("gstub_cutoff"); + if (!gs->gs_refcount) t_freebytes(gs, sizeof (*gs)); +} + +/* call this to verify that a pointer is fresh, i.e., that it either +points to real data or to the head of a list, and that in either case +the object hasn't disappeared since this pointer was generated. +Unless "headok" is set, the routine also fails for the head of a list. */ + +int gpointer_check(const t_gpointer *gp, int headok) +{ + t_gstub *gs = gp->gp_stub; + if (!gs) return (0); + if (gs->gs_which == GP_ARRAY) + { + if (gs->gs_un.gs_array->a_valid != gp->gp_valid) return (0); + else return (1); + } + else if (gs->gs_which == GP_GLIST) + { + if (!headok && !gp->gp_un.gp_scalar) return (0); + else if (gs->gs_un.gs_glist->gl_valid != gp->gp_valid) return (0); + else return (1); + } + else return (0); +} + +/* call this if you know the pointer is fresh but don't know if we're pointing +to the head of a list or to real data. Any pointer is known to be fresh +when it appears as the argument of a message, but if your "pointer" method +or inlet stores it and you use it later, call gpointer_check above. */ + +/* LATER reconsider the above... I no longer think it's true! */ + +static int gpointer_ishead(const t_gpointer *gp) +{ + return ((gp->gp_stub->gs_which == GP_GLIST) && !gp->gp_un.gp_scalar); +} + +/* get the template for the object pointer to. Assumes we've already checked +freshness. Returns 0 if head of list. */ + +static t_symbol *gpointer_gettemplatesym(const t_gpointer *gp) +{ + t_gstub *gs = gp->gp_stub; + if (gs->gs_which == GP_GLIST) + { + t_scalar *sc = gp->gp_un.gp_scalar; + if (sc) + return (sc->sc_template); + else return (0); + } + else + { + t_array *a = gs->gs_un.gs_array; + return (a->a_templatesym); + } +} + + /* copy a pointer to another, assuming the first one is fresh and + the second one hasn't yet been initialized. */ +void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto) +{ + *gpto = *gpfrom; + if (gpto->gp_stub) + gpto->gp_stub->gs_refcount++; + else bug("gpointer_copy"); +} + +void gpointer_unset(t_gpointer *gp) +{ + t_gstub *gs; + if (gs = gp->gp_stub) + { + gstub_dis(gs); + gp->gp_stub = 0; + } +} + +void gpointer_setglist(t_gpointer *gp, t_glist *glist, t_scalar *x) +{ + t_gstub *gs; + if (gs = gp->gp_stub) gstub_dis(gs); + gp->gp_stub = gs = glist->gl_stub; + gp->gp_valid = glist->gl_valid; + gp->gp_un.gp_scalar = x; + gs->gs_refcount++; +} + +static void gpointer_setarray(t_gpointer *gp, t_array *array, t_word *w) +{ + t_gstub *gs; + if (gs = gp->gp_stub) gstub_dis(gs); + gp->gp_stub = gs = array->a_stub; + gp->gp_valid = array->a_valid; + gp->gp_un.gp_w = w; + gs->gs_refcount++; +} + +void gpointer_init(t_gpointer *gp) +{ + gp->gp_stub = 0; + gp->gp_valid = 0; + gp->gp_un.gp_scalar = 0; +} + +/* ---------------------- pointers ----------------------------- */ + +static t_class *ptrobj_class; + +typedef struct +{ + t_symbol *to_type; + t_outlet *to_outlet; +} t_typedout; + +typedef struct _ptrobj +{ + t_object x_obj; + t_gpointer x_gp; + t_typedout *x_typedout; + int x_ntypedout; + t_outlet *x_otherout; + t_outlet *x_bangout; +} t_ptrobj; + +static void *ptrobj_new(t_symbol *classname, int argc, t_atom *argv) +{ + t_ptrobj *x = (t_ptrobj *)pd_new(ptrobj_class); + t_typedout *to; + int n; + gpointer_init(&x->x_gp); + x->x_typedout = to = (t_typedout *)getbytes(argc * sizeof (*to)); + x->x_ntypedout = n = argc; + for (; n--; to++) + { + to->to_outlet = outlet_new(&x->x_obj, &s_pointer); + to->to_type = canvas_makebindsym(atom_getsymbol(argv++)); + } + x->x_otherout = outlet_new(&x->x_obj, &s_pointer); + x->x_bangout = outlet_new(&x->x_obj, &s_bang); + pointerinlet_new(&x->x_obj, &x->x_gp); + return (x); +} + +static void ptrobj_traverse(t_ptrobj *x, t_symbol *s) +{ + t_glist *glist = (t_glist *)pd_findbyclass(s, canvas_class); + if (glist) gpointer_setglist(&x->x_gp, glist, 0); + else pd_error(x, "pointer: list '%s' not found", s->s_name); +} + +static void ptrobj_vnext(t_ptrobj *x, float f) +{ + t_gobj *gobj; + t_gpointer *gp = &x->x_gp; + t_gstub *gs = gp->gp_stub; + t_glist *glist; + int wantselected = (f != 0); + + if (!gs) + { + pd_error(x, "ptrobj_next: no current pointer"); + return; + } + if (gs->gs_which != GP_GLIST) + { + pd_error(x, "ptrobj_next: lists only, not arrays"); + return; + } + glist = gs->gs_un.gs_glist; + if (glist->gl_valid != gp->gp_valid) + { + pd_error(x, "ptrobj_next: stale pointer"); + return; + } + if (wantselected && !glist_isvisible(glist)) + { + pd_error(x, + "ptrobj_vnext: next-selected only works for a visible window"); + return; + } + gobj = &gp->gp_un.gp_scalar->sc_gobj; + + if (!gobj) gobj = glist->gl_list; + else gobj = gobj->g_next; + while (gobj && ((pd_class(&gobj->g_pd) != scalar_class) || + (wantselected && !glist_isselected(glist, gobj)))) + gobj = gobj->g_next; + + if (gobj) + { + t_typedout *to; + int n; + t_scalar *sc = (t_scalar *)gobj; + t_symbol *templatesym = sc->sc_template; + + gp->gp_un.gp_scalar = sc; + for (n = x->x_ntypedout, to = x->x_typedout; n--; to++) + { + if (to->to_type == templatesym) + { + outlet_pointer(to->to_outlet, &x->x_gp); + return; + } + } + outlet_pointer(x->x_otherout, &x->x_gp); + } + else + { + gpointer_unset(gp); + outlet_bang(x->x_bangout); + } +} + +static void ptrobj_next(t_ptrobj *x) +{ + ptrobj_vnext(x, 0); +} + +static void ptrobj_sendwindow(t_ptrobj *x, t_symbol *s, int argc, t_atom *argv) +{ + t_scalar *sc; + t_symbol *templatesym; + int n; + t_typedout *to; + t_glist *glist; + t_pd *canvas; + t_gstub *gs; + if (!gpointer_check(&x->x_gp, 1)) + { + pd_error(x, "ptrobj_bang: empty pointer"); + return; + } + gs = x->x_gp.gp_stub; + if (gs->gs_which == GP_GLIST) + glist = gs->gs_un.gs_glist; + else + { + t_array *owner_array = gs->gs_un.gs_array; + while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) + owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; + glist = owner_array->a_gp.gp_stub->gs_un.gs_glist; + } + canvas = (t_pd *)glist_getcanvas(glist); + if (argc && argv->a_type == A_SYMBOL) + pd_typedmess(canvas, argv->a_w.w_symbol, argc-1, argv+1); + else pd_error(x, "send-window: no message?"); +} + +static void ptrobj_bang(t_ptrobj *x) +{ + t_symbol *templatesym; + int n; + t_typedout *to; + if (!gpointer_check(&x->x_gp, 1)) + { + pd_error(x, "ptrobj_bang: empty pointer"); + return; + } + templatesym = gpointer_gettemplatesym(&x->x_gp); + for (n = x->x_ntypedout, to = x->x_typedout; n--; to++) + { + if (to->to_type == templatesym) + { + outlet_pointer(to->to_outlet, &x->x_gp); + return; + } + } + outlet_pointer(x->x_otherout, &x->x_gp); +} + + +static void ptrobj_pointer(t_ptrobj *x, t_gpointer *gp) +{ + gpointer_unset(&x->x_gp); + gpointer_copy(gp, &x->x_gp); + ptrobj_bang(x); +} + +static void ptrobj_free(t_ptrobj *x) +{ + freebytes(x->x_typedout, x->x_ntypedout * sizeof (*x->x_typedout)); + gpointer_unset(&x->x_gp); +} + +static void ptrobj_setup(void) +{ + ptrobj_class = class_new(gensym("pointer"), (t_newmethod)ptrobj_new, + (t_method)ptrobj_free, sizeof(t_ptrobj), 0, A_GIMME, 0); + class_addmethod(ptrobj_class, (t_method)ptrobj_traverse, gensym("traverse"), + A_SYMBOL, 0); + class_addmethod(ptrobj_class, (t_method)ptrobj_next, gensym("next"), 0); + class_addmethod(ptrobj_class, (t_method)ptrobj_vnext, gensym("vnext"), + A_DEFFLOAT, 0); + class_addmethod(ptrobj_class, (t_method)ptrobj_sendwindow, + gensym("send-window"), A_GIMME, 0); + class_addpointer(ptrobj_class, ptrobj_pointer); + class_addbang(ptrobj_class, ptrobj_bang); +} + +/* ---------------------- get ----------------------------- */ + +static t_class *get_class; + +typedef struct _getvariable +{ + t_symbol *gv_sym; + t_outlet *gv_outlet; +} t_getvariable; + +typedef struct _get +{ + t_object x_obj; + t_symbol *x_templatesym; + int x_nout; + t_getvariable *x_variables; +} t_get; + +static void *get_new(t_symbol *why, int argc, t_atom *argv) +{ + t_get *x = (t_get *)pd_new(get_class); + int i; + t_getvariable *sp; + x->x_templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (argc) argc--, argv++; + x->x_variables + = (t_getvariable *)getbytes(argc * sizeof (*x->x_variables)); + x->x_nout = argc; + for (i = 0, sp = x->x_variables; i < argc; i++, sp++) + { + sp->gv_sym = atom_getsymbolarg(i, argc, argv); + sp->gv_outlet = outlet_new(&x->x_obj, 0); + /* LATER connect with the template and set the outlet's type + correctly. We can't yet guarantee that the template is there + before we hit this routine. */ + } + return (x); +} + +static void get_pointer(t_get *x, t_gpointer *gp) +{ + int nitems = x->x_nout, i; + t_symbol *templatesym = x->x_templatesym; + t_template *template = template_findbyname(templatesym); + t_gstub *gs = gp->gp_stub; + t_word *vec; + t_getvariable *vp; + if (!template) + { + pd_error(x, "get: couldn't find template %s", templatesym->s_name); + return; + } + if (gpointer_ishead(gp)) + { + pd_error(x, "get: empty pointer"); + return; + } + if (gs->gs_which == GP_ARRAY) vec = gp->gp_un.gp_w; + else vec = gp->gp_un.gp_scalar->sc_vec; + for (i = nitems - 1, vp = x->x_variables + i; i >= 0; i--, vp--) + { + float f = template_getfloat(template, vp->gv_sym, vec, 1); + outlet_float(vp->gv_outlet, f); + /* LATER deal with other types. */ + } +} + +static void get_free(t_get *x) +{ + freebytes(x->x_variables, x->x_nout * sizeof (*x->x_variables)); +} + +static void get_setup(void) +{ + get_class = class_new(gensym("get"), (t_newmethod)get_new, + (t_method)get_free, sizeof(t_get), 0, A_GIMME, 0); + class_addpointer(get_class, get_pointer); +} + +/* ---------------------- set ----------------------------- */ + +static t_class *set_class; + +typedef struct _setvariable +{ + t_symbol *gv_sym; + t_float gv_f; /* LATER take other types */ +} t_setvariable; + +typedef struct _set +{ + t_object x_obj; + t_gpointer x_gp; + t_symbol *x_templatesym; + int x_nin; + t_setvariable *x_variables; +} t_set; + +static void *set_new(t_symbol *why, int argc, t_atom *argv) +{ + t_set *x = (t_set *)pd_new(set_class); + int i; + t_setvariable *sp; + x->x_templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (argc) argc--, argv++; + x->x_variables + = (t_setvariable *)getbytes(argc * sizeof (*x->x_variables)); + x->x_nin = argc; + if (argc) + { + for (i = 0, sp = x->x_variables; i < argc; i++, sp++) + { + sp->gv_sym = atom_getsymbolarg(i, argc, argv); + sp->gv_f = 0; + if (i) floatinlet_new(&x->x_obj, &sp->gv_f); + /* LATER figure out type as in "get" object. */ + } + } + pointerinlet_new(&x->x_obj, &x->x_gp); + gpointer_init(&x->x_gp); + return (x); +} + +static void set_float(t_set *x, t_float f) +{ + int nitems = x->x_nin, i; + t_symbol *templatesym = x->x_templatesym; + t_template *template = template_findbyname(templatesym); + t_setvariable *vp; + t_gpointer *gp = &x->x_gp; + t_gstub *gs = gp->gp_stub; + t_word *vec; + if (!template) + { + pd_error(x, "set: couldn't find template %s", templatesym->s_name); + return; + } + if (!gpointer_check(gp, 0)) + { + pd_error(x, "set: empty pointer"); + return; + } + if (gpointer_gettemplatesym(gp) != x->x_templatesym) + { + pd_error(x, "set %s: got wrong template (%s)", + x->x_templatesym->s_name, gpointer_gettemplatesym(gp)->s_name); + return; + } + if (!nitems) return; + x->x_variables[0].gv_f = f; + if (gs->gs_which == GP_ARRAY) vec = gp->gp_un.gp_w; + else vec = gp->gp_un.gp_scalar->sc_vec; + for (i = 0, vp = x->x_variables; i < nitems; i++, vp++) + { + template_setfloat(template, vp->gv_sym, vec, vp->gv_f, 1); + /* LATER deal with other types ala get_pointer. */ + } + if (gs->gs_which == GP_GLIST) + glist_redrawitem(gs->gs_un.gs_glist, (t_gobj *)(gp->gp_un.gp_scalar)); + else + { + t_array *owner_array = gs->gs_un.gs_array; + while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) + owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; + glist_redrawitem(owner_array->a_gp.gp_stub->gs_un.gs_glist, + (t_gobj *)(owner_array->a_gp.gp_un.gp_scalar)); + } +} + +static void set_free(t_set *x) +{ + freebytes(x->x_variables, x->x_nin * sizeof (*x->x_variables)); + gpointer_unset(&x->x_gp); +} + +static void set_setup(void) +{ + set_class = class_new(gensym("set"), (t_newmethod)set_new, + (t_method)set_free, sizeof(t_set), 0, A_GIMME, 0); + class_addfloat(set_class, set_float); +} + +/* ---------------------- elem ----------------------------- */ + +static t_class *elem_class; + +typedef struct _elem +{ + t_object x_obj; + t_symbol *x_templatesym; + t_symbol *x_fieldsym; + t_gpointer x_gp; + t_gpointer x_gparent; +} t_elem; + +static void *elem_new(t_symbol *templatesym, t_symbol *fieldsym) +{ + t_elem *x = (t_elem *)pd_new(elem_class); + x->x_templatesym = canvas_makebindsym(templatesym); + x->x_fieldsym = fieldsym; + gpointer_init(&x->x_gp); + gpointer_init(&x->x_gparent); + pointerinlet_new(&x->x_obj, &x->x_gparent); + outlet_new(&x->x_obj, &s_pointer); + return (x); +} + +static void elem_float(t_elem *x, t_float f) +{ + int indx = f, nitems, onset; + t_symbol *templatesym = x->x_templatesym, *fieldsym = x->x_fieldsym, + *elemtemplatesym; + t_template *template = template_findbyname(templatesym); + t_template *elemtemplate; + t_gpointer *gparent = &x->x_gparent; + t_word *w; + t_array *array; + int elemsize, type; + + if (!gpointer_check(gparent, 0)) + { + pd_error(x, "element: empty pointer"); + return; + } + if (gpointer_gettemplatesym(gparent) != x->x_templatesym) + { + pd_error(x, "element %s: got wrong template (%s)", + x->x_templatesym->s_name, gpointer_gettemplatesym(gparent)->s_name); + return; + } + if (gparent->gp_stub->gs_which == GP_ARRAY) w = gparent->gp_un.gp_w; + else w = gparent->gp_un.gp_scalar->sc_vec; + if (!template) + { + pd_error(x, "element: couldn't find template %s", templatesym->s_name); + return; + } + if (!template_find_field(template, fieldsym, + &onset, &type, &elemtemplatesym)) + { + pd_error(x, "element: couldn't find array field %s", fieldsym->s_name); + return; + } + if (type != DT_ARRAY) + { + pd_error(x, "element: field %s not of type array", fieldsym->s_name); + return; + } + if (!(elemtemplate = template_findbyname(elemtemplatesym))) + { + pd_error(x, "element: couldn't find field template %s", + elemtemplatesym->s_name); + return; + } + + elemsize = elemtemplate->t_n * sizeof(t_word); + + array = *(t_array **)(((char *)w) + onset); + + nitems = array->a_n; + if (indx < 0) indx = 0; + if (indx >= nitems) indx = nitems-1; + + gpointer_setarray(&x->x_gp, array, + (t_word *)((char *)(array->a_vec) + indx * elemsize)); + outlet_pointer(x->x_obj.ob_outlet, &x->x_gp); +} + +static void elem_free(t_elem *x, t_gpointer *gp) +{ + gpointer_unset(&x->x_gp); + gpointer_unset(&x->x_gparent); +} + +static void elem_setup(void) +{ + elem_class = class_new(gensym("element"), (t_newmethod)elem_new, + (t_method)elem_free, sizeof(t_elem), 0, A_DEFSYM, A_DEFSYM, 0); + class_addfloat(elem_class, elem_float); +} + +/* ---------------------- getsize ----------------------------- */ + +static t_class *getsize_class; + +typedef struct _getsize +{ + t_object x_obj; + t_symbol *x_templatesym; + t_symbol *x_fieldsym; +} t_getsize; + +static void *getsize_new(t_symbol *templatesym, t_symbol *fieldsym) +{ + t_getsize *x = (t_getsize *)pd_new(getsize_class); + x->x_templatesym = canvas_makebindsym(templatesym); + x->x_fieldsym = fieldsym; + outlet_new(&x->x_obj, &s_float); + return (x); +} + +static void getsize_pointer(t_getsize *x, t_gpointer *gp) +{ + int nitems, onset, type; + t_symbol *templatesym = x->x_templatesym, *fieldsym = x->x_fieldsym, + *elemtemplatesym; + t_template *template = template_findbyname(templatesym); + t_word *w; + t_array *array; + int elemsize; + t_gstub *gs = gp->gp_stub; + if (!template) + { + pd_error(x, "getsize: couldn't find template %s", templatesym->s_name); + return; + } + if (!template_find_field(template, fieldsym, + &onset, &type, &elemtemplatesym)) + { + pd_error(x, "getsize: couldn't find array field %s", fieldsym->s_name); + return; + } + if (type != DT_ARRAY) + { + pd_error(x, "getsize: field %s not of type array", fieldsym->s_name); + return; + } + if (gpointer_ishead(gp)) + { + pd_error(x, "getsize: empty pointer"); + return; + } + if (gpointer_gettemplatesym(gp) != x->x_templatesym) + { + pd_error(x, "getsize %s: got wrong template (%s)", + x->x_templatesym->s_name, gpointer_gettemplatesym(gp)->s_name); + return; + } + if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w; + else w = gp->gp_un.gp_scalar->sc_vec; + + array = *(t_array **)(((char *)w) + onset); + outlet_float(x->x_obj.ob_outlet, (float)(array->a_n)); +} + +static void getsize_setup(void) +{ + getsize_class = class_new(gensym("getsize"), (t_newmethod)getsize_new, 0, + sizeof(t_getsize), 0, A_DEFSYM, A_DEFSYM, 0); + class_addpointer(getsize_class, getsize_pointer); +} + +/* ---------------------- setsize ----------------------------- */ + +static t_class *setsize_class; + +typedef struct _setsize +{ + t_object x_obj; + t_symbol *x_templatesym; + t_symbol *x_fieldsym; + t_gpointer x_gp; +} t_setsize; + +static void *setsize_new(t_symbol *templatesym, t_symbol *fieldsym, + t_floatarg newsize) +{ + t_setsize *x = (t_setsize *)pd_new(setsize_class); + x->x_templatesym = canvas_makebindsym(templatesym); + x->x_fieldsym = fieldsym; + gpointer_init(&x->x_gp); + + pointerinlet_new(&x->x_obj, &x->x_gp); + return (x); +} + +static void setsize_float(t_setsize *x, t_float f) +{ + int nitems, onset, type; + t_symbol *templatesym = x->x_templatesym, *fieldsym = x->x_fieldsym, + *elemtemplatesym; + t_template *template = template_findbyname(templatesym); + t_template *elemtemplate; + t_word *w; + t_atom at; + t_array *array; + int elemsize; + int newsize = f; + t_gpointer *gp = &x->x_gp; + t_gstub *gs = gp->gp_stub; + if (!gpointer_check(&x->x_gp, 0)) + { + pd_error(x, "setsize: empty pointer"); + return; + } + if (gpointer_gettemplatesym(&x->x_gp) != x->x_templatesym) + { + pd_error(x, "setsize %s: got wrong template (%s)", + x->x_templatesym->s_name, + gpointer_gettemplatesym(&x->x_gp)->s_name); + return; + } + if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w; + else w = gp->gp_un.gp_scalar->sc_vec; + + if (!template) + { + pd_error(x,"setsize: couldn't find template %s", templatesym->s_name); + return; + } + if (!template_find_field(template, fieldsym, + &onset, &type, &elemtemplatesym)) + { + pd_error(x,"setsize: couldn't find array field %s", fieldsym->s_name); + return; + } + if (type != DT_ARRAY) + { + pd_error(x,"setsize: field %s not of type array", fieldsym->s_name); + return; + } + + if (!(elemtemplate = template_findbyname(elemtemplatesym))) + { + pd_error(x,"element: couldn't find field template %s", + elemtemplatesym->s_name); + return; + } + + elemsize = elemtemplate->t_n * sizeof(t_word); + + array = *(t_array **)(((char *)w) + onset); + + if (elemsize != array->a_elemsize) bug("setsize_gpointer"); + + nitems = array->a_n; + if (newsize < 1) newsize = 1; + if (newsize == nitems) return; + + /* erase the array before resizing it. If we belong to a + scalar it's easy, but if we belong to an element of another + array we have to search back until we get to a scalar to erase. + When graphics updates become queueable this may fall apart... */ + + + if (gs->gs_which == GP_GLIST) + { + if (glist_isvisible(gs->gs_un.gs_glist)) + gobj_vis((t_gobj *)(gp->gp_un.gp_scalar), gs->gs_un.gs_glist, 0); + } + else + { + t_array *owner_array = gs->gs_un.gs_array; + while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) + owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; + if (glist_isvisible(owner_array->a_gp.gp_stub->gs_un.gs_glist)) + gobj_vis((t_gobj *)(owner_array->a_gp.gp_un.gp_scalar), + owner_array->a_gp.gp_stub->gs_un.gs_glist, 0); + } + /* now do the resizing and, if growing, initialize new scalars */ + array->a_vec = (char *)resizebytes(array->a_vec, + elemsize * nitems, elemsize * newsize); + array->a_n = newsize; + if (newsize > nitems) + { + char *newelem = ((char *)array->a_vec) + nitems * elemsize; + int i = 0, nnew = newsize - nitems; + + while (nnew--) + { + word_init((t_word *)newelem, elemtemplate, gp); + newelem += elemsize; + /* post("new %x %x, ntypes %d", newelem, *(int *)newelem, ntypes); */ + } + } + + /* redraw again. */ + if (gs->gs_which == GP_GLIST) + { + if (glist_isvisible(gs->gs_un.gs_glist)) + gobj_vis((t_gobj *)(gp->gp_un.gp_scalar), gs->gs_un.gs_glist, 1); + } + else + { + t_array *owner_array = gs->gs_un.gs_array; + while (owner_array->a_gp.gp_stub->gs_which == GP_ARRAY) + owner_array = owner_array->a_gp.gp_stub->gs_un.gs_array; + if (glist_isvisible(owner_array->a_gp.gp_stub->gs_un.gs_glist)) + gobj_vis((t_gobj *)(owner_array->a_gp.gp_un.gp_scalar), + owner_array->a_gp.gp_stub->gs_un.gs_glist, 1); + } +} + + +static void setsize_free(t_setsize *x) +{ + gpointer_unset(&x->x_gp); +} + +static void setsize_setup(void) +{ + setsize_class = class_new(gensym("setsize"), (t_newmethod)setsize_new, + (t_method)setsize_free, sizeof(t_setsize), 0, + A_DEFSYM, A_DEFSYM, A_DEFFLOAT, 0); + class_addfloat(setsize_class, setsize_float); +} + +/* ---------------------- append ----------------------------- */ + +static t_class *append_class; + +typedef struct _appendvariable +{ + t_symbol *gv_sym; + t_float gv_f; +} t_appendvariable; + +typedef struct _append +{ + t_object x_obj; + t_gpointer x_gp; + t_symbol *x_templatesym; + int x_nin; + t_appendvariable *x_variables; +} t_append; + +static void *append_new(t_symbol *why, int argc, t_atom *argv) +{ + t_append *x = (t_append *)pd_new(append_class); + int i; + t_appendvariable *sp; + x->x_templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (argc) argc--, argv++; + x->x_variables + = (t_appendvariable *)getbytes(argc * sizeof (*x->x_variables)); + x->x_nin = argc; + if (argc) + { + for (i = 0, sp = x->x_variables; i < argc; i++, sp++) + { + sp->gv_sym = atom_getsymbolarg(i, argc, argv); + sp->gv_f = 0; + if (i) floatinlet_new(&x->x_obj, &sp->gv_f); + } + } + pointerinlet_new(&x->x_obj, &x->x_gp); + outlet_new(&x->x_obj, &s_pointer); + gpointer_init(&x->x_gp); + return (x); +} + +static void append_float(t_append *x, t_float f) +{ + int nitems = x->x_nin, i; + t_symbol *templatesym = x->x_templatesym; + t_template *template = template_findbyname(templatesym); + t_appendvariable *vp; + t_gpointer *gp = &x->x_gp; + t_gstub *gs = gp->gp_stub; + t_word *vec; + t_scalar *sc, *oldsc; + t_glist *glist; + if (!template) + { + pd_error(x, "append: couldn't find template %s", templatesym->s_name); + return; + } + if (!gs) + { + pd_error(x, "append: no current pointer"); + return; + } + if (gs->gs_which != GP_GLIST) + { + pd_error(x, "append: lists only, not arrays"); + return; + } + glist = gs->gs_un.gs_glist; + if (glist->gl_valid != gp->gp_valid) + { + pd_error(x, "append: stale pointer"); + return; + } + if (!nitems) return; + x->x_variables[0].gv_f = f; + + sc = scalar_new(glist, templatesym); + if (!sc) + { + pd_error(x, "%s: couldn't create scalar", templatesym->s_name); + return; + } + oldsc = gp->gp_un.gp_scalar; + + if (oldsc) + { + sc->sc_gobj.g_next = oldsc->sc_gobj.g_next; + oldsc->sc_gobj.g_next = &sc->sc_gobj; + } + else + { + sc->sc_gobj.g_next = glist->gl_list; + glist->gl_list = &sc->sc_gobj; + } + if (glist_isvisible(glist_getcanvas(glist))) + gobj_vis(&sc->sc_gobj, glist, 1); + + gp->gp_un.gp_scalar = sc; + vec = sc->sc_vec; + for (i = 0, vp = x->x_variables; i < nitems; i++, vp++) + { + template_setfloat(template, vp->gv_sym, vec, vp->gv_f, 1); + } + + glist_redrawitem(glist, (t_gobj *)sc); + + outlet_pointer(x->x_obj.ob_outlet, gp); +} + +static void append_free(t_append *x) +{ + freebytes(x->x_variables, x->x_nin * sizeof (*x->x_variables)); + gpointer_unset(&x->x_gp); +} + +static void append_setup(void) +{ + append_class = class_new(gensym("append"), (t_newmethod)append_new, + (t_method)append_free, sizeof(t_append), 0, A_GIMME, 0); + class_addfloat(append_class, append_float); +} + +/* ---------------------- sublist ----------------------------- */ + +static t_class *sublist_class; + +typedef struct _sublist +{ + t_object x_obj; + t_symbol *x_templatesym; + t_symbol *x_fieldsym; + t_gpointer x_gp; +} t_sublist; + +static void *sublist_new(t_symbol *templatesym, t_symbol *fieldsym) +{ + t_sublist *x = (t_sublist *)pd_new(sublist_class); + x->x_templatesym = canvas_makebindsym(templatesym); + x->x_fieldsym = fieldsym; + gpointer_init(&x->x_gp); + outlet_new(&x->x_obj, &s_pointer); + return (x); +} + +static void sublist_pointer(t_sublist *x, t_gpointer *gp) +{ + t_symbol *templatesym = x->x_templatesym, *dummy; + t_template *template = template_findbyname(templatesym); + t_gstub *gs = gp->gp_stub; + t_word *vec; + t_getvariable *vp; + int onset, type; + t_word *w; + + if (!template) + { + pd_error(x, "sublist: couldn't find template %s", templatesym->s_name); + return; + } + if (gpointer_ishead(gp)) + { + pd_error(x, "sublist: empty pointer"); + return; + } + if (!template_find_field(template, x->x_fieldsym, + &onset, &type, &dummy)) + { + pd_error(x, "sublist: couldn't find field %s", x->x_fieldsym->s_name); + return; + } + if (type != DT_LIST) + { + pd_error(x, "sublist: field %s not of type list", x->x_fieldsym->s_name); + return; + } + if (gs->gs_which == GP_ARRAY) w = gp->gp_un.gp_w; + else w = gp->gp_un.gp_scalar->sc_vec; + + gpointer_setglist(&x->x_gp, *(t_glist **)(((char *)w) + onset), 0); + + outlet_pointer(x->x_obj.ob_outlet, &x->x_gp); +} + +static void sublist_free(t_sublist *x, t_gpointer *gp) +{ + gpointer_unset(&x->x_gp); +} + +static void sublist_setup(void) +{ + sublist_class = class_new(gensym("sublist"), (t_newmethod)sublist_new, + (t_method)sublist_free, sizeof(t_sublist), 0, A_DEFSYM, A_DEFSYM, 0); + class_addpointer(sublist_class, sublist_pointer); +} + +/* ----------------- setup function ------------------- */ + +void g_traversal_setup(void) +{ + ptrobj_setup(); + get_setup(); + set_setup(); + elem_setup(); + getsize_setup(); + setsize_setup(); + append_setup(); + sublist_setup(); +} diff --git a/apps/plugins/pdbox/PDa/src/g_vdial.c b/apps/plugins/pdbox/PDa/src/g_vdial.c new file mode 100644 index 0000000..08b1338 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_vdial.c @@ -0,0 +1,1432 @@ +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* vdial.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ + +/* name change to vradio by MSP (it's a radio button really) and changed to +put out a "float" as in sliders, toggles, etc. */ + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +/*------------------ global variables -------------------------*/ + + +/*------------------ global functions -------------------------*/ + + + + +/* ------------- vdl gui-vertical radio button ---------------------- */ + +t_widgetbehavior vradio_widgetbehavior; +static t_class *vradio_class, *vradio_old_class; + +/* widget helper functions */ + +void vradio_draw_update(t_vradio *x, t_glist *glist) +{ + if(glist_isvisible(glist)) + { + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n", + canvas, x, x->x_on_old, + x->x_gui.x_bcol, x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n", + canvas, x, x->x_on, + x->x_gui.x_fcol, x->x_gui.x_fcol); + } +} + +void vradio_draw_new(t_vradio *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i, dy=x->x_gui.x_h, s4=dy/4; + int yy11b=text_ypix(&x->x_gui.x_obj, glist); + int yy11=yy11b, yy12=yy11+dy; + int yy21=yy11+s4, yy22=yy12-s4; + int xx11=text_xpix(&x->x_gui.x_obj, glist), xx12=xx11+dy; + int xx21=xx11+s4, xx22=xx12-s4; + + for(i=0; ix_gui.x_bcol, x, i); + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -outline #%6.6x -tags %xBUT%d\n", + canvas, xx21, yy21, xx22, yy22, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol, x, i); + yy11 += dy; + yy12 += dy; + yy21 += dy; + yy22 += dy; + } + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xx11+x->x_gui.x_ldx, yy11b+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_lcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xx11, yy11-1, xx11 + IOWIDTH, yy11, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xx11, yy11b, xx11 + IOWIDTH, yy11b+1, x, 0); +} + +void vradio_draw_move(t_vradio *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i, dy=x->x_gui.x_h, s4=dy/4; + int yy11b=text_ypix(&x->x_gui.x_obj, glist); + int yy11=yy11b, yy12=yy11+dy; + int yy21=yy11+s4, yy22=yy12-s4; + int xx11=text_xpix(&x->x_gui.x_obj, glist), xx12=xx11+dy; + int xx21=xx11+s4, xx22=xx12-s4; + + for(i=0; ix_gui.x_ldx, yy11b+x->x_gui.x_ldy); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, xx11, yy11-1, xx11 + IOWIDTH, yy11); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, xx11, yy11b, xx11 + IOWIDTH, yy11b+1); +} + +void vradio_draw_erase(t_vradio* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i; + + for(i=0; ix_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void vradio_draw_config(t_vradio* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i; + + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + for(i=0; ix_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n", canvas, x, i, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol); + } +} + +void vradio_draw_io(t_vradio* x, t_glist* glist, int old_snd_rcv_flags) +{ + t_canvas *canvas=glist_getcanvas(glist); + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xpos, + ypos+(x->x_number*x->x_gui.x_h)-1, + xpos+ IOWIDTH, + ypos+(x->x_number*x->x_gui.x_h), x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xpos, ypos, + xpos+ IOWIDTH, ypos+1, + x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void vradio_draw_select(t_vradio* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i; + + if(x->x_gui.x_fsf.x_selected) + { + for(i=0; ix_gui.x_lcol); + } +} + +void vradio_draw(t_vradio *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + vradio_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + vradio_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + vradio_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + vradio_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + vradio_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + vradio_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + vradio_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ vdl widgetbehaviour----------------------------- */ + +static void vradio_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_vradio *x = (t_vradio *)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist); + *yp1 = text_ypix(&x->x_gui.x_obj, glist); + *xp2 = *xp1 + x->x_gui.x_w; + *yp2 = *yp1 + x->x_gui.x_h*x->x_number; +} + +static void vradio_save(t_gobj *z, t_binbuf *b) +{ + t_vradio *x = (t_vradio *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiiiisssiiiiiiii", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, + (t_int)x->x_gui.x_obj.te_ypix, + (pd_class(&x->x_gui.x_obj.ob_pd) == vradio_old_class ? + gensym("vdl") : gensym("vradio")), + x->x_gui.x_w, + x->x_change, iem_symargstoint(&x->x_gui.x_isa), x->x_number, + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2], x->x_on); + binbuf_addv(b, ";"); +} + +static void vradio_properties(t_gobj *z, t_glist *owner) +{ + t_vradio *x = (t_vradio *)z; + char buf[800]; + t_symbol *srl[3]; + int hchange=-1; + + iemgui_properties(&x->x_gui, srl); + if(pd_class(&x->x_gui.x_obj.ob_pd) == vradio_old_class) + hchange = x->x_change; + sprintf(buf, "pdtk_iemgui_dialog %%s vradio \ + ----------dimensions(pix):----------- %d %d size: 0 0 empty \ + empty 0.0 empty 0.0 empty %d \ + %d new-only new&old %d %d number: %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, IEM_GUI_MINSIZE, + 0,/*no_schedule*/ + hchange, x->x_gui.x_isa.x_loadinit, -1, x->x_number, + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void vradio_dialog(t_vradio *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int a = (int)atom_getintarg(0, argc, argv); + int chg = (int)atom_getintarg(4, argc, argv); + int num = (int)atom_getintarg(6, argc, argv); + int sr_flags; + + if(chg != 0) chg = 1; + x->x_change = chg; + sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + if(x->x_number != num) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE); + x->x_number = num; + if(x->x_on >= x->x_number) + { + x->x_on = x->x_number - 1; + x->x_on_old = x->x_on; + } + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW); + } + else + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); + } +} + +static void vradio_set(t_vradio *x, t_floatarg f) +{ + int i=(int)f; + int old; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + if(x->x_on != x->x_on_old) + { + old = x->x_on_old; + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + x->x_on_old = old; + } + else + { + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } +} + +static void vradio_bang(t_vradio *x) +{ + /* compatibility with earlier "vdial" behavior */ + if (pd_class(&x->x_gui.x_obj.ob_pd) == vradio_old_class) + { + if((x->x_change)&&(x->x_on != x->x_on_old)) + { + SETFLOAT(x->x_at, (float)x->x_on_old); + SETFLOAT(x->x_at+1, 0.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + x->x_on_old = x->x_on; + SETFLOAT(x->x_at, (float)x->x_on); + SETFLOAT(x->x_at+1, 1.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + else + { + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); + } +} + +static void vradio_fout(t_vradio *x, t_floatarg f) +{ + int i=(int)f; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + + if (pd_class(&x->x_gui.x_obj.ob_pd) == vradio_old_class) + { + /* compatibility with earlier "vdial" behavior */ + if((x->x_change)&&(i != x->x_on_old)) + { + SETFLOAT(x->x_at, (float)x->x_on_old); + SETFLOAT(x->x_at+1, 0.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + if(x->x_on != x->x_on_old) + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + x->x_on_old = x->x_on; + SETFLOAT(x->x_at, (float)x->x_on); + SETFLOAT(x->x_at+1, 1.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + else + { + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if (x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); + } +} + +static void vradio_float(t_vradio *x, t_floatarg f) +{ + int i=(int)f; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + + if (pd_class(&x->x_gui.x_obj.ob_pd) == vradio_old_class) + { + /* compatibility with earlier "vdial" behavior */ + if((x->x_change)&&(i != x->x_on_old)) + { + if(x->x_gui.x_fsf.x_put_in2out) + { + SETFLOAT(x->x_at, (float)x->x_on_old); + SETFLOAT(x->x_at+1, 0.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + } + if(x->x_on != x->x_on_old) + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + x->x_on_old = x->x_on; + if(x->x_gui.x_fsf.x_put_in2out) + { + SETFLOAT(x->x_at, (float)x->x_on); + SETFLOAT(x->x_at+1, 1.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + } + else + { + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + if (x->x_gui.x_fsf.x_put_in2out) + { + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); + } + } +} + +static void vradio_click(t_vradio *x, t_floatarg xpos, t_floatarg ypos, + t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + int yy = (int)ypos - text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist); + + vradio_fout(x, (float)(yy / x->x_gui.x_h)); +} + +static int vradio_newclick(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + if(doit) + vradio_click((t_vradio *)z, (t_floatarg)xpix, (t_floatarg)ypix, + (t_floatarg)shift, 0, (t_floatarg)alt); + return (1); +} + +static void vradio_loadbang(t_vradio *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + vradio_bang(x); +} + +static void vradio_number(t_vradio *x, t_floatarg num) +{ + int n=(int)num; + + if(n < 1) + n = 1; + if(n > IEM_RADIO_MAX) + n = IEM_RADIO_MAX; + if(n != x->x_number) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE); + x->x_number = n; + if(x->x_on >= x->x_number) + x->x_on = x->x_number - 1; + x->x_on_old = x->x_on; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW); + } +} + +static void vradio_size(t_vradio *x, t_symbol *s, int ac, t_atom *av) +{ + x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av)); + x->x_gui.x_h = x->x_gui.x_w; + iemgui_size((void *)x, &x->x_gui); +} + +static void vradio_delta(t_vradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void vradio_pos(t_vradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void vradio_color(t_vradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void vradio_send(t_vradio *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void vradio_receive(t_vradio *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void vradio_label(t_vradio *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void vradio_label_pos(t_vradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void vradio_label_font(t_vradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void vradio_init(t_vradio *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void vradio_double_change(t_vradio *x) +{x->x_change = 1;} + +static void vradio_single_change(t_vradio *x) +{x->x_change = 0;} + +static void *vradio_donew(t_symbol *s, int argc, t_atom *argv, int old) +{ + t_vradio *x = (t_vradio *)pd_new(old? vradio_old_class : vradio_class); + int bflcol[]={-262144, -1, -1}; + int a=IEM_GUI_DEFAULTSIZE, on=0, f=0; + int ldx=0, ldy=-6, chg=1, num=8; + int fs=8; + int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, fthold=IEM_BNG_DEFAULTHOLDFLASHTIME; + char str[144]; + + if((argc == 15)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2) + &&IS_A_FLOAT(argv,3) + &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4)) + &&(IS_A_SYMBOL(argv,5)||IS_A_FLOAT(argv,5)) + &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) + &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8) + &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)&&IS_A_FLOAT(argv,11) + &&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13)&&IS_A_FLOAT(argv,14)) + { + a = (int)atom_getintarg(0, argc, argv); + chg = (int)atom_getintarg(1, argc, argv); + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(2, argc, argv)); + num = (int)atom_getintarg(3, argc, argv); + iemgui_new_getnames(&x->x_gui, 4, argv); + ldx = (int)atom_getintarg(7, argc, argv); + ldy = (int)atom_getintarg(8, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(9, argc, argv)); + fs = (int)atom_getintarg(10, argc, argv); + bflcol[0] = (int)atom_getintarg(11, argc, argv); + bflcol[1] = (int)atom_getintarg(12, argc, argv); + bflcol[2] = (int)atom_getintarg(13, argc, argv); + on = (int)atom_getintarg(14, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 4, 0); + x->x_gui.x_draw = (t_iemfunptr)vradio_draw; + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if (!strcmp(x->x_gui.x_snd->s_name, "empty")) + x->x_gui.x_fsf.x_snd_able = 0; + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + if(num < 1) + num = 1; + if(num > IEM_RADIO_MAX) + num = IEM_RADIO_MAX; + x->x_number = num; + if(on < 0) + on = 0; + if(on >= x->x_number) + on = x->x_number - 1; + if(x->x_gui.x_isa.x_loadinit) + x->x_on = on; + else + x->x_on = 0; + x->x_on_old = x->x_on; + x->x_change = (chg==0)?0:1; + if (x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + iemgui_verify_snd_ne_rcv(&x->x_gui); + iemgui_all_colfromload(&x->x_gui, bflcol); + outlet_new(&x->x_gui.x_obj, &s_list); + return (x); +} + +static void *vradio_new(t_symbol *s, int argc, t_atom *argv) +{ + return (vradio_donew(s, argc, argv, 0)); +} + +static void *vdial_new(t_symbol *s, int argc, t_atom *argv) +{ + return (vradio_donew(s, argc, argv, 1)); +} + +static void vradio_ff(t_vradio *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + gfxstub_deleteforkey(x); +} + +void g_vradio_setup(void) +{ + vradio_class = class_new(gensym("vradio"), (t_newmethod)vradio_new, + (t_method)vradio_ff, sizeof(t_vradio), 0, A_GIMME, 0); + class_addbang(vradio_class, vradio_bang); + class_addfloat(vradio_class, vradio_float); + class_addmethod(vradio_class, (t_method)vradio_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(vradio_class, (t_method)vradio_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(vradio_class, (t_method)vradio_loadbang, + gensym("loadbang"), 0); + class_addmethod(vradio_class, (t_method)vradio_set, + gensym("set"), A_FLOAT, 0); + class_addmethod(vradio_class, (t_method)vradio_size, + gensym("size"), A_GIMME, 0); + class_addmethod(vradio_class, (t_method)vradio_delta, + gensym("delta"), A_GIMME, 0); + class_addmethod(vradio_class, (t_method)vradio_pos, + gensym("pos"), A_GIMME, 0); + class_addmethod(vradio_class, (t_method)vradio_color, + gensym("color"), A_GIMME, 0); + class_addmethod(vradio_class, (t_method)vradio_send, + gensym("send"), A_DEFSYM, 0); + class_addmethod(vradio_class, (t_method)vradio_receive, + gensym("receive"), A_DEFSYM, 0); + class_addmethod(vradio_class, (t_method)vradio_label, + gensym("label"), A_DEFSYM, 0); + class_addmethod(vradio_class, (t_method)vradio_label_pos, + gensym("label_pos"), A_GIMME, 0); + class_addmethod(vradio_class, (t_method)vradio_label_font, + gensym("label_font"), A_GIMME, 0); + class_addmethod(vradio_class, (t_method)vradio_init, + gensym("init"), A_FLOAT, 0); + class_addmethod(vradio_class, (t_method)vradio_number, + gensym("number"), A_FLOAT, 0); + class_addmethod(vradio_class, (t_method)vradio_single_change, + gensym("single_change"), 0); + class_addmethod(vradio_class, (t_method)vradio_double_change, + gensym("double_change"), 0); + vradio_widgetbehavior.w_getrectfn = vradio_getrect; + vradio_widgetbehavior.w_displacefn = iemgui_displace; + vradio_widgetbehavior.w_selectfn = iemgui_select; + vradio_widgetbehavior.w_activatefn = NULL; + vradio_widgetbehavior.w_deletefn = iemgui_delete; + vradio_widgetbehavior.w_visfn = iemgui_vis; + vradio_widgetbehavior.w_clickfn = vradio_newclick; + class_setwidget(vradio_class, &vradio_widgetbehavior); + class_sethelpsymbol(vradio_class, gensym("vradio")); + class_setsavefn(vradio_class, vradio_save); + class_setpropertiesfn(vradio_class, vradio_properties); + + /* obsolete version (0.34-0.35) */ + vradio_old_class = class_new(gensym("vdl"), (t_newmethod)vdial_new, + (t_method)vradio_ff, sizeof(t_vradio), 0, A_GIMME, 0); + class_addbang(vradio_old_class, vradio_bang); + class_addfloat(vradio_old_class, vradio_float); + class_addmethod(vradio_old_class, (t_method)vradio_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(vradio_old_class, (t_method)vradio_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(vradio_old_class, (t_method)vradio_loadbang, gensym("loadbang"), 0); + class_addmethod(vradio_old_class, (t_method)vradio_set, gensym("set"), A_FLOAT, 0); + class_addmethod(vradio_old_class, (t_method)vradio_size, gensym("size"), A_GIMME, 0); + class_addmethod(vradio_old_class, (t_method)vradio_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(vradio_old_class, (t_method)vradio_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(vradio_old_class, (t_method)vradio_color, gensym("color"), A_GIMME, 0); + class_addmethod(vradio_old_class, (t_method)vradio_send, gensym("send"), A_DEFSYM, 0); + class_addmethod(vradio_old_class, (t_method)vradio_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(vradio_old_class, (t_method)vradio_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(vradio_old_class, (t_method)vradio_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(vradio_old_class, (t_method)vradio_label_font, gensym("label_font"), A_GIMME, 0); + class_addmethod(vradio_old_class, (t_method)vradio_init, gensym("init"), A_FLOAT, 0); + class_addmethod(vradio_old_class, (t_method)vradio_number, gensym("number"), A_FLOAT, 0); + class_addmethod(vradio_old_class, (t_method)vradio_single_change, gensym("single_change"), 0); + class_addmethod(vradio_old_class, (t_method)vradio_double_change, gensym("double_change"), 0); + class_setwidget(vradio_old_class, &vradio_widgetbehavior); + class_sethelpsymbol(vradio_old_class, gensym("vradio")); +} +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* vdial.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ + +/* name change to vradio by MSP (it's a radio button really) and changed to +put out a "float" as in sliders, toggles, etc. */ + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +/*------------------ global variables -------------------------*/ + + +/*------------------ global functions -------------------------*/ + + + + +/* ------------- vdl gui-vertical radio button ---------------------- */ + +t_widgetbehavior vradio_widgetbehavior; +static t_class *vradio_class, *vradio_old_class; + +/* widget helper functions */ + +void vradio_draw_update(t_vradio *x, t_glist *glist) +{ + if(glist_isvisible(glist)) + { + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n", + canvas, x, x->x_on_old, + x->x_gui.x_bcol, x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n", + canvas, x, x->x_on, + x->x_gui.x_fcol, x->x_gui.x_fcol); + } +} + +void vradio_draw_new(t_vradio *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i, dy=x->x_gui.x_h, s4=dy/4; + int yy11b=text_ypix(&x->x_gui.x_obj, glist); + int yy11=yy11b, yy12=yy11+dy; + int yy21=yy11+s4, yy22=yy12-s4; + int xx11=text_xpix(&x->x_gui.x_obj, glist), xx12=xx11+dy; + int xx21=xx11+s4, xx22=xx12-s4; + + for(i=0; ix_gui.x_bcol, x, i); + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -outline #%6.6x -tags %xBUT%d\n", + canvas, xx21, yy21, xx22, yy22, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol, x, i); + yy11 += dy; + yy12 += dy; + yy21 += dy; + yy22 += dy; + } + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xx11+x->x_gui.x_ldx, yy11b+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_lcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xx11, yy11-1, xx11 + IOWIDTH, yy11, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xx11, yy11b, xx11 + IOWIDTH, yy11b+1, x, 0); +} + +void vradio_draw_move(t_vradio *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i, dy=x->x_gui.x_h, s4=dy/4; + int yy11b=text_ypix(&x->x_gui.x_obj, glist); + int yy11=yy11b, yy12=yy11+dy; + int yy21=yy11+s4, yy22=yy12-s4; + int xx11=text_xpix(&x->x_gui.x_obj, glist), xx12=xx11+dy; + int xx21=xx11+s4, xx22=xx12-s4; + + for(i=0; ix_gui.x_ldx, yy11b+x->x_gui.x_ldy); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, xx11, yy11-1, xx11 + IOWIDTH, yy11); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, xx11, yy11b, xx11 + IOWIDTH, yy11b+1); +} + +void vradio_draw_erase(t_vradio* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i; + + for(i=0; ix_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void vradio_draw_config(t_vradio* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i; + + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + for(i=0; ix_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xBUT%d -fill #%6.6x -outline #%6.6x\n", canvas, x, i, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol, + (x->x_on==i)?x->x_gui.x_fcol:x->x_gui.x_bcol); + } +} + +void vradio_draw_io(t_vradio* x, t_glist* glist, int old_snd_rcv_flags) +{ + t_canvas *canvas=glist_getcanvas(glist); + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, xpos, + ypos+(x->x_number*x->x_gui.x_h)-1, + xpos+ IOWIDTH, + ypos+(x->x_number*x->x_gui.x_h), x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, xpos, ypos, + xpos+ IOWIDTH, ypos+1, + x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +void vradio_draw_select(t_vradio* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i; + + if(x->x_gui.x_fsf.x_selected) + { + for(i=0; ix_gui.x_lcol); + } +} + +void vradio_draw(t_vradio *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + vradio_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + vradio_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + vradio_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + vradio_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + vradio_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + vradio_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + vradio_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ vdl widgetbehaviour----------------------------- */ + +static void vradio_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_vradio *x = (t_vradio *)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist); + *yp1 = text_ypix(&x->x_gui.x_obj, glist); + *xp2 = *xp1 + x->x_gui.x_w; + *yp2 = *yp1 + x->x_gui.x_h*x->x_number; +} + +static void vradio_save(t_gobj *z, t_binbuf *b) +{ + t_vradio *x = (t_vradio *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiiiisssiiiiiiii", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, + (t_int)x->x_gui.x_obj.te_ypix, + (pd_class(&x->x_gui.x_obj.ob_pd) == vradio_old_class ? + gensym("vdl") : gensym("vradio")), + x->x_gui.x_w, + x->x_change, iem_symargstoint(&x->x_gui.x_isa), x->x_number, + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2], x->x_on); + binbuf_addv(b, ";"); +} + +static void vradio_properties(t_gobj *z, t_glist *owner) +{ + t_vradio *x = (t_vradio *)z; + char buf[800]; + t_symbol *srl[3]; + int hchange=-1; + + iemgui_properties(&x->x_gui, srl); + if(pd_class(&x->x_gui.x_obj.ob_pd) == vradio_old_class) + hchange = x->x_change; + sprintf(buf, "pdtk_iemgui_dialog %%s vradio \ + ----------dimensions(pix):----------- %d %d size: 0 0 empty \ + empty 0.0 empty 0.0 empty %d \ + %d new-only new&old %d %d number: %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, IEM_GUI_MINSIZE, + 0,/*no_schedule*/ + hchange, x->x_gui.x_isa.x_loadinit, -1, x->x_number, + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void vradio_dialog(t_vradio *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int a = (int)atom_getintarg(0, argc, argv); + int chg = (int)atom_getintarg(4, argc, argv); + int num = (int)atom_getintarg(6, argc, argv); + int sr_flags; + + if(chg != 0) chg = 1; + x->x_change = chg; + sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + if(x->x_number != num) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE); + x->x_number = num; + if(x->x_on >= x->x_number) + { + x->x_on = x->x_number - 1; + x->x_on_old = x->x_on; + } + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW); + } + else + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); + } +} + +static void vradio_set(t_vradio *x, t_floatarg f) +{ + int i=(int)f; + int old; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + if(x->x_on != x->x_on_old) + { + old = x->x_on_old; + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + x->x_on_old = old; + } + else + { + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + } +} + +static void vradio_bang(t_vradio *x) +{ + /* compatibility with earlier "vdial" behavior */ + if (pd_class(&x->x_gui.x_obj.ob_pd) == vradio_old_class) + { + if((x->x_change)&&(x->x_on != x->x_on_old)) + { + SETFLOAT(x->x_at, (float)x->x_on_old); + SETFLOAT(x->x_at+1, 0.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + x->x_on_old = x->x_on; + SETFLOAT(x->x_at, (float)x->x_on); + SETFLOAT(x->x_at+1, 1.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + else + { + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); + } +} + +static void vradio_fout(t_vradio *x, t_floatarg f) +{ + int i=(int)f; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + + if (pd_class(&x->x_gui.x_obj.ob_pd) == vradio_old_class) + { + /* compatibility with earlier "vdial" behavior */ + if((x->x_change)&&(i != x->x_on_old)) + { + SETFLOAT(x->x_at, (float)x->x_on_old); + SETFLOAT(x->x_at+1, 0.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + if(x->x_on != x->x_on_old) + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + x->x_on_old = x->x_on; + SETFLOAT(x->x_at, (float)x->x_on); + SETFLOAT(x->x_at+1, 1.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + else + { + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if (x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); + } +} + +static void vradio_float(t_vradio *x, t_floatarg f) +{ + int i=(int)f; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + + if (pd_class(&x->x_gui.x_obj.ob_pd) == vradio_old_class) + { + /* compatibility with earlier "vdial" behavior */ + if((x->x_change)&&(i != x->x_on_old)) + { + if(x->x_gui.x_fsf.x_put_in2out) + { + SETFLOAT(x->x_at, (float)x->x_on_old); + SETFLOAT(x->x_at+1, 0.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + } + if(x->x_on != x->x_on_old) + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + x->x_on_old = x->x_on; + if(x->x_gui.x_fsf.x_put_in2out) + { + SETFLOAT(x->x_at, (float)x->x_on); + SETFLOAT(x->x_at+1, 1.0); + outlet_list(x->x_gui.x_obj.ob_outlet, &s_list, 2, x->x_at); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_list(x->x_gui.x_snd->s_thing, &s_list, 2, x->x_at); + } + } + else + { + x->x_on_old = x->x_on; + x->x_on = i; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + if (x->x_gui.x_fsf.x_put_in2out) + { + outlet_float(x->x_gui.x_obj.ob_outlet, x->x_on); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, x->x_on); + } + } +} + +static void vradio_click(t_vradio *x, t_floatarg xpos, t_floatarg ypos, + t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + int yy = (int)ypos - text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist); + + vradio_fout(x, (float)(yy / x->x_gui.x_h)); +} + +static int vradio_newclick(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + if(doit) + vradio_click((t_vradio *)z, (t_floatarg)xpix, (t_floatarg)ypix, + (t_floatarg)shift, 0, (t_floatarg)alt); + return (1); +} + +static void vradio_loadbang(t_vradio *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + vradio_bang(x); +} + +static void vradio_number(t_vradio *x, t_floatarg num) +{ + int n=(int)num; + + if(n < 1) + n = 1; + if(n > IEM_RADIO_MAX) + n = IEM_RADIO_MAX; + if(n != x->x_number) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_ERASE); + x->x_number = n; + if(x->x_on >= x->x_number) + x->x_on = x->x_number - 1; + x->x_on_old = x->x_on; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_NEW); + } +} + +static void vradio_size(t_vradio *x, t_symbol *s, int ac, t_atom *av) +{ + x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av)); + x->x_gui.x_h = x->x_gui.x_w; + iemgui_size((void *)x, &x->x_gui); +} + +static void vradio_delta(t_vradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void vradio_pos(t_vradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void vradio_color(t_vradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void vradio_send(t_vradio *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void vradio_receive(t_vradio *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void vradio_label(t_vradio *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void vradio_label_pos(t_vradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void vradio_label_font(t_vradio *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void vradio_init(t_vradio *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void vradio_double_change(t_vradio *x) +{x->x_change = 1;} + +static void vradio_single_change(t_vradio *x) +{x->x_change = 0;} + +static void *vradio_donew(t_symbol *s, int argc, t_atom *argv, int old) +{ + t_vradio *x = (t_vradio *)pd_new(old? vradio_old_class : vradio_class); + int bflcol[]={-262144, -1, -1}; + int a=IEM_GUI_DEFAULTSIZE, on=0, f=0; + int ldx=0, ldy=-6, chg=1, num=8; + int fs=8; + int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, fthold=IEM_BNG_DEFAULTHOLDFLASHTIME; + char str[144]; + + if((argc == 15)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1)&&IS_A_FLOAT(argv,2) + &&IS_A_FLOAT(argv,3) + &&(IS_A_SYMBOL(argv,4)||IS_A_FLOAT(argv,4)) + &&(IS_A_SYMBOL(argv,5)||IS_A_FLOAT(argv,5)) + &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) + &&IS_A_FLOAT(argv,7)&&IS_A_FLOAT(argv,8) + &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)&&IS_A_FLOAT(argv,11) + &&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13)&&IS_A_FLOAT(argv,14)) + { + a = (int)atom_getintarg(0, argc, argv); + chg = (int)atom_getintarg(1, argc, argv); + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(2, argc, argv)); + num = (int)atom_getintarg(3, argc, argv); + iemgui_new_getnames(&x->x_gui, 4, argv); + ldx = (int)atom_getintarg(7, argc, argv); + ldy = (int)atom_getintarg(8, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(9, argc, argv)); + fs = (int)atom_getintarg(10, argc, argv); + bflcol[0] = (int)atom_getintarg(11, argc, argv); + bflcol[1] = (int)atom_getintarg(12, argc, argv); + bflcol[2] = (int)atom_getintarg(13, argc, argv); + on = (int)atom_getintarg(14, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 4, 0); + x->x_gui.x_draw = (t_iemfunptr)vradio_draw; + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if (!strcmp(x->x_gui.x_snd->s_name, "empty")) + x->x_gui.x_fsf.x_snd_able = 0; + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + if(num < 1) + num = 1; + if(num > IEM_RADIO_MAX) + num = IEM_RADIO_MAX; + x->x_number = num; + if(on < 0) + on = 0; + if(on >= x->x_number) + on = x->x_number - 1; + if(x->x_gui.x_isa.x_loadinit) + x->x_on = on; + else + x->x_on = 0; + x->x_on_old = x->x_on; + x->x_change = (chg==0)?0:1; + if (x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + x->x_gui.x_w = iemgui_clip_size(a); + x->x_gui.x_h = x->x_gui.x_w; + iemgui_verify_snd_ne_rcv(&x->x_gui); + iemgui_all_colfromload(&x->x_gui, bflcol); + outlet_new(&x->x_gui.x_obj, &s_list); + return (x); +} + +static void *vradio_new(t_symbol *s, int argc, t_atom *argv) +{ + return (vradio_donew(s, argc, argv, 0)); +} + +static void *vdial_new(t_symbol *s, int argc, t_atom *argv) +{ + return (vradio_donew(s, argc, argv, 1)); +} + +static void vradio_ff(t_vradio *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + gfxstub_deleteforkey(x); +} + +void g_vradio_setup(void) +{ + vradio_class = class_new(gensym("vradio"), (t_newmethod)vradio_new, + (t_method)vradio_ff, sizeof(t_vradio), 0, A_GIMME, 0); + class_addbang(vradio_class, vradio_bang); + class_addfloat(vradio_class, vradio_float); + class_addmethod(vradio_class, (t_method)vradio_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(vradio_class, (t_method)vradio_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(vradio_class, (t_method)vradio_loadbang, + gensym("loadbang"), 0); + class_addmethod(vradio_class, (t_method)vradio_set, + gensym("set"), A_FLOAT, 0); + class_addmethod(vradio_class, (t_method)vradio_size, + gensym("size"), A_GIMME, 0); + class_addmethod(vradio_class, (t_method)vradio_delta, + gensym("delta"), A_GIMME, 0); + class_addmethod(vradio_class, (t_method)vradio_pos, + gensym("pos"), A_GIMME, 0); + class_addmethod(vradio_class, (t_method)vradio_color, + gensym("color"), A_GIMME, 0); + class_addmethod(vradio_class, (t_method)vradio_send, + gensym("send"), A_DEFSYM, 0); + class_addmethod(vradio_class, (t_method)vradio_receive, + gensym("receive"), A_DEFSYM, 0); + class_addmethod(vradio_class, (t_method)vradio_label, + gensym("label"), A_DEFSYM, 0); + class_addmethod(vradio_class, (t_method)vradio_label_pos, + gensym("label_pos"), A_GIMME, 0); + class_addmethod(vradio_class, (t_method)vradio_label_font, + gensym("label_font"), A_GIMME, 0); + class_addmethod(vradio_class, (t_method)vradio_init, + gensym("init"), A_FLOAT, 0); + class_addmethod(vradio_class, (t_method)vradio_number, + gensym("number"), A_FLOAT, 0); + class_addmethod(vradio_class, (t_method)vradio_single_change, + gensym("single_change"), 0); + class_addmethod(vradio_class, (t_method)vradio_double_change, + gensym("double_change"), 0); + vradio_widgetbehavior.w_getrectfn = vradio_getrect; + vradio_widgetbehavior.w_displacefn = iemgui_displace; + vradio_widgetbehavior.w_selectfn = iemgui_select; + vradio_widgetbehavior.w_activatefn = NULL; + vradio_widgetbehavior.w_deletefn = iemgui_delete; + vradio_widgetbehavior.w_visfn = iemgui_vis; + vradio_widgetbehavior.w_clickfn = vradio_newclick; + class_setwidget(vradio_class, &vradio_widgetbehavior); + class_sethelpsymbol(vradio_class, gensym("vradio")); + class_setsavefn(vradio_class, vradio_save); + class_setpropertiesfn(vradio_class, vradio_properties); + + /* obsolete version (0.34-0.35) */ + vradio_old_class = class_new(gensym("vdl"), (t_newmethod)vdial_new, + (t_method)vradio_ff, sizeof(t_vradio), 0, A_GIMME, 0); + class_addbang(vradio_old_class, vradio_bang); + class_addfloat(vradio_old_class, vradio_float); + class_addmethod(vradio_old_class, (t_method)vradio_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(vradio_old_class, (t_method)vradio_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(vradio_old_class, (t_method)vradio_loadbang, gensym("loadbang"), 0); + class_addmethod(vradio_old_class, (t_method)vradio_set, gensym("set"), A_FLOAT, 0); + class_addmethod(vradio_old_class, (t_method)vradio_size, gensym("size"), A_GIMME, 0); + class_addmethod(vradio_old_class, (t_method)vradio_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(vradio_old_class, (t_method)vradio_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(vradio_old_class, (t_method)vradio_color, gensym("color"), A_GIMME, 0); + class_addmethod(vradio_old_class, (t_method)vradio_send, gensym("send"), A_DEFSYM, 0); + class_addmethod(vradio_old_class, (t_method)vradio_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(vradio_old_class, (t_method)vradio_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(vradio_old_class, (t_method)vradio_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(vradio_old_class, (t_method)vradio_label_font, gensym("label_font"), A_GIMME, 0); + class_addmethod(vradio_old_class, (t_method)vradio_init, gensym("init"), A_FLOAT, 0); + class_addmethod(vradio_old_class, (t_method)vradio_number, gensym("number"), A_FLOAT, 0); + class_addmethod(vradio_old_class, (t_method)vradio_single_change, gensym("single_change"), 0); + class_addmethod(vradio_old_class, (t_method)vradio_double_change, gensym("double_change"), 0); + class_setwidget(vradio_old_class, &vradio_widgetbehavior); + class_sethelpsymbol(vradio_old_class, gensym("vradio")); +} diff --git a/apps/plugins/pdbox/PDa/src/g_vslider.c b/apps/plugins/pdbox/PDa/src/g_vslider.c new file mode 100644 index 0000000..8e2e326 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_vslider.c @@ -0,0 +1,1254 @@ +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + + +/* ------------ vsl gui-vertical slider ----------------------- */ + +t_widgetbehavior vslider_widgetbehavior; +static t_class *vslider_class; + +/* widget helper functions */ + +static void vslider_draw_update(t_vslider *x, t_glist *glist) +{ + if (glist_isvisible(glist)) + { + int r = text_ypix(&x->x_gui.x_obj, glist) + x->x_gui.x_h - (x->x_val + 50)/100; + int xpos=text_xpix(&x->x_gui.x_obj, glist); + + sys_vgui(".x%x.c coords %xKNOB %d %d %d %d\n", + glist_getcanvas(glist), x, xpos+1, r, + xpos + x->x_gui.x_w, r); + } +} + +static void vslider_draw_new(t_vslider *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + int r = ypos + x->x_gui.x_h - (x->x_val + 50)/100; + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n", + canvas, xpos, ypos-2, + xpos + x->x_gui.x_w, ypos + x->x_gui.x_h+3, + x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create line %d %d %d %d -width 3 -fill #%6.6x -tags %xKNOB\n", + canvas, xpos+1, r, + xpos + x->x_gui.x_w, r, x->x_gui.x_fcol, x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos, ypos + x->x_gui.x_h+2, + xpos+7, ypos + x->x_gui.x_h+3, + x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos, ypos-2, + xpos+7, ypos-1, + x, 0); +} + +static void vslider_draw_move(t_vslider *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + int r = ypos + x->x_gui.x_h - (x->x_val + 50)/100; + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n", + canvas, x, + xpos, ypos-2, + xpos + x->x_gui.x_w, ypos + x->x_gui.x_h+3); + sys_vgui(".x%x.c coords %xKNOB %d %d %d %d\n", + canvas, x, xpos+1, r, + xpos + x->x_gui.x_w, r); + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + canvas, x, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, + xpos, ypos + x->x_gui.x_h+2, + xpos+7, ypos + x->x_gui.x_h+3); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, + xpos, ypos-2, + xpos+7, ypos-1); +} + +static void vslider_draw_erase(t_vslider* x,t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c delete %xBASE\n", canvas, x); + sys_vgui(".x%x.c delete %xKNOB\n", canvas, x); + sys_vgui(".x%x.c delete %xLABEL\n", canvas, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +static void vslider_draw_config(t_vslider* x,t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + sys_vgui(".x%x.c itemconfigure %xKNOB -fill #%6.6x\n", canvas, + x, x->x_gui.x_fcol); + sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas, + x, x->x_gui.x_bcol); +} + +static void vslider_draw_io(t_vslider* x,t_glist* glist, int old_snd_rcv_flags) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos, ypos + x->x_gui.x_h+2, + xpos+7, ypos + x->x_gui.x_h+3, + x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos, ypos-2, + xpos+7, ypos-1, + x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +static void vslider_draw_select(t_vslider *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + if(x->x_gui.x_fsf.x_selected) + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol); + } +} + +void vslider_draw(t_vslider *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + vslider_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + vslider_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + vslider_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + vslider_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + vslider_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + vslider_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + vslider_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ vsl widgetbehaviour----------------------------- */ + + +static void vslider_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_vslider* x = (t_vslider*)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist); + *yp1 = text_ypix(&x->x_gui.x_obj, glist) - 2; + *xp2 = *xp1 + x->x_gui.x_w; + *yp2 = *yp1 + x->x_gui.x_h + 5; +} + +static void vslider_save(t_gobj *z, t_binbuf *b) +{ + t_vslider *x = (t_vslider *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiiffiisssiiiiiiiii", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix, + gensym("vsl"), x->x_gui.x_w, x->x_gui.x_h, + (float)x->x_min, (float)x->x_max, + x->x_lin0_log1, iem_symargstoint(&x->x_gui.x_isa), + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2], + x->x_val, x->x_steady); + binbuf_addv(b, ";"); +} + +void vslider_check_height(t_vslider *x, int h) +{ + if(h < IEM_SL_MINSIZE) + h = IEM_SL_MINSIZE; + x->x_gui.x_h = h; + if(x->x_val > (x->x_gui.x_h*100 - 100)) + { + x->x_pos = x->x_gui.x_h*100 - 100; + x->x_val = x->x_pos; + } + if(x->x_lin0_log1) + x->x_k = log(x->x_max/x->x_min)/(double)(x->x_gui.x_h - 1); + else + x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_h - 1); +} + +void vslider_check_minmax(t_vslider *x, double min, double max) +{ + if(x->x_lin0_log1) + { + if((min == 0.0)&&(max == 0.0)) + max = 1.0; + if(max > 0.0) + { + if(min <= 0.0) + min = 0.01*max; + } + else + { + if(min > 0.0) + max = 0.01*min; + } + } + x->x_min = min; + x->x_max = max; + if(x->x_min > x->x_max) /* bugfix */ + x->x_gui.x_isa.x_reverse = 1; + else + x->x_gui.x_isa.x_reverse = 0; + if(x->x_lin0_log1) + x->x_k = log(x->x_max/x->x_min)/(double)(x->x_gui.x_h - 1); + else + x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_h - 1); +} + +static void vslider_properties(t_gobj *z, t_glist *owner) +{ + t_vslider *x = (t_vslider *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + + sprintf(buf, "pdtk_iemgui_dialog %%s VSLIDER \ + --------dimensions(pix)(pix):-------- %d %d width: %d %d height: \ + -----------output-range:----------- %g bottom: %g top: %d \ + %d lin log %d %d empty %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, IEM_GUI_MINSIZE, x->x_gui.x_h, IEM_SL_MINSIZE, + x->x_min, x->x_max, 0,/*no_schedule*/ + x->x_lin0_log1, x->x_gui.x_isa.x_loadinit, x->x_steady, -1,/*no multi, but iem-characteristic*/ + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void vslider_bang(t_vslider *x) +{ + double out; + + if(x->x_lin0_log1) + out = x->x_min*exp(x->x_k*(double)(x->x_val)*0.01); + else + out = (double)(x->x_val)*0.01*x->x_k + x->x_min; + if((out < 1.0e-10)&&(out > -1.0e-10)) + out = 0.0; + + outlet_float(x->x_gui.x_obj.ob_outlet, out); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, out); +} + +static void vslider_dialog(t_vslider *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int w = (int)atom_getintarg(0, argc, argv); + int h = (int)atom_getintarg(1, argc, argv); + double min = (double)atom_getfloatarg(2, argc, argv); + double max = (double)atom_getfloatarg(3, argc, argv); + int lilo = (int)atom_getintarg(4, argc, argv); + int steady = (int)atom_getintarg(17, argc, argv); + int sr_flags; + + if(lilo != 0) lilo = 1; + x->x_lin0_log1 = lilo; + if(steady) + x->x_steady = 1; + else + x->x_steady = 0; + sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + x->x_gui.x_w = iemgui_clip_size(w); + vslider_check_height(x, h); + vslider_check_minmax(x, min, max); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); +} + +static void vslider_motion(t_vslider *x, t_floatarg dx, t_floatarg dy) +{ + int old = x->x_val; + + if(x->x_gui.x_fsf.x_finemoved) + x->x_pos -= (int)dy; + else + x->x_pos -= 100*(int)dy; + x->x_val = x->x_pos; + if(x->x_val > (100*x->x_gui.x_h - 100)) + { + x->x_val = 100*x->x_gui.x_h - 100; + x->x_pos += 50; + x->x_pos -= x->x_pos%100; + } + if(x->x_val < 0) + { + x->x_val = 0; + x->x_pos -= 50; + x->x_pos -= x->x_pos%100; + } + if(old != x->x_val) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + vslider_bang(x); + } +} + +static void vslider_click(t_vslider *x, t_floatarg xpos, t_floatarg ypos, + t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + if(!x->x_steady) + x->x_val = (int)(100.0 * (x->x_gui.x_h + text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist) - ypos)); + if(x->x_val > (100*x->x_gui.x_h - 100)) + x->x_val = 100*x->x_gui.x_h - 100; + if(x->x_val < 0) + x->x_val = 0; + x->x_pos = x->x_val; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + vslider_bang(x); + glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.te_g, + (t_glistmotionfn)vslider_motion, 0, xpos, ypos); +} + +static int vslider_newclick(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_vslider* x = (t_vslider *)z; + + if(doit) + { + vslider_click( x, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, + 0, (t_floatarg)alt); + if(shift) + x->x_gui.x_fsf.x_finemoved = 1; + else + x->x_gui.x_fsf.x_finemoved = 0; + } + return (1); +} + +static void vslider_set(t_vslider *x, t_floatarg f) +{ + double g; + + if(x->x_gui.x_isa.x_reverse) /* bugfix */ + { + if(f > x->x_min) + f = x->x_min; + if(f < x->x_max) + f = x->x_max; + } + else + { + if(f > x->x_max) + f = x->x_max; + if(f < x->x_min) + f = x->x_min; + } + if(x->x_lin0_log1) + g = log(f/x->x_min)/x->x_k; + else + g = (f - x->x_min) / x->x_k; + x->x_val = (int)(100.0*g + 0.49999); + x->x_pos = x->x_val; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); +} + +static void vslider_float(t_vslider *x, t_floatarg f) +{ + vslider_set(x, f); + if(x->x_gui.x_fsf.x_put_in2out) + vslider_bang(x); +} + +static void vslider_size(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{ + x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av)); + if(ac > 1) + vslider_check_height(x, (int)atom_getintarg(1, ac, av)); + iemgui_size((void *)x, &x->x_gui); +} + +static void vslider_delta(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void vslider_pos(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void vslider_range(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{ + vslider_check_minmax(x, (double)atom_getfloatarg(0, ac, av), + (double)atom_getfloatarg(1, ac, av)); +} + +static void vslider_color(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void vslider_send(t_vslider *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void vslider_receive(t_vslider *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void vslider_label(t_vslider *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void vslider_label_pos(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void vslider_label_font(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void vslider_log(t_vslider *x) +{ + x->x_lin0_log1 = 1; + vslider_check_minmax(x, x->x_min, x->x_max); +} + +static void vslider_lin(t_vslider *x) +{ + x->x_lin0_log1 = 0; + x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_h - 1); +} + +static void vslider_init(t_vslider *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void vslider_steady(t_vslider *x, t_floatarg f) +{ + x->x_steady = (f==0.0)?0:1; +} + +static void vslider_loadbang(t_vslider *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + vslider_bang(x); + } +} + +static void *vslider_new(t_symbol *s, int argc, t_atom *argv) +{ + t_vslider *x = (t_vslider *)pd_new(vslider_class); + int bflcol[]={-262144, -1, -1}; + int w=IEM_GUI_DEFAULTSIZE, h=IEM_SL_DEFAULTSIZE; + int lilo=0, f=0, ldx=0, ldy=-8; + int fs=8, v=0, steady=1; + double min=0.0, max=(double)(IEM_SL_DEFAULTSIZE-1); + char str[144]; + + iem_inttosymargs(&x->x_gui.x_isa, 0); + iem_inttofstyle(&x->x_gui.x_fsf, 0); + + if(((argc == 17)||(argc == 18))&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1) + &&IS_A_FLOAT(argv,2)&&IS_A_FLOAT(argv,3) + &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5) + &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) + &&(IS_A_SYMBOL(argv,7)||IS_A_FLOAT(argv,7)) + &&(IS_A_SYMBOL(argv,8)||IS_A_FLOAT(argv,8)) + &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10) + &&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13) + &&IS_A_FLOAT(argv,14)&&IS_A_FLOAT(argv,15)&&IS_A_FLOAT(argv,16)) + { + w = (int)atom_getintarg(0, argc, argv); + h = (int)atom_getintarg(1, argc, argv); + min = (double)atom_getfloatarg(2, argc, argv); + max = (double)atom_getfloatarg(3, argc, argv); + lilo = (int)atom_getintarg(4, argc, argv); + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(5, argc, argv)); + iemgui_new_getnames(&x->x_gui, 6, argv); + ldx = (int)atom_getintarg(9, argc, argv); + ldy = (int)atom_getintarg(10, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(11, argc, argv)); + fs = (int)atom_getintarg(12, argc, argv); + bflcol[0] = (int)atom_getintarg(13, argc, argv); + bflcol[1] = (int)atom_getintarg(14, argc, argv); + bflcol[2] = (int)atom_getintarg(15, argc, argv); + v = (int)atom_getintarg(16, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 6, 0); + if((argc == 18)&&IS_A_FLOAT(argv,17)) + steady = (int)atom_getintarg(17, argc, argv); + x->x_gui.x_draw = (t_iemfunptr)vslider_draw; + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if(x->x_gui.x_isa.x_loadinit) + x->x_val = v; + else + x->x_val = 0; + x->x_pos = x->x_val; + if(lilo != 0) lilo = 1; + x->x_lin0_log1 = lilo; + if(steady != 0) steady = 1; + x->x_steady = steady; + if(!strcmp(x->x_gui.x_snd->s_name, "empty")) x->x_gui.x_fsf.x_snd_able = 0; + if(!strcmp(x->x_gui.x_rcv->s_name, "empty")) x->x_gui.x_fsf.x_rcv_able = 0; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + x->x_gui.x_w = iemgui_clip_size(w); + vslider_check_height(x, h); + vslider_check_minmax(x, min, max); + iemgui_all_colfromload(&x->x_gui, bflcol); + iemgui_verify_snd_ne_rcv(&x->x_gui); + outlet_new(&x->x_gui.x_obj, &s_float); + return (x); +} + +static void vslider_free(t_vslider *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + gfxstub_deleteforkey(x); +} + +void g_vslider_setup(void) +{ + vslider_class = class_new(gensym("vsl"), (t_newmethod)vslider_new, + (t_method)vslider_free, sizeof(t_vslider), 0, A_GIMME, 0); + class_addcreator((t_newmethod)vslider_new, gensym("vslider"), A_GIMME, 0); + class_addbang(vslider_class,vslider_bang); + class_addfloat(vslider_class,vslider_float); + class_addmethod(vslider_class, (t_method)vslider_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(vslider_class, (t_method)vslider_motion, gensym("motion"), + A_FLOAT, A_FLOAT, 0); + class_addmethod(vslider_class, (t_method)vslider_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_loadbang, gensym("loadbang"), 0); + class_addmethod(vslider_class, (t_method)vslider_set, gensym("set"), A_FLOAT, 0); + class_addmethod(vslider_class, (t_method)vslider_size, gensym("size"), A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_range, gensym("range"), A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_color, gensym("color"), A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_send, gensym("send"), A_DEFSYM, 0); + class_addmethod(vslider_class, (t_method)vslider_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(vslider_class, (t_method)vslider_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(vslider_class, (t_method)vslider_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_label_font, gensym("label_font"), A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_log, gensym("log"), 0); + class_addmethod(vslider_class, (t_method)vslider_lin, gensym("lin"), 0); + class_addmethod(vslider_class, (t_method)vslider_init, gensym("init"), A_FLOAT, 0); + class_addmethod(vslider_class, (t_method)vslider_steady, gensym("steady"), A_FLOAT, 0); + vslider_widgetbehavior.w_getrectfn = vslider_getrect; + vslider_widgetbehavior.w_displacefn = iemgui_displace; + vslider_widgetbehavior.w_selectfn = iemgui_select; + vslider_widgetbehavior.w_activatefn = NULL; + vslider_widgetbehavior.w_deletefn = iemgui_delete; + vslider_widgetbehavior.w_visfn = iemgui_vis; + vslider_widgetbehavior.w_clickfn = vslider_newclick; + class_setwidget(vslider_class, &vslider_widgetbehavior); + class_sethelpsymbol(vslider_class, gensym("vslider")); + class_setsavefn(vslider_class, vslider_save); + class_setpropertiesfn(vslider_class, vslider_properties); +} +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + + +/* ------------ vsl gui-vertical slider ----------------------- */ + +t_widgetbehavior vslider_widgetbehavior; +static t_class *vslider_class; + +/* widget helper functions */ + +static void vslider_draw_update(t_vslider *x, t_glist *glist) +{ + if (glist_isvisible(glist)) + { + int r = text_ypix(&x->x_gui.x_obj, glist) + x->x_gui.x_h - (x->x_val + 50)/100; + int xpos=text_xpix(&x->x_gui.x_obj, glist); + + sys_vgui(".x%x.c coords %xKNOB %d %d %d %d\n", + glist_getcanvas(glist), x, xpos+1, r, + xpos + x->x_gui.x_w, r); + } +} + +static void vslider_draw_new(t_vslider *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + int r = ypos + x->x_gui.x_h - (x->x_val + 50)/100; + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n", + canvas, xpos, ypos-2, + xpos + x->x_gui.x_w, ypos + x->x_gui.x_h+3, + x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create line %d %d %d %d -width 3 -fill #%6.6x -tags %xKNOB\n", + canvas, xpos+1, r, + xpos + x->x_gui.x_w, r, x->x_gui.x_fcol, x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos, ypos + x->x_gui.x_h+2, + xpos+7, ypos + x->x_gui.x_h+3, + x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos, ypos-2, + xpos+7, ypos-1, + x, 0); +} + +static void vslider_draw_move(t_vslider *x, t_glist *glist) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + int r = ypos + x->x_gui.x_h - (x->x_val + 50)/100; + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n", + canvas, x, + xpos, ypos-2, + xpos + x->x_gui.x_w, ypos + x->x_gui.x_h+3); + sys_vgui(".x%x.c coords %xKNOB %d %d %d %d\n", + canvas, x, xpos+1, r, + xpos + x->x_gui.x_w, r); + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + canvas, x, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, + xpos, ypos + x->x_gui.x_h+2, + xpos+7, ypos + x->x_gui.x_h+3); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, + xpos, ypos-2, + xpos+7, ypos-1); +} + +static void vslider_draw_erase(t_vslider* x,t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c delete %xBASE\n", canvas, x); + sys_vgui(".x%x.c delete %xKNOB\n", canvas, x); + sys_vgui(".x%x.c delete %xLABEL\n", canvas, x); + if(!x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if(!x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +static void vslider_draw_config(t_vslider* x,t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + sys_vgui(".x%x.c itemconfigure %xKNOB -fill #%6.6x\n", canvas, + x, x->x_gui.x_fcol); + sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas, + x, x->x_gui.x_bcol); +} + +static void vslider_draw_io(t_vslider* x,t_glist* glist, int old_snd_rcv_flags) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos, ypos + x->x_gui.x_h+2, + xpos+7, ypos + x->x_gui.x_h+3, + x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos, ypos-2, + xpos+7, ypos-1, + x, 0); + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); +} + +static void vslider_draw_select(t_vslider *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + if(x->x_gui.x_fsf.x_selected) + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL); + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol); + } +} + +void vslider_draw(t_vslider *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + vslider_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + vslider_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + vslider_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + vslider_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + vslider_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + vslider_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + vslider_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ vsl widgetbehaviour----------------------------- */ + + +static void vslider_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_vslider* x = (t_vslider*)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist); + *yp1 = text_ypix(&x->x_gui.x_obj, glist) - 2; + *xp2 = *xp1 + x->x_gui.x_w; + *yp2 = *yp1 + x->x_gui.x_h + 5; +} + +static void vslider_save(t_gobj *z, t_binbuf *b) +{ + t_vslider *x = (t_vslider *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiiffiisssiiiiiiiii", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix, + gensym("vsl"), x->x_gui.x_w, x->x_gui.x_h, + (float)x->x_min, (float)x->x_max, + x->x_lin0_log1, iem_symargstoint(&x->x_gui.x_isa), + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2], + x->x_val, x->x_steady); + binbuf_addv(b, ";"); +} + +void vslider_check_height(t_vslider *x, int h) +{ + if(h < IEM_SL_MINSIZE) + h = IEM_SL_MINSIZE; + x->x_gui.x_h = h; + if(x->x_val > (x->x_gui.x_h*100 - 100)) + { + x->x_pos = x->x_gui.x_h*100 - 100; + x->x_val = x->x_pos; + } + if(x->x_lin0_log1) + x->x_k = log(x->x_max/x->x_min)/(double)(x->x_gui.x_h - 1); + else + x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_h - 1); +} + +void vslider_check_minmax(t_vslider *x, double min, double max) +{ + if(x->x_lin0_log1) + { + if((min == 0.0)&&(max == 0.0)) + max = 1.0; + if(max > 0.0) + { + if(min <= 0.0) + min = 0.01*max; + } + else + { + if(min > 0.0) + max = 0.01*min; + } + } + x->x_min = min; + x->x_max = max; + if(x->x_min > x->x_max) /* bugfix */ + x->x_gui.x_isa.x_reverse = 1; + else + x->x_gui.x_isa.x_reverse = 0; + if(x->x_lin0_log1) + x->x_k = log(x->x_max/x->x_min)/(double)(x->x_gui.x_h - 1); + else + x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_h - 1); +} + +static void vslider_properties(t_gobj *z, t_glist *owner) +{ + t_vslider *x = (t_vslider *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + + sprintf(buf, "pdtk_iemgui_dialog %%s VSLIDER \ + --------dimensions(pix)(pix):-------- %d %d width: %d %d height: \ + -----------output-range:----------- %g bottom: %g top: %d \ + %d lin log %d %d empty %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, IEM_GUI_MINSIZE, x->x_gui.x_h, IEM_SL_MINSIZE, + x->x_min, x->x_max, 0,/*no_schedule*/ + x->x_lin0_log1, x->x_gui.x_isa.x_loadinit, x->x_steady, -1,/*no multi, but iem-characteristic*/ + srl[0]->s_name, srl[1]->s_name, + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, 0xffffff & x->x_gui.x_fcol, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void vslider_bang(t_vslider *x) +{ + double out; + + if(x->x_lin0_log1) + out = x->x_min*exp(x->x_k*(double)(x->x_val)*0.01); + else + out = (double)(x->x_val)*0.01*x->x_k + x->x_min; + if((out < 1.0e-10)&&(out > -1.0e-10)) + out = 0.0; + + outlet_float(x->x_gui.x_obj.ob_outlet, out); + if(x->x_gui.x_fsf.x_snd_able && x->x_gui.x_snd->s_thing) + pd_float(x->x_gui.x_snd->s_thing, out); +} + +static void vslider_dialog(t_vslider *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int w = (int)atom_getintarg(0, argc, argv); + int h = (int)atom_getintarg(1, argc, argv); + double min = (double)atom_getfloatarg(2, argc, argv); + double max = (double)atom_getfloatarg(3, argc, argv); + int lilo = (int)atom_getintarg(4, argc, argv); + int steady = (int)atom_getintarg(17, argc, argv); + int sr_flags; + + if(lilo != 0) lilo = 1; + x->x_lin0_log1 = lilo; + if(steady) + x->x_steady = 1; + else + x->x_steady = 0; + sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + x->x_gui.x_w = iemgui_clip_size(w); + vslider_check_height(x, h); + vslider_check_minmax(x, min, max); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); +} + +static void vslider_motion(t_vslider *x, t_floatarg dx, t_floatarg dy) +{ + int old = x->x_val; + + if(x->x_gui.x_fsf.x_finemoved) + x->x_pos -= (int)dy; + else + x->x_pos -= 100*(int)dy; + x->x_val = x->x_pos; + if(x->x_val > (100*x->x_gui.x_h - 100)) + { + x->x_val = 100*x->x_gui.x_h - 100; + x->x_pos += 50; + x->x_pos -= x->x_pos%100; + } + if(x->x_val < 0) + { + x->x_val = 0; + x->x_pos -= 50; + x->x_pos -= x->x_pos%100; + } + if(old != x->x_val) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + vslider_bang(x); + } +} + +static void vslider_click(t_vslider *x, t_floatarg xpos, t_floatarg ypos, + t_floatarg shift, t_floatarg ctrl, t_floatarg alt) +{ + if(!x->x_steady) + x->x_val = (int)(100.0 * (x->x_gui.x_h + text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist) - ypos)); + if(x->x_val > (100*x->x_gui.x_h - 100)) + x->x_val = 100*x->x_gui.x_h - 100; + if(x->x_val < 0) + x->x_val = 0; + x->x_pos = x->x_val; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + vslider_bang(x); + glist_grab(x->x_gui.x_glist, &x->x_gui.x_obj.te_g, + (t_glistmotionfn)vslider_motion, 0, xpos, ypos); +} + +static int vslider_newclick(t_gobj *z, struct _glist *glist, + int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + t_vslider* x = (t_vslider *)z; + + if(doit) + { + vslider_click( x, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, + 0, (t_floatarg)alt); + if(shift) + x->x_gui.x_fsf.x_finemoved = 1; + else + x->x_gui.x_fsf.x_finemoved = 0; + } + return (1); +} + +static void vslider_set(t_vslider *x, t_floatarg f) +{ + double g; + + if(x->x_gui.x_isa.x_reverse) /* bugfix */ + { + if(f > x->x_min) + f = x->x_min; + if(f < x->x_max) + f = x->x_max; + } + else + { + if(f > x->x_max) + f = x->x_max; + if(f < x->x_min) + f = x->x_min; + } + if(x->x_lin0_log1) + g = log(f/x->x_min)/x->x_k; + else + g = (f - x->x_min) / x->x_k; + x->x_val = (int)(100.0*g + 0.49999); + x->x_pos = x->x_val; + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); +} + +static void vslider_float(t_vslider *x, t_floatarg f) +{ + vslider_set(x, f); + if(x->x_gui.x_fsf.x_put_in2out) + vslider_bang(x); +} + +static void vslider_size(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{ + x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av)); + if(ac > 1) + vslider_check_height(x, (int)atom_getintarg(1, ac, av)); + iemgui_size((void *)x, &x->x_gui); +} + +static void vslider_delta(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void vslider_pos(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void vslider_range(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{ + vslider_check_minmax(x, (double)atom_getfloatarg(0, ac, av), + (double)atom_getfloatarg(1, ac, av)); +} + +static void vslider_color(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void vslider_send(t_vslider *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void vslider_receive(t_vslider *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void vslider_label(t_vslider *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void vslider_label_pos(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void vslider_label_font(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void vslider_log(t_vslider *x) +{ + x->x_lin0_log1 = 1; + vslider_check_minmax(x, x->x_min, x->x_max); +} + +static void vslider_lin(t_vslider *x) +{ + x->x_lin0_log1 = 0; + x->x_k = (x->x_max - x->x_min)/(double)(x->x_gui.x_h - 1); +} + +static void vslider_init(t_vslider *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void vslider_steady(t_vslider *x, t_floatarg f) +{ + x->x_steady = (f==0.0)?0:1; +} + +static void vslider_loadbang(t_vslider *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + vslider_bang(x); + } +} + +static void *vslider_new(t_symbol *s, int argc, t_atom *argv) +{ + t_vslider *x = (t_vslider *)pd_new(vslider_class); + int bflcol[]={-262144, -1, -1}; + int w=IEM_GUI_DEFAULTSIZE, h=IEM_SL_DEFAULTSIZE; + int lilo=0, f=0, ldx=0, ldy=-8; + int fs=8, v=0, steady=1; + double min=0.0, max=(double)(IEM_SL_DEFAULTSIZE-1); + char str[144]; + + iem_inttosymargs(&x->x_gui.x_isa, 0); + iem_inttofstyle(&x->x_gui.x_fsf, 0); + + if(((argc == 17)||(argc == 18))&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1) + &&IS_A_FLOAT(argv,2)&&IS_A_FLOAT(argv,3) + &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5) + &&(IS_A_SYMBOL(argv,6)||IS_A_FLOAT(argv,6)) + &&(IS_A_SYMBOL(argv,7)||IS_A_FLOAT(argv,7)) + &&(IS_A_SYMBOL(argv,8)||IS_A_FLOAT(argv,8)) + &&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10) + &&IS_A_FLOAT(argv,11)&&IS_A_FLOAT(argv,12)&&IS_A_FLOAT(argv,13) + &&IS_A_FLOAT(argv,14)&&IS_A_FLOAT(argv,15)&&IS_A_FLOAT(argv,16)) + { + w = (int)atom_getintarg(0, argc, argv); + h = (int)atom_getintarg(1, argc, argv); + min = (double)atom_getfloatarg(2, argc, argv); + max = (double)atom_getfloatarg(3, argc, argv); + lilo = (int)atom_getintarg(4, argc, argv); + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(5, argc, argv)); + iemgui_new_getnames(&x->x_gui, 6, argv); + ldx = (int)atom_getintarg(9, argc, argv); + ldy = (int)atom_getintarg(10, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(11, argc, argv)); + fs = (int)atom_getintarg(12, argc, argv); + bflcol[0] = (int)atom_getintarg(13, argc, argv); + bflcol[1] = (int)atom_getintarg(14, argc, argv); + bflcol[2] = (int)atom_getintarg(15, argc, argv); + v = (int)atom_getintarg(16, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 6, 0); + if((argc == 18)&&IS_A_FLOAT(argv,17)) + steady = (int)atom_getintarg(17, argc, argv); + x->x_gui.x_draw = (t_iemfunptr)vslider_draw; + x->x_gui.x_fsf.x_snd_able = 1; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if(x->x_gui.x_isa.x_loadinit) + x->x_val = v; + else + x->x_val = 0; + x->x_pos = x->x_val; + if(lilo != 0) lilo = 1; + x->x_lin0_log1 = lilo; + if(steady != 0) steady = 1; + x->x_steady = steady; + if(!strcmp(x->x_gui.x_snd->s_name, "empty")) x->x_gui.x_fsf.x_snd_able = 0; + if(!strcmp(x->x_gui.x_rcv->s_name, "empty")) x->x_gui.x_fsf.x_rcv_able = 0; + if(x->x_gui.x_fsf.x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + x->x_gui.x_w = iemgui_clip_size(w); + vslider_check_height(x, h); + vslider_check_minmax(x, min, max); + iemgui_all_colfromload(&x->x_gui, bflcol); + iemgui_verify_snd_ne_rcv(&x->x_gui); + outlet_new(&x->x_gui.x_obj, &s_float); + return (x); +} + +static void vslider_free(t_vslider *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + gfxstub_deleteforkey(x); +} + +void g_vslider_setup(void) +{ + vslider_class = class_new(gensym("vsl"), (t_newmethod)vslider_new, + (t_method)vslider_free, sizeof(t_vslider), 0, A_GIMME, 0); + class_addcreator((t_newmethod)vslider_new, gensym("vslider"), A_GIMME, 0); + class_addbang(vslider_class,vslider_bang); + class_addfloat(vslider_class,vslider_float); + class_addmethod(vslider_class, (t_method)vslider_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(vslider_class, (t_method)vslider_motion, gensym("motion"), + A_FLOAT, A_FLOAT, 0); + class_addmethod(vslider_class, (t_method)vslider_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_loadbang, gensym("loadbang"), 0); + class_addmethod(vslider_class, (t_method)vslider_set, gensym("set"), A_FLOAT, 0); + class_addmethod(vslider_class, (t_method)vslider_size, gensym("size"), A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_range, gensym("range"), A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_color, gensym("color"), A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_send, gensym("send"), A_DEFSYM, 0); + class_addmethod(vslider_class, (t_method)vslider_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(vslider_class, (t_method)vslider_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(vslider_class, (t_method)vslider_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_label_font, gensym("label_font"), A_GIMME, 0); + class_addmethod(vslider_class, (t_method)vslider_log, gensym("log"), 0); + class_addmethod(vslider_class, (t_method)vslider_lin, gensym("lin"), 0); + class_addmethod(vslider_class, (t_method)vslider_init, gensym("init"), A_FLOAT, 0); + class_addmethod(vslider_class, (t_method)vslider_steady, gensym("steady"), A_FLOAT, 0); + vslider_widgetbehavior.w_getrectfn = vslider_getrect; + vslider_widgetbehavior.w_displacefn = iemgui_displace; + vslider_widgetbehavior.w_selectfn = iemgui_select; + vslider_widgetbehavior.w_activatefn = NULL; + vslider_widgetbehavior.w_deletefn = iemgui_delete; + vslider_widgetbehavior.w_visfn = iemgui_vis; + vslider_widgetbehavior.w_clickfn = vslider_newclick; + class_setwidget(vslider_class, &vslider_widgetbehavior); + class_sethelpsymbol(vslider_class, gensym("vslider")); + class_setsavefn(vslider_class, vslider_save); + class_setpropertiesfn(vslider_class, vslider_properties); +} diff --git a/apps/plugins/pdbox/PDa/src/g_vumeter.c b/apps/plugins/pdbox/PDa/src/g_vumeter.c new file mode 100644 index 0000000..c7b3f1f --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/g_vumeter.c @@ -0,0 +1,1426 @@ +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + +/* ----- vu gui-peak- & rms- vu-meter-display ---------- */ + +t_widgetbehavior vu_widgetbehavior; +static t_class *vu_class; + +/* widget helper functions */ + +static void vu_update_rms(t_vu *x, t_glist *glist) +{ + if(glist_isvisible(glist)) + { + int w4=x->x_gui.x_w/4, off=text_ypix(&x->x_gui.x_obj, glist)-1; + int xpos=text_xpix(&x->x_gui.x_obj, glist), quad1=xpos+w4+1, quad3=xpos+x->x_gui.x_w-w4-1; + + sys_vgui(".x%x.c coords %xRCOVER %d %d %d %d\n", + glist_getcanvas(glist), x, quad1, off, quad3, + off + (x->x_led_size+1)*(IEM_VU_STEPS-x->x_rms)); + } +} + +static void vu_update_peak(t_vu *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + if(glist_isvisible(glist)) + { + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + + if(x->x_peak) + { + int i=iemgui_vu_col[x->x_peak]; + int j=ypos + (x->x_led_size+1)*(IEM_VU_STEPS+1-x->x_peak) + - (x->x_led_size+1)/2; + + sys_vgui(".x%x.c coords %xPLED %d %d %d %d\n", canvas, x, + xpos, j, + xpos+x->x_gui.x_w+1, j); + sys_vgui(".x%x.c itemconfigure %xPLED -fill #%6.6x\n", canvas, x, + iemgui_color_hex[i]); + } + else + { + int mid=xpos+x->x_gui.x_w/2; + + sys_vgui(".x%x.c itemconfigure %xPLED -fill #%6.6x\n", + canvas, x, x->x_gui.x_bcol); + sys_vgui(".x%x.c coords %xPLED %d %d %d %d\n", + canvas, x, mid, ypos+20, + mid, ypos+20); + } + } +} + +static void vu_draw_new(t_vu *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + int w4=x->x_gui.x_w/4, mid=xpos+x->x_gui.x_w/2, + quad1=xpos+w4+1; + int quad3=xpos+x->x_gui.x_w-w4, + end=xpos+x->x_gui.x_w+4; + int k1=x->x_led_size+1, k2=IEM_VU_STEPS+1, k3=k1/2; + int led_col, yyy, i, k4=ypos-k3; + + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n", + canvas, xpos-1, ypos-2, + xpos+x->x_gui.x_w+1, + ypos+x->x_gui.x_h+2, x->x_gui.x_bcol, x); + for(i=1; i<=IEM_VU_STEPS; i++) + { + led_col = iemgui_vu_col[i]; + yyy = k4 + k1*(k2-i); + sys_vgui(".x%x.c create line %d %d %d %d -width %d -fill #%6.6x -tags %xRLED%d\n", + canvas, quad1, yyy, quad3, yyy, x->x_led_size, iemgui_color_hex[led_col], x, i); + if(((i+2)&3) && (x->x_scale)) + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xSCALE%d\n", + canvas, end, yyy+k3, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_lcol, x, i); + } + if(x->x_scale) + { + i=IEM_VU_STEPS+1; + yyy = k4 + k1*(k2-i); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xSCALE%d\n", + canvas, end, yyy+k3, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_lcol, x, i); + } + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -outline #%6.6x -tags %xRCOVER\n", + canvas, quad1, ypos-1, quad3-1, + ypos-1 + k1*IEM_VU_STEPS, x->x_gui.x_bcol, x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create line %d %d %d %d -width %d -fill #%6.6x -tags %xPLED\n", + canvas, mid, ypos+10, + mid, ypos+10, x->x_led_size, x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + { + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos-1, ypos + x->x_gui.x_h+1, + xpos + IOWIDTH-1, ypos + x->x_gui.x_h+2, + x, 0); + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos+x->x_gui.x_w+1-IOWIDTH, ypos + x->x_gui.x_h+1, + xpos+x->x_gui.x_w+1, ypos + x->x_gui.x_h+2, + x, 1); + } + if(!x->x_gui.x_fsf.x_rcv_able) + { + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos-1, ypos-2, + xpos + IOWIDTH-1, ypos-1, + x, 0); + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos+x->x_gui.x_w+1-IOWIDTH, ypos-2, + xpos+x->x_gui.x_w+1, ypos-1, + x, 1); + } +} + + +static void vu_draw_move(t_vu *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + int w4=x->x_gui.x_w/4, quad1=xpos+w4+1; + int quad3=xpos+x->x_gui.x_w-w4, + end=xpos+x->x_gui.x_w+4; + int k1=x->x_led_size+1, k2=IEM_VU_STEPS+1, k3=k1/2; + int yyy, i, k4=ypos-k3; + + sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n", + canvas, x, xpos-1, ypos-2, + xpos+x->x_gui.x_w+1,ypos+x->x_gui.x_h+2); + for(i=1; i<=IEM_VU_STEPS; i++) + { + yyy = k4 + k1*(k2-i); + sys_vgui(".x%x.c coords %xRLED%d %d %d %d %d\n", + canvas, x, i, quad1, yyy, quad3, yyy); + if(((i+2)&3) && (x->x_scale)) + sys_vgui(".x%x.c coords %xSCALE%d %d %d\n", + canvas, x, i, end, yyy+k3); + } + if(x->x_scale) + { + i=IEM_VU_STEPS+1; + yyy = k4 + k1*(k2-i); + sys_vgui(".x%x.c coords %xSCALE%d %d %d\n", + canvas, x, i, end, yyy+k3); + } + vu_update_peak(x, glist); + vu_update_rms(x, glist); + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + canvas, x, xpos+x->x_gui.x_ldx, + ypos+x->x_gui.x_ldy); + if(!x->x_gui.x_fsf.x_snd_able) + { + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, + xpos-1, ypos + x->x_gui.x_h+1, + xpos + IOWIDTH-1, ypos + x->x_gui.x_h+2); + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 1, + xpos+x->x_gui.x_w+1-IOWIDTH, ypos + x->x_gui.x_h+1, + xpos+x->x_gui.x_w+1, ypos + x->x_gui.x_h+2); + } + if(!x->x_gui.x_fsf.x_rcv_able) + { + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, + xpos-1, ypos-2, + xpos + IOWIDTH-1, ypos-1); + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 1, + xpos+x->x_gui.x_w+1-IOWIDTH, ypos-2, + xpos+x->x_gui.x_w+1, ypos-1); + } +} + +static void vu_draw_erase(t_vu* x,t_glist* glist) +{ + int i; + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c delete %xBASE\n", canvas, x); + for(i=1; i<=IEM_VU_STEPS; i++) + { + sys_vgui(".x%x.c delete %xRLED%d\n", canvas, x, i); + if(((i+2)&3) && (x->x_scale)) + sys_vgui(".x%x.c delete %xSCALE%d\n", canvas, x, i); + } + if(x->x_scale) + { + i=IEM_VU_STEPS+1; + sys_vgui(".x%x.c delete %xSCALE%d\n", canvas, x, i); + } + sys_vgui(".x%x.c delete %xPLED\n", canvas, x); + sys_vgui(".x%x.c delete %xRCOVER\n", canvas, x); + sys_vgui(".x%x.c delete %xLABEL\n", canvas, x); + if(!x->x_gui.x_fsf.x_snd_able) + { + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 1); + } + if(!x->x_gui.x_fsf.x_rcv_able) + { + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 1); + } +} + +static void vu_draw_config(t_vu* x, t_glist* glist) +{ + int i; + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas, x, x->x_gui.x_bcol); + for(i=1; i<=IEM_VU_STEPS; i++) + { + sys_vgui(".x%x.c itemconfigure %xRLED%d -width %d\n", canvas, x, i, + x->x_led_size); + if(((i+2)&3) && (x->x_scale)) + sys_vgui(".x%x.c itemconfigure %xSCALE%d -text {%s} -font {%s %d bold} -fill #%6.6x\n", + canvas, x, i, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol); + } + if(x->x_scale) + { + i=IEM_VU_STEPS+1; + sys_vgui(".x%x.c itemconfigure %xSCALE%d -text {%s} -font {%s %d bold} -fill #%6.6x\n", + canvas, x, i, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol); + } + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + + sys_vgui(".x%x.c itemconfigure %xRCOVER -fill #%6.6x -outline #%6.6x\n", canvas, + x, x->x_gui.x_bcol, x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xPLED -width %d\n", canvas, x, + x->x_led_size); +} + +static void vu_draw_io(t_vu* x, t_glist* glist, int old_snd_rcv_flags) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + { + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos-1, ypos + x->x_gui.x_h+1, + xpos + IOWIDTH-1, ypos + x->x_gui.x_h+2, + x, 0); + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos+x->x_gui.x_w+1-IOWIDTH, ypos + x->x_gui.x_h+1, + xpos+x->x_gui.x_w+1, ypos + x->x_gui.x_h+2, + x, 1); + } + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + { + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 1); + } + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + { + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos-1, ypos-2, + xpos + IOWIDTH-1, ypos-1, + x, 0); + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos+x->x_gui.x_w+1-IOWIDTH, ypos-2, + xpos+x->x_gui.x_w+1, ypos-1, + x, 1); + } + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + { + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 1); + } +} + +static void vu_draw_select(t_vu* x,t_glist* glist) +{ + int i; + t_canvas *canvas=glist_getcanvas(glist); + + if(x->x_gui.x_fsf.x_selected) + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + for(i=1; i<=IEM_VU_STEPS; i++) + { + if(((i+2)&3) && (x->x_scale)) + sys_vgui(".x%x.c itemconfigure %xSCALE%d -fill #%6.6x\n", + canvas, x, i, IEM_GUI_COLOR_SELECTED); + } + if(x->x_scale) + { + i=IEM_VU_STEPS+1; + sys_vgui(".x%x.c itemconfigure %xSCALE%d -fill #%6.6x\n", + canvas, x, i, IEM_GUI_COLOR_SELECTED); + } + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL); + for(i=1; i<=IEM_VU_STEPS; i++) + { + if(((i+2)&3) && (x->x_scale)) + sys_vgui(".x%x.c itemconfigure %xSCALE%d -fill #%6.6x\n", + canvas, x, i, x->x_gui.x_lcol); + } + if(x->x_scale) + { + i=IEM_VU_STEPS+1; + sys_vgui(".x%x.c itemconfigure %xSCALE%d -fill #%6.6x\n", + canvas, x, i, x->x_gui.x_lcol); + } + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol); + } +} + +void vu_draw(t_vu *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_MOVE) + vu_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + vu_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + vu_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + vu_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + vu_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + vu_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ vu widgetbehaviour----------------------------- */ + + +static void vu_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_vu* x = (t_vu*)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist) - 1; + *yp1 = text_ypix(&x->x_gui.x_obj, glist) - 2; + *xp2 = *xp1 + x->x_gui.x_w + 2; + *yp2 = *yp1 + x->x_gui.x_h + 4; +} + +static void vu_save(t_gobj *z, t_binbuf *b) +{ + t_vu *x = (t_vu *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiissiiiiiiii", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix, + gensym("vu"), x->x_gui.x_w, x->x_gui.x_h, + srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[2], x->x_scale, + iem_symargstoint(&x->x_gui.x_isa)); + binbuf_addv(b, ";"); +} + +void vu_check_height(t_vu *x, int h) +{ + int n; + + n = h / IEM_VU_STEPS; + if(n < IEM_VU_MINSIZE) + n = IEM_VU_MINSIZE; + x->x_led_size = n-1; + x->x_gui.x_h = IEM_VU_STEPS * n; +} + +static void vu_scale(t_vu *x, t_floatarg fscale) +{ + int i, scale = (int)fscale; + + if(scale != 0) scale = 1; + if(x->x_scale && !scale) + { + t_canvas *canvas=glist_getcanvas(x->x_gui.x_glist); + + x->x_scale = (int)scale; + if(glist_isvisible(x->x_gui.x_glist)) + { + for(i=1; i<=IEM_VU_STEPS; i++) + { + if((i+2)&3) + sys_vgui(".x%x.c delete %xSCALE%d\n", canvas, x, i); + } + i=IEM_VU_STEPS+1; + sys_vgui(".x%x.c delete %xSCALE%d\n", canvas, x, i); + } + } + if(!x->x_scale && scale) + { + int w4=x->x_gui.x_w/4, end=text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist)+x->x_gui.x_w+4; + int k1=x->x_led_size+1, k2=IEM_VU_STEPS+1, k3=k1/2; + int yyy, k4=text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist)-k3; + t_canvas *canvas=glist_getcanvas(x->x_gui.x_glist); + + x->x_scale = (int)scale; + if(glist_isvisible(x->x_gui.x_glist)) + { + for(i=1; i<=IEM_VU_STEPS; i++) + { + yyy = k4 + k1*(k2-i); + if((i+2)&3) + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xSCALE%d\n", + canvas, end, yyy+k3, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_lcol, x, i); + } + i=IEM_VU_STEPS+1; + yyy = k4 + k1*(k2-i); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xSCALE%d\n", + canvas, end, yyy+k3, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_lcol, x, i); + } + } +} + +static void vu_properties(t_gobj *z, t_glist *owner) +{ + t_vu *x = (t_vu *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + sprintf(buf, "pdtk_iemgui_dialog %%s VU-METER \ + --------dimensions(pix)(pix):-------- %d %d width: %d %d height: \ + empty 0.0 empty 0.0 empty %d \ + %d no_scale scale %d %d empty %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, IEM_GUI_MINSIZE, x->x_gui.x_h, IEM_VU_STEPS*IEM_VU_MINSIZE, + 0,/*no_schedule*/ + x->x_scale, -1, -1, -1,/*no linlog, no init, no multi*/ + "nosndno", srl[1]->s_name,/*no send*/ + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, -1/*no front-color*/, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void vu_dialog(t_vu *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int w = (int)atom_getintarg(0, argc, argv); + int h = (int)atom_getintarg(1, argc, argv); + int scale = (int)atom_getintarg(4, argc, argv); + int sr_flags; + + srl[0] = gensym("empty"); + sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + x->x_gui.x_fsf.x_snd_able = 0; + x->x_gui.x_isa.x_loadinit = 0; + x->x_gui.x_w = iemgui_clip_size(w); + vu_check_height(x, h); + if(scale != 0) + scale = 1; + vu_scale(x, (float)scale); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); +} + +static void vu_size(t_vu *x, t_symbol *s, int ac, t_atom *av) +{ + x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av)); + if(ac > 1) + vu_check_height(x, (int)atom_getintarg(1, ac, av)); + if(glist_isvisible(x->x_gui.x_glist)) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); + } +} + +static void vu_delta(t_vu *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void vu_pos(t_vu *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void vu_color(t_vu *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void vu_receive(t_vu *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void vu_label(t_vu *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void vu_label_pos(t_vu *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void vu_label_font(t_vu *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void vu_float(t_vu *x, t_floatarg rms) +{ + int i; + + if(rms <= IEM_VU_MINDB) + x->x_rms = 0; + else if(rms >= IEM_VU_MAXDB) + x->x_rms = IEM_VU_STEPS; + else + { + int i = (int)(2.0*(rms + IEM_VU_OFFSET)); + x->x_rms = iemgui_vu_db2i[i]; + } + i = (int)(100.0*rms + 10000.5); + rms = 0.01*(float)(i - 10000); + x->x_fr = rms; + outlet_float(x->x_out_rms, rms); + vu_update_rms(x, x->x_gui.x_glist); +} + +static void vu_ft1(t_vu *x, t_floatarg peak) +{ + int i; + + if(peak <= IEM_VU_MINDB) + x->x_peak = 0; + else if(peak >= IEM_VU_MAXDB) + x->x_peak = IEM_VU_STEPS; + else + { + int i = (int)(2.0*(peak + IEM_VU_OFFSET)); + x->x_peak = iemgui_vu_db2i[i]; + } + i = (int)(100.0*peak + 10000.5); + peak = 0.01*(float)(i - 10000); + x->x_fp = peak; + outlet_float(x->x_out_peak, peak); + vu_update_peak(x, x->x_gui.x_glist); +} + +static void vu_bang(t_vu *x) +{ + outlet_float(x->x_out_peak, x->x_fp); + outlet_float(x->x_out_rms, x->x_fr); + vu_update_rms(x, x->x_gui.x_glist); + vu_update_peak(x, x->x_gui.x_glist); +} + +static void *vu_new(t_symbol *s, int argc, t_atom *argv) +{ + t_vu *x = (t_vu *)pd_new(vu_class); + int bflcol[]={-66577, -1, -1}; + int w=IEM_GUI_DEFAULTSIZE, h=IEM_VU_STEPS*IEM_VU_DEFAULTSIZE; + int ldx=-1, ldy=-8, f=0, fs=8, scale=1; + int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, fthold=IEM_BNG_DEFAULTHOLDFLASHTIME; + char str[144]; + + iem_inttosymargs(&x->x_gui.x_isa, 0); + iem_inttofstyle(&x->x_gui.x_fsf, 0); + + if((argc >= 11)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1) + &&(IS_A_SYMBOL(argv,2)||IS_A_FLOAT(argv,2)) + &&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3)) + &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5) + &&IS_A_FLOAT(argv,6)&&IS_A_FLOAT(argv,7) + &&IS_A_FLOAT(argv,8)&&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)) + { + w = (int)atom_getintarg(0, argc, argv); + h = (int)atom_getintarg(1, argc, argv); + iemgui_new_getnames(&x->x_gui, 1, argv); + ldx = (int)atom_getintarg(4, argc, argv); + ldy = (int)atom_getintarg(5, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(6, argc, argv)); + fs = (int)atom_getintarg(7, argc, argv); + bflcol[0] = (int)atom_getintarg(8, argc, argv); + bflcol[2] = (int)atom_getintarg(9, argc, argv); + scale = (int)atom_getintarg(10, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 1, 0); + if((argc == 12)&&IS_A_FLOAT(argv,11)) + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(11, argc, argv)); + x->x_gui.x_draw = (t_iemfunptr)vu_draw; + + x->x_gui.x_fsf.x_snd_able = 0; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if (x->x_gui.x_fsf.x_font_style == 1) + strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) + strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + if(x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + x->x_gui.x_w = iemgui_clip_size(w); + vu_check_height(x, h); + iemgui_all_colfromload(&x->x_gui, bflcol); + if(scale != 0) + scale = 1; + x->x_scale = scale; + x->x_peak = 0; + x->x_rms = 0; + x->x_fp = -101.0; + x->x_fr = -101.0; + iemgui_verify_snd_ne_rcv(&x->x_gui); + inlet_new(&x->x_gui.x_obj, &x->x_gui.x_obj.ob_pd, &s_float, gensym("ft1")); + x->x_out_rms = outlet_new(&x->x_gui.x_obj, &s_float); + x->x_out_peak = outlet_new(&x->x_gui.x_obj, &s_float); + return (x); +} + +static void vu_free(t_vu *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + gfxstub_deleteforkey(x); +} + +void g_vumeter_setup(void) +{ + vu_class = class_new(gensym("vu"), (t_newmethod)vu_new, (t_method)vu_free, + sizeof(t_vu), 0, A_GIMME, 0); + class_addbang(vu_class,vu_bang); + class_addfloat(vu_class,vu_float); + class_addmethod(vu_class, (t_method)vu_ft1, gensym("ft1"), A_FLOAT, 0); + class_addmethod(vu_class, (t_method)vu_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(vu_class, (t_method)vu_size, gensym("size"), A_GIMME, 0); + class_addmethod(vu_class, (t_method)vu_scale, gensym("scale"), A_DEFFLOAT, 0); + class_addmethod(vu_class, (t_method)vu_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(vu_class, (t_method)vu_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(vu_class, (t_method)vu_color, gensym("color"), A_GIMME, 0); + class_addmethod(vu_class, (t_method)vu_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(vu_class, (t_method)vu_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(vu_class, (t_method)vu_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(vu_class, (t_method)vu_label_font, gensym("label_font"), A_GIMME, 0); + vu_widgetbehavior.w_getrectfn = vu_getrect; + vu_widgetbehavior.w_displacefn = iemgui_displace; + vu_widgetbehavior.w_selectfn = iemgui_select; + vu_widgetbehavior.w_activatefn = NULL; + vu_widgetbehavior.w_deletefn = iemgui_delete; + vu_widgetbehavior.w_visfn = iemgui_vis; + vu_widgetbehavior.w_clickfn = NULL; + class_setwidget(vu_class,&vu_widgetbehavior); + class_sethelpsymbol(vu_class, gensym("vu")); + class_setsavefn(vu_class, vu_save); + class_setpropertiesfn(vu_class, vu_properties); +} +/* Copyright (c) 1997-1999 Miller Puckette. + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* g_7_guis.c written by Thomas Musil (c) IEM KUG Graz Austria 2000-2001 */ +/* thanks to Miller Puckette, Guenther Geiger and Krzystof Czaja */ + + +#include +#include +#include +#include +#include "m_pd.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef MSW +#include +#else +#include +#endif + +/* ----- vu gui-peak- & rms- vu-meter-display ---------- */ + +t_widgetbehavior vu_widgetbehavior; +static t_class *vu_class; + +/* widget helper functions */ + +static void vu_update_rms(t_vu *x, t_glist *glist) +{ + if(glist_isvisible(glist)) + { + int w4=x->x_gui.x_w/4, off=text_ypix(&x->x_gui.x_obj, glist)-1; + int xpos=text_xpix(&x->x_gui.x_obj, glist), quad1=xpos+w4+1, quad3=xpos+x->x_gui.x_w-w4-1; + + sys_vgui(".x%x.c coords %xRCOVER %d %d %d %d\n", + glist_getcanvas(glist), x, quad1, off, quad3, + off + (x->x_led_size+1)*(IEM_VU_STEPS-x->x_rms)); + } +} + +static void vu_update_peak(t_vu *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + if(glist_isvisible(glist)) + { + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + + if(x->x_peak) + { + int i=iemgui_vu_col[x->x_peak]; + int j=ypos + (x->x_led_size+1)*(IEM_VU_STEPS+1-x->x_peak) + - (x->x_led_size+1)/2; + + sys_vgui(".x%x.c coords %xPLED %d %d %d %d\n", canvas, x, + xpos, j, + xpos+x->x_gui.x_w+1, j); + sys_vgui(".x%x.c itemconfigure %xPLED -fill #%6.6x\n", canvas, x, + iemgui_color_hex[i]); + } + else + { + int mid=xpos+x->x_gui.x_w/2; + + sys_vgui(".x%x.c itemconfigure %xPLED -fill #%6.6x\n", + canvas, x, x->x_gui.x_bcol); + sys_vgui(".x%x.c coords %xPLED %d %d %d %d\n", + canvas, x, mid, ypos+20, + mid, ypos+20); + } + } +} + +static void vu_draw_new(t_vu *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + int w4=x->x_gui.x_w/4, mid=xpos+x->x_gui.x_w/2, + quad1=xpos+w4+1; + int quad3=xpos+x->x_gui.x_w-w4, + end=xpos+x->x_gui.x_w+4; + int k1=x->x_led_size+1, k2=IEM_VU_STEPS+1, k3=k1/2; + int led_col, yyy, i, k4=ypos-k3; + + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -tags %xBASE\n", + canvas, xpos-1, ypos-2, + xpos+x->x_gui.x_w+1, + ypos+x->x_gui.x_h+2, x->x_gui.x_bcol, x); + for(i=1; i<=IEM_VU_STEPS; i++) + { + led_col = iemgui_vu_col[i]; + yyy = k4 + k1*(k2-i); + sys_vgui(".x%x.c create line %d %d %d %d -width %d -fill #%6.6x -tags %xRLED%d\n", + canvas, quad1, yyy, quad3, yyy, x->x_led_size, iemgui_color_hex[led_col], x, i); + if(((i+2)&3) && (x->x_scale)) + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xSCALE%d\n", + canvas, end, yyy+k3, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_lcol, x, i); + } + if(x->x_scale) + { + i=IEM_VU_STEPS+1; + yyy = k4 + k1*(k2-i); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xSCALE%d\n", + canvas, end, yyy+k3, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_lcol, x, i); + } + sys_vgui(".x%x.c create rectangle %d %d %d %d -fill #%6.6x -outline #%6.6x -tags %xRCOVER\n", + canvas, quad1, ypos-1, quad3-1, + ypos-1 + k1*IEM_VU_STEPS, x->x_gui.x_bcol, x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create line %d %d %d %d -width %d -fill #%6.6x -tags %xPLED\n", + canvas, mid, ypos+10, + mid, ypos+10, x->x_led_size, x->x_gui.x_bcol, x); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xLABEL\n", + canvas, xpos+x->x_gui.x_ldx, ypos+x->x_gui.x_ldy, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:"", + x->x_gui.x_font, x->x_gui.x_fontsize, x->x_gui.x_lcol, x); + if(!x->x_gui.x_fsf.x_snd_able) + { + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos-1, ypos + x->x_gui.x_h+1, + xpos + IOWIDTH-1, ypos + x->x_gui.x_h+2, + x, 0); + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos+x->x_gui.x_w+1-IOWIDTH, ypos + x->x_gui.x_h+1, + xpos+x->x_gui.x_w+1, ypos + x->x_gui.x_h+2, + x, 1); + } + if(!x->x_gui.x_fsf.x_rcv_able) + { + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos-1, ypos-2, + xpos + IOWIDTH-1, ypos-1, + x, 0); + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos+x->x_gui.x_w+1-IOWIDTH, ypos-2, + xpos+x->x_gui.x_w+1, ypos-1, + x, 1); + } +} + + +static void vu_draw_move(t_vu *x, t_glist *glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + int w4=x->x_gui.x_w/4, quad1=xpos+w4+1; + int quad3=xpos+x->x_gui.x_w-w4, + end=xpos+x->x_gui.x_w+4; + int k1=x->x_led_size+1, k2=IEM_VU_STEPS+1, k3=k1/2; + int yyy, i, k4=ypos-k3; + + sys_vgui(".x%x.c coords %xBASE %d %d %d %d\n", + canvas, x, xpos-1, ypos-2, + xpos+x->x_gui.x_w+1,ypos+x->x_gui.x_h+2); + for(i=1; i<=IEM_VU_STEPS; i++) + { + yyy = k4 + k1*(k2-i); + sys_vgui(".x%x.c coords %xRLED%d %d %d %d %d\n", + canvas, x, i, quad1, yyy, quad3, yyy); + if(((i+2)&3) && (x->x_scale)) + sys_vgui(".x%x.c coords %xSCALE%d %d %d\n", + canvas, x, i, end, yyy+k3); + } + if(x->x_scale) + { + i=IEM_VU_STEPS+1; + yyy = k4 + k1*(k2-i); + sys_vgui(".x%x.c coords %xSCALE%d %d %d\n", + canvas, x, i, end, yyy+k3); + } + vu_update_peak(x, glist); + vu_update_rms(x, glist); + sys_vgui(".x%x.c coords %xLABEL %d %d\n", + canvas, x, xpos+x->x_gui.x_ldx, + ypos+x->x_gui.x_ldy); + if(!x->x_gui.x_fsf.x_snd_able) + { + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 0, + xpos-1, ypos + x->x_gui.x_h+1, + xpos + IOWIDTH-1, ypos + x->x_gui.x_h+2); + sys_vgui(".x%x.c coords %xOUT%d %d %d %d %d\n", + canvas, x, 1, + xpos+x->x_gui.x_w+1-IOWIDTH, ypos + x->x_gui.x_h+1, + xpos+x->x_gui.x_w+1, ypos + x->x_gui.x_h+2); + } + if(!x->x_gui.x_fsf.x_rcv_able) + { + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 0, + xpos-1, ypos-2, + xpos + IOWIDTH-1, ypos-1); + sys_vgui(".x%x.c coords %xIN%d %d %d %d %d\n", + canvas, x, 1, + xpos+x->x_gui.x_w+1-IOWIDTH, ypos-2, + xpos+x->x_gui.x_w+1, ypos-1); + } +} + +static void vu_draw_erase(t_vu* x,t_glist* glist) +{ + int i; + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c delete %xBASE\n", canvas, x); + for(i=1; i<=IEM_VU_STEPS; i++) + { + sys_vgui(".x%x.c delete %xRLED%d\n", canvas, x, i); + if(((i+2)&3) && (x->x_scale)) + sys_vgui(".x%x.c delete %xSCALE%d\n", canvas, x, i); + } + if(x->x_scale) + { + i=IEM_VU_STEPS+1; + sys_vgui(".x%x.c delete %xSCALE%d\n", canvas, x, i); + } + sys_vgui(".x%x.c delete %xPLED\n", canvas, x); + sys_vgui(".x%x.c delete %xRCOVER\n", canvas, x); + sys_vgui(".x%x.c delete %xLABEL\n", canvas, x); + if(!x->x_gui.x_fsf.x_snd_able) + { + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 1); + } + if(!x->x_gui.x_fsf.x_rcv_able) + { + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 1); + } +} + +static void vu_draw_config(t_vu* x, t_glist* glist) +{ + int i; + t_canvas *canvas=glist_getcanvas(glist); + + sys_vgui(".x%x.c itemconfigure %xBASE -fill #%6.6x\n", canvas, x, x->x_gui.x_bcol); + for(i=1; i<=IEM_VU_STEPS; i++) + { + sys_vgui(".x%x.c itemconfigure %xRLED%d -width %d\n", canvas, x, i, + x->x_led_size); + if(((i+2)&3) && (x->x_scale)) + sys_vgui(".x%x.c itemconfigure %xSCALE%d -text {%s} -font {%s %d bold} -fill #%6.6x\n", + canvas, x, i, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol); + } + if(x->x_scale) + { + i=IEM_VU_STEPS+1; + sys_vgui(".x%x.c itemconfigure %xSCALE%d -text {%s} -font {%s %d bold} -fill #%6.6x\n", + canvas, x, i, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol); + } + sys_vgui(".x%x.c itemconfigure %xLABEL -font {%s %d bold} -fill #%6.6x -text {%s} \n", + canvas, x, x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_fsf.x_selected?IEM_GUI_COLOR_SELECTED:x->x_gui.x_lcol, + strcmp(x->x_gui.x_lab->s_name, "empty")?x->x_gui.x_lab->s_name:""); + + sys_vgui(".x%x.c itemconfigure %xRCOVER -fill #%6.6x -outline #%6.6x\n", canvas, + x, x->x_gui.x_bcol, x->x_gui.x_bcol); + sys_vgui(".x%x.c itemconfigure %xPLED -width %d\n", canvas, x, + x->x_led_size); +} + +static void vu_draw_io(t_vu* x, t_glist* glist, int old_snd_rcv_flags) +{ + int xpos=text_xpix(&x->x_gui.x_obj, glist); + int ypos=text_ypix(&x->x_gui.x_obj, glist); + t_canvas *canvas=glist_getcanvas(glist); + + if((old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && !x->x_gui.x_fsf.x_snd_able) + { + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos-1, ypos + x->x_gui.x_h+1, + xpos + IOWIDTH-1, ypos + x->x_gui.x_h+2, + x, 0); + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xOUT%d\n", + canvas, + xpos+x->x_gui.x_w+1-IOWIDTH, ypos + x->x_gui.x_h+1, + xpos+x->x_gui.x_w+1, ypos + x->x_gui.x_h+2, + x, 1); + } + if(!(old_snd_rcv_flags & IEM_GUI_OLD_SND_FLAG) && x->x_gui.x_fsf.x_snd_able) + { + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 0); + sys_vgui(".x%x.c delete %xOUT%d\n", canvas, x, 1); + } + if((old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && !x->x_gui.x_fsf.x_rcv_able) + { + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos-1, ypos-2, + xpos + IOWIDTH-1, ypos-1, + x, 0); + sys_vgui(".x%x.c create rectangle %d %d %d %d -tags %xIN%d\n", + canvas, + xpos+x->x_gui.x_w+1-IOWIDTH, ypos-2, + xpos+x->x_gui.x_w+1, ypos-1, + x, 1); + } + if(!(old_snd_rcv_flags & IEM_GUI_OLD_RCV_FLAG) && x->x_gui.x_fsf.x_rcv_able) + { + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 0); + sys_vgui(".x%x.c delete %xIN%d\n", canvas, x, 1); + } +} + +static void vu_draw_select(t_vu* x,t_glist* glist) +{ + int i; + t_canvas *canvas=glist_getcanvas(glist); + + if(x->x_gui.x_fsf.x_selected) + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + for(i=1; i<=IEM_VU_STEPS; i++) + { + if(((i+2)&3) && (x->x_scale)) + sys_vgui(".x%x.c itemconfigure %xSCALE%d -fill #%6.6x\n", + canvas, x, i, IEM_GUI_COLOR_SELECTED); + } + if(x->x_scale) + { + i=IEM_VU_STEPS+1; + sys_vgui(".x%x.c itemconfigure %xSCALE%d -fill #%6.6x\n", + canvas, x, i, IEM_GUI_COLOR_SELECTED); + } + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_NORMAL); + for(i=1; i<=IEM_VU_STEPS; i++) + { + if(((i+2)&3) && (x->x_scale)) + sys_vgui(".x%x.c itemconfigure %xSCALE%d -fill #%6.6x\n", + canvas, x, i, x->x_gui.x_lcol); + } + if(x->x_scale) + { + i=IEM_VU_STEPS+1; + sys_vgui(".x%x.c itemconfigure %xSCALE%d -fill #%6.6x\n", + canvas, x, i, x->x_gui.x_lcol); + } + sys_vgui(".x%x.c itemconfigure %xLABEL -fill #%6.6x\n", canvas, x, x->x_gui.x_lcol); + } +} + +void vu_draw(t_vu *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_MOVE) + vu_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + vu_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + vu_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + vu_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + vu_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + vu_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ vu widgetbehaviour----------------------------- */ + + +static void vu_getrect(t_gobj *z, t_glist *glist, + int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_vu* x = (t_vu*)z; + + *xp1 = text_xpix(&x->x_gui.x_obj, glist) - 1; + *yp1 = text_ypix(&x->x_gui.x_obj, glist) - 2; + *xp2 = *xp1 + x->x_gui.x_w + 2; + *yp2 = *yp1 + x->x_gui.x_h + 4; +} + +static void vu_save(t_gobj *z, t_binbuf *b) +{ + t_vu *x = (t_vu *)z; + int bflcol[3]; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + binbuf_addv(b, "ssiisiissiiiiiiii", gensym("#X"),gensym("obj"), + (t_int)x->x_gui.x_obj.te_xpix, (t_int)x->x_gui.x_obj.te_ypix, + gensym("vu"), x->x_gui.x_w, x->x_gui.x_h, + srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + iem_fstyletoint(&x->x_gui.x_fsf), x->x_gui.x_fontsize, + bflcol[0], bflcol[2], x->x_scale, + iem_symargstoint(&x->x_gui.x_isa)); + binbuf_addv(b, ";"); +} + +void vu_check_height(t_vu *x, int h) +{ + int n; + + n = h / IEM_VU_STEPS; + if(n < IEM_VU_MINSIZE) + n = IEM_VU_MINSIZE; + x->x_led_size = n-1; + x->x_gui.x_h = IEM_VU_STEPS * n; +} + +static void vu_scale(t_vu *x, t_floatarg fscale) +{ + int i, scale = (int)fscale; + + if(scale != 0) scale = 1; + if(x->x_scale && !scale) + { + t_canvas *canvas=glist_getcanvas(x->x_gui.x_glist); + + x->x_scale = (int)scale; + if(glist_isvisible(x->x_gui.x_glist)) + { + for(i=1; i<=IEM_VU_STEPS; i++) + { + if((i+2)&3) + sys_vgui(".x%x.c delete %xSCALE%d\n", canvas, x, i); + } + i=IEM_VU_STEPS+1; + sys_vgui(".x%x.c delete %xSCALE%d\n", canvas, x, i); + } + } + if(!x->x_scale && scale) + { + int w4=x->x_gui.x_w/4, end=text_xpix(&x->x_gui.x_obj, x->x_gui.x_glist)+x->x_gui.x_w+4; + int k1=x->x_led_size+1, k2=IEM_VU_STEPS+1, k3=k1/2; + int yyy, k4=text_ypix(&x->x_gui.x_obj, x->x_gui.x_glist)-k3; + t_canvas *canvas=glist_getcanvas(x->x_gui.x_glist); + + x->x_scale = (int)scale; + if(glist_isvisible(x->x_gui.x_glist)) + { + for(i=1; i<=IEM_VU_STEPS; i++) + { + yyy = k4 + k1*(k2-i); + if((i+2)&3) + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xSCALE%d\n", + canvas, end, yyy+k3, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_lcol, x, i); + } + i=IEM_VU_STEPS+1; + yyy = k4 + k1*(k2-i); + sys_vgui(".x%x.c create text %d %d -text {%s} -anchor w \ + -font {%s %d bold} -fill #%6.6x -tags %xSCALE%d\n", + canvas, end, yyy+k3, iemgui_vu_scale_str[i], x->x_gui.x_font, x->x_gui.x_fontsize, + x->x_gui.x_lcol, x, i); + } + } +} + +static void vu_properties(t_gobj *z, t_glist *owner) +{ + t_vu *x = (t_vu *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + sprintf(buf, "pdtk_iemgui_dialog %%s VU-METER \ + --------dimensions(pix)(pix):-------- %d %d width: %d %d height: \ + empty 0.0 empty 0.0 empty %d \ + %d no_scale scale %d %d empty %d \ + %s %s \ + %s %d %d \ + %d %d \ + %d %d %d\n", + x->x_gui.x_w, IEM_GUI_MINSIZE, x->x_gui.x_h, IEM_VU_STEPS*IEM_VU_MINSIZE, + 0,/*no_schedule*/ + x->x_scale, -1, -1, -1,/*no linlog, no init, no multi*/ + "nosndno", srl[1]->s_name,/*no send*/ + srl[2]->s_name, x->x_gui.x_ldx, x->x_gui.x_ldy, + x->x_gui.x_fsf.x_font_style, x->x_gui.x_fontsize, + 0xffffff & x->x_gui.x_bcol, -1/*no front-color*/, 0xffffff & x->x_gui.x_lcol); + gfxstub_new(&x->x_gui.x_obj.ob_pd, x, buf); +} + +static void vu_dialog(t_vu *x, t_symbol *s, int argc, t_atom *argv) +{ + t_symbol *srl[3]; + int w = (int)atom_getintarg(0, argc, argv); + int h = (int)atom_getintarg(1, argc, argv); + int scale = (int)atom_getintarg(4, argc, argv); + int sr_flags; + + srl[0] = gensym("empty"); + sr_flags = iemgui_dialog(&x->x_gui, srl, argc, argv); + x->x_gui.x_fsf.x_snd_able = 0; + x->x_gui.x_isa.x_loadinit = 0; + x->x_gui.x_w = iemgui_clip_size(w); + vu_check_height(x, h); + if(scale != 0) + scale = 1; + vu_scale(x, (float)scale); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_IO + sr_flags); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); +} + +static void vu_size(t_vu *x, t_symbol *s, int ac, t_atom *av) +{ + x->x_gui.x_w = iemgui_clip_size((int)atom_getintarg(0, ac, av)); + if(ac > 1) + vu_check_height(x, (int)atom_getintarg(1, ac, av)); + if(glist_isvisible(x->x_gui.x_glist)) + { + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_MOVE); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_CONFIG); + canvas_fixlinesfor(glist_getcanvas(x->x_gui.x_glist), (t_text*)x); + } +} + +static void vu_delta(t_vu *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void vu_pos(t_vu *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void vu_color(t_vu *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void vu_receive(t_vu *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void vu_label(t_vu *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void vu_label_pos(t_vu *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void vu_label_font(t_vu *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void vu_float(t_vu *x, t_floatarg rms) +{ + int i; + + if(rms <= IEM_VU_MINDB) + x->x_rms = 0; + else if(rms >= IEM_VU_MAXDB) + x->x_rms = IEM_VU_STEPS; + else + { + int i = (int)(2.0*(rms + IEM_VU_OFFSET)); + x->x_rms = iemgui_vu_db2i[i]; + } + i = (int)(100.0*rms + 10000.5); + rms = 0.01*(float)(i - 10000); + x->x_fr = rms; + outlet_float(x->x_out_rms, rms); + vu_update_rms(x, x->x_gui.x_glist); +} + +static void vu_ft1(t_vu *x, t_floatarg peak) +{ + int i; + + if(peak <= IEM_VU_MINDB) + x->x_peak = 0; + else if(peak >= IEM_VU_MAXDB) + x->x_peak = IEM_VU_STEPS; + else + { + int i = (int)(2.0*(peak + IEM_VU_OFFSET)); + x->x_peak = iemgui_vu_db2i[i]; + } + i = (int)(100.0*peak + 10000.5); + peak = 0.01*(float)(i - 10000); + x->x_fp = peak; + outlet_float(x->x_out_peak, peak); + vu_update_peak(x, x->x_gui.x_glist); +} + +static void vu_bang(t_vu *x) +{ + outlet_float(x->x_out_peak, x->x_fp); + outlet_float(x->x_out_rms, x->x_fr); + vu_update_rms(x, x->x_gui.x_glist); + vu_update_peak(x, x->x_gui.x_glist); +} + +static void *vu_new(t_symbol *s, int argc, t_atom *argv) +{ + t_vu *x = (t_vu *)pd_new(vu_class); + int bflcol[]={-66577, -1, -1}; + int w=IEM_GUI_DEFAULTSIZE, h=IEM_VU_STEPS*IEM_VU_DEFAULTSIZE; + int ldx=-1, ldy=-8, f=0, fs=8, scale=1; + int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, fthold=IEM_BNG_DEFAULTHOLDFLASHTIME; + char str[144]; + + iem_inttosymargs(&x->x_gui.x_isa, 0); + iem_inttofstyle(&x->x_gui.x_fsf, 0); + + if((argc >= 11)&&IS_A_FLOAT(argv,0)&&IS_A_FLOAT(argv,1) + &&(IS_A_SYMBOL(argv,2)||IS_A_FLOAT(argv,2)) + &&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3)) + &&IS_A_FLOAT(argv,4)&&IS_A_FLOAT(argv,5) + &&IS_A_FLOAT(argv,6)&&IS_A_FLOAT(argv,7) + &&IS_A_FLOAT(argv,8)&&IS_A_FLOAT(argv,9)&&IS_A_FLOAT(argv,10)) + { + w = (int)atom_getintarg(0, argc, argv); + h = (int)atom_getintarg(1, argc, argv); + iemgui_new_getnames(&x->x_gui, 1, argv); + ldx = (int)atom_getintarg(4, argc, argv); + ldy = (int)atom_getintarg(5, argc, argv); + iem_inttofstyle(&x->x_gui.x_fsf, atom_getintarg(6, argc, argv)); + fs = (int)atom_getintarg(7, argc, argv); + bflcol[0] = (int)atom_getintarg(8, argc, argv); + bflcol[2] = (int)atom_getintarg(9, argc, argv); + scale = (int)atom_getintarg(10, argc, argv); + } + else iemgui_new_getnames(&x->x_gui, 1, 0); + if((argc == 12)&&IS_A_FLOAT(argv,11)) + iem_inttosymargs(&x->x_gui.x_isa, atom_getintarg(11, argc, argv)); + x->x_gui.x_draw = (t_iemfunptr)vu_draw; + + x->x_gui.x_fsf.x_snd_able = 0; + x->x_gui.x_fsf.x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + if (!strcmp(x->x_gui.x_rcv->s_name, "empty")) + x->x_gui.x_fsf.x_rcv_able = 0; + if (x->x_gui.x_fsf.x_font_style == 1) + strcpy(x->x_gui.x_font, "helvetica"); + else if(x->x_gui.x_fsf.x_font_style == 2) + strcpy(x->x_gui.x_font, "times"); + else { x->x_gui.x_fsf.x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + if(x->x_gui.x_fsf.x_rcv_able) + pd_bind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + x->x_gui.x_ldx = ldx; + x->x_gui.x_ldy = ldy; + + if(fs < 4) + fs = 4; + x->x_gui.x_fontsize = fs; + x->x_gui.x_w = iemgui_clip_size(w); + vu_check_height(x, h); + iemgui_all_colfromload(&x->x_gui, bflcol); + if(scale != 0) + scale = 1; + x->x_scale = scale; + x->x_peak = 0; + x->x_rms = 0; + x->x_fp = -101.0; + x->x_fr = -101.0; + iemgui_verify_snd_ne_rcv(&x->x_gui); + inlet_new(&x->x_gui.x_obj, &x->x_gui.x_obj.ob_pd, &s_float, gensym("ft1")); + x->x_out_rms = outlet_new(&x->x_gui.x_obj, &s_float); + x->x_out_peak = outlet_new(&x->x_gui.x_obj, &s_float); + return (x); +} + +static void vu_free(t_vu *x) +{ + if(x->x_gui.x_fsf.x_rcv_able) + pd_unbind(&x->x_gui.x_obj.ob_pd, x->x_gui.x_rcv); + gfxstub_deleteforkey(x); +} + +void g_vumeter_setup(void) +{ + vu_class = class_new(gensym("vu"), (t_newmethod)vu_new, (t_method)vu_free, + sizeof(t_vu), 0, A_GIMME, 0); + class_addbang(vu_class,vu_bang); + class_addfloat(vu_class,vu_float); + class_addmethod(vu_class, (t_method)vu_ft1, gensym("ft1"), A_FLOAT, 0); + class_addmethod(vu_class, (t_method)vu_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(vu_class, (t_method)vu_size, gensym("size"), A_GIMME, 0); + class_addmethod(vu_class, (t_method)vu_scale, gensym("scale"), A_DEFFLOAT, 0); + class_addmethod(vu_class, (t_method)vu_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(vu_class, (t_method)vu_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(vu_class, (t_method)vu_color, gensym("color"), A_GIMME, 0); + class_addmethod(vu_class, (t_method)vu_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(vu_class, (t_method)vu_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(vu_class, (t_method)vu_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(vu_class, (t_method)vu_label_font, gensym("label_font"), A_GIMME, 0); + vu_widgetbehavior.w_getrectfn = vu_getrect; + vu_widgetbehavior.w_displacefn = iemgui_displace; + vu_widgetbehavior.w_selectfn = iemgui_select; + vu_widgetbehavior.w_activatefn = NULL; + vu_widgetbehavior.w_deletefn = iemgui_delete; + vu_widgetbehavior.w_visfn = iemgui_vis; + vu_widgetbehavior.w_clickfn = NULL; + class_setwidget(vu_class,&vu_widgetbehavior); + class_sethelpsymbol(vu_class, gensym("vu")); + class_setsavefn(vu_class, vu_save); + class_setpropertiesfn(vu_class, vu_properties); +} diff --git a/apps/plugins/pdbox/PDa/src/m_atom.c b/apps/plugins/pdbox/PDa/src/m_atom.c new file mode 100644 index 0000000..a4b08ff --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/m_atom.c @@ -0,0 +1,258 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include "m_pd.h" +#include +#include + + /* convenience routines for checking and getting values of + atoms. There's no "pointer" version since there's nothing + safe to return if there's an error. */ + +t_float atom_getfloat(t_atom *a) +{ + if (a->a_type == A_FLOAT) return (a->a_w.w_float); + else return (0); +} + +t_int atom_getint(t_atom *a) +{ + return (atom_getfloat(a)); +} + +t_symbol *atom_getsymbol(t_atom *a) /* LATER think about this more carefully */ +{ + char buf[30]; + if (a->a_type == A_SYMBOL) return (a->a_w.w_symbol); + else return (&s_float); +} + +t_symbol *atom_gensym(t_atom *a) /* this works better for graph labels */ +{ + char buf[30]; + if (a->a_type == A_SYMBOL) return (a->a_w.w_symbol); + else if (a->a_type == A_FLOAT) + sprintf(buf, "%g", a->a_w.w_float); + else strcpy(buf, "???"); + return (gensym(buf)); +} + +t_float atom_getfloatarg(int which, int argc, t_atom *argv) +{ + if (argc <= which) return (0); + argv += which; + if (argv->a_type == A_FLOAT) return (argv->a_w.w_float); + else return (0); +} + +t_int atom_getintarg(int which, int argc, t_atom *argv) +{ + return (atom_getfloatarg(which, argc, argv)); +} + +t_symbol *atom_getsymbolarg(int which, int argc, t_atom *argv) +{ + if (argc <= which) return (&s_); + argv += which; + if (argv->a_type == A_SYMBOL) return (argv->a_w.w_symbol); + else return (&s_); +} + +/* convert an atom into a string, in the reverse sense of binbuf_text (q.v.) +* special attention is paid to symbols containing the special characters +* ';', ',', '$', and '\'; these are quoted with a preceding '\', except that +* the '$' only gets quoted at the beginning of the string. +*/ + +void atom_string(t_atom *a, char *buf, unsigned int bufsize) +{ + char tbuf[30]; + switch(a->a_type) + { + case A_SEMI: strcpy(buf, ";"); break; + case A_COMMA: strcpy(buf, ","); break; + case A_POINTER: + strcpy(buf, "(pointer)"); + break; + case A_FLOAT: + sprintf(tbuf, "%g", a->a_w.w_float); + if (strlen(tbuf) < bufsize-1) strcpy(buf, tbuf); + else if (a->a_w.w_float < 0) strcpy(buf, "-"); + else strcat(buf, "+"); + break; + case A_SYMBOL: + { + char *sp; + unsigned int len; + int quote; + for (sp = a->a_w.w_symbol->s_name, len = 0, quote = 0; *sp; sp++, len++) + if (*sp == ';' || *sp == ',' || *sp == '\\' || + (*sp == '$' && sp == a->a_w.w_symbol->s_name && sp[1] >= '0' + && sp[1] <= '9')) + quote = 1; + if (quote) + { + char *bp = buf, *ep = buf + (bufsize-2); + sp = a->a_w.w_symbol->s_name; + while (bp < ep && *sp) + { + if (*sp == ';' || *sp == ',' || *sp == '\\' || + (*sp == '$' && bp == buf && sp[1] >= '0' && sp[1] <= '9')) + *bp++ = '\\'; + *bp++ = *sp++; + } + if (*sp) *bp++ = '*'; + *bp = 0; + /* post("quote %s -> %s", a->a_w.w_symbol->s_name, buf); */ + } + else + { + if (len < bufsize-1) strcpy(buf, a->a_w.w_symbol->s_name); + else + { + strncpy(buf, a->a_w.w_symbol->s_name, bufsize - 2); + strcpy(buf + (bufsize - 2), "*"); + } + } + } + break; + case A_DOLLAR: + sprintf(buf, "$%d", a->a_w.w_index); + break; + case A_DOLLSYM: + sprintf(buf, "$%s", a->a_w.w_symbol->s_name); + break; + default: + bug("atom_string"); + } +} +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include "m_pd.h" +#include +#include + + /* convenience routines for checking and getting values of + atoms. There's no "pointer" version since there's nothing + safe to return if there's an error. */ + +t_float atom_getfloat(t_atom *a) +{ + if (a->a_type == A_FLOAT) return (a->a_w.w_float); + else return (0); +} + +t_int atom_getint(t_atom *a) +{ + return (atom_getfloat(a)); +} + +t_symbol *atom_getsymbol(t_atom *a) /* LATER think about this more carefully */ +{ + char buf[30]; + if (a->a_type == A_SYMBOL) return (a->a_w.w_symbol); + else return (&s_float); +} + +t_symbol *atom_gensym(t_atom *a) /* this works better for graph labels */ +{ + char buf[30]; + if (a->a_type == A_SYMBOL) return (a->a_w.w_symbol); + else if (a->a_type == A_FLOAT) + sprintf(buf, "%g", a->a_w.w_float); + else strcpy(buf, "???"); + return (gensym(buf)); +} + +t_float atom_getfloatarg(int which, int argc, t_atom *argv) +{ + if (argc <= which) return (0); + argv += which; + if (argv->a_type == A_FLOAT) return (argv->a_w.w_float); + else return (0); +} + +t_int atom_getintarg(int which, int argc, t_atom *argv) +{ + return (atom_getfloatarg(which, argc, argv)); +} + +t_symbol *atom_getsymbolarg(int which, int argc, t_atom *argv) +{ + if (argc <= which) return (&s_); + argv += which; + if (argv->a_type == A_SYMBOL) return (argv->a_w.w_symbol); + else return (&s_); +} + +/* convert an atom into a string, in the reverse sense of binbuf_text (q.v.) +* special attention is paid to symbols containing the special characters +* ';', ',', '$', and '\'; these are quoted with a preceding '\', except that +* the '$' only gets quoted at the beginning of the string. +*/ + +void atom_string(t_atom *a, char *buf, unsigned int bufsize) +{ + char tbuf[30]; + switch(a->a_type) + { + case A_SEMI: strcpy(buf, ";"); break; + case A_COMMA: strcpy(buf, ","); break; + case A_POINTER: + strcpy(buf, "(pointer)"); + break; + case A_FLOAT: + sprintf(tbuf, "%g", a->a_w.w_float); + if (strlen(tbuf) < bufsize-1) strcpy(buf, tbuf); + else if (a->a_w.w_float < 0) strcpy(buf, "-"); + else strcat(buf, "+"); + break; + case A_SYMBOL: + { + char *sp; + unsigned int len; + int quote; + for (sp = a->a_w.w_symbol->s_name, len = 0, quote = 0; *sp; sp++, len++) + if (*sp == ';' || *sp == ',' || *sp == '\\' || + (*sp == '$' && sp == a->a_w.w_symbol->s_name && sp[1] >= '0' + && sp[1] <= '9')) + quote = 1; + if (quote) + { + char *bp = buf, *ep = buf + (bufsize-2); + sp = a->a_w.w_symbol->s_name; + while (bp < ep && *sp) + { + if (*sp == ';' || *sp == ',' || *sp == '\\' || + (*sp == '$' && bp == buf && sp[1] >= '0' && sp[1] <= '9')) + *bp++ = '\\'; + *bp++ = *sp++; + } + if (*sp) *bp++ = '*'; + *bp = 0; + /* post("quote %s -> %s", a->a_w.w_symbol->s_name, buf); */ + } + else + { + if (len < bufsize-1) strcpy(buf, a->a_w.w_symbol->s_name); + else + { + strncpy(buf, a->a_w.w_symbol->s_name, bufsize - 2); + strcpy(buf + (bufsize - 2), "*"); + } + } + } + break; + case A_DOLLAR: + sprintf(buf, "$%d", a->a_w.w_index); + break; + case A_DOLLSYM: + sprintf(buf, "$%s", a->a_w.w_symbol->s_name); + break; + default: + bug("atom_string"); + } +} diff --git a/apps/plugins/pdbox/PDa/src/m_binbuf.c b/apps/plugins/pdbox/PDa/src/m_binbuf.c new file mode 100644 index 0000000..c215e39 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/m_binbuf.c @@ -0,0 +1,2438 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + + +/* IOhannes : + * changed the canvas_restore in "g_canvas.c", so that it might accept $args as well (like "pd $0_test") + * so you can make multiple & distinguishable templates + * 1511:forum::für::umläute:2001 + * change marked with IOhannes + */ + +#include +#include "m_pd.h" +#include "s_stuff.h" +#include +#ifdef UNIX +#include +#endif +#ifdef MSW +#include +#endif +#include +#include +#include + +struct _binbuf +{ + int b_n; + t_atom *b_vec; +}; + +t_binbuf *binbuf_new(void) +{ + t_binbuf *x = (t_binbuf *)t_getbytes(sizeof(*x)); + x->b_n = 0; + x->b_vec = t_getbytes(0); + return (x); +} + +void binbuf_free(t_binbuf *x) +{ + t_freebytes(x->b_vec, x->b_n * sizeof(*x->b_vec)); + t_freebytes(x, sizeof(*x)); +} + +t_binbuf *binbuf_duplicate(t_binbuf *y) +{ + t_binbuf *x = (t_binbuf *)t_getbytes(sizeof(*x)); + x->b_n = y->b_n; + x->b_vec = t_getbytes(x->b_n * sizeof(*x->b_vec)); + memcpy(x->b_vec, y->b_vec, x->b_n * sizeof(*x->b_vec)); + return (x); +} + +void binbuf_clear(t_binbuf *x) +{ + x->b_vec = t_resizebytes(x->b_vec, x->b_n * sizeof(*x->b_vec), 0); + x->b_n = 0; +} + + /* convert text to a binbuf */ +void binbuf_text(t_binbuf *x, char *text, size_t size) +{ + char buf[MAXPDSTRING+1], *bufp, *ebuf = buf+MAXPDSTRING; + const char *textp = text, *etext = text+size; + t_atom *ap; + int nalloc = 16, natom = 0; + t_freebytes(x->b_vec, x->b_n * sizeof(*x->b_vec)); + x->b_vec = t_getbytes(nalloc * sizeof(*x->b_vec)); + ap = x->b_vec; + x->b_n = 0; + while (1) + { + int type; + /* skip leading space */ + while ((textp != etext) && (*textp == ' ' || *textp == '\n' + || *textp == '\r' || *textp == '\t')) textp++; + if (textp == etext) break; + if (*textp == ';') SETSEMI(ap), textp++; + else if (*textp == ',') SETCOMMA(ap), textp++; + else + { + /* it's an atom other than a comma or semi */ + char c; + int floatstate = 0, slash = 0, lastslash = 0, + firstslash = (*textp == '\\'); + bufp = buf; + do + { + c = *bufp = *textp++; + lastslash = slash; + slash = (c == '\\'); + + if (floatstate >= 0) + { + int digit = (c >= '0' && c <= '9'), + dot = (c == '.'), minus = (c == '-'), + plusminus = (minus || (c == '+')), + expon = (c == 'e' || c == 'E'); + if (floatstate == 0) /* beginning */ + { + if (minus) floatstate = 1; + else if (digit) floatstate = 2; + else if (dot) floatstate = 3; + else floatstate = -1; + } + else if (floatstate == 1) /* got minus */ + { + if (digit) floatstate = 2; + else if (dot) floatstate = 3; + else floatstate = -1; + } + else if (floatstate == 2) /* got digits */ + { + if (dot) floatstate = 4; + else if (expon) floatstate = 6; + else if (!digit) floatstate = -1; + } + else if (floatstate == 3) /* got '.' without digits */ + { + if (digit) floatstate = 5; + else floatstate = -1; + } + else if (floatstate == 4) /* got '.' after digits */ + { + if (digit) floatstate = 5; + else if (expon) floatstate = 6; + else floatstate = -1; + } + else if (floatstate == 5) /* got digits after . */ + { + if (expon) floatstate = 6; + else if (!digit) floatstate = -1; + } + else if (floatstate == 6) /* got 'e' */ + { + if (plusminus) floatstate = 7; + else if (digit) floatstate = 8; + else floatstate = -1; + } + else if (floatstate == 7) /* got plus or minus */ + { + if (digit) floatstate = 8; + else floatstate = -1; + } + else if (floatstate == 8) /* got digits */ + { + if (!digit) floatstate = -1; + } + } + if (!slash) bufp++; + } + while (textp != etext && bufp != ebuf && + (slash || (*textp != ' ' && *textp != '\n' && *textp != '\r' + && *textp != '\t' &&*textp != ',' && *textp != ';'))); + *bufp = 0; +#if 0 + post("buf %s", buf); +#endif + if (*buf == '$' && buf[1] >= '0' && buf[1] <= '9' && !firstslash) + { + for (bufp = buf+2; *bufp; bufp++) + if (*bufp < '0' || *bufp > '9') + { + SETDOLLSYM(ap, gensym(buf+1)); + goto didit; + } + SETDOLLAR(ap, atoi(buf+1)); + didit: ; + } + else + { + if (floatstate == 2 || floatstate == 4 || floatstate == 5 || + floatstate == 8) + SETFLOAT(ap, atof(buf)); + else SETSYMBOL(ap, gensym(buf)); + } + } + ap++; + natom++; + if (natom == nalloc) + { + x->b_vec = t_resizebytes(x->b_vec, nalloc * sizeof(*x->b_vec), + nalloc * (2*sizeof(*x->b_vec))); + nalloc = nalloc * 2; + ap = x->b_vec + natom; + } + if (textp == etext) break; + } + /* reallocate the vector to exactly the right size */ + x->b_vec = t_resizebytes(x->b_vec, nalloc * sizeof(*x->b_vec), + natom * sizeof(*x->b_vec)); + x->b_n = natom; +} + + /* convert a binbuf to text; no null termination. */ +void binbuf_gettext(t_binbuf *x, char **bufp, int *lengthp) +{ + char *buf = getbytes(0), *newbuf; + int length = 0; + char string[MAXPDSTRING]; + t_atom *ap; + int indx; + + for (ap = x->b_vec, indx = x->b_n; indx--; ap++) + { + int newlength; + if ((ap->a_type == A_SEMI || ap->a_type == A_COMMA) && + length && buf[length-1] == ' ') length--; + atom_string(ap, string, MAXPDSTRING); + newlength = length + strlen(string) + 1; + if (!(newbuf = resizebytes(buf, length, newlength))) break; + buf = newbuf; + strcpy(buf + length, string); + length = newlength; + if (ap->a_type == A_SEMI) buf[length-1] = '\n'; + else buf[length-1] = ' '; + } + if (length && buf[length-1] == ' ') + { + if (newbuf = t_resizebytes(buf, length, length-1)) + { + buf = newbuf; + length--; + } + } + *bufp = buf; + *lengthp = length; +} + +/* LATER improve the out-of-space behavior below. Also fix this so that +writing to file doesn't buffer everything together. */ + +void binbuf_add(t_binbuf *x, int argc, t_atom *argv) +{ + int newsize = x->b_n + argc, i; + t_atom *ap; + if (ap = t_resizebytes(x->b_vec, x->b_n * sizeof(*x->b_vec), + newsize * sizeof(*x->b_vec))) + x->b_vec = ap; + else + { + error("binbuf_addmessage: out of space"); + return; + } +#if 0 + startpost("binbuf_add: "); + postatom(argc, argv); + endpost(); +#endif + for (ap = x->b_vec + x->b_n, i = argc; i--; ap++) + *ap = *(argv++); + x->b_n = newsize; +} + +#define MAXADDMESSV 100 +void binbuf_addv(t_binbuf *x, char *fmt, ...) +{ + va_list ap; + t_atom arg[MAXADDMESSV], *at =arg; + int nargs = 0; + char *fp = fmt; + + va_start(ap, fmt); + while (1) + { + if (nargs >= MAXADDMESSV) + { + error("binbuf_addmessv: only %d allowed", MAXADDMESSV); + break; + } + switch(*fp++) + { + case 'i': SETFLOAT(at, va_arg(ap, t_int)); break; + case 'f': SETFLOAT(at, va_arg(ap, double)); break; + case 's': SETSYMBOL(at, va_arg(ap, t_symbol *)); break; + case ';': SETSEMI(at); break; + case ',': SETCOMMA(at); break; + default: goto done; + } + at++; + nargs++; + } +done: + va_end(ap); + binbuf_add(x, nargs, arg); +} + +/* add a binbuf to another one for saving. Semicolons and commas go to +symbols ";", "'",; the symbol ";" goes to "\;", etc. */ + +void binbuf_addbinbuf(t_binbuf *x, t_binbuf *y) +{ + t_binbuf *z = binbuf_new(); + int i; + t_atom *ap; + binbuf_add(z, y->b_n, y->b_vec); + for (i = 0, ap = z->b_vec; i < z->b_n; i++, ap++) + { + char tbuf[MAXPDSTRING]; + switch (ap->a_type) + { + case A_FLOAT: + break; + case A_SEMI: + SETSYMBOL(ap, gensym(";")); + break; + case A_COMMA: + SETSYMBOL(ap, gensym(",")); + break; + case A_DOLLAR: + sprintf(tbuf, "$%d", ap->a_w.w_index); + SETSYMBOL(ap, gensym(tbuf)); + break; + case A_DOLLSYM: + sprintf(tbuf, "$%s", ap->a_w.w_symbol->s_name); + SETSYMBOL(ap, gensym(tbuf)); + break; + case A_SYMBOL: + /* FIXME make this general */ + if (!strcmp(ap->a_w.w_symbol->s_name, ";")) + SETSYMBOL(ap, gensym(";")); + else if (!strcmp(ap->a_w.w_symbol->s_name, ",")) + SETSYMBOL(ap, gensym(",")); + break; + default: + bug("binbuf_addbinbuf"); + } + } + + binbuf_add(x, z->b_n, z->b_vec); +} + +void binbuf_addsemi(t_binbuf *x) +{ + t_atom a; + SETSEMI(&a); + binbuf_add(x, 1, &a); +} + +/* Supply atoms to a binbuf from a message, making the opposite changes +from binbuf_addbinbuf. The symbol ";" goes to a semicolon, etc. */ + +void binbuf_restore(t_binbuf *x, int argc, t_atom *argv) +{ + int newsize = x->b_n + argc, i; + t_atom *ap; + if (ap = t_resizebytes(x->b_vec, x->b_n * sizeof(*x->b_vec), + newsize * sizeof(*x->b_vec))) + x->b_vec = ap; + else + { + error("binbuf_addmessage: out of space"); + return; + } + + for (ap = x->b_vec + x->b_n, i = argc; i--; ap++) + { + if (argv->a_type == A_SYMBOL) + { + char *str = argv->a_w.w_symbol->s_name; + if (!strcmp(str, ";")) SETSEMI(ap); + else if (!strcmp(str, ",")) SETCOMMA(ap); + else if (str[0] == '$' && str[1] >= '0' && str[1] <= '9') + { + int dollsym = 0; + char *str2; + for (str2 = str + 2; *str2; str2++) + if (*str2 < '0' || *str2 > '9') + dollsym = 1; + if (dollsym) + SETDOLLSYM(ap, gensym(str + 1)); + else + { + int dollar = 0; + sscanf(argv->a_w.w_symbol->s_name + 1, "%d", &dollar); + SETDOLLAR(ap, dollar); + } + } + else *ap = *argv; + argv++; + } + else *ap = *(argv++); + } + x->b_n = newsize; +} + + +#define MSTACKSIZE 2048 + +void binbuf_print(t_binbuf *x) +{ + int i, startedpost = 0, newline = 1; + for (i = 0; i < x->b_n; i++) + { + if (newline) + { + if (startedpost) endpost(); + startpost(""); + startedpost = 1; + } + postatom(1, x->b_vec + i); + if (x->b_vec[i].a_type == A_SEMI) + newline = 1; + else newline = 0; + } + if (startedpost) endpost(); +} + +int binbuf_getnatom(t_binbuf *x) +{ + return (x->b_n); +} + +t_atom *binbuf_getvec(t_binbuf *x) +{ + return (x->b_vec); +} + +int canvas_getdollarzero( void); + +t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, t_atom *av, int tonew) +{ + int argno = atol(s->s_name), lastnum; + char buf[MAXPDSTRING], c, *sp; + for (lastnum = 0, sp = s->s_name; ((c = *sp) && c >= '0' && c <= '9'); + sp++, lastnum++) + if (!c || argno < 0 || argno > ac) + { + if (!tonew) + return (0); + else sprintf(buf, "$%d", argno); + } + else if (argno == 0) + sprintf(buf, "%d", canvas_getdollarzero()); + else + atom_string(av+(argno-1), buf, MAXPDSTRING/2-1); + strncat(buf, sp, MAXPDSTRING/2-1); + return (gensym(buf)); +} + +void binbuf_eval(t_binbuf *x, t_pd *target, int argc, t_atom *argv) +{ + static t_atom mstack[MSTACKSIZE], *msp = mstack, *ems = mstack+MSTACKSIZE; + t_atom *stackwas = msp; + t_atom *at = x->b_vec; + int ac = x->b_n; + int nargs; + while (1) + { + t_pd *nexttarget; + /* get a target. */ + while (!target) + { + t_symbol *s; + while (ac && (at->a_type == A_SEMI || at->a_type == A_COMMA)) + ac--, at++; + if (!ac) break; + if (at->a_type == A_DOLLAR) + { + if (at->a_w.w_index <= 0 || at->a_w.w_index > argc) + { + error("$%d: not enough arguments supplied", + at->a_w.w_index); + goto cleanup; + } + else if (argv[at->a_w.w_index-1].a_type != A_SYMBOL) + { + error("$%d: symbol needed as message destination", + at->a_w.w_index); + goto cleanup; + } + else s = argv[at->a_w.w_index-1].a_w.w_symbol; + } + else if (at->a_type == A_DOLLSYM) + { + if (!(s = binbuf_realizedollsym(at->a_w.w_symbol, + argc, argv, 0))) + { + error("$%s: not enough arguments supplied", + at->a_w.w_symbol->s_name); + goto cleanup; + } + } + else s = atom_getsymbol(at); + if (!(target = s->s_thing)) + { + error("%s: no such object", s->s_name); + cleanup: + do at++, ac--; + while (ac && at->a_type != A_SEMI); + /* LATER eat args until semicolon and continue */ + continue; + } + else + { + at++, ac--; + break; + } + } + if (!ac) break; + nargs = 0; + nexttarget = target; + while (1) + { + t_symbol *s9; + if (!ac) goto gotmess; + if (msp >= ems) + { + error("message stack overflow"); + goto broken; + } + switch (at->a_type) + { + case A_SEMI: + /* semis and commas in new message just get bashed to + a symbol. This is needed so you can pass them to "expr." */ + if (target == &pd_objectmaker) + { + SETSYMBOL(msp, gensym(";")); + break; + } + else + { + nexttarget = 0; + goto gotmess; + } + case A_COMMA: + if (target == &pd_objectmaker) + { + SETSYMBOL(msp, gensym(",")); + break; + } + else goto gotmess; + case A_FLOAT: + case A_SYMBOL: + *msp = *at; + break; + case A_DOLLAR: + if (at->a_w.w_index > 0 && at->a_w.w_index <= argc) + *msp = argv[at->a_w.w_index-1]; + else if (at->a_w.w_index == 0) + SETFLOAT(msp, canvas_getdollarzero()); + else + { + if (target == &pd_objectmaker) + SETFLOAT(msp, 0); + else + { + error("$%d: argument number out of range", + at->a_w.w_index); + SETFLOAT(msp, 0); + } + } + break; + case A_DOLLSYM: + s9 = binbuf_realizedollsym(at->a_w.w_symbol, argc, argv, + target == &pd_objectmaker); + if (!s9) + goto broken; + SETSYMBOL(msp, s9); + break; + default: + bug("bad item in binbuf"); + goto broken; + } + msp++; + ac--; + at++; + nargs++; + } + gotmess: + if (nargs) + { + switch (stackwas->a_type) + { + case A_SYMBOL: + typedmess(target, stackwas->a_w.w_symbol, nargs-1, stackwas+1); + break; + case A_FLOAT: + if (nargs == 1) pd_float(target, stackwas->a_w.w_float); + else pd_list(target, 0, nargs, stackwas); + break; + } + } + msp = stackwas; + if (!ac) break; + target = nexttarget; + at++; + ac--; + } + + return; +broken: + msp = stackwas; +} + +static int binbuf_doopen(char *s, int mode) +{ + char namebuf[MAXPDSTRING]; +#ifdef MSW + mode |= O_BINARY; +#endif + sys_bashfilename(s, namebuf); + return (open(namebuf, mode)); +} + +static FILE *binbuf_dofopen(char *s, char *mode) +{ + char namebuf[MAXPDSTRING]; + sys_bashfilename(s, namebuf); + return (fopen(namebuf, mode)); +} + +int binbuf_read(t_binbuf *b, char *filename, char *dirname, int crflag) +{ + long length; + int fd; + int readret; + char *buf; + char namebuf[MAXPDSTRING]; + + namebuf[0] = 0; + if (*dirname) + strcat(namebuf, dirname), strcat(namebuf, "/"); + strcat(namebuf, filename); + + if ((fd = binbuf_doopen(namebuf, 0)) < 0) + { + fprintf(stderr, "open: "); + perror(namebuf); + return (1); + } + if ((length = lseek(fd, 0, SEEK_END)) < 0 || lseek(fd, 0, SEEK_SET) < 0 + || !(buf = t_getbytes(length))) + { + fprintf(stderr, "lseek: "); + perror(namebuf); + close(fd); + return(1); + } + if ((readret = read(fd, buf, length)) < length) + { + fprintf(stderr, "read (%d %ld) -> %d\n", fd, length, readret); + perror(namebuf); + close(fd); + t_freebytes(buf, length); + return(1); + } + /* optionally map carriage return to semicolon */ + if (crflag) + { + int i; + for (i = 0; i < length; i++) + if (buf[i] == '\n') + buf[i] = ';'; + } + binbuf_text(b, buf, length); + +#if 0 + startpost("binbuf_read "); postatom(b->b_n, b->b_vec); endpost(); +#endif + + t_freebytes(buf, length); + close(fd); + return (0); +} + +int binbuf_read_via_path(t_binbuf *b, char *filename, char *dirname, + int crflag) +{ + int filedesc; + char buf[MAXPDSTRING], *bufptr; + if ((filedesc = open_via_path( + dirname, filename, "", buf, &bufptr, MAXPDSTRING, 0)) < 0) + { + error("%s: can't open", filename); + return (1); + } + else close (filedesc); + if (binbuf_read(b, bufptr, buf, crflag)) + return (1); + else return (0); +} + +#define WBUFSIZE 4096 +static t_binbuf *binbuf_convert(t_binbuf *oldb, int maxtopd); + + /* write a binbuf to a text file. If "crflag" is set we suppress + semicolons. */ +int binbuf_write(t_binbuf *x, char *filename, char *dir, int crflag) +{ + FILE *f = 0; + char sbuf[WBUFSIZE], fbuf[MAXPDSTRING], *bp = sbuf, *ep = sbuf + WBUFSIZE; + t_atom *ap; + int indx, deleteit = 0; + int ncolumn = 0; + + fbuf[0] = 0; + if (*dir) + strcat(fbuf, dir), strcat(fbuf, "/"); + strcat(fbuf, filename); + if (!strcmp(filename + strlen(filename) - 4, ".pat")) + { + x = binbuf_convert(x, 0); + deleteit = 1; + } + + if (!(f = binbuf_dofopen(fbuf, "w"))) + { + fprintf(stderr, "open: "); + sys_unixerror(fbuf); + goto fail; + } + for (ap = x->b_vec, indx = x->b_n; indx--; ap++) + { + int length; + /* estimate how many characters will be needed. Printing out + symbols may need extra characters for inserting backslashes. */ + if (ap->a_type == A_SYMBOL || ap->a_type == A_DOLLSYM) + length = 80 + strlen(ap->a_w.w_symbol->s_name); + else length = 40; + if (ep - bp < length) + { + if (fwrite(sbuf, bp-sbuf, 1, f) < 1) + { + sys_unixerror(fbuf); + goto fail; + } + bp = sbuf; + } + if ((ap->a_type == A_SEMI || ap->a_type == A_COMMA) && + bp > sbuf && bp[-1] == ' ') bp--; + if (!crflag || ap->a_type != A_SEMI) + { + atom_string(ap, bp, (ep-bp)-2); + length = strlen(bp); + bp += length; + ncolumn += length; + } + if (ap->a_type == A_SEMI || (!crflag && ncolumn > 65)) + { + *bp++ = '\n'; + ncolumn = 0; + } + else + { + *bp++ = ' '; + ncolumn++; + } + } + if (fwrite(sbuf, bp-sbuf, 1, f) < 1) + { + sys_unixerror(fbuf); + goto fail; + } + if (deleteit) + binbuf_free(x); + fclose(f); + return (0); +fail: + if (deleteit) + binbuf_free(x); + if (f) + fclose(f); + return (1); +} + +/* The following routine attempts to convert from max to pd or back. The +max to pd direction is working OK but you will need to make lots of +abstractions for objects like "gate" which don't exist in Pd. conversion +from Pd to Max hasn't been tested for patches with subpatches yet! */ + +#define MAXSTACK 1000 + +#define ISSYMBOL(a, b) ((a)->a_type == A_SYMBOL && \ + !strcmp((a)->a_w.w_symbol->s_name, (b))) + +static t_binbuf *binbuf_convert(t_binbuf *oldb, int maxtopd) +{ + t_binbuf *newb = binbuf_new(); + t_atom *vec = oldb->b_vec; + t_int n = oldb->b_n, nextindex, stackdepth = 0, stack[MAXSTACK], + nobj = 0, i; + t_atom outmess[MAXSTACK], *nextmess; + if (!maxtopd) + binbuf_addv(newb, "ss;", gensym("max"), gensym("v2")); + for (nextindex = 0; nextindex < n; ) + { + int endmess, natom; + char *first, *second; + for (endmess = nextindex; endmess < n && vec[endmess].a_type != A_SEMI; + endmess++) + ; + if (endmess == n) break; + if (endmess == nextindex || endmess == nextindex + 1 + || vec[nextindex].a_type != A_SYMBOL || + vec[nextindex+1].a_type != A_SYMBOL) + { + nextindex = endmess + 1; + continue; + } + natom = endmess - nextindex; + if (natom > MAXSTACK-10) natom = MAXSTACK-10; + nextmess = vec + nextindex; + first = nextmess->a_w.w_symbol->s_name; + second = (nextmess+1)->a_w.w_symbol->s_name; + if (maxtopd) + { + /* case 1: importing a ".pat" file into Pd. */ + + /* dollar signs in file translate to symbols */ + for (i = 0; i < natom; i++) + { + if (nextmess[i].a_type == A_DOLLAR) + { + char buf[100]; + sprintf(buf, "$%d", nextmess[i].a_w.w_index); + SETSYMBOL(nextmess+i, gensym(buf)); + } + else if (nextmess[i].a_type == A_DOLLSYM) + { + char buf[100]; + sprintf(buf, "$%s", nextmess[i].a_w.w_symbol->s_name); + SETSYMBOL(nextmess+i, gensym(buf)); + } + } + if (!strcmp(first, "#N")) + { + if (!strcmp(second, "vpatcher")) + { + if (stackdepth >= MAXSTACK) + { + post("too many embedded patches"); + return (newb); + } + stack[stackdepth] = nobj; + stackdepth++; + nobj = 0; + binbuf_addv(newb, "ssfffff;", + gensym("#N"), gensym("canvas"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + atom_getfloatarg(4, natom, nextmess) - + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(5, natom, nextmess) - + atom_getfloatarg(3, natom, nextmess), + (float)sys_defaultfont); + } + } + if (!strcmp(first, "#P")) + { + /* drop initial "hidden" flag */ + if (!strcmp(second, "hidden")) + { + nextmess++; + natom--; + second = (nextmess+1)->a_w.w_symbol->s_name; + } + if (natom >= 7 && !strcmp(second, "newobj") + && (ISSYMBOL(&nextmess[6], "patcher") || + ISSYMBOL(&nextmess[6], "p"))) + { + binbuf_addv(newb, "ssffss;", + gensym("#X"), gensym("restore"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + gensym("pd"), atom_getsymbolarg(7, natom, nextmess)); + if (stackdepth) stackdepth--; + nobj = stack[stackdepth]; + nobj++; + } + else if (!strcmp(second, "newex") || !strcmp(second, "newobj")) + { + t_symbol *classname = + atom_getsymbolarg(6, natom, nextmess); + if (classname == gensym("trigger") || + classname == gensym("t")) + { + for (i = 7; i < natom; i++) + if (nextmess[i].a_type == A_SYMBOL && + nextmess[i].a_w.w_symbol == gensym("i")) + nextmess[i].a_w.w_symbol = gensym("f"); + } + if (classname == gensym("table")) + classname = gensym("TABLE"); + SETSYMBOL(outmess, gensym("#X")); + SETSYMBOL(outmess + 1, gensym("obj")); + outmess[2] = nextmess[2]; + outmess[3] = nextmess[3]; + SETSYMBOL(outmess+4, classname); + for (i = 7; i < natom; i++) + outmess[i-2] = nextmess[i]; + SETSEMI(outmess + natom - 2); + binbuf_add(newb, natom - 1, outmess); + nobj++; + } + else if (!strcmp(second, "message") || + !strcmp(second, "comment")) + { + SETSYMBOL(outmess, gensym("#X")); + SETSYMBOL(outmess + 1, gensym( + (strcmp(second, "message") ? "text" : "msg"))); + outmess[2] = nextmess[2]; + outmess[3] = nextmess[3]; + for (i = 6; i < natom; i++) + outmess[i-2] = nextmess[i]; + SETSEMI(outmess + natom - 2); + binbuf_add(newb, natom - 1, outmess); + nobj++; + } + else if (!strcmp(second, "button")) + { + binbuf_addv(newb, "ssffs;", + gensym("#X"), gensym("obj"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + gensym("bng")); + nobj++; + } + else if (!strcmp(second, "number") || !strcmp(second, "flonum")) + { + binbuf_addv(newb, "ssff;", + gensym("#X"), gensym("floatatom"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess)); + nobj++; + } + else if (!strcmp(second, "slider")) + { + float inc = atom_getfloatarg(7, natom, nextmess); + if (inc <= 0) + inc = 1; + binbuf_addv(newb, "ssffsffffffsssfffffffff;", + gensym("#X"), gensym("obj"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + gensym("vsl"), + atom_getfloatarg(4, natom, nextmess), + atom_getfloatarg(5, natom, nextmess), + atom_getfloatarg(6, natom, nextmess), + atom_getfloatarg(6, natom, nextmess) + + (atom_getfloatarg(5, natom, nextmess) - 1) * inc, + 0., 0., + gensym("empty"), gensym("empty"), gensym("empty"), + 0., -8., 0., 8., -262144., -1., -1., 0., 1.); + nobj++; + } + else if (!strcmp(second, "toggle")) + { + binbuf_addv(newb, "ssffs;", + gensym("#X"), gensym("obj"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + gensym("tgl")); + nobj++; + } + else if (!strcmp(second, "inlet")) + { + binbuf_addv(newb, "ssffs;", + gensym("#X"), gensym("obj"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + gensym((natom > 5 ? "inlet~" : "inlet"))); + nobj++; + } + else if (!strcmp(second, "outlet")) + { + binbuf_addv(newb, "ssffs;", + gensym("#X"), gensym("obj"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + gensym((natom > 5 ? "outlet~" : "outlet"))); + nobj++; + } + else if (!strcmp(second, "user")) + { + binbuf_addv(newb, "ssffs;", + gensym("#X"), gensym("obj"), + atom_getfloatarg(3, natom, nextmess), + atom_getfloatarg(4, natom, nextmess), + atom_getsymbolarg(2, natom, nextmess)); + nobj++; + } + else if (!strcmp(second, "connect")|| + !strcmp(second, "fasten")) + { + binbuf_addv(newb, "ssffff;", + gensym("#X"), gensym("connect"), + nobj - atom_getfloatarg(2, natom, nextmess) - 1, + atom_getfloatarg(3, natom, nextmess), + nobj - atom_getfloatarg(4, natom, nextmess) - 1, + atom_getfloatarg(5, natom, nextmess)); + } + } + } + else /* Pd to Max */ + { + if (!strcmp(first, "#N")) + { + if (!strcmp(second, "canvas")) + { + if (stackdepth >= MAXSTACK) + { + post("too many embedded patches"); + return (newb); + } + stack[stackdepth] = nobj; + stackdepth++; + nobj = 0; + binbuf_addv(newb, "ssffff;", + gensym("#N"), gensym("vpatcher"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + atom_getfloatarg(4, natom, nextmess), + atom_getfloatarg(5, natom, nextmess)); + } + } + if (!strcmp(first, "#X")) + { + if (natom >= 5 && !strcmp(second, "restore") + && (ISSYMBOL (&nextmess[4], "pd"))) + { + binbuf_addv(newb, "ss;", gensym("#P"), gensym("pop")); + binbuf_addv(newb, "ssffffss;", + gensym("#P"), gensym("newobj"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), 50., 1., + gensym("patcher"), + atom_getsymbolarg(5, natom, nextmess)); + if (stackdepth) stackdepth--; + nobj = stack[stackdepth]; + nobj++; + } + else if (!strcmp(second, "obj")) + { + t_symbol *classname = + atom_getsymbolarg(4, natom, nextmess); + if (classname == gensym("inlet")) + binbuf_addv(newb, "ssfff;", gensym("#P"), + gensym("inlet"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + 15.); + else if (classname == gensym("inlet~")) + binbuf_addv(newb, "ssffff;", gensym("#P"), + gensym("inlet"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + 15., 1.); + else if (classname == gensym("outlet")) + binbuf_addv(newb, "ssfff;", gensym("#P"), + gensym("outlet"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + 15.); + else if (classname == gensym("outlet~")) + binbuf_addv(newb, "ssffff;", gensym("#P"), + gensym("outlet"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + 15., 1.); + else if (classname == gensym("bng")) + binbuf_addv(newb, "ssffff;", gensym("#P"), + gensym("button"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + atom_getfloatarg(5, natom, nextmess), 0.); + else if (classname == gensym("tgl")) + binbuf_addv(newb, "ssffff;", gensym("#P"), + gensym("toggle"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + atom_getfloatarg(5, natom, nextmess), 0.); + else if (classname == gensym("vsl")) + binbuf_addv(newb, "ssffffff;", gensym("#P"), + gensym("slider"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + atom_getfloatarg(5, natom, nextmess), + atom_getfloatarg(6, natom, nextmess), + (atom_getfloatarg(8, natom, nextmess) - + atom_getfloatarg(7, natom, nextmess)) / + (atom_getfloatarg(6, natom, nextmess) == 1? 1 : + atom_getfloatarg(6, natom, nextmess) - 1), + atom_getfloatarg(7, natom, nextmess)); + else + { + SETSYMBOL(outmess, gensym("#P")); + SETSYMBOL(outmess + 1, gensym("newex")); + outmess[2] = nextmess[2]; + outmess[3] = nextmess[3]; + SETFLOAT(outmess + 4, 50); + SETFLOAT(outmess + 5, 1); + for (i = 4; i < natom; i++) + outmess[i+2] = nextmess[i]; + SETSEMI(outmess + natom + 2); + binbuf_add(newb, natom + 3, outmess); + } + nobj++; + + } + else if (!strcmp(second, "msg") || + !strcmp(second, "text")) + { + SETSYMBOL(outmess, gensym("#P")); + SETSYMBOL(outmess + 1, gensym( + (strcmp(second, "msg") ? "comment" : "message"))); + outmess[2] = nextmess[2]; + outmess[3] = nextmess[3]; + SETFLOAT(outmess + 4, 50); + SETFLOAT(outmess + 5, 1); + for (i = 4; i < natom; i++) + outmess[i+2] = nextmess[i]; + SETSEMI(outmess + natom + 2); + binbuf_add(newb, natom + 3, outmess); + nobj++; + } + else if (!strcmp(second, "floatatom")) + { + binbuf_addv(newb, "ssfff;", + gensym("#P"), gensym("flonum"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), 35); + nobj++; + } + else if (!strcmp(second, "connect")) + { + binbuf_addv(newb, "ssffff;", + gensym("#P"), gensym("connect"), + nobj - atom_getfloatarg(2, natom, nextmess) - 1, + atom_getfloatarg(3, natom, nextmess), + nobj - atom_getfloatarg(4, natom, nextmess) - 1, + atom_getfloatarg(5, natom, nextmess)); + } + } + } + nextindex = endmess + 1; + } + if (!maxtopd) + binbuf_addv(newb, "ss;", gensym("#P"), gensym("pop")); +#if 0 + binbuf_write(newb, "import-result.pd", "/tmp", 0); +#endif + return (newb); +} + + /* function to support searching */ +int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf) +{ + int indexin, nmatched; + for (indexin = 0; indexin <= inbuf->b_n - searchbuf->b_n; indexin++) + { + for (nmatched = 0; nmatched < searchbuf->b_n; nmatched++) + { + t_atom *a1 = &inbuf->b_vec[indexin + nmatched], + *a2 = &searchbuf->b_vec[nmatched]; + if (a1->a_type != a2->a_type || + a1->a_type == A_SYMBOL && a1->a_w.w_symbol != a2->a_w.w_symbol + || + a1->a_type == A_FLOAT && a1->a_w.w_float != a2->a_w.w_float + || + a1->a_type == A_DOLLAR && a1->a_w.w_index != a2->a_w.w_index + || + a1->a_type == A_DOLLSYM && a1->a_w.w_symbol != a2->a_w.w_symbol) + goto nomatch; + } + return (1); + nomatch: ; + } + return (0); +} + +void pd_doloadbang(void); + +/* LATER make this evaluate the file on-the-fly. */ +/* LATER figure out how to log errors */ +void binbuf_evalfile(t_symbol *name, t_symbol *dir) +{ + t_binbuf *b = binbuf_new(); + int import = !strcmp(name->s_name + strlen(name->s_name) - 4, ".pat"); + /* set filename so that new canvases can pick them up */ + int dspstate = canvas_suspend_dsp(); + glob_setfilename(0, name, dir); + if (binbuf_read(b, name->s_name, dir->s_name, 0)) + { + perror(name->s_name); + } + else + { + if (import) + { + t_binbuf *newb = binbuf_convert(b, 1); + binbuf_free(b); + b = newb; + } + binbuf_eval(b, 0, 0, 0); + } + glob_setfilename(0, &s_, &s_); /* bug fix by Krzysztof Czaja */ + binbuf_free(b); + canvas_resume_dsp(dspstate); +} + +void glob_evalfile(t_pd *ignore, t_symbol *name, t_symbol *dir) +{ + t_pd *x = 0; + /* even though binbuf_evalfile appears to take care of dspstate, + we have to do it again here, because canvas_startdsp() assumes + that all toplevel canvases are visible. LATER check if this + is still necessary -- probably not. */ + + int dspstate = canvas_suspend_dsp(); + binbuf_evalfile(name, dir); + while ((x != s__X.s_thing) && (x = s__X.s_thing)) + vmess(x, gensym("pop"), "i", 1); + pd_doloadbang(); + canvas_resume_dsp(dspstate); +} +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + + +/* IOhannes : + * changed the canvas_restore in "g_canvas.c", so that it might accept $args as well (like "pd $0_test") + * so you can make multiple & distinguishable templates + * 1511:forum::für::umläute:2001 + * change marked with IOhannes + */ + +#include +#include "m_pd.h" +#include "s_stuff.h" +#include +#ifdef UNIX +#include +#endif +#ifdef MSW +#include +#endif +#include +#include +#include + +struct _binbuf +{ + int b_n; + t_atom *b_vec; +}; + +t_binbuf *binbuf_new(void) +{ + t_binbuf *x = (t_binbuf *)t_getbytes(sizeof(*x)); + x->b_n = 0; + x->b_vec = t_getbytes(0); + return (x); +} + +void binbuf_free(t_binbuf *x) +{ + t_freebytes(x->b_vec, x->b_n * sizeof(*x->b_vec)); + t_freebytes(x, sizeof(*x)); +} + +t_binbuf *binbuf_duplicate(t_binbuf *y) +{ + t_binbuf *x = (t_binbuf *)t_getbytes(sizeof(*x)); + x->b_n = y->b_n; + x->b_vec = t_getbytes(x->b_n * sizeof(*x->b_vec)); + memcpy(x->b_vec, y->b_vec, x->b_n * sizeof(*x->b_vec)); + return (x); +} + +void binbuf_clear(t_binbuf *x) +{ + x->b_vec = t_resizebytes(x->b_vec, x->b_n * sizeof(*x->b_vec), 0); + x->b_n = 0; +} + + /* convert text to a binbuf */ +void binbuf_text(t_binbuf *x, char *text, size_t size) +{ + char buf[MAXPDSTRING+1], *bufp, *ebuf = buf+MAXPDSTRING; + const char *textp = text, *etext = text+size; + t_atom *ap; + int nalloc = 16, natom = 0; + t_freebytes(x->b_vec, x->b_n * sizeof(*x->b_vec)); + x->b_vec = t_getbytes(nalloc * sizeof(*x->b_vec)); + ap = x->b_vec; + x->b_n = 0; + while (1) + { + int type; + /* skip leading space */ + while ((textp != etext) && (*textp == ' ' || *textp == '\n' + || *textp == '\r' || *textp == '\t')) textp++; + if (textp == etext) break; + if (*textp == ';') SETSEMI(ap), textp++; + else if (*textp == ',') SETCOMMA(ap), textp++; + else + { + /* it's an atom other than a comma or semi */ + char c; + int floatstate = 0, slash = 0, lastslash = 0, + firstslash = (*textp == '\\'); + bufp = buf; + do + { + c = *bufp = *textp++; + lastslash = slash; + slash = (c == '\\'); + + if (floatstate >= 0) + { + int digit = (c >= '0' && c <= '9'), + dot = (c == '.'), minus = (c == '-'), + plusminus = (minus || (c == '+')), + expon = (c == 'e' || c == 'E'); + if (floatstate == 0) /* beginning */ + { + if (minus) floatstate = 1; + else if (digit) floatstate = 2; + else if (dot) floatstate = 3; + else floatstate = -1; + } + else if (floatstate == 1) /* got minus */ + { + if (digit) floatstate = 2; + else if (dot) floatstate = 3; + else floatstate = -1; + } + else if (floatstate == 2) /* got digits */ + { + if (dot) floatstate = 4; + else if (expon) floatstate = 6; + else if (!digit) floatstate = -1; + } + else if (floatstate == 3) /* got '.' without digits */ + { + if (digit) floatstate = 5; + else floatstate = -1; + } + else if (floatstate == 4) /* got '.' after digits */ + { + if (digit) floatstate = 5; + else if (expon) floatstate = 6; + else floatstate = -1; + } + else if (floatstate == 5) /* got digits after . */ + { + if (expon) floatstate = 6; + else if (!digit) floatstate = -1; + } + else if (floatstate == 6) /* got 'e' */ + { + if (plusminus) floatstate = 7; + else if (digit) floatstate = 8; + else floatstate = -1; + } + else if (floatstate == 7) /* got plus or minus */ + { + if (digit) floatstate = 8; + else floatstate = -1; + } + else if (floatstate == 8) /* got digits */ + { + if (!digit) floatstate = -1; + } + } + if (!slash) bufp++; + } + while (textp != etext && bufp != ebuf && + (slash || (*textp != ' ' && *textp != '\n' && *textp != '\r' + && *textp != '\t' &&*textp != ',' && *textp != ';'))); + *bufp = 0; +#if 0 + post("buf %s", buf); +#endif + if (*buf == '$' && buf[1] >= '0' && buf[1] <= '9' && !firstslash) + { + for (bufp = buf+2; *bufp; bufp++) + if (*bufp < '0' || *bufp > '9') + { + SETDOLLSYM(ap, gensym(buf+1)); + goto didit; + } + SETDOLLAR(ap, atoi(buf+1)); + didit: ; + } + else + { + if (floatstate == 2 || floatstate == 4 || floatstate == 5 || + floatstate == 8) + SETFLOAT(ap, atof(buf)); + else SETSYMBOL(ap, gensym(buf)); + } + } + ap++; + natom++; + if (natom == nalloc) + { + x->b_vec = t_resizebytes(x->b_vec, nalloc * sizeof(*x->b_vec), + nalloc * (2*sizeof(*x->b_vec))); + nalloc = nalloc * 2; + ap = x->b_vec + natom; + } + if (textp == etext) break; + } + /* reallocate the vector to exactly the right size */ + x->b_vec = t_resizebytes(x->b_vec, nalloc * sizeof(*x->b_vec), + natom * sizeof(*x->b_vec)); + x->b_n = natom; +} + + /* convert a binbuf to text; no null termination. */ +void binbuf_gettext(t_binbuf *x, char **bufp, int *lengthp) +{ + char *buf = getbytes(0), *newbuf; + int length = 0; + char string[MAXPDSTRING]; + t_atom *ap; + int indx; + + for (ap = x->b_vec, indx = x->b_n; indx--; ap++) + { + int newlength; + if ((ap->a_type == A_SEMI || ap->a_type == A_COMMA) && + length && buf[length-1] == ' ') length--; + atom_string(ap, string, MAXPDSTRING); + newlength = length + strlen(string) + 1; + if (!(newbuf = resizebytes(buf, length, newlength))) break; + buf = newbuf; + strcpy(buf + length, string); + length = newlength; + if (ap->a_type == A_SEMI) buf[length-1] = '\n'; + else buf[length-1] = ' '; + } + if (length && buf[length-1] == ' ') + { + if (newbuf = t_resizebytes(buf, length, length-1)) + { + buf = newbuf; + length--; + } + } + *bufp = buf; + *lengthp = length; +} + +/* LATER improve the out-of-space behavior below. Also fix this so that +writing to file doesn't buffer everything together. */ + +void binbuf_add(t_binbuf *x, int argc, t_atom *argv) +{ + int newsize = x->b_n + argc, i; + t_atom *ap; + if (ap = t_resizebytes(x->b_vec, x->b_n * sizeof(*x->b_vec), + newsize * sizeof(*x->b_vec))) + x->b_vec = ap; + else + { + error("binbuf_addmessage: out of space"); + return; + } +#if 0 + startpost("binbuf_add: "); + postatom(argc, argv); + endpost(); +#endif + for (ap = x->b_vec + x->b_n, i = argc; i--; ap++) + *ap = *(argv++); + x->b_n = newsize; +} + +#define MAXADDMESSV 100 +void binbuf_addv(t_binbuf *x, char *fmt, ...) +{ + va_list ap; + t_atom arg[MAXADDMESSV], *at =arg; + int nargs = 0; + char *fp = fmt; + + va_start(ap, fmt); + while (1) + { + if (nargs >= MAXADDMESSV) + { + error("binbuf_addmessv: only %d allowed", MAXADDMESSV); + break; + } + switch(*fp++) + { + case 'i': SETFLOAT(at, va_arg(ap, t_int)); break; + case 'f': SETFLOAT(at, va_arg(ap, double)); break; + case 's': SETSYMBOL(at, va_arg(ap, t_symbol *)); break; + case ';': SETSEMI(at); break; + case ',': SETCOMMA(at); break; + default: goto done; + } + at++; + nargs++; + } +done: + va_end(ap); + binbuf_add(x, nargs, arg); +} + +/* add a binbuf to another one for saving. Semicolons and commas go to +symbols ";", "'",; the symbol ";" goes to "\;", etc. */ + +void binbuf_addbinbuf(t_binbuf *x, t_binbuf *y) +{ + t_binbuf *z = binbuf_new(); + int i; + t_atom *ap; + binbuf_add(z, y->b_n, y->b_vec); + for (i = 0, ap = z->b_vec; i < z->b_n; i++, ap++) + { + char tbuf[MAXPDSTRING]; + switch (ap->a_type) + { + case A_FLOAT: + break; + case A_SEMI: + SETSYMBOL(ap, gensym(";")); + break; + case A_COMMA: + SETSYMBOL(ap, gensym(",")); + break; + case A_DOLLAR: + sprintf(tbuf, "$%d", ap->a_w.w_index); + SETSYMBOL(ap, gensym(tbuf)); + break; + case A_DOLLSYM: + sprintf(tbuf, "$%s", ap->a_w.w_symbol->s_name); + SETSYMBOL(ap, gensym(tbuf)); + break; + case A_SYMBOL: + /* FIXME make this general */ + if (!strcmp(ap->a_w.w_symbol->s_name, ";")) + SETSYMBOL(ap, gensym(";")); + else if (!strcmp(ap->a_w.w_symbol->s_name, ",")) + SETSYMBOL(ap, gensym(",")); + break; + default: + bug("binbuf_addbinbuf"); + } + } + + binbuf_add(x, z->b_n, z->b_vec); +} + +void binbuf_addsemi(t_binbuf *x) +{ + t_atom a; + SETSEMI(&a); + binbuf_add(x, 1, &a); +} + +/* Supply atoms to a binbuf from a message, making the opposite changes +from binbuf_addbinbuf. The symbol ";" goes to a semicolon, etc. */ + +void binbuf_restore(t_binbuf *x, int argc, t_atom *argv) +{ + int newsize = x->b_n + argc, i; + t_atom *ap; + if (ap = t_resizebytes(x->b_vec, x->b_n * sizeof(*x->b_vec), + newsize * sizeof(*x->b_vec))) + x->b_vec = ap; + else + { + error("binbuf_addmessage: out of space"); + return; + } + + for (ap = x->b_vec + x->b_n, i = argc; i--; ap++) + { + if (argv->a_type == A_SYMBOL) + { + char *str = argv->a_w.w_symbol->s_name; + if (!strcmp(str, ";")) SETSEMI(ap); + else if (!strcmp(str, ",")) SETCOMMA(ap); + else if (str[0] == '$' && str[1] >= '0' && str[1] <= '9') + { + int dollsym = 0; + char *str2; + for (str2 = str + 2; *str2; str2++) + if (*str2 < '0' || *str2 > '9') + dollsym = 1; + if (dollsym) + SETDOLLSYM(ap, gensym(str + 1)); + else + { + int dollar = 0; + sscanf(argv->a_w.w_symbol->s_name + 1, "%d", &dollar); + SETDOLLAR(ap, dollar); + } + } + else *ap = *argv; + argv++; + } + else *ap = *(argv++); + } + x->b_n = newsize; +} + + +#define MSTACKSIZE 2048 + +void binbuf_print(t_binbuf *x) +{ + int i, startedpost = 0, newline = 1; + for (i = 0; i < x->b_n; i++) + { + if (newline) + { + if (startedpost) endpost(); + startpost(""); + startedpost = 1; + } + postatom(1, x->b_vec + i); + if (x->b_vec[i].a_type == A_SEMI) + newline = 1; + else newline = 0; + } + if (startedpost) endpost(); +} + +int binbuf_getnatom(t_binbuf *x) +{ + return (x->b_n); +} + +t_atom *binbuf_getvec(t_binbuf *x) +{ + return (x->b_vec); +} + +int canvas_getdollarzero( void); + +t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, t_atom *av, int tonew) +{ + int argno = atol(s->s_name), lastnum; + char buf[MAXPDSTRING], c, *sp; + for (lastnum = 0, sp = s->s_name; ((c = *sp) && c >= '0' && c <= '9'); + sp++, lastnum++) + if (!c || argno < 0 || argno > ac) + { + if (!tonew) + return (0); + else sprintf(buf, "$%d", argno); + } + else if (argno == 0) + sprintf(buf, "%d", canvas_getdollarzero()); + else + atom_string(av+(argno-1), buf, MAXPDSTRING/2-1); + strncat(buf, sp, MAXPDSTRING/2-1); + return (gensym(buf)); +} + +void binbuf_eval(t_binbuf *x, t_pd *target, int argc, t_atom *argv) +{ + static t_atom mstack[MSTACKSIZE], *msp = mstack, *ems = mstack+MSTACKSIZE; + t_atom *stackwas = msp; + t_atom *at = x->b_vec; + int ac = x->b_n; + int nargs; + while (1) + { + t_pd *nexttarget; + /* get a target. */ + while (!target) + { + t_symbol *s; + while (ac && (at->a_type == A_SEMI || at->a_type == A_COMMA)) + ac--, at++; + if (!ac) break; + if (at->a_type == A_DOLLAR) + { + if (at->a_w.w_index <= 0 || at->a_w.w_index > argc) + { + error("$%d: not enough arguments supplied", + at->a_w.w_index); + goto cleanup; + } + else if (argv[at->a_w.w_index-1].a_type != A_SYMBOL) + { + error("$%d: symbol needed as message destination", + at->a_w.w_index); + goto cleanup; + } + else s = argv[at->a_w.w_index-1].a_w.w_symbol; + } + else if (at->a_type == A_DOLLSYM) + { + if (!(s = binbuf_realizedollsym(at->a_w.w_symbol, + argc, argv, 0))) + { + error("$%s: not enough arguments supplied", + at->a_w.w_symbol->s_name); + goto cleanup; + } + } + else s = atom_getsymbol(at); + if (!(target = s->s_thing)) + { + error("%s: no such object", s->s_name); + cleanup: + do at++, ac--; + while (ac && at->a_type != A_SEMI); + /* LATER eat args until semicolon and continue */ + continue; + } + else + { + at++, ac--; + break; + } + } + if (!ac) break; + nargs = 0; + nexttarget = target; + while (1) + { + t_symbol *s9; + if (!ac) goto gotmess; + if (msp >= ems) + { + error("message stack overflow"); + goto broken; + } + switch (at->a_type) + { + case A_SEMI: + /* semis and commas in new message just get bashed to + a symbol. This is needed so you can pass them to "expr." */ + if (target == &pd_objectmaker) + { + SETSYMBOL(msp, gensym(";")); + break; + } + else + { + nexttarget = 0; + goto gotmess; + } + case A_COMMA: + if (target == &pd_objectmaker) + { + SETSYMBOL(msp, gensym(",")); + break; + } + else goto gotmess; + case A_FLOAT: + case A_SYMBOL: + *msp = *at; + break; + case A_DOLLAR: + if (at->a_w.w_index > 0 && at->a_w.w_index <= argc) + *msp = argv[at->a_w.w_index-1]; + else if (at->a_w.w_index == 0) + SETFLOAT(msp, canvas_getdollarzero()); + else + { + if (target == &pd_objectmaker) + SETFLOAT(msp, 0); + else + { + error("$%d: argument number out of range", + at->a_w.w_index); + SETFLOAT(msp, 0); + } + } + break; + case A_DOLLSYM: + s9 = binbuf_realizedollsym(at->a_w.w_symbol, argc, argv, + target == &pd_objectmaker); + if (!s9) + goto broken; + SETSYMBOL(msp, s9); + break; + default: + bug("bad item in binbuf"); + goto broken; + } + msp++; + ac--; + at++; + nargs++; + } + gotmess: + if (nargs) + { + switch (stackwas->a_type) + { + case A_SYMBOL: + typedmess(target, stackwas->a_w.w_symbol, nargs-1, stackwas+1); + break; + case A_FLOAT: + if (nargs == 1) pd_float(target, stackwas->a_w.w_float); + else pd_list(target, 0, nargs, stackwas); + break; + } + } + msp = stackwas; + if (!ac) break; + target = nexttarget; + at++; + ac--; + } + + return; +broken: + msp = stackwas; +} + +static int binbuf_doopen(char *s, int mode) +{ + char namebuf[MAXPDSTRING]; +#ifdef MSW + mode |= O_BINARY; +#endif + sys_bashfilename(s, namebuf); + return (open(namebuf, mode)); +} + +static FILE *binbuf_dofopen(char *s, char *mode) +{ + char namebuf[MAXPDSTRING]; + sys_bashfilename(s, namebuf); + return (fopen(namebuf, mode)); +} + +int binbuf_read(t_binbuf *b, char *filename, char *dirname, int crflag) +{ + long length; + int fd; + int readret; + char *buf; + char namebuf[MAXPDSTRING]; + + namebuf[0] = 0; + if (*dirname) + strcat(namebuf, dirname), strcat(namebuf, "/"); + strcat(namebuf, filename); + + if ((fd = binbuf_doopen(namebuf, 0)) < 0) + { + fprintf(stderr, "open: "); + perror(namebuf); + return (1); + } + if ((length = lseek(fd, 0, SEEK_END)) < 0 || lseek(fd, 0, SEEK_SET) < 0 + || !(buf = t_getbytes(length))) + { + fprintf(stderr, "lseek: "); + perror(namebuf); + close(fd); + return(1); + } + if ((readret = read(fd, buf, length)) < length) + { + fprintf(stderr, "read (%d %ld) -> %d\n", fd, length, readret); + perror(namebuf); + close(fd); + t_freebytes(buf, length); + return(1); + } + /* optionally map carriage return to semicolon */ + if (crflag) + { + int i; + for (i = 0; i < length; i++) + if (buf[i] == '\n') + buf[i] = ';'; + } + binbuf_text(b, buf, length); + +#if 0 + startpost("binbuf_read "); postatom(b->b_n, b->b_vec); endpost(); +#endif + + t_freebytes(buf, length); + close(fd); + return (0); +} + +int binbuf_read_via_path(t_binbuf *b, char *filename, char *dirname, + int crflag) +{ + int filedesc; + char buf[MAXPDSTRING], *bufptr; + if ((filedesc = open_via_path( + dirname, filename, "", buf, &bufptr, MAXPDSTRING, 0)) < 0) + { + error("%s: can't open", filename); + return (1); + } + else close (filedesc); + if (binbuf_read(b, bufptr, buf, crflag)) + return (1); + else return (0); +} + +#define WBUFSIZE 4096 +static t_binbuf *binbuf_convert(t_binbuf *oldb, int maxtopd); + + /* write a binbuf to a text file. If "crflag" is set we suppress + semicolons. */ +int binbuf_write(t_binbuf *x, char *filename, char *dir, int crflag) +{ + FILE *f = 0; + char sbuf[WBUFSIZE], fbuf[MAXPDSTRING], *bp = sbuf, *ep = sbuf + WBUFSIZE; + t_atom *ap; + int indx, deleteit = 0; + int ncolumn = 0; + + fbuf[0] = 0; + if (*dir) + strcat(fbuf, dir), strcat(fbuf, "/"); + strcat(fbuf, filename); + if (!strcmp(filename + strlen(filename) - 4, ".pat")) + { + x = binbuf_convert(x, 0); + deleteit = 1; + } + + if (!(f = binbuf_dofopen(fbuf, "w"))) + { + fprintf(stderr, "open: "); + sys_unixerror(fbuf); + goto fail; + } + for (ap = x->b_vec, indx = x->b_n; indx--; ap++) + { + int length; + /* estimate how many characters will be needed. Printing out + symbols may need extra characters for inserting backslashes. */ + if (ap->a_type == A_SYMBOL || ap->a_type == A_DOLLSYM) + length = 80 + strlen(ap->a_w.w_symbol->s_name); + else length = 40; + if (ep - bp < length) + { + if (fwrite(sbuf, bp-sbuf, 1, f) < 1) + { + sys_unixerror(fbuf); + goto fail; + } + bp = sbuf; + } + if ((ap->a_type == A_SEMI || ap->a_type == A_COMMA) && + bp > sbuf && bp[-1] == ' ') bp--; + if (!crflag || ap->a_type != A_SEMI) + { + atom_string(ap, bp, (ep-bp)-2); + length = strlen(bp); + bp += length; + ncolumn += length; + } + if (ap->a_type == A_SEMI || (!crflag && ncolumn > 65)) + { + *bp++ = '\n'; + ncolumn = 0; + } + else + { + *bp++ = ' '; + ncolumn++; + } + } + if (fwrite(sbuf, bp-sbuf, 1, f) < 1) + { + sys_unixerror(fbuf); + goto fail; + } + if (deleteit) + binbuf_free(x); + fclose(f); + return (0); +fail: + if (deleteit) + binbuf_free(x); + if (f) + fclose(f); + return (1); +} + +/* The following routine attempts to convert from max to pd or back. The +max to pd direction is working OK but you will need to make lots of +abstractions for objects like "gate" which don't exist in Pd. conversion +from Pd to Max hasn't been tested for patches with subpatches yet! */ + +#define MAXSTACK 1000 + +#define ISSYMBOL(a, b) ((a)->a_type == A_SYMBOL && \ + !strcmp((a)->a_w.w_symbol->s_name, (b))) + +static t_binbuf *binbuf_convert(t_binbuf *oldb, int maxtopd) +{ + t_binbuf *newb = binbuf_new(); + t_atom *vec = oldb->b_vec; + t_int n = oldb->b_n, nextindex, stackdepth = 0, stack[MAXSTACK], + nobj = 0, i; + t_atom outmess[MAXSTACK], *nextmess; + if (!maxtopd) + binbuf_addv(newb, "ss;", gensym("max"), gensym("v2")); + for (nextindex = 0; nextindex < n; ) + { + int endmess, natom; + char *first, *second; + for (endmess = nextindex; endmess < n && vec[endmess].a_type != A_SEMI; + endmess++) + ; + if (endmess == n) break; + if (endmess == nextindex || endmess == nextindex + 1 + || vec[nextindex].a_type != A_SYMBOL || + vec[nextindex+1].a_type != A_SYMBOL) + { + nextindex = endmess + 1; + continue; + } + natom = endmess - nextindex; + if (natom > MAXSTACK-10) natom = MAXSTACK-10; + nextmess = vec + nextindex; + first = nextmess->a_w.w_symbol->s_name; + second = (nextmess+1)->a_w.w_symbol->s_name; + if (maxtopd) + { + /* case 1: importing a ".pat" file into Pd. */ + + /* dollar signs in file translate to symbols */ + for (i = 0; i < natom; i++) + { + if (nextmess[i].a_type == A_DOLLAR) + { + char buf[100]; + sprintf(buf, "$%d", nextmess[i].a_w.w_index); + SETSYMBOL(nextmess+i, gensym(buf)); + } + else if (nextmess[i].a_type == A_DOLLSYM) + { + char buf[100]; + sprintf(buf, "$%s", nextmess[i].a_w.w_symbol->s_name); + SETSYMBOL(nextmess+i, gensym(buf)); + } + } + if (!strcmp(first, "#N")) + { + if (!strcmp(second, "vpatcher")) + { + if (stackdepth >= MAXSTACK) + { + post("too many embedded patches"); + return (newb); + } + stack[stackdepth] = nobj; + stackdepth++; + nobj = 0; + binbuf_addv(newb, "ssfffff;", + gensym("#N"), gensym("canvas"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + atom_getfloatarg(4, natom, nextmess) - + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(5, natom, nextmess) - + atom_getfloatarg(3, natom, nextmess), + (float)sys_defaultfont); + } + } + if (!strcmp(first, "#P")) + { + /* drop initial "hidden" flag */ + if (!strcmp(second, "hidden")) + { + nextmess++; + natom--; + second = (nextmess+1)->a_w.w_symbol->s_name; + } + if (natom >= 7 && !strcmp(second, "newobj") + && (ISSYMBOL(&nextmess[6], "patcher") || + ISSYMBOL(&nextmess[6], "p"))) + { + binbuf_addv(newb, "ssffss;", + gensym("#X"), gensym("restore"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + gensym("pd"), atom_getsymbolarg(7, natom, nextmess)); + if (stackdepth) stackdepth--; + nobj = stack[stackdepth]; + nobj++; + } + else if (!strcmp(second, "newex") || !strcmp(second, "newobj")) + { + t_symbol *classname = + atom_getsymbolarg(6, natom, nextmess); + if (classname == gensym("trigger") || + classname == gensym("t")) + { + for (i = 7; i < natom; i++) + if (nextmess[i].a_type == A_SYMBOL && + nextmess[i].a_w.w_symbol == gensym("i")) + nextmess[i].a_w.w_symbol = gensym("f"); + } + if (classname == gensym("table")) + classname = gensym("TABLE"); + SETSYMBOL(outmess, gensym("#X")); + SETSYMBOL(outmess + 1, gensym("obj")); + outmess[2] = nextmess[2]; + outmess[3] = nextmess[3]; + SETSYMBOL(outmess+4, classname); + for (i = 7; i < natom; i++) + outmess[i-2] = nextmess[i]; + SETSEMI(outmess + natom - 2); + binbuf_add(newb, natom - 1, outmess); + nobj++; + } + else if (!strcmp(second, "message") || + !strcmp(second, "comment")) + { + SETSYMBOL(outmess, gensym("#X")); + SETSYMBOL(outmess + 1, gensym( + (strcmp(second, "message") ? "text" : "msg"))); + outmess[2] = nextmess[2]; + outmess[3] = nextmess[3]; + for (i = 6; i < natom; i++) + outmess[i-2] = nextmess[i]; + SETSEMI(outmess + natom - 2); + binbuf_add(newb, natom - 1, outmess); + nobj++; + } + else if (!strcmp(second, "button")) + { + binbuf_addv(newb, "ssffs;", + gensym("#X"), gensym("obj"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + gensym("bng")); + nobj++; + } + else if (!strcmp(second, "number") || !strcmp(second, "flonum")) + { + binbuf_addv(newb, "ssff;", + gensym("#X"), gensym("floatatom"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess)); + nobj++; + } + else if (!strcmp(second, "slider")) + { + float inc = atom_getfloatarg(7, natom, nextmess); + if (inc <= 0) + inc = 1; + binbuf_addv(newb, "ssffsffffffsssfffffffff;", + gensym("#X"), gensym("obj"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + gensym("vsl"), + atom_getfloatarg(4, natom, nextmess), + atom_getfloatarg(5, natom, nextmess), + atom_getfloatarg(6, natom, nextmess), + atom_getfloatarg(6, natom, nextmess) + + (atom_getfloatarg(5, natom, nextmess) - 1) * inc, + 0., 0., + gensym("empty"), gensym("empty"), gensym("empty"), + 0., -8., 0., 8., -262144., -1., -1., 0., 1.); + nobj++; + } + else if (!strcmp(second, "toggle")) + { + binbuf_addv(newb, "ssffs;", + gensym("#X"), gensym("obj"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + gensym("tgl")); + nobj++; + } + else if (!strcmp(second, "inlet")) + { + binbuf_addv(newb, "ssffs;", + gensym("#X"), gensym("obj"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + gensym((natom > 5 ? "inlet~" : "inlet"))); + nobj++; + } + else if (!strcmp(second, "outlet")) + { + binbuf_addv(newb, "ssffs;", + gensym("#X"), gensym("obj"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + gensym((natom > 5 ? "outlet~" : "outlet"))); + nobj++; + } + else if (!strcmp(second, "user")) + { + binbuf_addv(newb, "ssffs;", + gensym("#X"), gensym("obj"), + atom_getfloatarg(3, natom, nextmess), + atom_getfloatarg(4, natom, nextmess), + atom_getsymbolarg(2, natom, nextmess)); + nobj++; + } + else if (!strcmp(second, "connect")|| + !strcmp(second, "fasten")) + { + binbuf_addv(newb, "ssffff;", + gensym("#X"), gensym("connect"), + nobj - atom_getfloatarg(2, natom, nextmess) - 1, + atom_getfloatarg(3, natom, nextmess), + nobj - atom_getfloatarg(4, natom, nextmess) - 1, + atom_getfloatarg(5, natom, nextmess)); + } + } + } + else /* Pd to Max */ + { + if (!strcmp(first, "#N")) + { + if (!strcmp(second, "canvas")) + { + if (stackdepth >= MAXSTACK) + { + post("too many embedded patches"); + return (newb); + } + stack[stackdepth] = nobj; + stackdepth++; + nobj = 0; + binbuf_addv(newb, "ssffff;", + gensym("#N"), gensym("vpatcher"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + atom_getfloatarg(4, natom, nextmess), + atom_getfloatarg(5, natom, nextmess)); + } + } + if (!strcmp(first, "#X")) + { + if (natom >= 5 && !strcmp(second, "restore") + && (ISSYMBOL (&nextmess[4], "pd"))) + { + binbuf_addv(newb, "ss;", gensym("#P"), gensym("pop")); + binbuf_addv(newb, "ssffffss;", + gensym("#P"), gensym("newobj"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), 50., 1., + gensym("patcher"), + atom_getsymbolarg(5, natom, nextmess)); + if (stackdepth) stackdepth--; + nobj = stack[stackdepth]; + nobj++; + } + else if (!strcmp(second, "obj")) + { + t_symbol *classname = + atom_getsymbolarg(4, natom, nextmess); + if (classname == gensym("inlet")) + binbuf_addv(newb, "ssfff;", gensym("#P"), + gensym("inlet"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + 15.); + else if (classname == gensym("inlet~")) + binbuf_addv(newb, "ssffff;", gensym("#P"), + gensym("inlet"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + 15., 1.); + else if (classname == gensym("outlet")) + binbuf_addv(newb, "ssfff;", gensym("#P"), + gensym("outlet"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + 15.); + else if (classname == gensym("outlet~")) + binbuf_addv(newb, "ssffff;", gensym("#P"), + gensym("outlet"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + 15., 1.); + else if (classname == gensym("bng")) + binbuf_addv(newb, "ssffff;", gensym("#P"), + gensym("button"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + atom_getfloatarg(5, natom, nextmess), 0.); + else if (classname == gensym("tgl")) + binbuf_addv(newb, "ssffff;", gensym("#P"), + gensym("toggle"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + atom_getfloatarg(5, natom, nextmess), 0.); + else if (classname == gensym("vsl")) + binbuf_addv(newb, "ssffffff;", gensym("#P"), + gensym("slider"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + atom_getfloatarg(5, natom, nextmess), + atom_getfloatarg(6, natom, nextmess), + (atom_getfloatarg(8, natom, nextmess) - + atom_getfloatarg(7, natom, nextmess)) / + (atom_getfloatarg(6, natom, nextmess) == 1? 1 : + atom_getfloatarg(6, natom, nextmess) - 1), + atom_getfloatarg(7, natom, nextmess)); + else + { + SETSYMBOL(outmess, gensym("#P")); + SETSYMBOL(outmess + 1, gensym("newex")); + outmess[2] = nextmess[2]; + outmess[3] = nextmess[3]; + SETFLOAT(outmess + 4, 50); + SETFLOAT(outmess + 5, 1); + for (i = 4; i < natom; i++) + outmess[i+2] = nextmess[i]; + SETSEMI(outmess + natom + 2); + binbuf_add(newb, natom + 3, outmess); + } + nobj++; + + } + else if (!strcmp(second, "msg") || + !strcmp(second, "text")) + { + SETSYMBOL(outmess, gensym("#P")); + SETSYMBOL(outmess + 1, gensym( + (strcmp(second, "msg") ? "comment" : "message"))); + outmess[2] = nextmess[2]; + outmess[3] = nextmess[3]; + SETFLOAT(outmess + 4, 50); + SETFLOAT(outmess + 5, 1); + for (i = 4; i < natom; i++) + outmess[i+2] = nextmess[i]; + SETSEMI(outmess + natom + 2); + binbuf_add(newb, natom + 3, outmess); + nobj++; + } + else if (!strcmp(second, "floatatom")) + { + binbuf_addv(newb, "ssfff;", + gensym("#P"), gensym("flonum"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), 35); + nobj++; + } + else if (!strcmp(second, "connect")) + { + binbuf_addv(newb, "ssffff;", + gensym("#P"), gensym("connect"), + nobj - atom_getfloatarg(2, natom, nextmess) - 1, + atom_getfloatarg(3, natom, nextmess), + nobj - atom_getfloatarg(4, natom, nextmess) - 1, + atom_getfloatarg(5, natom, nextmess)); + } + } + } + nextindex = endmess + 1; + } + if (!maxtopd) + binbuf_addv(newb, "ss;", gensym("#P"), gensym("pop")); +#if 0 + binbuf_write(newb, "import-result.pd", "/tmp", 0); +#endif + return (newb); +} + + /* function to support searching */ +int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf) +{ + int indexin, nmatched; + for (indexin = 0; indexin <= inbuf->b_n - searchbuf->b_n; indexin++) + { + for (nmatched = 0; nmatched < searchbuf->b_n; nmatched++) + { + t_atom *a1 = &inbuf->b_vec[indexin + nmatched], + *a2 = &searchbuf->b_vec[nmatched]; + if (a1->a_type != a2->a_type || + a1->a_type == A_SYMBOL && a1->a_w.w_symbol != a2->a_w.w_symbol + || + a1->a_type == A_FLOAT && a1->a_w.w_float != a2->a_w.w_float + || + a1->a_type == A_DOLLAR && a1->a_w.w_index != a2->a_w.w_index + || + a1->a_type == A_DOLLSYM && a1->a_w.w_symbol != a2->a_w.w_symbol) + goto nomatch; + } + return (1); + nomatch: ; + } + return (0); +} + +void pd_doloadbang(void); + +/* LATER make this evaluate the file on-the-fly. */ +/* LATER figure out how to log errors */ +void binbuf_evalfile(t_symbol *name, t_symbol *dir) +{ + t_binbuf *b = binbuf_new(); + int import = !strcmp(name->s_name + strlen(name->s_name) - 4, ".pat"); + /* set filename so that new canvases can pick them up */ + int dspstate = canvas_suspend_dsp(); + glob_setfilename(0, name, dir); + if (binbuf_read(b, name->s_name, dir->s_name, 0)) + { + perror(name->s_name); + } + else + { + if (import) + { + t_binbuf *newb = binbuf_convert(b, 1); + binbuf_free(b); + b = newb; + } + binbuf_eval(b, 0, 0, 0); + } + glob_setfilename(0, &s_, &s_); /* bug fix by Krzysztof Czaja */ + binbuf_free(b); + canvas_resume_dsp(dspstate); +} + +void glob_evalfile(t_pd *ignore, t_symbol *name, t_symbol *dir) +{ + t_pd *x = 0; + /* even though binbuf_evalfile appears to take care of dspstate, + we have to do it again here, because canvas_startdsp() assumes + that all toplevel canvases are visible. LATER check if this + is still necessary -- probably not. */ + + int dspstate = canvas_suspend_dsp(); + binbuf_evalfile(name, dir); + while ((x != s__X.s_thing) && (x = s__X.s_thing)) + vmess(x, gensym("pop"), "i", 1); + pd_doloadbang(); + canvas_resume_dsp(dspstate); +} diff --git a/apps/plugins/pdbox/PDa/src/m_class.c b/apps/plugins/pdbox/PDa/src/m_class.c new file mode 100644 index 0000000..0b6d4df --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/m_class.c @@ -0,0 +1,1648 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#define PD_CLASS_DEF +#include "m_pd.h" +#include "m_imp.h" +#include "s_stuff.h" +#include +#ifdef UNIX +#include +#endif +#ifdef MSW +#include +#endif + +#include +#include + +static t_symbol *class_loadsym; /* name under which an extern is invoked */ +static void pd_defaultfloat(t_pd *x, t_float f); +static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv); +t_pd pd_objectmaker; /* factory for creating "object" boxes */ +t_pd pd_canvasmaker; /* factory for creating canvases */ + +static t_symbol *class_extern_dir = &s_; + +static void pd_defaultanything(t_pd *x, t_symbol *s, int argc, t_atom *argv) +{ + pd_error(x, "%s: no method for '%s'", (*x)->c_name->s_name, s->s_name); +} + +static void pd_defaultbang(t_pd *x) +{ + if (*(*x)->c_listmethod != pd_defaultlist) + (*(*x)->c_listmethod)(x, 0, 0, 0); + else (*(*x)->c_anymethod)(x, &s_bang, 0, 0); +} + +static void pd_defaultpointer(t_pd *x, t_gpointer *gp) +{ + if (*(*x)->c_listmethod != pd_defaultlist) + { + t_atom at; + SETPOINTER(&at, gp); + (*(*x)->c_listmethod)(x, 0, 1, &at); + } + else + { + t_atom at; + SETPOINTER(&at, gp); + (*(*x)->c_anymethod)(x, &s_pointer, 1, &at); + } +} + +static void pd_defaultfloat(t_pd *x, t_float f) +{ + if (*(*x)->c_listmethod != pd_defaultlist) + { + t_atom at; + SETFLOAT(&at, f); + (*(*x)->c_listmethod)(x, 0, 1, &at); + } + else + { + t_atom at; + SETFLOAT(&at, f); + (*(*x)->c_anymethod)(x, &s_float, 1, &at); + } +} + +static void pd_defaultsymbol(t_pd *x, t_symbol *s) +{ + if (*(*x)->c_listmethod != pd_defaultlist) + { + t_atom at; + SETSYMBOL(&at, s); + (*(*x)->c_listmethod)(x, 0, 1, &at); + } + else + { + t_atom at; + SETSYMBOL(&at, s); + (*(*x)->c_anymethod)(x, &s_symbol, 1, &at); + } +} + +void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv); +static void class_nosavefn(t_gobj *z, t_binbuf *b); + + /* handle "list" messages to Pds without explicit list methods defined. */ +static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv) +{ + /* a list with one element which is a number can be handled by a + "float" method if any is defined; same for "symbol", "pointer". */ + if (argc == 1) + { + if (argv->a_type == A_FLOAT && + *(*x)->c_floatmethod != pd_defaultfloat) + { + (*(*x)->c_floatmethod)(x, argv->a_w.w_float); + return; + } + else if (argv->a_type == A_SYMBOL && + *(*x)->c_symbolmethod != pd_defaultsymbol) + { + (*(*x)->c_symbolmethod)(x, argv->a_w.w_symbol); + return; + } + else if (argv->a_type == A_POINTER && + *(*x)->c_pointermethod != pd_defaultpointer) + { + (*(*x)->c_pointermethod)(x, argv->a_w.w_gpointer); + return; + } + } + /* Next try for an "anything" method */ + if ((*x)->c_anymethod != pd_defaultanything) + (*(*x)->c_anymethod)(x, &s_list, argc, argv); + + /* if the object is patchable (i.e., can have proper inlets) + send it on to obj_list which will unpack the list into the inlets */ + else if ((*x)->c_patchable) + obj_list((t_object *)x, s, argc, argv); + /* otherwise gove up and complain. */ + else pd_defaultanything(x, &s_list, argc, argv); +} + + /* for now we assume that all "gobjs" are text unless explicitly + overridden later by calling class_setbehavior(). I'm not sure + how to deal with Pds that aren't gobjs; shouldn't there be a + way to check that at run time? Perhaps the presence of a "newmethod" + should be our cue, or perhaps the "tiny" flag. */ + + /* another matter. This routine does two unrelated things: it creates + a Pd class, but also adds a "new" method to create an instance of it. + These are combined for historical reasons and for brevity in writing + objects. To avoid adding a "new" method send a null function pointer. + To add additional ones, use class_addcreator below. Some "classes", like + "select", are actually two classes of the same name, one for the single- + argument form, one for the multiple one; see select_setup() to find out + how this is handled. */ + +extern t_widgetbehavior text_widgetbehavior; +extern void text_save(t_gobj *z, t_binbuf *b); + +t_class *class_new(t_symbol *s, t_newmethod newmethod, t_method freemethod, + size_t size, int flags, t_atomtype type1, ...) +{ + va_list ap; + t_atomtype vec[MAXPDARG+1], *vp = vec; + int count = 0; + t_class *c; + int typeflag = flags & CLASS_TYPEMASK; + if (!typeflag) typeflag = CLASS_PATCHABLE; + *vp = type1; + + va_start(ap, type1); + while (*vp) + { + if (count == MAXPDARG) + { + error("class %s: sorry: only %d creation args allowed", + s->s_name, MAXPDARG); + break; + } + vp++; + count++; + *vp = va_arg(ap, t_atomtype); + } + va_end(ap); + if (pd_objectmaker && newmethod) + { + /* add a "new" method by the name specified by the object */ + class_addmethod(pd_objectmaker, (t_method)newmethod, s, + vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]); + if (class_loadsym) + { + /* if we're loading an extern it might have been invoked by a + longer file name; in this case, make this an admissible name + too. */ + char *loadstring = class_loadsym->s_name, + l1 = strlen(s->s_name), l2 = strlen(loadstring); + if (l2 > l1 && !strcmp(s->s_name, loadstring + (l2 - l1))) + class_addmethod(pd_objectmaker, (t_method)newmethod, + class_loadsym, + vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]); + } + } + c = (t_class *)t_getbytes(sizeof(*c)); + c->c_name = c->c_helpname = s; + c->c_size = size; + c->c_methods = t_getbytes(0); + c->c_nmethod = 0; + c->c_freemethod = (t_method)freemethod; + c->c_bangmethod = pd_defaultbang; + c->c_pointermethod = pd_defaultpointer; + c->c_floatmethod = pd_defaultfloat; + c->c_symbolmethod = pd_defaultsymbol; + c->c_listmethod = pd_defaultlist; + c->c_anymethod = pd_defaultanything; + c->c_wb = (typeflag == CLASS_PATCHABLE ? &text_widgetbehavior : 0); + c->c_pwb = 0; + c->c_firstin = ((flags & CLASS_NOINLET) == 0); + c->c_patchable = (typeflag == CLASS_PATCHABLE); + c->c_gobj = (typeflag >= CLASS_GOBJ); + c->c_drawcommand = 0; + c->c_floatsignalin = 0; + c->c_externdir = class_extern_dir; + c->c_savefn = (typeflag == CLASS_PATCHABLE ? text_save : class_nosavefn); +#if 0 + post("class: %s", c->c_name->s_name); +#endif + return (c); +} + + /* add a creation method, which is a function that returns a Pd object + suitable for putting in an object box. We presume you've got a class it + can belong to, but this won't be used until the newmethod is actually + called back (and the new method explicitly takes care of this.) */ + +void class_addcreator(t_newmethod newmethod, t_symbol *s, + t_atomtype type1, ...) +{ + va_list ap; + t_atomtype vec[MAXPDARG+1], *vp = vec; + int count = 0; + *vp = type1; + + va_start(ap, type1); + while (*vp) + { + if (count == MAXPDARG) + { + error("class %s: sorry: only %d creation args allowed", + s->s_name, MAXPDARG); + break; + } + vp++; + count++; + *vp = va_arg(ap, t_atomtype); + } + va_end(ap); + class_addmethod(pd_objectmaker, (t_method)newmethod, s, + vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]); +} + +void class_addmethod(t_class *c, t_method fn, t_symbol *sel, + t_atomtype arg1, ...) +{ + va_list ap; + t_methodentry *m; + t_atomtype argtype = arg1; + int nargs; + + va_start(ap, arg1); + /* "signal" method specifies that we take audio signals but + that we don't want automatic float to signal conversion. This + is obsolete; you should now use the CLASS_MAINSIGNALIN macro. */ + if (sel == &s_signal) + { + if (c->c_floatsignalin) + post("warning: signal method overrides class_mainsignalin"); + c->c_floatsignalin = -1; + } + /* check for special cases. "Pointer" is missing here so that + pd_objectmaker's pointer method can be typechecked differently. */ + if (sel == &s_bang) + { + if (argtype) goto phooey; + class_addbang(c, fn); + } + else if (sel == &s_float) + { + if (argtype != A_FLOAT || va_arg(ap, t_atomtype)) goto phooey; + class_doaddfloat(c, fn); + } + else if (sel == &s_symbol) + { + if (argtype != A_SYMBOL || va_arg(ap, t_atomtype)) goto phooey; + class_addsymbol(c, fn); + } + else if (sel == &s_list) + { + if (argtype != A_GIMME) goto phooey; + class_addlist(c, fn); + } + else if (sel == &s_anything) + { + if (argtype != A_GIMME) goto phooey; + class_addanything(c, fn); + } + else + { + c->c_methods = t_resizebytes(c->c_methods, + c->c_nmethod * sizeof(*c->c_methods), + (c->c_nmethod + 1) * sizeof(*c->c_methods)); + m = c->c_methods + c->c_nmethod; + c->c_nmethod++; + m->me_name = sel; + m->me_fun = (t_gotfn)fn; + nargs = 0; + while (argtype != A_NULL && nargs < MAXPDARG) + { + m->me_arg[nargs++] = argtype; + argtype = va_arg(ap, t_atomtype); + } + if (argtype != A_NULL) + error("%s_%s: only 5 arguments are typecheckable; use A_GIMME", + c->c_name->s_name, sel->s_name); + va_end(ap); + m->me_arg[nargs] = A_NULL; + } + return; +phooey: + bug("class_addmethod: %s_%s: bad argument types\n", + c->c_name->s_name, sel->s_name); +} + + /* Instead of these, see the "class_addfloat", etc., macros in m_pd.h */ +void class_addbang(t_class *c, t_method fn) +{ + c->c_bangmethod = (t_bangmethod)fn; +} + +void class_addpointer(t_class *c, t_method fn) +{ + c->c_pointermethod = (t_pointermethod)fn; +} + +void class_doaddfloat(t_class *c, t_method fn) +{ + c->c_floatmethod = (t_floatmethod)fn; +} + +void class_addsymbol(t_class *c, t_method fn) +{ + c->c_symbolmethod = (t_symbolmethod)fn; +} + +void class_addlist(t_class *c, t_method fn) +{ + c->c_listmethod = (t_listmethod)fn; +} + +void class_addanything(t_class *c, t_method fn) +{ + c->c_anymethod = (t_anymethod)fn; +} + +void class_setwidget(t_class *c, t_widgetbehavior *w) +{ + c->c_wb = w; +} + +void class_setparentwidget(t_class *c, t_parentwidgetbehavior *pw) +{ + c->c_pwb = pw; +} + +char *class_getname(t_class *c) +{ + return (c->c_name->s_name); +} + +char *class_gethelpname(t_class *c) +{ + return (c->c_helpname->s_name); +} + +void class_sethelpsymbol(t_class *c, t_symbol *s) +{ + c->c_helpname = s; +} + +t_parentwidgetbehavior *pd_getparentwidget(t_pd *x) +{ + return ((*x)->c_pwb); +} + +void class_setdrawcommand(t_class *c) +{ + c->c_drawcommand = 1; +} + +int class_isdrawcommand(t_class *c) +{ + return (c->c_drawcommand); +} + +static void pd_floatforsignal(t_pd *x, t_float f) +{ + int offset = (*x)->c_floatsignalin; + if (offset > 0) + *(t_sample *)(((char *)x) + offset) = ftofix(f); + else + pd_error(x, "%s: float unexpected for signal input", + (*x)->c_name->s_name); +} + +void class_domainsignalin(t_class *c, int onset) +{ + if (onset <= 0) onset = -1; + else + { + if (c->c_floatmethod != pd_defaultfloat) + post("warning: %s: float method overwritten", c->c_name->s_name); + c->c_floatmethod = (t_floatmethod)pd_floatforsignal; + } + c->c_floatsignalin = onset; +} + +void class_set_extern_dir(t_symbol *s) +{ + class_extern_dir = s; +} + +char *class_gethelpdir(t_class *c) +{ + return (c->c_externdir->s_name); +} + +static void class_nosavefn(t_gobj *z, t_binbuf *b) +{ + bug("save function called but not defined"); +} + +void class_setsavefn(t_class *c, t_savefn f) +{ + c->c_savefn = f; +} + +t_savefn class_getsavefn(t_class *c) +{ + return (c->c_savefn); +} + +void class_setpropertiesfn(t_class *c, t_propertiesfn f) +{ + c->c_propertiesfn = f; +} + +t_propertiesfn class_getpropertiesfn(t_class *c) +{ + return (c->c_propertiesfn); +} + +/* ---------------- the symbol table ------------------------ */ + +#define HASHSIZE 1024 + +static t_symbol *symhash[HASHSIZE]; + +t_symbol *dogensym(char *s, t_symbol *oldsym) +{ + t_symbol **sym1, *sym2; + unsigned int hash1 = 0, hash2 = 0; + int length = 0; + char *s2 = s; + while (*s2) + { + hash1 += *s2; + hash2 += hash1; + length++; + s2++; + } + sym1 = symhash + (hash2 & (HASHSIZE-1)); + while (sym2 = *sym1) + { + if (!strcmp(sym2->s_name, s)) return(sym2); + sym1 = &sym2->s_next; + } + if (oldsym) sym2 = oldsym; + else + { + sym2 = (t_symbol *)t_getbytes(sizeof(*sym2)); + sym2->s_name = t_getbytes(length+1); + sym2->s_next = 0; + sym2->s_thing = 0; + strcpy(sym2->s_name, s); + } + *sym1 = sym2; + return (sym2); +} + +t_symbol *gensym(char *s) +{ + return(dogensym(s, 0)); +} + +static t_symbol *addfileextent(t_symbol *s) +{ + char namebuf[MAXPDSTRING], *str = s->s_name; + int ln = strlen(str); + if (!strcmp(str + ln - 3, ".pd")) return (s); + strcpy(namebuf, str); + strcpy(namebuf+ln, ".pd"); + return (gensym(namebuf)); +} + +static int tryingalready; + +void canvas_popabstraction(t_canvas *x); +extern t_pd *newest; + +t_symbol* pathsearch(t_symbol *s,char* ext); +int pd_setloadingabstraction(t_symbol *sym); + + /* this routine is called when a new "object" is requested whose class Pd + doesn't know. Pd tries to load it as an extern, then as an abstraction. */ +void new_anything(void *dummy, t_symbol *s, int argc, t_atom *argv) +{ + t_pd *current; + t_symbol *dir = canvas_getcurrentdir(); + int fd; + char dirbuf[MAXPDSTRING], *nameptr; + if (tryingalready) return; + newest = 0; + class_loadsym = s; + if (sys_load_lib(dir->s_name, s->s_name)) + { + tryingalready = 1; + typedmess(dummy, s, argc, argv); + tryingalready = 0; + return; + } + class_loadsym = 0; + current = s__X.s_thing; + if ((fd = open_via_path(dir->s_name, s->s_name, ".pd", + dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0 || + (fd = open_via_path(dir->s_name, s->s_name, ".pat", + dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0) + { + close (fd); + if (!pd_setloadingabstraction(s)) + { + canvas_setargs(argc, argv); /* bug fix by Krzysztof Czaja */ + binbuf_evalfile(gensym(nameptr), gensym(dirbuf)); + if (s__X.s_thing != current) + canvas_popabstraction((t_canvas *)(s__X.s_thing)); + canvas_setargs(0, 0); + } + else error("%s: can't load abstraction within itself\n", s->s_name); + } + else newest = 0; +} + +t_symbol s_pointer = {"pointer", 0, 0}; +t_symbol s_float = {"float", 0, 0}; +t_symbol s_symbol = {"symbol", 0, 0}; +t_symbol s_bang = {"bang", 0, 0}; +t_symbol s_list = {"list", 0, 0}; +t_symbol s_anything = {"anything", 0, 0}; +t_symbol s_signal = {"signal", 0, 0}; +t_symbol s__N = {"#N", 0, 0}; +t_symbol s__X = {"#X", 0, 0}; +t_symbol s_x = {"x", 0, 0}; +t_symbol s_y = {"y", 0, 0}; +t_symbol s_ = {"", 0, 0}; + +static t_symbol *symlist[] = { &s_pointer, &s_float, &s_symbol, &s_bang, + &s_list, &s_anything, &s_signal, &s__N, &s__X, &s_x, &s_y, &s_}; + +void mess_init(void) +{ + t_symbol **sp; + int i; + + if (pd_objectmaker) return; + for (i = sizeof(symlist)/sizeof(*symlist), sp = symlist; i--; sp++) + (void) dogensym((*sp)->s_name, *sp); + pd_objectmaker = class_new(gensym("objectmaker"), 0, 0, sizeof(t_pd), + CLASS_DEFAULT, A_NULL); + pd_canvasmaker = class_new(gensym("classmaker"), 0, 0, sizeof(t_pd), + CLASS_DEFAULT, A_NULL); + pd_bind(&pd_canvasmaker, &s__N); + class_addanything(pd_objectmaker, (t_method)new_anything); +} + +t_pd *newest; + +/* This is externally available, but note that it might later disappear; the +whole "newest" thing is a hack which needs to be redesigned. */ +t_pd *pd_newest(void) +{ + return (newest); +} + + /* horribly, we need prototypes for each of the artificial function + calls in typedmess(), to keep the compiler quiet. */ +typedef t_pd *(*t_newgimme)(t_symbol *s, int argc, t_atom *argv); +typedef void(*t_messgimme)(t_pd *x, t_symbol *s, int argc, t_atom *argv); + +typedef t_pd *(*t_fun0)( + t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); +typedef t_pd *(*t_fun1)(t_int i1, + t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); +typedef t_pd *(*t_fun2)(t_int i1, t_int i2, + t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); +typedef t_pd *(*t_fun3)(t_int i1, t_int i2, t_int i3, + t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); +typedef t_pd *(*t_fun4)(t_int i1, t_int i2, t_int i3, t_int i4, + t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); +typedef t_pd *(*t_fun5)(t_int i1, t_int i2, t_int i3, t_int i4, t_int i5, + t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); +typedef t_pd *(*t_fun6)(t_int i1, t_int i2, t_int i3, t_int i4, t_int i5, t_int i6, + t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); + +void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv) +{ + t_method *f; + t_class *c = *x; + t_methodentry *m; + t_atomtype *wp, wanttype; + int i; + t_int ai[MAXPDARG+1], *ap = ai; + t_floatarg ad[MAXPDARG+1], *dp = ad; + int narg = 0; + t_pd *bonzo; + + /* check for messages that are handled by fixed slots in the class + structure. We don't catch "pointer" though so that sending "pointer" + to pd_objectmaker doesn't require that we supply a pointer value. */ + if (s == &s_float) + { + if (!argc) (*c->c_floatmethod)(x, 0.); + else if (argv->a_type == A_FLOAT) + (*c->c_floatmethod)(x, argv->a_w.w_float); + else goto badarg; + return; + } + if (s == &s_bang) + { + (*c->c_bangmethod)(x); + return; + } + if (s == &s_list) + { + (*c->c_listmethod)(x, s, argc, argv); + return; + } + if (s == &s_symbol) + { + if (argc && argv->a_type == A_SYMBOL) + (*c->c_symbolmethod)(x, argv->a_w.w_symbol); + else + (*c->c_symbolmethod)(x, &s_); + return; + } + for (i = c->c_nmethod, m = c->c_methods; i--; m++) + if (m->me_name == s) + { + wp = m->me_arg; + if (*wp == A_GIMME) + { + if (x == &pd_objectmaker) + newest = (*((t_newgimme)(m->me_fun)))(s, argc, argv); + else (*((t_messgimme)(m->me_fun)))(x, s, argc, argv); + return; + } + if (argc > MAXPDARG) argc = MAXPDARG; + if (x != &pd_objectmaker) *(ap++) = (t_int)x, narg++; + while (wanttype = *wp++) + { + switch (wanttype) + { + case A_POINTER: + if (!argc) goto badarg; + else + { + if (argv->a_type == A_POINTER) + *ap = (t_int)(argv->a_w.w_gpointer); + else goto badarg; + argc--; + argv++; + } + narg++; + ap++; + break; + case A_FLOAT: + if (!argc) goto badarg; + case A_DEFFLOAT: + if (!argc) *dp = 0; + else + { + if (argv->a_type == A_FLOAT) + *dp = argv->a_w.w_float; + else goto badarg; + argc--; + argv++; + } + dp++; + break; + case A_SYMBOL: + if (!argc) goto badarg; + case A_DEFSYM: + if (!argc) *ap = (t_int)(&s_); + else + { + if (argv->a_type == A_SYMBOL) + *ap = (t_int)(argv->a_w.w_symbol); + /* if it's an unfilled "dollar" argument it appears + as zero here; cheat and bash it to the null + symbol. Unfortunately, this lets real zeros + pass as symbols too, which seems wrong... */ + else if (x == &pd_objectmaker && argv->a_type == A_FLOAT + && argv->a_w.w_float == 0) + *ap = (t_int)(&s_); + else goto badarg; + argc--; + argv++; + } + narg++; + ap++; + } + } + switch (narg) + { + case 0 : bonzo = (*(t_fun0)(m->me_fun)) + (ad[0], ad[1], ad[2], ad[3], ad[4]); break; + case 1 : bonzo = (*(t_fun1)(m->me_fun)) + (ai[0], ad[0], ad[1], ad[2], ad[3], ad[4]); break; + case 2 : bonzo = (*(t_fun2)(m->me_fun)) + (ai[0], ai[1], ad[0], ad[1], ad[2], ad[3], ad[4]); break; + case 3 : bonzo = (*(t_fun3)(m->me_fun)) + (ai[0], ai[1], ai[2], ad[0], ad[1], ad[2], ad[3], ad[4]); break; + case 4 : bonzo = (*(t_fun4)(m->me_fun)) + (ai[0], ai[1], ai[2], ai[3], + ad[0], ad[1], ad[2], ad[3], ad[4]); break; + case 5 : bonzo = (*(t_fun5)(m->me_fun)) + (ai[0], ai[1], ai[2], ai[3], ai[4], + ad[0], ad[1], ad[2], ad[3], ad[4]); break; + case 6 : bonzo = (*(t_fun6)(m->me_fun)) + (ai[0], ai[1], ai[2], ai[3], ai[4], ai[5], + ad[0], ad[1], ad[2], ad[3], ad[4]); break; + default: bonzo = 0; + } + if (x == &pd_objectmaker) + newest = bonzo; + return; + } + (*c->c_anymethod)(x, s, argc, argv); + return; +badarg: + pd_error(x, "Bad arguments for message '%s' to object '%s'", + s->s_name, c->c_name->s_name); +} + +void pd_vmess(t_pd *x, t_symbol *sel, char *fmt, ...) +{ + va_list ap; + t_atom arg[MAXPDARG], *at =arg; + int nargs = 0; + char *fp = fmt; + + va_start(ap, fmt); + while (1) + { + if (nargs > MAXPDARG) + { + pd_error(x, "pd_vmess: only %d allowed", MAXPDARG); + break; + } + switch(*fp++) + { + case 'f': SETFLOAT(at, va_arg(ap, double)); break; + case 's': SETSYMBOL(at, va_arg(ap, t_symbol *)); break; + case 'i': SETFLOAT(at, va_arg(ap, t_int)); break; + case 'p': SETPOINTER(at, va_arg(ap, t_gpointer *)); break; + default: goto done; + } + at++; + nargs++; + } +done: + va_end(ap); + typedmess(x, sel, nargs, arg); +} + +void pd_forwardmess(t_pd *x, int argc, t_atom *argv) +{ + if (argc) + { + t_atomtype t = argv->a_type; + if (t == A_SYMBOL) pd_typedmess(x, argv->a_w.w_symbol, argc-1, argv+1); + else if (t == A_POINTER) + { + if (argc == 1) pd_pointer(x, argv->a_w.w_gpointer); + else pd_list(x, &s_list, argc, argv); + } + else if (t == A_FLOAT) + { + if (argc == 1) pd_float(x, argv->a_w.w_float); + else pd_list(x, &s_list, argc, argv); + } + else bug("pd_forwardmess"); + } + +} + +void nullfn(void) {} + +t_gotfn getfn(t_pd *x, t_symbol *s) +{ + t_class *c = *x; + t_methodentry *m; + int i; + + for (i = c->c_nmethod, m = c->c_methods; i--; m++) + if (m->me_name == s) return(m->me_fun); + pd_error(x, "%s: no method for message '%s'", c->c_name->s_name, s->s_name); + return((t_gotfn)nullfn); +} + +t_gotfn zgetfn(t_pd *x, t_symbol *s) +{ + t_class *c = *x; + t_methodentry *m; + int i; + + for (i = c->c_nmethod, m = c->c_methods; i--; m++) + if (m->me_name == s) return(m->me_fun); + return(0); +} +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#define PD_CLASS_DEF +#include "m_pd.h" +#include "m_imp.h" +#include "s_stuff.h" +#include +#ifdef UNIX +#include +#endif +#ifdef MSW +#include +#endif + +#include +#include + +static t_symbol *class_loadsym; /* name under which an extern is invoked */ +static void pd_defaultfloat(t_pd *x, t_float f); +static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv); +t_pd pd_objectmaker; /* factory for creating "object" boxes */ +t_pd pd_canvasmaker; /* factory for creating canvases */ + +static t_symbol *class_extern_dir = &s_; + +static void pd_defaultanything(t_pd *x, t_symbol *s, int argc, t_atom *argv) +{ + pd_error(x, "%s: no method for '%s'", (*x)->c_name->s_name, s->s_name); +} + +static void pd_defaultbang(t_pd *x) +{ + if (*(*x)->c_listmethod != pd_defaultlist) + (*(*x)->c_listmethod)(x, 0, 0, 0); + else (*(*x)->c_anymethod)(x, &s_bang, 0, 0); +} + +static void pd_defaultpointer(t_pd *x, t_gpointer *gp) +{ + if (*(*x)->c_listmethod != pd_defaultlist) + { + t_atom at; + SETPOINTER(&at, gp); + (*(*x)->c_listmethod)(x, 0, 1, &at); + } + else + { + t_atom at; + SETPOINTER(&at, gp); + (*(*x)->c_anymethod)(x, &s_pointer, 1, &at); + } +} + +static void pd_defaultfloat(t_pd *x, t_float f) +{ + if (*(*x)->c_listmethod != pd_defaultlist) + { + t_atom at; + SETFLOAT(&at, f); + (*(*x)->c_listmethod)(x, 0, 1, &at); + } + else + { + t_atom at; + SETFLOAT(&at, f); + (*(*x)->c_anymethod)(x, &s_float, 1, &at); + } +} + +static void pd_defaultsymbol(t_pd *x, t_symbol *s) +{ + if (*(*x)->c_listmethod != pd_defaultlist) + { + t_atom at; + SETSYMBOL(&at, s); + (*(*x)->c_listmethod)(x, 0, 1, &at); + } + else + { + t_atom at; + SETSYMBOL(&at, s); + (*(*x)->c_anymethod)(x, &s_symbol, 1, &at); + } +} + +void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv); +static void class_nosavefn(t_gobj *z, t_binbuf *b); + + /* handle "list" messages to Pds without explicit list methods defined. */ +static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv) +{ + /* a list with one element which is a number can be handled by a + "float" method if any is defined; same for "symbol", "pointer". */ + if (argc == 1) + { + if (argv->a_type == A_FLOAT && + *(*x)->c_floatmethod != pd_defaultfloat) + { + (*(*x)->c_floatmethod)(x, argv->a_w.w_float); + return; + } + else if (argv->a_type == A_SYMBOL && + *(*x)->c_symbolmethod != pd_defaultsymbol) + { + (*(*x)->c_symbolmethod)(x, argv->a_w.w_symbol); + return; + } + else if (argv->a_type == A_POINTER && + *(*x)->c_pointermethod != pd_defaultpointer) + { + (*(*x)->c_pointermethod)(x, argv->a_w.w_gpointer); + return; + } + } + /* Next try for an "anything" method */ + if ((*x)->c_anymethod != pd_defaultanything) + (*(*x)->c_anymethod)(x, &s_list, argc, argv); + + /* if the object is patchable (i.e., can have proper inlets) + send it on to obj_list which will unpack the list into the inlets */ + else if ((*x)->c_patchable) + obj_list((t_object *)x, s, argc, argv); + /* otherwise gove up and complain. */ + else pd_defaultanything(x, &s_list, argc, argv); +} + + /* for now we assume that all "gobjs" are text unless explicitly + overridden later by calling class_setbehavior(). I'm not sure + how to deal with Pds that aren't gobjs; shouldn't there be a + way to check that at run time? Perhaps the presence of a "newmethod" + should be our cue, or perhaps the "tiny" flag. */ + + /* another matter. This routine does two unrelated things: it creates + a Pd class, but also adds a "new" method to create an instance of it. + These are combined for historical reasons and for brevity in writing + objects. To avoid adding a "new" method send a null function pointer. + To add additional ones, use class_addcreator below. Some "classes", like + "select", are actually two classes of the same name, one for the single- + argument form, one for the multiple one; see select_setup() to find out + how this is handled. */ + +extern t_widgetbehavior text_widgetbehavior; +extern void text_save(t_gobj *z, t_binbuf *b); + +t_class *class_new(t_symbol *s, t_newmethod newmethod, t_method freemethod, + size_t size, int flags, t_atomtype type1, ...) +{ + va_list ap; + t_atomtype vec[MAXPDARG+1], *vp = vec; + int count = 0; + t_class *c; + int typeflag = flags & CLASS_TYPEMASK; + if (!typeflag) typeflag = CLASS_PATCHABLE; + *vp = type1; + + va_start(ap, type1); + while (*vp) + { + if (count == MAXPDARG) + { + error("class %s: sorry: only %d creation args allowed", + s->s_name, MAXPDARG); + break; + } + vp++; + count++; + *vp = va_arg(ap, t_atomtype); + } + va_end(ap); + if (pd_objectmaker && newmethod) + { + /* add a "new" method by the name specified by the object */ + class_addmethod(pd_objectmaker, (t_method)newmethod, s, + vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]); + if (class_loadsym) + { + /* if we're loading an extern it might have been invoked by a + longer file name; in this case, make this an admissible name + too. */ + char *loadstring = class_loadsym->s_name, + l1 = strlen(s->s_name), l2 = strlen(loadstring); + if (l2 > l1 && !strcmp(s->s_name, loadstring + (l2 - l1))) + class_addmethod(pd_objectmaker, (t_method)newmethod, + class_loadsym, + vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]); + } + } + c = (t_class *)t_getbytes(sizeof(*c)); + c->c_name = c->c_helpname = s; + c->c_size = size; + c->c_methods = t_getbytes(0); + c->c_nmethod = 0; + c->c_freemethod = (t_method)freemethod; + c->c_bangmethod = pd_defaultbang; + c->c_pointermethod = pd_defaultpointer; + c->c_floatmethod = pd_defaultfloat; + c->c_symbolmethod = pd_defaultsymbol; + c->c_listmethod = pd_defaultlist; + c->c_anymethod = pd_defaultanything; + c->c_wb = (typeflag == CLASS_PATCHABLE ? &text_widgetbehavior : 0); + c->c_pwb = 0; + c->c_firstin = ((flags & CLASS_NOINLET) == 0); + c->c_patchable = (typeflag == CLASS_PATCHABLE); + c->c_gobj = (typeflag >= CLASS_GOBJ); + c->c_drawcommand = 0; + c->c_floatsignalin = 0; + c->c_externdir = class_extern_dir; + c->c_savefn = (typeflag == CLASS_PATCHABLE ? text_save : class_nosavefn); +#if 0 + post("class: %s", c->c_name->s_name); +#endif + return (c); +} + + /* add a creation method, which is a function that returns a Pd object + suitable for putting in an object box. We presume you've got a class it + can belong to, but this won't be used until the newmethod is actually + called back (and the new method explicitly takes care of this.) */ + +void class_addcreator(t_newmethod newmethod, t_symbol *s, + t_atomtype type1, ...) +{ + va_list ap; + t_atomtype vec[MAXPDARG+1], *vp = vec; + int count = 0; + *vp = type1; + + va_start(ap, type1); + while (*vp) + { + if (count == MAXPDARG) + { + error("class %s: sorry: only %d creation args allowed", + s->s_name, MAXPDARG); + break; + } + vp++; + count++; + *vp = va_arg(ap, t_atomtype); + } + va_end(ap); + class_addmethod(pd_objectmaker, (t_method)newmethod, s, + vec[0], vec[1], vec[2], vec[3], vec[4], vec[5]); +} + +void class_addmethod(t_class *c, t_method fn, t_symbol *sel, + t_atomtype arg1, ...) +{ + va_list ap; + t_methodentry *m; + t_atomtype argtype = arg1; + int nargs; + + va_start(ap, arg1); + /* "signal" method specifies that we take audio signals but + that we don't want automatic float to signal conversion. This + is obsolete; you should now use the CLASS_MAINSIGNALIN macro. */ + if (sel == &s_signal) + { + if (c->c_floatsignalin) + post("warning: signal method overrides class_mainsignalin"); + c->c_floatsignalin = -1; + } + /* check for special cases. "Pointer" is missing here so that + pd_objectmaker's pointer method can be typechecked differently. */ + if (sel == &s_bang) + { + if (argtype) goto phooey; + class_addbang(c, fn); + } + else if (sel == &s_float) + { + if (argtype != A_FLOAT || va_arg(ap, t_atomtype)) goto phooey; + class_doaddfloat(c, fn); + } + else if (sel == &s_symbol) + { + if (argtype != A_SYMBOL || va_arg(ap, t_atomtype)) goto phooey; + class_addsymbol(c, fn); + } + else if (sel == &s_list) + { + if (argtype != A_GIMME) goto phooey; + class_addlist(c, fn); + } + else if (sel == &s_anything) + { + if (argtype != A_GIMME) goto phooey; + class_addanything(c, fn); + } + else + { + c->c_methods = t_resizebytes(c->c_methods, + c->c_nmethod * sizeof(*c->c_methods), + (c->c_nmethod + 1) * sizeof(*c->c_methods)); + m = c->c_methods + c->c_nmethod; + c->c_nmethod++; + m->me_name = sel; + m->me_fun = (t_gotfn)fn; + nargs = 0; + while (argtype != A_NULL && nargs < MAXPDARG) + { + m->me_arg[nargs++] = argtype; + argtype = va_arg(ap, t_atomtype); + } + if (argtype != A_NULL) + error("%s_%s: only 5 arguments are typecheckable; use A_GIMME", + c->c_name->s_name, sel->s_name); + va_end(ap); + m->me_arg[nargs] = A_NULL; + } + return; +phooey: + bug("class_addmethod: %s_%s: bad argument types\n", + c->c_name->s_name, sel->s_name); +} + + /* Instead of these, see the "class_addfloat", etc., macros in m_pd.h */ +void class_addbang(t_class *c, t_method fn) +{ + c->c_bangmethod = (t_bangmethod)fn; +} + +void class_addpointer(t_class *c, t_method fn) +{ + c->c_pointermethod = (t_pointermethod)fn; +} + +void class_doaddfloat(t_class *c, t_method fn) +{ + c->c_floatmethod = (t_floatmethod)fn; +} + +void class_addsymbol(t_class *c, t_method fn) +{ + c->c_symbolmethod = (t_symbolmethod)fn; +} + +void class_addlist(t_class *c, t_method fn) +{ + c->c_listmethod = (t_listmethod)fn; +} + +void class_addanything(t_class *c, t_method fn) +{ + c->c_anymethod = (t_anymethod)fn; +} + +void class_setwidget(t_class *c, t_widgetbehavior *w) +{ + c->c_wb = w; +} + +void class_setparentwidget(t_class *c, t_parentwidgetbehavior *pw) +{ + c->c_pwb = pw; +} + +char *class_getname(t_class *c) +{ + return (c->c_name->s_name); +} + +char *class_gethelpname(t_class *c) +{ + return (c->c_helpname->s_name); +} + +void class_sethelpsymbol(t_class *c, t_symbol *s) +{ + c->c_helpname = s; +} + +t_parentwidgetbehavior *pd_getparentwidget(t_pd *x) +{ + return ((*x)->c_pwb); +} + +void class_setdrawcommand(t_class *c) +{ + c->c_drawcommand = 1; +} + +int class_isdrawcommand(t_class *c) +{ + return (c->c_drawcommand); +} + +static void pd_floatforsignal(t_pd *x, t_float f) +{ + int offset = (*x)->c_floatsignalin; + if (offset > 0) + *(t_sample *)(((char *)x) + offset) = ftofix(f); + else + pd_error(x, "%s: float unexpected for signal input", + (*x)->c_name->s_name); +} + +void class_domainsignalin(t_class *c, int onset) +{ + if (onset <= 0) onset = -1; + else + { + if (c->c_floatmethod != pd_defaultfloat) + post("warning: %s: float method overwritten", c->c_name->s_name); + c->c_floatmethod = (t_floatmethod)pd_floatforsignal; + } + c->c_floatsignalin = onset; +} + +void class_set_extern_dir(t_symbol *s) +{ + class_extern_dir = s; +} + +char *class_gethelpdir(t_class *c) +{ + return (c->c_externdir->s_name); +} + +static void class_nosavefn(t_gobj *z, t_binbuf *b) +{ + bug("save function called but not defined"); +} + +void class_setsavefn(t_class *c, t_savefn f) +{ + c->c_savefn = f; +} + +t_savefn class_getsavefn(t_class *c) +{ + return (c->c_savefn); +} + +void class_setpropertiesfn(t_class *c, t_propertiesfn f) +{ + c->c_propertiesfn = f; +} + +t_propertiesfn class_getpropertiesfn(t_class *c) +{ + return (c->c_propertiesfn); +} + +/* ---------------- the symbol table ------------------------ */ + +#define HASHSIZE 1024 + +static t_symbol *symhash[HASHSIZE]; + +t_symbol *dogensym(char *s, t_symbol *oldsym) +{ + t_symbol **sym1, *sym2; + unsigned int hash1 = 0, hash2 = 0; + int length = 0; + char *s2 = s; + while (*s2) + { + hash1 += *s2; + hash2 += hash1; + length++; + s2++; + } + sym1 = symhash + (hash2 & (HASHSIZE-1)); + while (sym2 = *sym1) + { + if (!strcmp(sym2->s_name, s)) return(sym2); + sym1 = &sym2->s_next; + } + if (oldsym) sym2 = oldsym; + else + { + sym2 = (t_symbol *)t_getbytes(sizeof(*sym2)); + sym2->s_name = t_getbytes(length+1); + sym2->s_next = 0; + sym2->s_thing = 0; + strcpy(sym2->s_name, s); + } + *sym1 = sym2; + return (sym2); +} + +t_symbol *gensym(char *s) +{ + return(dogensym(s, 0)); +} + +static t_symbol *addfileextent(t_symbol *s) +{ + char namebuf[MAXPDSTRING], *str = s->s_name; + int ln = strlen(str); + if (!strcmp(str + ln - 3, ".pd")) return (s); + strcpy(namebuf, str); + strcpy(namebuf+ln, ".pd"); + return (gensym(namebuf)); +} + +static int tryingalready; + +void canvas_popabstraction(t_canvas *x); +extern t_pd *newest; + +t_symbol* pathsearch(t_symbol *s,char* ext); +int pd_setloadingabstraction(t_symbol *sym); + + /* this routine is called when a new "object" is requested whose class Pd + doesn't know. Pd tries to load it as an extern, then as an abstraction. */ +void new_anything(void *dummy, t_symbol *s, int argc, t_atom *argv) +{ + t_pd *current; + t_symbol *dir = canvas_getcurrentdir(); + int fd; + char dirbuf[MAXPDSTRING], *nameptr; + if (tryingalready) return; + newest = 0; + class_loadsym = s; + if (sys_load_lib(dir->s_name, s->s_name)) + { + tryingalready = 1; + typedmess(dummy, s, argc, argv); + tryingalready = 0; + return; + } + class_loadsym = 0; + current = s__X.s_thing; + if ((fd = open_via_path(dir->s_name, s->s_name, ".pd", + dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0 || + (fd = open_via_path(dir->s_name, s->s_name, ".pat", + dirbuf, &nameptr, MAXPDSTRING, 0)) >= 0) + { + close (fd); + if (!pd_setloadingabstraction(s)) + { + canvas_setargs(argc, argv); /* bug fix by Krzysztof Czaja */ + binbuf_evalfile(gensym(nameptr), gensym(dirbuf)); + if (s__X.s_thing != current) + canvas_popabstraction((t_canvas *)(s__X.s_thing)); + canvas_setargs(0, 0); + } + else error("%s: can't load abstraction within itself\n", s->s_name); + } + else newest = 0; +} + +t_symbol s_pointer = {"pointer", 0, 0}; +t_symbol s_float = {"float", 0, 0}; +t_symbol s_symbol = {"symbol", 0, 0}; +t_symbol s_bang = {"bang", 0, 0}; +t_symbol s_list = {"list", 0, 0}; +t_symbol s_anything = {"anything", 0, 0}; +t_symbol s_signal = {"signal", 0, 0}; +t_symbol s__N = {"#N", 0, 0}; +t_symbol s__X = {"#X", 0, 0}; +t_symbol s_x = {"x", 0, 0}; +t_symbol s_y = {"y", 0, 0}; +t_symbol s_ = {"", 0, 0}; + +static t_symbol *symlist[] = { &s_pointer, &s_float, &s_symbol, &s_bang, + &s_list, &s_anything, &s_signal, &s__N, &s__X, &s_x, &s_y, &s_}; + +void mess_init(void) +{ + t_symbol **sp; + int i; + + if (pd_objectmaker) return; + for (i = sizeof(symlist)/sizeof(*symlist), sp = symlist; i--; sp++) + (void) dogensym((*sp)->s_name, *sp); + pd_objectmaker = class_new(gensym("objectmaker"), 0, 0, sizeof(t_pd), + CLASS_DEFAULT, A_NULL); + pd_canvasmaker = class_new(gensym("classmaker"), 0, 0, sizeof(t_pd), + CLASS_DEFAULT, A_NULL); + pd_bind(&pd_canvasmaker, &s__N); + class_addanything(pd_objectmaker, (t_method)new_anything); +} + +t_pd *newest; + +/* This is externally available, but note that it might later disappear; the +whole "newest" thing is a hack which needs to be redesigned. */ +t_pd *pd_newest(void) +{ + return (newest); +} + + /* horribly, we need prototypes for each of the artificial function + calls in typedmess(), to keep the compiler quiet. */ +typedef t_pd *(*t_newgimme)(t_symbol *s, int argc, t_atom *argv); +typedef void(*t_messgimme)(t_pd *x, t_symbol *s, int argc, t_atom *argv); + +typedef t_pd *(*t_fun0)( + t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); +typedef t_pd *(*t_fun1)(t_int i1, + t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); +typedef t_pd *(*t_fun2)(t_int i1, t_int i2, + t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); +typedef t_pd *(*t_fun3)(t_int i1, t_int i2, t_int i3, + t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); +typedef t_pd *(*t_fun4)(t_int i1, t_int i2, t_int i3, t_int i4, + t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); +typedef t_pd *(*t_fun5)(t_int i1, t_int i2, t_int i3, t_int i4, t_int i5, + t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); +typedef t_pd *(*t_fun6)(t_int i1, t_int i2, t_int i3, t_int i4, t_int i5, t_int i6, + t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5); + +void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv) +{ + t_method *f; + t_class *c = *x; + t_methodentry *m; + t_atomtype *wp, wanttype; + int i; + t_int ai[MAXPDARG+1], *ap = ai; + t_floatarg ad[MAXPDARG+1], *dp = ad; + int narg = 0; + t_pd *bonzo; + + /* check for messages that are handled by fixed slots in the class + structure. We don't catch "pointer" though so that sending "pointer" + to pd_objectmaker doesn't require that we supply a pointer value. */ + if (s == &s_float) + { + if (!argc) (*c->c_floatmethod)(x, 0.); + else if (argv->a_type == A_FLOAT) + (*c->c_floatmethod)(x, argv->a_w.w_float); + else goto badarg; + return; + } + if (s == &s_bang) + { + (*c->c_bangmethod)(x); + return; + } + if (s == &s_list) + { + (*c->c_listmethod)(x, s, argc, argv); + return; + } + if (s == &s_symbol) + { + if (argc && argv->a_type == A_SYMBOL) + (*c->c_symbolmethod)(x, argv->a_w.w_symbol); + else + (*c->c_symbolmethod)(x, &s_); + return; + } + for (i = c->c_nmethod, m = c->c_methods; i--; m++) + if (m->me_name == s) + { + wp = m->me_arg; + if (*wp == A_GIMME) + { + if (x == &pd_objectmaker) + newest = (*((t_newgimme)(m->me_fun)))(s, argc, argv); + else (*((t_messgimme)(m->me_fun)))(x, s, argc, argv); + return; + } + if (argc > MAXPDARG) argc = MAXPDARG; + if (x != &pd_objectmaker) *(ap++) = (t_int)x, narg++; + while (wanttype = *wp++) + { + switch (wanttype) + { + case A_POINTER: + if (!argc) goto badarg; + else + { + if (argv->a_type == A_POINTER) + *ap = (t_int)(argv->a_w.w_gpointer); + else goto badarg; + argc--; + argv++; + } + narg++; + ap++; + break; + case A_FLOAT: + if (!argc) goto badarg; + case A_DEFFLOAT: + if (!argc) *dp = 0; + else + { + if (argv->a_type == A_FLOAT) + *dp = argv->a_w.w_float; + else goto badarg; + argc--; + argv++; + } + dp++; + break; + case A_SYMBOL: + if (!argc) goto badarg; + case A_DEFSYM: + if (!argc) *ap = (t_int)(&s_); + else + { + if (argv->a_type == A_SYMBOL) + *ap = (t_int)(argv->a_w.w_symbol); + /* if it's an unfilled "dollar" argument it appears + as zero here; cheat and bash it to the null + symbol. Unfortunately, this lets real zeros + pass as symbols too, which seems wrong... */ + else if (x == &pd_objectmaker && argv->a_type == A_FLOAT + && argv->a_w.w_float == 0) + *ap = (t_int)(&s_); + else goto badarg; + argc--; + argv++; + } + narg++; + ap++; + } + } + switch (narg) + { + case 0 : bonzo = (*(t_fun0)(m->me_fun)) + (ad[0], ad[1], ad[2], ad[3], ad[4]); break; + case 1 : bonzo = (*(t_fun1)(m->me_fun)) + (ai[0], ad[0], ad[1], ad[2], ad[3], ad[4]); break; + case 2 : bonzo = (*(t_fun2)(m->me_fun)) + (ai[0], ai[1], ad[0], ad[1], ad[2], ad[3], ad[4]); break; + case 3 : bonzo = (*(t_fun3)(m->me_fun)) + (ai[0], ai[1], ai[2], ad[0], ad[1], ad[2], ad[3], ad[4]); break; + case 4 : bonzo = (*(t_fun4)(m->me_fun)) + (ai[0], ai[1], ai[2], ai[3], + ad[0], ad[1], ad[2], ad[3], ad[4]); break; + case 5 : bonzo = (*(t_fun5)(m->me_fun)) + (ai[0], ai[1], ai[2], ai[3], ai[4], + ad[0], ad[1], ad[2], ad[3], ad[4]); break; + case 6 : bonzo = (*(t_fun6)(m->me_fun)) + (ai[0], ai[1], ai[2], ai[3], ai[4], ai[5], + ad[0], ad[1], ad[2], ad[3], ad[4]); break; + default: bonzo = 0; + } + if (x == &pd_objectmaker) + newest = bonzo; + return; + } + (*c->c_anymethod)(x, s, argc, argv); + return; +badarg: + pd_error(x, "Bad arguments for message '%s' to object '%s'", + s->s_name, c->c_name->s_name); +} + +void pd_vmess(t_pd *x, t_symbol *sel, char *fmt, ...) +{ + va_list ap; + t_atom arg[MAXPDARG], *at =arg; + int nargs = 0; + char *fp = fmt; + + va_start(ap, fmt); + while (1) + { + if (nargs > MAXPDARG) + { + pd_error(x, "pd_vmess: only %d allowed", MAXPDARG); + break; + } + switch(*fp++) + { + case 'f': SETFLOAT(at, va_arg(ap, double)); break; + case 's': SETSYMBOL(at, va_arg(ap, t_symbol *)); break; + case 'i': SETFLOAT(at, va_arg(ap, t_int)); break; + case 'p': SETPOINTER(at, va_arg(ap, t_gpointer *)); break; + default: goto done; + } + at++; + nargs++; + } +done: + va_end(ap); + typedmess(x, sel, nargs, arg); +} + +void pd_forwardmess(t_pd *x, int argc, t_atom *argv) +{ + if (argc) + { + t_atomtype t = argv->a_type; + if (t == A_SYMBOL) pd_typedmess(x, argv->a_w.w_symbol, argc-1, argv+1); + else if (t == A_POINTER) + { + if (argc == 1) pd_pointer(x, argv->a_w.w_gpointer); + else pd_list(x, &s_list, argc, argv); + } + else if (t == A_FLOAT) + { + if (argc == 1) pd_float(x, argv->a_w.w_float); + else pd_list(x, &s_list, argc, argv); + } + else bug("pd_forwardmess"); + } + +} + +void nullfn(void) {} + +t_gotfn getfn(t_pd *x, t_symbol *s) +{ + t_class *c = *x; + t_methodentry *m; + int i; + + for (i = c->c_nmethod, m = c->c_methods; i--; m++) + if (m->me_name == s) return(m->me_fun); + pd_error(x, "%s: no method for message '%s'", c->c_name->s_name, s->s_name); + return((t_gotfn)nullfn); +} + +t_gotfn zgetfn(t_pd *x, t_symbol *s) +{ + t_class *c = *x; + t_methodentry *m; + int i; + + for (i = c->c_nmethod, m = c->c_methods; i--; m++) + if (m->me_name == s) return(m->me_fun); + return(0); +} diff --git a/apps/plugins/pdbox/PDa/src/m_conf.c b/apps/plugins/pdbox/PDa/src/m_conf.c new file mode 100644 index 0000000..8d3f542 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/m_conf.c @@ -0,0 +1,202 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* changes by Thomas Musil IEM KUG Graz Austria 2001 */ +/* all changes are labeled with iemlib */ + +#include "m_pd.h" + +void g_array_setup(void); +void g_canvas_setup(void); +void g_guiconnect_setup(void); +/* iemlib */ +void g_bang_setup(void); +void g_hradio_setup(void); +void g_hslider_setup(void); +void g_mycanvas_setup(void); +void g_numbox_setup(void); +void g_toggle_setup(void); +void g_vradio_setup(void); +void g_vslider_setup(void); +void g_vumeter_setup(void); +/* iemlib */ +void g_io_setup(void); +void g_scalar_setup(void); +void g_template_setup(void); +void g_text_setup(void); +void g_traversal_setup(void); +void m_pd_setup(void); +void x_acoustics_setup(void); +void x_interface_setup(void); +void x_connective_setup(void); +void x_time_setup(void); +void x_arithmetic_setup(void); +void x_midi_setup(void); +void x_misc_setup(void); +void x_net_setup(void); +void x_qlist_setup(void); +void x_gui_setup(void); +void d_arithmetic_setup(void); +void d_array_setup(void); +void d_ctl_setup(void); +void d_dac_setup(void); +void d_delay_setup(void); +void d_fft_setup(void); +void d_filter_setup(void); +void d_global_setup(void); +void d_math_setup(void); +void d_misc_setup(void); +void d_osc_setup(void); +void d_soundfile_setup(void); +void d_ugen_setup(void); + +void conf_init(void) +{ + g_array_setup(); + g_canvas_setup(); + g_guiconnect_setup(); +/* iemlib */ + + g_bang_setup(); + g_hradio_setup(); + g_hslider_setup(); + g_mycanvas_setup(); + g_numbox_setup(); + g_toggle_setup(); + g_vradio_setup(); + g_vslider_setup(); + g_vumeter_setup(); +/* iemlib */ + + g_io_setup(); + g_scalar_setup(); + g_template_setup(); + g_text_setup(); + g_traversal_setup(); + m_pd_setup(); + x_acoustics_setup(); + x_interface_setup(); + x_connective_setup(); + x_time_setup(); + x_arithmetic_setup(); + + x_midi_setup(); + x_misc_setup(); + x_net_setup(); + x_qlist_setup(); + x_gui_setup(); + d_arithmetic_setup(); + d_dac_setup(); + d_fft_setup(); + d_global_setup(); + d_misc_setup(); +#ifdef STATIC + d_intern_setup(); +#endif + d_soundfile_setup(); + d_ugen_setup(); + +} + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* changes by Thomas Musil IEM KUG Graz Austria 2001 */ +/* all changes are labeled with iemlib */ + +#include "m_pd.h" + +void g_array_setup(void); +void g_canvas_setup(void); +void g_guiconnect_setup(void); +/* iemlib */ +void g_bang_setup(void); +void g_hradio_setup(void); +void g_hslider_setup(void); +void g_mycanvas_setup(void); +void g_numbox_setup(void); +void g_toggle_setup(void); +void g_vradio_setup(void); +void g_vslider_setup(void); +void g_vumeter_setup(void); +/* iemlib */ +void g_io_setup(void); +void g_scalar_setup(void); +void g_template_setup(void); +void g_text_setup(void); +void g_traversal_setup(void); +void m_pd_setup(void); +void x_acoustics_setup(void); +void x_interface_setup(void); +void x_connective_setup(void); +void x_time_setup(void); +void x_arithmetic_setup(void); +void x_midi_setup(void); +void x_misc_setup(void); +void x_net_setup(void); +void x_qlist_setup(void); +void x_gui_setup(void); +void d_arithmetic_setup(void); +void d_array_setup(void); +void d_ctl_setup(void); +void d_dac_setup(void); +void d_delay_setup(void); +void d_fft_setup(void); +void d_filter_setup(void); +void d_global_setup(void); +void d_math_setup(void); +void d_misc_setup(void); +void d_osc_setup(void); +void d_soundfile_setup(void); +void d_ugen_setup(void); + +void conf_init(void) +{ + g_array_setup(); + g_canvas_setup(); + g_guiconnect_setup(); +/* iemlib */ + + g_bang_setup(); + g_hradio_setup(); + g_hslider_setup(); + g_mycanvas_setup(); + g_numbox_setup(); + g_toggle_setup(); + g_vradio_setup(); + g_vslider_setup(); + g_vumeter_setup(); +/* iemlib */ + + g_io_setup(); + g_scalar_setup(); + g_template_setup(); + g_text_setup(); + g_traversal_setup(); + m_pd_setup(); + x_acoustics_setup(); + x_interface_setup(); + x_connective_setup(); + x_time_setup(); + x_arithmetic_setup(); + + x_midi_setup(); + x_misc_setup(); + x_net_setup(); + x_qlist_setup(); + x_gui_setup(); + d_arithmetic_setup(); + d_dac_setup(); + d_fft_setup(); + d_global_setup(); + d_misc_setup(); +#ifdef STATIC + d_intern_setup(); +#endif + d_soundfile_setup(); + d_ugen_setup(); + +} + diff --git a/apps/plugins/pdbox/PDa/src/m_fixed.c b/apps/plugins/pdbox/PDa/src/m_fixed.c new file mode 100644 index 0000000..c91fa05 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/m_fixed.c @@ -0,0 +1,252 @@ + +#include +#include +#include +#include +#include + +#include "m_pd.h" +#include "m_imp.h" + +static t_class *ipod_class = 0; + +typedef struct _ipod +{ + t_object x_obj; + t_symbol* x_what; +} t_ipod; + +static t_ipod* ipod; +static t_int x_fd = -1; + + + +static void ipod_connect() +{ + struct sockaddr_in server; + struct hostent *hp; + int sockfd; + int portno = 3334; + char hostname[] = "127.0.0.1"; + int intarg; + if (x_fd >= 0) + { + error("ipod_connect: already connected"); + return; + } + + /* create a socket */ + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + + if (sockfd < 0) + { + sys_sockerror("socket"); + return; + } + + /* connect socket using hostname provided in command line */ + + server.sin_family = AF_INET; + hp = gethostbyname(hostname); + if (hp == 0) + { + post("bad host?\n"); + return; + } + + memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); + + server.sin_port = htons((u_short)portno); + if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) + { + sys_sockerror("connecting stream socket"); + sys_closesocket(sockfd); + return; + } + post("connected %s %d",hostname,portno); + x_fd = sockfd; +} + + + +static void ipod_bang(t_ipod *x) +{ + static char sendme[200]; + sprintf(sendme,"%s bang;\n",x->x_what->s_name); + send(x_fd,sendme,strlen(sendme),0); + +// if (x->x_sym->s_thing) pd_bang(x->x_sym->s_thing); +} + +static void ipod_float(t_ipod *x, t_float f) +{ + static char sendme[200]; + + sprintf(sendme,"%s %f;\n",x->x_what->s_name,f); + send(x_fd,sendme,strlen(sendme),0); + +// post("forwarding float %s",x->x_what->s_name); +// if (x->x_sym->s_thing) pd_float(x->x_sym->s_thing, f); +} + +static void *ipod_new(t_symbol* what) +{ + t_ipod *x = (t_ipod *)pd_new(ipod_class); + post("new ipod %s",what->s_name); + x->x_what = what; + return (x); +} + +static void ipod_setup(void) +{ + ipod_class = class_new(gensym("ipod"), (t_newmethod)ipod_new, 0, + sizeof(t_ipod), 0, A_DEFSYM, 0); + class_addbang(ipod_class, ipod_bang); + class_addfloat(ipod_class, ipod_float); + ipod_connect(); +} + +void pd_checkgui(t_pd *x, t_symbol *s) +{ + if (!strncmp(s->s_name,"pod_",4)) + if (!strcmp((*x)->c_name->s_name,"gatom") || + !strcmp((*x)->c_name->s_name,"vsl") || + !strcmp((*x)->c_name->s_name,"hsl") || + !strcmp((*x)->c_name->s_name,"bng") || + !strcmp((*x)->c_name->s_name,"vradio") || + !strcmp((*x)->c_name->s_name,"hradio")) { + + post("binding %s to %s",s->s_name,(*x)->c_name->s_name); + if (!ipod_class) ipod_setup(); + ipod = ipod_new(s); + pd_bind(&ipod->x_obj.ob_pd,s); + } +} + + + +#include +#include +#include +#include +#include + +#include "m_pd.h" +#include "m_imp.h" + +static t_class *ipod_class = 0; + +typedef struct _ipod +{ + t_object x_obj; + t_symbol* x_what; +} t_ipod; + +static t_ipod* ipod; +static t_int x_fd = -1; + + + +static void ipod_connect() +{ + struct sockaddr_in server; + struct hostent *hp; + int sockfd; + int portno = 3334; + char hostname[] = "127.0.0.1"; + int intarg; + if (x_fd >= 0) + { + error("ipod_connect: already connected"); + return; + } + + /* create a socket */ + sockfd = socket(AF_INET, SOCK_DGRAM, 0); + + if (sockfd < 0) + { + sys_sockerror("socket"); + return; + } + + /* connect socket using hostname provided in command line */ + + server.sin_family = AF_INET; + hp = gethostbyname(hostname); + if (hp == 0) + { + post("bad host?\n"); + return; + } + + memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); + + server.sin_port = htons((u_short)portno); + if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) + { + sys_sockerror("connecting stream socket"); + sys_closesocket(sockfd); + return; + } + post("connected %s %d",hostname,portno); + x_fd = sockfd; +} + + + +static void ipod_bang(t_ipod *x) +{ + static char sendme[200]; + sprintf(sendme,"%s bang;\n",x->x_what->s_name); + send(x_fd,sendme,strlen(sendme),0); + +// if (x->x_sym->s_thing) pd_bang(x->x_sym->s_thing); +} + +static void ipod_float(t_ipod *x, t_float f) +{ + static char sendme[200]; + + sprintf(sendme,"%s %f;\n",x->x_what->s_name,f); + send(x_fd,sendme,strlen(sendme),0); + +// post("forwarding float %s",x->x_what->s_name); +// if (x->x_sym->s_thing) pd_float(x->x_sym->s_thing, f); +} + +static void *ipod_new(t_symbol* what) +{ + t_ipod *x = (t_ipod *)pd_new(ipod_class); + post("new ipod %s",what->s_name); + x->x_what = what; + return (x); +} + +static void ipod_setup(void) +{ + ipod_class = class_new(gensym("ipod"), (t_newmethod)ipod_new, 0, + sizeof(t_ipod), 0, A_DEFSYM, 0); + class_addbang(ipod_class, ipod_bang); + class_addfloat(ipod_class, ipod_float); + ipod_connect(); +} + +void pd_checkgui(t_pd *x, t_symbol *s) +{ + if (!strncmp(s->s_name,"pod_",4)) + if (!strcmp((*x)->c_name->s_name,"gatom") || + !strcmp((*x)->c_name->s_name,"vsl") || + !strcmp((*x)->c_name->s_name,"hsl") || + !strcmp((*x)->c_name->s_name,"bng") || + !strcmp((*x)->c_name->s_name,"vradio") || + !strcmp((*x)->c_name->s_name,"hradio")) { + + post("binding %s to %s",s->s_name,(*x)->c_name->s_name); + if (!ipod_class) ipod_setup(); + ipod = ipod_new(s); + pd_bind(&ipod->x_obj.ob_pd,s); + } +} + + diff --git a/apps/plugins/pdbox/PDa/src/m_fixed.h b/apps/plugins/pdbox/PDa/src/m_fixed.h new file mode 100644 index 0000000..4ccc121 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/m_fixed.h @@ -0,0 +1,110 @@ +#ifndef __M_FIXED_H__ +#define __M_FIXED_H__ + +typedef int t_sample; + +#define t_fixed int +#define fix1 18 /* (18) number of bits after comma */ + + +#define fixfac ((float)(1<>fix1) +#define idiv(a,b) ((((long long) (a) )<>fix1) + + +/* Not working !! */ + +#define fnum(a) ( (a) >>(fix1-16)) +#define ffrac(a) (0) + + +/* mapping of fft functions */ + +#ifdef FIXEDPOINT +#define mayer_realifft imayer_realifft +#define mayer_realfft imayer_realfft +#define mayer_fft imayer_fft +#define mayer_ifft imayer_ifft +#endif + +#ifdef FIXEDPOINT +#define SCALE16(x) (x>>(fix1-15)) +#define SCALE32(x) (x<<(32-fix1)) +#define INVSCALE16(x) (x<<8) +#else +#define SCALE16(x) (32767.*x) +#define SCALE32(x) (2147483648.*x) +#define INVSCALE16(x) ((float)3.051850e-05*x) +#endif + + +#endif + + +#ifndef __M_FIXED_H__ +#define __M_FIXED_H__ + +typedef int t_sample; + +#define t_fixed int +#define fix1 18 /* (18) number of bits after comma */ + + +#define fixfac ((float)(1<>fix1) +#define idiv(a,b) ((((long long) (a) )<>fix1) + + +/* Not working !! */ + +#define fnum(a) ( (a) >>(fix1-16)) +#define ffrac(a) (0) + + +/* mapping of fft functions */ + +#ifdef FIXEDPOINT +#define mayer_realifft imayer_realifft +#define mayer_realfft imayer_realfft +#define mayer_fft imayer_fft +#define mayer_ifft imayer_ifft +#endif + +#ifdef FIXEDPOINT +#define SCALE16(x) (x>>(fix1-15)) +#define SCALE32(x) (x<<(32-fix1)) +#define INVSCALE16(x) (x<<8) +#else +#define SCALE16(x) (32767.*x) +#define SCALE32(x) (2147483648.*x) +#define INVSCALE16(x) ((float)3.051850e-05*x) +#endif + + +#endif + + diff --git a/apps/plugins/pdbox/PDa/src/m_glob.c b/apps/plugins/pdbox/PDa/src/m_glob.c new file mode 100644 index 0000000..5d62087 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/m_glob.c @@ -0,0 +1,210 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include "m_pd.h" +#include "m_imp.h" + +t_class *glob_pdobject; +static t_class *maxclass; + +/* These "glob" routines, which implement messages to Pd, are from all +over. Some others are prototyped in m_imp.h as well. */ + +void glob_setfilename(void *dummy, t_symbol *name, t_symbol *dir); +void glob_quit(void *dummy); +void glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_meters(void *dummy, t_floatarg f); +void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av); +void glob_audiostatus(void *dummy); +void glob_finderror(t_pd *dummy); +void glob_audio_properties(t_pd *dummy, t_floatarg flongform); +void glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_audio_setapi(t_pd *dummy, t_floatarg f); +void glob_midi_properties(t_pd *dummy, t_floatarg flongform); +void glob_midi_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_start_path_dialog(t_pd *dummy, t_floatarg flongform); +void glob_path_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_ping(t_pd *dummy); + +void alsa_resync( void); + + +#ifdef MSW +void glob_audio(void *dummy, t_floatarg adc, t_floatarg dac); +#endif + +/* a method you add for debugging printout */ +void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv); + +#if 0 +void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) +{ + *(int *)1 = 3; +} +#endif + +void max_default(t_pd *x, t_symbol *s, int argc, t_atom *argv) +{ + int i; + char str[80]; + startpost("%s: unknown message %s ", class_getname(pd_class(x)), + s->s_name); + for (i = 0; i < argc; i++) + { + atom_string(argv+i, str, 80); + poststring(str); + } + endpost(); +} + +void glob_init(void) +{ + maxclass = class_new(gensym("max"), 0, 0, sizeof(t_pd), + CLASS_DEFAULT, A_NULL); + class_addanything(maxclass, max_default); + pd_bind(&maxclass, gensym("max")); + + glob_pdobject = class_new(gensym("pd"), 0, 0, sizeof(t_pd), + CLASS_DEFAULT, A_NULL); + class_addmethod(glob_pdobject, (t_method)glob_initfromgui, gensym("init"), + A_GIMME, 0); + class_addmethod(glob_pdobject, (t_method)glob_setfilename, gensym("filename"), + A_SYMBOL, A_SYMBOL, 0); + class_addmethod(glob_pdobject, (t_method)glob_evalfile, gensym("open"), + A_SYMBOL, A_SYMBOL, 0); + class_addmethod(glob_pdobject, (t_method)glob_quit, gensym("quit"), 0); + class_addmethod(glob_pdobject, (t_method)glob_foo, gensym("foo"), A_GIMME, 0); + class_addmethod(glob_pdobject, (t_method)glob_dsp, gensym("dsp"), A_GIMME, 0); + class_addmethod(glob_pdobject, (t_method)glob_meters, gensym("meters"), + A_FLOAT, 0); + class_addmethod(glob_pdobject, (t_method)glob_key, gensym("key"), A_GIMME, 0); + class_addmethod(glob_pdobject, (t_method)glob_audiostatus, + gensym("audiostatus"), 0); + class_addmethod(glob_pdobject, (t_method)glob_finderror, + gensym("finderror"), 0); + class_addmethod(glob_pdobject, (t_method)glob_audio_properties, + gensym("audio-properties"), A_DEFFLOAT, 0); + class_addmethod(glob_pdobject, (t_method)glob_audio_dialog, + gensym("audio-dialog"), A_GIMME, 0); + class_addmethod(glob_pdobject, (t_method)glob_audio_setapi, + gensym("audio-setapi"), A_FLOAT, 0); + class_addmethod(glob_pdobject, (t_method)glob_midi_properties, + gensym("midi-properties"), A_DEFFLOAT, 0); + class_addmethod(glob_pdobject, (t_method)glob_midi_dialog, + gensym("midi-dialog"), A_GIMME, 0); + class_addmethod(glob_pdobject, (t_method)glob_start_path_dialog, + gensym("start-path-dialog"), A_DEFFLOAT, 0); + class_addmethod(glob_pdobject, (t_method)glob_path_dialog, + gensym("path-dialog"), A_GIMME, 0); +#ifdef __linux__ + class_addmethod(glob_pdobject, (t_method)glob_ping, gensym("ping"), 0); +#endif + class_addanything(glob_pdobject, max_default); + pd_bind(&glob_pdobject, gensym("pd")); +} +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include "m_pd.h" +#include "m_imp.h" + +t_class *glob_pdobject; +static t_class *maxclass; + +/* These "glob" routines, which implement messages to Pd, are from all +over. Some others are prototyped in m_imp.h as well. */ + +void glob_setfilename(void *dummy, t_symbol *name, t_symbol *dir); +void glob_quit(void *dummy); +void glob_dsp(void *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_meters(void *dummy, t_floatarg f); +void glob_key(void *dummy, t_symbol *s, int ac, t_atom *av); +void glob_audiostatus(void *dummy); +void glob_finderror(t_pd *dummy); +void glob_audio_properties(t_pd *dummy, t_floatarg flongform); +void glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_audio_setapi(t_pd *dummy, t_floatarg f); +void glob_midi_properties(t_pd *dummy, t_floatarg flongform); +void glob_midi_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_start_path_dialog(t_pd *dummy, t_floatarg flongform); +void glob_path_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_ping(t_pd *dummy); + +void alsa_resync( void); + + +#ifdef MSW +void glob_audio(void *dummy, t_floatarg adc, t_floatarg dac); +#endif + +/* a method you add for debugging printout */ +void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv); + +#if 0 +void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) +{ + *(int *)1 = 3; +} +#endif + +void max_default(t_pd *x, t_symbol *s, int argc, t_atom *argv) +{ + int i; + char str[80]; + startpost("%s: unknown message %s ", class_getname(pd_class(x)), + s->s_name); + for (i = 0; i < argc; i++) + { + atom_string(argv+i, str, 80); + poststring(str); + } + endpost(); +} + +void glob_init(void) +{ + maxclass = class_new(gensym("max"), 0, 0, sizeof(t_pd), + CLASS_DEFAULT, A_NULL); + class_addanything(maxclass, max_default); + pd_bind(&maxclass, gensym("max")); + + glob_pdobject = class_new(gensym("pd"), 0, 0, sizeof(t_pd), + CLASS_DEFAULT, A_NULL); + class_addmethod(glob_pdobject, (t_method)glob_initfromgui, gensym("init"), + A_GIMME, 0); + class_addmethod(glob_pdobject, (t_method)glob_setfilename, gensym("filename"), + A_SYMBOL, A_SYMBOL, 0); + class_addmethod(glob_pdobject, (t_method)glob_evalfile, gensym("open"), + A_SYMBOL, A_SYMBOL, 0); + class_addmethod(glob_pdobject, (t_method)glob_quit, gensym("quit"), 0); + class_addmethod(glob_pdobject, (t_method)glob_foo, gensym("foo"), A_GIMME, 0); + class_addmethod(glob_pdobject, (t_method)glob_dsp, gensym("dsp"), A_GIMME, 0); + class_addmethod(glob_pdobject, (t_method)glob_meters, gensym("meters"), + A_FLOAT, 0); + class_addmethod(glob_pdobject, (t_method)glob_key, gensym("key"), A_GIMME, 0); + class_addmethod(glob_pdobject, (t_method)glob_audiostatus, + gensym("audiostatus"), 0); + class_addmethod(glob_pdobject, (t_method)glob_finderror, + gensym("finderror"), 0); + class_addmethod(glob_pdobject, (t_method)glob_audio_properties, + gensym("audio-properties"), A_DEFFLOAT, 0); + class_addmethod(glob_pdobject, (t_method)glob_audio_dialog, + gensym("audio-dialog"), A_GIMME, 0); + class_addmethod(glob_pdobject, (t_method)glob_audio_setapi, + gensym("audio-setapi"), A_FLOAT, 0); + class_addmethod(glob_pdobject, (t_method)glob_midi_properties, + gensym("midi-properties"), A_DEFFLOAT, 0); + class_addmethod(glob_pdobject, (t_method)glob_midi_dialog, + gensym("midi-dialog"), A_GIMME, 0); + class_addmethod(glob_pdobject, (t_method)glob_start_path_dialog, + gensym("start-path-dialog"), A_DEFFLOAT, 0); + class_addmethod(glob_pdobject, (t_method)glob_path_dialog, + gensym("path-dialog"), A_GIMME, 0); +#ifdef __linux__ + class_addmethod(glob_pdobject, (t_method)glob_ping, gensym("ping"), 0); +#endif + class_addanything(glob_pdobject, max_default); + pd_bind(&glob_pdobject, gensym("pd")); +} diff --git a/apps/plugins/pdbox/PDa/src/m_imp.h b/apps/plugins/pdbox/PDa/src/m_imp.h new file mode 100644 index 0000000..7632af1 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/m_imp.h @@ -0,0 +1,156 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* This file contains function prototypes and data types used to implement +Pd, but not shared with Pd objects. */ + +/* NOTE: this file describes Pd implementation details which may change +in future releases. The public (stable) API is in m_pd.h. */ + +/* LATER consider whether to use 'char' for method arg types to save space */ + +/* the structure for a method handler ala Max */ +typedef struct _methodentry +{ + t_symbol *me_name; + t_gotfn me_fun; + t_atomtype me_arg[MAXPDARG+1]; +} t_methodentry; + +EXTERN_STRUCT _widgetbehavior; + +typedef void (*t_bangmethod)(t_pd *x); +typedef void (*t_pointermethod)(t_pd *x, t_gpointer *gp); +typedef void (*t_floatmethod)(t_pd *x, t_float f); +typedef void (*t_symbolmethod)(t_pd *x, t_symbol *s); +typedef void (*t_listmethod)(t_pd *x, t_symbol *s, int argc, t_atom *argv); +typedef void (*t_anymethod)(t_pd *x, t_symbol *s, int argc, t_atom *argv); + +struct _class +{ + t_symbol *c_name; /* name (mostly for error reporting) */ + t_symbol *c_helpname; /* name of help file */ + t_symbol *c_externdir; /* directory extern was loaded from */ + size_t c_size; /* size of an instance */ + t_methodentry *c_methods; /* methods other than bang, etc below */ + int c_nmethod; /* number of methods */ + t_method c_freemethod; /* function to call before freeing */ + t_bangmethod c_bangmethod; /* common methods */ + t_pointermethod c_pointermethod; + t_floatmethod c_floatmethod; + t_symbolmethod c_symbolmethod; + t_listmethod c_listmethod; + t_anymethod c_anymethod; + struct _widgetbehavior *c_wb; /* "gobjs" only */ + struct _parentwidgetbehavior *c_pwb;/* widget behavior in parent */ + t_savefn c_savefn; /* function to call when saving */ + t_propertiesfn c_propertiesfn; /* function to start prop dialog */ + int c_floatsignalin; /* onset to float for signal input */ + char c_gobj; /* true if is a gobj */ + char c_patchable; /* true if we have a t_object header */ + char c_firstin; /* if patchable, true if draw first inlet */ + char c_drawcommand; /* a drawing command for a template */ +}; + + +/* m_obj.c */ +EXTERN int obj_noutlets(t_object *x); +EXTERN int obj_ninlets(t_object *x); +EXTERN t_outconnect *obj_starttraverseoutlet(t_object *x, t_outlet **op, + int nout); +EXTERN t_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect, + t_object **destp, t_inlet **inletp, int *whichp); +EXTERN t_outconnect *obj_connect(t_object *source, int outno, + t_object *sink, int inno); +EXTERN void obj_disconnect(t_object *source, int outno, t_object *sink, + int inno); +EXTERN void outlet_setstacklim(void); +EXTERN int obj_issignalinlet(t_object *x, int m); +EXTERN int obj_issignaloutlet(t_object *x, int m); +EXTERN int obj_nsiginlets(t_object *x); +EXTERN int obj_nsigoutlets(t_object *x); +EXTERN int obj_siginletindex(t_object *x, int m); +EXTERN int obj_sigoutletindex(t_object *x, int m); + +/* misc */ +EXTERN void glob_evalfile(t_pd *ignore, t_symbol *name, t_symbol *dir); +EXTERN void glob_initfromgui(void *dummy, t_symbol *s, int argc, t_atom *argv); +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* This file contains function prototypes and data types used to implement +Pd, but not shared with Pd objects. */ + +/* NOTE: this file describes Pd implementation details which may change +in future releases. The public (stable) API is in m_pd.h. */ + +/* LATER consider whether to use 'char' for method arg types to save space */ + +/* the structure for a method handler ala Max */ +typedef struct _methodentry +{ + t_symbol *me_name; + t_gotfn me_fun; + t_atomtype me_arg[MAXPDARG+1]; +} t_methodentry; + +EXTERN_STRUCT _widgetbehavior; + +typedef void (*t_bangmethod)(t_pd *x); +typedef void (*t_pointermethod)(t_pd *x, t_gpointer *gp); +typedef void (*t_floatmethod)(t_pd *x, t_float f); +typedef void (*t_symbolmethod)(t_pd *x, t_symbol *s); +typedef void (*t_listmethod)(t_pd *x, t_symbol *s, int argc, t_atom *argv); +typedef void (*t_anymethod)(t_pd *x, t_symbol *s, int argc, t_atom *argv); + +struct _class +{ + t_symbol *c_name; /* name (mostly for error reporting) */ + t_symbol *c_helpname; /* name of help file */ + t_symbol *c_externdir; /* directory extern was loaded from */ + size_t c_size; /* size of an instance */ + t_methodentry *c_methods; /* methods other than bang, etc below */ + int c_nmethod; /* number of methods */ + t_method c_freemethod; /* function to call before freeing */ + t_bangmethod c_bangmethod; /* common methods */ + t_pointermethod c_pointermethod; + t_floatmethod c_floatmethod; + t_symbolmethod c_symbolmethod; + t_listmethod c_listmethod; + t_anymethod c_anymethod; + struct _widgetbehavior *c_wb; /* "gobjs" only */ + struct _parentwidgetbehavior *c_pwb;/* widget behavior in parent */ + t_savefn c_savefn; /* function to call when saving */ + t_propertiesfn c_propertiesfn; /* function to start prop dialog */ + int c_floatsignalin; /* onset to float for signal input */ + char c_gobj; /* true if is a gobj */ + char c_patchable; /* true if we have a t_object header */ + char c_firstin; /* if patchable, true if draw first inlet */ + char c_drawcommand; /* a drawing command for a template */ +}; + + +/* m_obj.c */ +EXTERN int obj_noutlets(t_object *x); +EXTERN int obj_ninlets(t_object *x); +EXTERN t_outconnect *obj_starttraverseoutlet(t_object *x, t_outlet **op, + int nout); +EXTERN t_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect, + t_object **destp, t_inlet **inletp, int *whichp); +EXTERN t_outconnect *obj_connect(t_object *source, int outno, + t_object *sink, int inno); +EXTERN void obj_disconnect(t_object *source, int outno, t_object *sink, + int inno); +EXTERN void outlet_setstacklim(void); +EXTERN int obj_issignalinlet(t_object *x, int m); +EXTERN int obj_issignaloutlet(t_object *x, int m); +EXTERN int obj_nsiginlets(t_object *x); +EXTERN int obj_nsigoutlets(t_object *x); +EXTERN int obj_siginletindex(t_object *x, int m); +EXTERN int obj_sigoutletindex(t_object *x, int m); + +/* misc */ +EXTERN void glob_evalfile(t_pd *ignore, t_symbol *name, t_symbol *dir); +EXTERN void glob_initfromgui(void *dummy, t_symbol *s, int argc, t_atom *argv); diff --git a/apps/plugins/pdbox/PDa/src/m_memory.c b/apps/plugins/pdbox/PDa/src/m_memory.c new file mode 100644 index 0000000..0851542 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/m_memory.c @@ -0,0 +1,178 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include +#include +#include "m_pd.h" +#include "m_imp.h" + +/* #define LOUD */ +#ifdef LOUD +#include +#endif + +/* #define DEBUGMEM */ +#ifdef DEBUGMEM +static int totalmem = 0; +#endif + +void *getbytes(size_t nbytes) +{ + void *ret; + if (nbytes < 1) nbytes = 1; + ret = (void *)calloc(nbytes, 1); +#ifdef LOUD + fprintf(stderr, "new %x %d\n", (int)ret, nbytes); +#endif /* LOUD */ +#ifdef DEBUGMEM + totalmem += nbytes; +#endif + if (!ret) + post("pd: getbytes() failed -- out of memory"); + return (ret); +} + +void *getzbytes(size_t nbytes) /* obsolete name */ +{ + return (getbytes(nbytes)); +} + +void *copybytes(void *src, size_t nbytes) +{ + void *ret; + ret = getbytes(nbytes); + if (nbytes) + memcpy(ret, src, nbytes); + return (ret); +} + +void *resizebytes(void *old, size_t oldsize, size_t newsize) +{ + void *ret; + if (newsize < 1) newsize = 1; + if (oldsize < 1) oldsize = 1; + ret = (void *)realloc((char *)old, newsize); + if (newsize > oldsize && ret) + memset(((char *)ret) + oldsize, 0, newsize - oldsize); +#ifdef LOUD + fprintf(stderr, "resize %x %d --> %x %d\n", (int)old, oldsize, (int)ret, newsize); +#endif /* LOUD */ +#ifdef DEBUGMEM + totalmem += (newsize - oldsize); +#endif + if (!ret) + post("pd: resizebytes() failed -- out of memory"); + return (ret); +} + +void freebytes(void *fatso, size_t nbytes) +{ + if (nbytes == 0) + nbytes = 1; +#ifdef LOUD + fprintf(stderr, "free %x %d\n", (int)fatso, nbytes); +#endif /* LOUD */ +#ifdef DEBUGMEM + totalmem -= nbytes; +#endif + free(fatso); +} + +#ifdef DEBUGMEM +#include + +void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) +{ + fprintf(stderr, "total mem %d\n", totalmem); +} +#endif +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include +#include +#include "m_pd.h" +#include "m_imp.h" + +/* #define LOUD */ +#ifdef LOUD +#include +#endif + +/* #define DEBUGMEM */ +#ifdef DEBUGMEM +static int totalmem = 0; +#endif + +void *getbytes(size_t nbytes) +{ + void *ret; + if (nbytes < 1) nbytes = 1; + ret = (void *)calloc(nbytes, 1); +#ifdef LOUD + fprintf(stderr, "new %x %d\n", (int)ret, nbytes); +#endif /* LOUD */ +#ifdef DEBUGMEM + totalmem += nbytes; +#endif + if (!ret) + post("pd: getbytes() failed -- out of memory"); + return (ret); +} + +void *getzbytes(size_t nbytes) /* obsolete name */ +{ + return (getbytes(nbytes)); +} + +void *copybytes(void *src, size_t nbytes) +{ + void *ret; + ret = getbytes(nbytes); + if (nbytes) + memcpy(ret, src, nbytes); + return (ret); +} + +void *resizebytes(void *old, size_t oldsize, size_t newsize) +{ + void *ret; + if (newsize < 1) newsize = 1; + if (oldsize < 1) oldsize = 1; + ret = (void *)realloc((char *)old, newsize); + if (newsize > oldsize && ret) + memset(((char *)ret) + oldsize, 0, newsize - oldsize); +#ifdef LOUD + fprintf(stderr, "resize %x %d --> %x %d\n", (int)old, oldsize, (int)ret, newsize); +#endif /* LOUD */ +#ifdef DEBUGMEM + totalmem += (newsize - oldsize); +#endif + if (!ret) + post("pd: resizebytes() failed -- out of memory"); + return (ret); +} + +void freebytes(void *fatso, size_t nbytes) +{ + if (nbytes == 0) + nbytes = 1; +#ifdef LOUD + fprintf(stderr, "free %x %d\n", (int)fatso, nbytes); +#endif /* LOUD */ +#ifdef DEBUGMEM + totalmem -= nbytes; +#endif + free(fatso); +} + +#ifdef DEBUGMEM +#include + +void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) +{ + fprintf(stderr, "total mem %d\n", totalmem); +} +#endif diff --git a/apps/plugins/pdbox/PDa/src/m_obj.c b/apps/plugins/pdbox/PDa/src/m_obj.c new file mode 100644 index 0000000..d53da57 --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/m_obj.c @@ -0,0 +1,1394 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* this file handles Max-style patchable objects, i.e., objects which +can interconnect via inlets and outlets; also, the (terse) generic +behavior for "gobjs" appears at the end of this file. */ + +#include "m_pd.h" +#include "m_imp.h" + +union inletunion +{ + t_symbol *iu_symto; + t_gpointer *iu_pointerslot; + t_float *iu_floatslot; + t_symbol **iu_symslot; + t_sample iu_floatsignalvalue; +}; + +struct _inlet +{ + t_pd i_pd; + struct _inlet *i_next; + t_object *i_owner; + t_pd *i_dest; + t_symbol *i_symfrom; + union inletunion i_un; +}; + +#define i_symto i_un.iu_symto +#define i_pointerslot i_un.iu_pointerslot +#define i_floatslot i_un.iu_floatslot +#define i_symslot i_un.iu_symslot + +static t_class *inlet_class, *pointerinlet_class, *floatinlet_class, + *symbolinlet_class; + +#define ISINLET(pd) ((*(pd) == inlet_class) || \ + (*(pd) == pointerinlet_class) || \ + (*(pd) == floatinlet_class) || \ + (*(pd) == symbolinlet_class)) + +/* --------------------- generic inlets ala max ------------------ */ + +t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1, t_symbol *s2) +{ + t_inlet *x = (t_inlet *)pd_new(inlet_class), *y, *y2; + x->i_owner = owner; + x->i_dest = dest; + if (s1 == &s_signal) + x->i_un.iu_floatsignalvalue = 0; + else x->i_symto = s2; + x->i_symfrom = s1; + x->i_next = 0; + if (y = owner->ob_inlet) + { + while (y2 = y->i_next) y = y2; + y->i_next = x; + } + else owner->ob_inlet = x; + return (x); +} + +static void inlet_wrong(t_inlet *x, t_symbol *s) +{ + pd_error(x->i_owner, "inlet: expected '%s' but got '%s'", + x->i_symfrom->s_name, s->s_name); +} + + /* LATER figure out how to make these efficient: */ +static void inlet_bang(t_inlet *x) +{ + if (x->i_symfrom == &s_bang) + pd_vmess(x->i_dest, x->i_symto, ""); + else if (!x->i_symfrom) pd_bang(x->i_dest); + else inlet_wrong(x, &s_bang); +} + +static void inlet_pointer(t_inlet *x, t_gpointer *gp) +{ + if (x->i_symfrom == &s_pointer) + pd_vmess(x->i_dest, x->i_symto, "p", gp); + else if (!x->i_symfrom) pd_pointer(x->i_dest, gp); + else inlet_wrong(x, &s_pointer); +} + +static void inlet_float(t_inlet *x, t_float f) +{ + if (x->i_symfrom == &s_float) + pd_vmess(x->i_dest, x->i_symto, "f", (t_floatarg)f); + else if (x->i_symfrom == &s_signal) + x->i_un.iu_floatsignalvalue = ftofix(f); + else if (!x->i_symfrom) + pd_float(x->i_dest, f); + else inlet_wrong(x, &s_float); +} + +static void inlet_symbol(t_inlet *x, t_symbol *s) +{ + if (x->i_symfrom == &s_symbol) + pd_vmess(x->i_dest, x->i_symto, "s", s); + else if (!x->i_symfrom) pd_symbol(x->i_dest, s); + else inlet_wrong(x, &s_symbol); +} + +static void inlet_list(t_inlet *x, t_symbol *s, int argc, t_atom *argv) +{ + t_atom at; + if (x->i_symfrom == &s_list || x->i_symfrom == &s_float + || x->i_symfrom == &s_symbol || x->i_symfrom == &s_pointer) + typedmess(x->i_dest, x->i_symto, argc, argv); + else if (!x->i_symfrom) pd_list(x->i_dest, s, argc, argv); + else inlet_wrong(x, &s_list); +} + +static void inlet_anything(t_inlet *x, t_symbol *s, int argc, t_atom *argv) +{ + if (x->i_symfrom == s) + typedmess(x->i_dest, x->i_symto, argc, argv); + else if (!x->i_symfrom) + typedmess(x->i_dest, s, argc, argv); + else inlet_wrong(x, s); +} + +void inlet_free(t_inlet *x) +{ + t_object *y = x->i_owner; + t_inlet *x2; + if (y->ob_inlet == x) y->ob_inlet = x->i_next; + else for (x2 = y->ob_inlet; x2; x2 = x2->i_next) + if (x2->i_next == x) + { + x2->i_next = x->i_next; + break; + } + t_freebytes(x, sizeof(*x)); +} + +/* ----- pointerinlets, floatinlets, syminlets: optimized inlets ------- */ + +static void pointerinlet_pointer(t_inlet *x, t_gpointer *gp) +{ + gpointer_unset(x->i_pointerslot); + *(x->i_pointerslot) = *gp; + if (gp->gp_stub) gp->gp_stub->gs_refcount++; +} + +t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp) +{ + t_inlet *x = (t_inlet *)pd_new(pointerinlet_class), *y, *y2; + x->i_owner = owner; + x->i_dest = 0; + x->i_symfrom = &s_pointer; + x->i_pointerslot = gp; + x->i_next = 0; + if (y = owner->ob_inlet) + { + while (y2 = y->i_next) y = y2; + y->i_next = x; + } + else owner->ob_inlet = x; + return (x); +} + +static void floatinlet_float(t_inlet *x, t_float f) +{ + *(x->i_floatslot) = f; +} + +t_inlet *floatinlet_new(t_object *owner, t_float *fp) +{ + t_inlet *x = (t_inlet *)pd_new(floatinlet_class), *y, *y2; + x->i_owner = owner; + x->i_dest = 0; + x->i_symfrom = &s_float; + x->i_floatslot = fp; + x->i_next = 0; + if (y = owner->ob_inlet) + { + while (y2 = y->i_next) y = y2; + y->i_next = x; + } + else owner->ob_inlet = x; + return (x); +} + +static void symbolinlet_symbol(t_inlet *x, t_symbol *s) +{ + *(x->i_symslot) = s; +} + +t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp) +{ + t_inlet *x = (t_inlet *)pd_new(symbolinlet_class), *y, *y2; + x->i_owner = owner; + x->i_dest = 0; + x->i_symfrom = &s_symbol; + x->i_symslot = sp; + x->i_next = 0; + if (y = owner->ob_inlet) + { + while (y2 = y->i_next) y = y2; + y->i_next = x; + } + else owner->ob_inlet = x; + return (x); +} + +/* ---------------------- routine to handle lists ---------------------- */ + + /* objects interpret lists by feeding them to the individual inlets. + Before you call this check that the object doesn't have a more + specific way to handle lists. */ +void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv) +{ + t_atom *ap; + int count; + t_inlet *ip = ((t_object *)x)->ob_inlet; + if (!argc) return; + for (count = argc-1, ap = argv+1; ip && count--; ap++, ip = ip->i_next) + { + if (ap->a_type == A_POINTER) pd_pointer(&ip->i_pd, ap->a_w.w_gpointer); + else if (ap->a_type == A_FLOAT) pd_float(&ip->i_pd, ap->a_w.w_float); + else pd_symbol(&ip->i_pd, ap->a_w.w_symbol); + } + if (argv->a_type == A_POINTER) pd_pointer(&x->ob_pd, argv->a_w.w_gpointer); + else if (argv->a_type == A_FLOAT) pd_float(&x->ob_pd, argv->a_w.w_float); + else pd_symbol(&x->ob_pd, argv->a_w.w_symbol); +} + +void obj_init(void) +{ + inlet_class = class_new(gensym("inlet"), 0, 0, + sizeof(t_inlet), CLASS_PD, 0); + class_addbang(inlet_class, inlet_bang); + class_addpointer(inlet_class, inlet_pointer); + class_addfloat(inlet_class, inlet_float); + class_addsymbol(inlet_class, inlet_symbol); + class_addlist(inlet_class, inlet_list); + class_addanything(inlet_class, inlet_anything); + + pointerinlet_class = class_new(gensym("inlet"), 0, 0, + sizeof(t_inlet), CLASS_PD, 0); + class_addpointer(pointerinlet_class, pointerinlet_pointer); + + floatinlet_class = class_new(gensym("inlet"), 0, 0, + sizeof(t_inlet), CLASS_PD, 0); + class_addfloat(floatinlet_class, (t_method)floatinlet_float); + + symbolinlet_class = class_new(gensym("inlet"), 0, 0, + sizeof(t_inlet), CLASS_PD, 0); + class_addsymbol(symbolinlet_class, symbolinlet_symbol); + +} + +/* --------------------------- outlets ------------------------------ */ + +static char *stacklimit, *topstack; +#define STACKSIZE 1000000 +static int outlet_eventno; + + /* set a stack limit (on each incoming event that can set off messages) + for the outlet functions to check to prevent stack overflow from message + recursion */ +void outlet_setstacklim(void) +{ + char c; + topstack = &c; + stacklimit = (&c) - STACKSIZE; + outlet_eventno++; +} + + /* get a number unique to the (clock, MIDI, GUI, etc.) event we're on */ +int sched_geteventno( void) +{ + return (outlet_eventno); +} + +struct _outconnect +{ + struct _outconnect *oc_next; + t_pd *oc_to; +}; + +struct _outlet +{ + t_object *o_owner; + struct _outlet *o_next; + t_outconnect *o_connections; + t_symbol *o_sym; +}; + +t_outlet *outlet_new(t_object *owner, t_symbol *s) +{ + t_outlet *x = (t_outlet *)getbytes(sizeof(*x)), *y, *y2; + x->o_owner = owner; + x->o_next = 0; + if (y = owner->ob_outlet) + { + while (y2 = y->o_next) y = y2; + y->o_next = x; + } + else owner->ob_outlet = x; + x->o_connections = 0; + x->o_sym = s; + return (x); +} + +static void outlet_stackerror(t_outlet *x) +{ + pd_error(x->o_owner, "stack overflow"); + stacklimit = topstack; +} + +void outlet_bang(t_outlet *x) +{ + t_outconnect *oc; + char c; + if (&c < stacklimit) + outlet_stackerror(x); + else for (oc = x->o_connections; oc; oc = oc->oc_next) + pd_bang(oc->oc_to); +} + +void outlet_pointer(t_outlet *x, t_gpointer *gp) +{ + t_outconnect *oc; + t_gpointer gpointer; + char c; + if (&c < stacklimit) + outlet_stackerror(x); + else + { +#if 0 + gpointer_copy(gp, &gpointer); + for (oc = x->o_connections; oc; oc = oc->oc_next) + pd_pointer(oc->oc_to, &gpointer); + gpointer_unset(&gpointer); +#else + gpointer = *gp; + for (oc = x->o_connections; oc; oc = oc->oc_next) + pd_pointer(oc->oc_to, &gpointer); +#endif + } +} + +void outlet_float(t_outlet *x, t_float f) +{ + t_outconnect *oc; + char c; + if (&c < stacklimit) + outlet_stackerror(x); + else for (oc = x->o_connections; oc; oc = oc->oc_next) + pd_float(oc->oc_to, f); +} + +void outlet_symbol(t_outlet *x, t_symbol *s) +{ + t_outconnect *oc; + char c; + if (&c < stacklimit) + outlet_stackerror(x); + else for (oc = x->o_connections; oc; oc = oc->oc_next) + pd_symbol(oc->oc_to, s); +} + +void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv) +{ + t_outconnect *oc; + char c; + if (&c < stacklimit) + outlet_stackerror(x); + else for (oc = x->o_connections; oc; oc = oc->oc_next) + pd_list(oc->oc_to, s, argc, argv); +} + +void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv) +{ + t_outconnect *oc; + char c; + if (&c < stacklimit) + outlet_stackerror(x); + else for (oc = x->o_connections; oc; oc = oc->oc_next) + typedmess(oc->oc_to, s, argc, argv); +} + + /* get the outlet's declared symbol */ +t_symbol *outlet_getsymbol(t_outlet *x) +{ + return (x->o_sym); +} + +void outlet_free(t_outlet *x) +{ + t_object *y = x->o_owner; + t_outlet *x2; + if (y->ob_outlet == x) y->ob_outlet = x->o_next; + else for (x2 = y->ob_outlet; x2; x2 = x2->o_next) + if (x2->o_next == x) + { + x2->o_next = x->o_next; + break; + } + t_freebytes(x, sizeof(*x)); +} + +t_outconnect *obj_connect(t_object *source, int outno, + t_object *sink, int inno) +{ + t_inlet *i; + t_outlet *o; + t_pd *to; + t_outconnect *oc, *oc2; + + for (o = source->ob_outlet; o && outno; o = o->o_next, outno--) ; + if (!o) return (0); + + if (sink->ob_pd->c_firstin) + { + if (!inno) + { + to = &sink->ob_pd; + goto doit; + } + else inno--; + } + for (i = sink->ob_inlet; i && inno; i = i->i_next, inno--) ; + if (!i) return (0); + to = &i->i_pd; +doit: + oc = (t_outconnect *)t_getbytes(sizeof(*oc)); + oc->oc_next = 0; + oc->oc_to = to; + /* append it to the end of the list */ + /* LATER we might cache the last "oc" to make this faster. */ + if ((oc2 = o->o_connections)) + { + while (oc2->oc_next) oc2 = oc2->oc_next; + oc2->oc_next = oc; + } + else o->o_connections = oc; + if (o->o_sym == &s_signal) canvas_update_dsp(); + + return (oc); +} + +void obj_disconnect(t_object *source, int outno, t_object *sink, int inno) +{ + t_inlet *i; + t_outlet *o; + t_pd *to; + t_outconnect *oc, *oc2; + + for (o = source->ob_outlet; o && outno; o = o->o_next, outno--) + if (!o) return; + if (sink->ob_pd->c_firstin) + { + if (!inno) + { + to = &sink->ob_pd; + goto doit; + } + else inno--; + } + for (i = sink->ob_inlet; i && inno; i = i->i_next, inno--) ; + if (!i) return; + to = &i->i_pd; +doit: + if (!(oc = o->o_connections)) return; + if (oc->oc_to == to) + { + o->o_connections = oc->oc_next; + freebytes(oc, sizeof(*oc)); + goto done; + } + while (oc2 = oc->oc_next) + { + if (oc2->oc_to == to) + { + oc->oc_next = oc2->oc_next; + freebytes(oc2, sizeof(*oc2)); + goto done; + } + oc = oc2; + } +done: + if (o->o_sym == &s_signal) canvas_update_dsp(); +} + +/* ------ traversal routines for code that can't see our structures ------ */ + +int obj_noutlets(t_object *x) +{ + int n; + t_outlet *o; + for (o = x->ob_outlet, n = 0; o; o = o->o_next) n++; + return (n); +} + +int obj_ninlets(t_object *x) +{ + int n; + t_inlet *i; + for (i = x->ob_inlet, n = 0; i; i = i->i_next) n++; + if (x->ob_pd->c_firstin) n++; + return (n); +} + +t_outconnect *obj_starttraverseoutlet(t_object *x, t_outlet **op, int nout) +{ + t_outlet *o = x->ob_outlet; + while (nout-- && o) o = o->o_next; + *op = o; + if (o) return (o->o_connections); + else return (0); +} + +t_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect, + t_object **destp, t_inlet **inletp, int *whichp) +{ + t_pd *y; + y = lastconnect->oc_to; + if (ISINLET(y)) + { + int n; + t_inlet *i = (t_inlet *)y, *i2; + t_object *dest = i->i_owner; + for (n = dest->ob_pd->c_firstin, i2 = dest->ob_inlet; + i2 && i2 != i; i2 = i2->i_next) n++; + *whichp = n; + *destp = dest; + *inletp = i; + } + else + { + *whichp = 0; + *inletp = 0; + *destp = ((t_object *)y); + } + return (lastconnect->oc_next); +} + + /* this one checks that a pd is indeed a patchable object, and returns + it, correctly typed, or zero if the check failed. */ +t_object *pd_checkobject(t_pd *x) +{ + if ((*x)->c_patchable) return ((t_object *)x); + else return (0); +} + + /* move an inlet or outlet to the head of the list */ +void obj_moveinletfirst(t_object *x, t_inlet *i) +{ + t_inlet *i2; + if (x->ob_inlet == i) return; + else for (i2 = x->ob_inlet; i2; i2 = i2->i_next) + if (i2->i_next == i) + { + i2->i_next = i->i_next; + i->i_next = x->ob_inlet; + x->ob_inlet = i; + return; + } +} + +void obj_moveoutletfirst(t_object *x, t_outlet *o) +{ + t_outlet *o2; + if (x->ob_outlet == o) return; + else for (o2 = x->ob_outlet; o2; o2 = o2->o_next) + if (o2->o_next == o) + { + o2->o_next = o->o_next; + o->o_next = x->ob_outlet; + x->ob_outlet = o; + return; + } +} + + /* routines for DSP sorting, which are used in d_ugen.c and g_canvas.c */ + /* LATER try to consolidate all the slightly different routines. */ + +int obj_nsiginlets(t_object *x) +{ + int n; + t_inlet *i; + for (i = x->ob_inlet, n = 0; i; i = i->i_next) + if (i->i_symfrom == &s_signal) n++; + if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin) n++; + return (n); +} + + /* get the index, among signal inlets, of the mth inlet overall */ +int obj_siginletindex(t_object *x, int m) +{ + int n = 0; + t_inlet *i; + if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin) + { + if (!m--) return (0); + n++; + } + for (i = x->ob_inlet; i; i = i->i_next, m--) + if (i->i_symfrom == &s_signal) + { + if (m == 0) return (n); + n++; + } + return (-1); +} + +int obj_issignalinlet(t_object *x, int m) +{ + t_inlet *i; + if (x->ob_pd->c_firstin) + { + if (!m) + return (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin); + else m--; + } + for (i = x->ob_inlet; i && m; i = i->i_next, m--) + ; + return (i && (i->i_symfrom == &s_signal)); +} + +int obj_nsigoutlets(t_object *x) +{ + int n; + t_outlet *o; + for (o = x->ob_outlet, n = 0; o; o = o->o_next) + if (o->o_sym == &s_signal) n++; + return (n); +} + +int obj_sigoutletindex(t_object *x, int m) +{ + int n; + t_outlet *o2; + for (o2 = x->ob_outlet, n = 0; o2; o2 = o2->o_next, m--) + if (o2->o_sym == &s_signal) + { + if (m == 0) return (n); + n++; + } + return (-1); +} + +int obj_issignaloutlet(t_object *x, int m) +{ + int n; + t_outlet *o2; + for (o2 = x->ob_outlet, n = 0; o2 && m--; o2 = o2->o_next); + return (o2 && (o2->o_sym == &s_signal)); +} + +t_sample *obj_findsignalscalar(t_object *x, int m) +{ + int n = 0; + t_inlet *i; + if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin) + { + if (!m--) + return (x->ob_pd->c_floatsignalin > 0 ? + (t_sample *)(((char *)x) + x->ob_pd->c_floatsignalin) : 0); + n++; + } + for (i = x->ob_inlet; i; i = i->i_next, m--) + if (i->i_symfrom == &s_signal) + { + if (m == 0) + return (&i->i_un.iu_floatsignalvalue); + n++; + } + return (0); +} + +/* and these are only used in g_io.c... */ + +int inlet_getsignalindex(t_inlet *x) +{ + int n = 0; + t_inlet *i; + for (i = x->i_owner->ob_inlet, n = 0; i && i != x; i = i->i_next) + if (i->i_symfrom == &s_signal) n++; + return (n); +} + +int outlet_getsignalindex(t_outlet *x) +{ + int n = 0; + t_outlet *o; + for (o = x->o_owner->ob_outlet, n = 0; o && o != x; o = o->o_next) + if (o->o_sym == &s_signal) n++; + return (n); +} + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* this file handles Max-style patchable objects, i.e., objects which +can interconnect via inlets and outlets; also, the (terse) generic +behavior for "gobjs" appears at the end of this file. */ + +#include "m_pd.h" +#include "m_imp.h" + +union inletunion +{ + t_symbol *iu_symto; + t_gpointer *iu_pointerslot; + t_float *iu_floatslot; + t_symbol **iu_symslot; + t_sample iu_floatsignalvalue; +}; + +struct _inlet +{ + t_pd i_pd; + struct _inlet *i_next; + t_object *i_owner; + t_pd *i_dest; + t_symbol *i_symfrom; + union inletunion i_un; +}; + +#define i_symto i_un.iu_symto +#define i_pointerslot i_un.iu_pointerslot +#define i_floatslot i_un.iu_floatslot +#define i_symslot i_un.iu_symslot + +static t_class *inlet_class, *pointerinlet_class, *floatinlet_class, + *symbolinlet_class; + +#define ISINLET(pd) ((*(pd) == inlet_class) || \ + (*(pd) == pointerinlet_class) || \ + (*(pd) == floatinlet_class) || \ + (*(pd) == symbolinlet_class)) + +/* --------------------- generic inlets ala max ------------------ */ + +t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1, t_symbol *s2) +{ + t_inlet *x = (t_inlet *)pd_new(inlet_class), *y, *y2; + x->i_owner = owner; + x->i_dest = dest; + if (s1 == &s_signal) + x->i_un.iu_floatsignalvalue = 0; + else x->i_symto = s2; + x->i_symfrom = s1; + x->i_next = 0; + if (y = owner->ob_inlet) + { + while (y2 = y->i_next) y = y2; + y->i_next = x; + } + else owner->ob_inlet = x; + return (x); +} + +static void inlet_wrong(t_inlet *x, t_symbol *s) +{ + pd_error(x->i_owner, "inlet: expected '%s' but got '%s'", + x->i_symfrom->s_name, s->s_name); +} + + /* LATER figure out how to make these efficient: */ +static void inlet_bang(t_inlet *x) +{ + if (x->i_symfrom == &s_bang) + pd_vmess(x->i_dest, x->i_symto, ""); + else if (!x->i_symfrom) pd_bang(x->i_dest); + else inlet_wrong(x, &s_bang); +} + +static void inlet_pointer(t_inlet *x, t_gpointer *gp) +{ + if (x->i_symfrom == &s_pointer) + pd_vmess(x->i_dest, x->i_symto, "p", gp); + else if (!x->i_symfrom) pd_pointer(x->i_dest, gp); + else inlet_wrong(x, &s_pointer); +} + +static void inlet_float(t_inlet *x, t_float f) +{ + if (x->i_symfrom == &s_float) + pd_vmess(x->i_dest, x->i_symto, "f", (t_floatarg)f); + else if (x->i_symfrom == &s_signal) + x->i_un.iu_floatsignalvalue = ftofix(f); + else if (!x->i_symfrom) + pd_float(x->i_dest, f); + else inlet_wrong(x, &s_float); +} + +static void inlet_symbol(t_inlet *x, t_symbol *s) +{ + if (x->i_symfrom == &s_symbol) + pd_vmess(x->i_dest, x->i_symto, "s", s); + else if (!x->i_symfrom) pd_symbol(x->i_dest, s); + else inlet_wrong(x, &s_symbol); +} + +static void inlet_list(t_inlet *x, t_symbol *s, int argc, t_atom *argv) +{ + t_atom at; + if (x->i_symfrom == &s_list || x->i_symfrom == &s_float + || x->i_symfrom == &s_symbol || x->i_symfrom == &s_pointer) + typedmess(x->i_dest, x->i_symto, argc, argv); + else if (!x->i_symfrom) pd_list(x->i_dest, s, argc, argv); + else inlet_wrong(x, &s_list); +} + +static void inlet_anything(t_inlet *x, t_symbol *s, int argc, t_atom *argv) +{ + if (x->i_symfrom == s) + typedmess(x->i_dest, x->i_symto, argc, argv); + else if (!x->i_symfrom) + typedmess(x->i_dest, s, argc, argv); + else inlet_wrong(x, s); +} + +void inlet_free(t_inlet *x) +{ + t_object *y = x->i_owner; + t_inlet *x2; + if (y->ob_inlet == x) y->ob_inlet = x->i_next; + else for (x2 = y->ob_inlet; x2; x2 = x2->i_next) + if (x2->i_next == x) + { + x2->i_next = x->i_next; + break; + } + t_freebytes(x, sizeof(*x)); +} + +/* ----- pointerinlets, floatinlets, syminlets: optimized inlets ------- */ + +static void pointerinlet_pointer(t_inlet *x, t_gpointer *gp) +{ + gpointer_unset(x->i_pointerslot); + *(x->i_pointerslot) = *gp; + if (gp->gp_stub) gp->gp_stub->gs_refcount++; +} + +t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp) +{ + t_inlet *x = (t_inlet *)pd_new(pointerinlet_class), *y, *y2; + x->i_owner = owner; + x->i_dest = 0; + x->i_symfrom = &s_pointer; + x->i_pointerslot = gp; + x->i_next = 0; + if (y = owner->ob_inlet) + { + while (y2 = y->i_next) y = y2; + y->i_next = x; + } + else owner->ob_inlet = x; + return (x); +} + +static void floatinlet_float(t_inlet *x, t_float f) +{ + *(x->i_floatslot) = f; +} + +t_inlet *floatinlet_new(t_object *owner, t_float *fp) +{ + t_inlet *x = (t_inlet *)pd_new(floatinlet_class), *y, *y2; + x->i_owner = owner; + x->i_dest = 0; + x->i_symfrom = &s_float; + x->i_floatslot = fp; + x->i_next = 0; + if (y = owner->ob_inlet) + { + while (y2 = y->i_next) y = y2; + y->i_next = x; + } + else owner->ob_inlet = x; + return (x); +} + +static void symbolinlet_symbol(t_inlet *x, t_symbol *s) +{ + *(x->i_symslot) = s; +} + +t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp) +{ + t_inlet *x = (t_inlet *)pd_new(symbolinlet_class), *y, *y2; + x->i_owner = owner; + x->i_dest = 0; + x->i_symfrom = &s_symbol; + x->i_symslot = sp; + x->i_next = 0; + if (y = owner->ob_inlet) + { + while (y2 = y->i_next) y = y2; + y->i_next = x; + } + else owner->ob_inlet = x; + return (x); +} + +/* ---------------------- routine to handle lists ---------------------- */ + + /* objects interpret lists by feeding them to the individual inlets. + Before you call this check that the object doesn't have a more + specific way to handle lists. */ +void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv) +{ + t_atom *ap; + int count; + t_inlet *ip = ((t_object *)x)->ob_inlet; + if (!argc) return; + for (count = argc-1, ap = argv+1; ip && count--; ap++, ip = ip->i_next) + { + if (ap->a_type == A_POINTER) pd_pointer(&ip->i_pd, ap->a_w.w_gpointer); + else if (ap->a_type == A_FLOAT) pd_float(&ip->i_pd, ap->a_w.w_float); + else pd_symbol(&ip->i_pd, ap->a_w.w_symbol); + } + if (argv->a_type == A_POINTER) pd_pointer(&x->ob_pd, argv->a_w.w_gpointer); + else if (argv->a_type == A_FLOAT) pd_float(&x->ob_pd, argv->a_w.w_float); + else pd_symbol(&x->ob_pd, argv->a_w.w_symbol); +} + +void obj_init(void) +{ + inlet_class = class_new(gensym("inlet"), 0, 0, + sizeof(t_inlet), CLASS_PD, 0); + class_addbang(inlet_class, inlet_bang); + class_addpointer(inlet_class, inlet_pointer); + class_addfloat(inlet_class, inlet_float); + class_addsymbol(inlet_class, inlet_symbol); + class_addlist(inlet_class, inlet_list); + class_addanything(inlet_class, inlet_anything); + + pointerinlet_class = class_new(gensym("inlet"), 0, 0, + sizeof(t_inlet), CLASS_PD, 0); + class_addpointer(pointerinlet_class, pointerinlet_pointer); + + floatinlet_class = class_new(gensym("inlet"), 0, 0, + sizeof(t_inlet), CLASS_PD, 0); + class_addfloat(floatinlet_class, (t_method)floatinlet_float); + + symbolinlet_class = class_new(gensym("inlet"), 0, 0, + sizeof(t_inlet), CLASS_PD, 0); + class_addsymbol(symbolinlet_class, symbolinlet_symbol); + +} + +/* --------------------------- outlets ------------------------------ */ + +static char *stacklimit, *topstack; +#define STACKSIZE 1000000 +static int outlet_eventno; + + /* set a stack limit (on each incoming event that can set off messages) + for the outlet functions to check to prevent stack overflow from message + recursion */ +void outlet_setstacklim(void) +{ + char c; + topstack = &c; + stacklimit = (&c) - STACKSIZE; + outlet_eventno++; +} + + /* get a number unique to the (clock, MIDI, GUI, etc.) event we're on */ +int sched_geteventno( void) +{ + return (outlet_eventno); +} + +struct _outconnect +{ + struct _outconnect *oc_next; + t_pd *oc_to; +}; + +struct _outlet +{ + t_object *o_owner; + struct _outlet *o_next; + t_outconnect *o_connections; + t_symbol *o_sym; +}; + +t_outlet *outlet_new(t_object *owner, t_symbol *s) +{ + t_outlet *x = (t_outlet *)getbytes(sizeof(*x)), *y, *y2; + x->o_owner = owner; + x->o_next = 0; + if (y = owner->ob_outlet) + { + while (y2 = y->o_next) y = y2; + y->o_next = x; + } + else owner->ob_outlet = x; + x->o_connections = 0; + x->o_sym = s; + return (x); +} + +static void outlet_stackerror(t_outlet *x) +{ + pd_error(x->o_owner, "stack overflow"); + stacklimit = topstack; +} + +void outlet_bang(t_outlet *x) +{ + t_outconnect *oc; + char c; + if (&c < stacklimit) + outlet_stackerror(x); + else for (oc = x->o_connections; oc; oc = oc->oc_next) + pd_bang(oc->oc_to); +} + +void outlet_pointer(t_outlet *x, t_gpointer *gp) +{ + t_outconnect *oc; + t_gpointer gpointer; + char c; + if (&c < stacklimit) + outlet_stackerror(x); + else + { +#if 0 + gpointer_copy(gp, &gpointer); + for (oc = x->o_connections; oc; oc = oc->oc_next) + pd_pointer(oc->oc_to, &gpointer); + gpointer_unset(&gpointer); +#else + gpointer = *gp; + for (oc = x->o_connections; oc; oc = oc->oc_next) + pd_pointer(oc->oc_to, &gpointer); +#endif + } +} + +void outlet_float(t_outlet *x, t_float f) +{ + t_outconnect *oc; + char c; + if (&c < stacklimit) + outlet_stackerror(x); + else for (oc = x->o_connections; oc; oc = oc->oc_next) + pd_float(oc->oc_to, f); +} + +void outlet_symbol(t_outlet *x, t_symbol *s) +{ + t_outconnect *oc; + char c; + if (&c < stacklimit) + outlet_stackerror(x); + else for (oc = x->o_connections; oc; oc = oc->oc_next) + pd_symbol(oc->oc_to, s); +} + +void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv) +{ + t_outconnect *oc; + char c; + if (&c < stacklimit) + outlet_stackerror(x); + else for (oc = x->o_connections; oc; oc = oc->oc_next) + pd_list(oc->oc_to, s, argc, argv); +} + +void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv) +{ + t_outconnect *oc; + char c; + if (&c < stacklimit) + outlet_stackerror(x); + else for (oc = x->o_connections; oc; oc = oc->oc_next) + typedmess(oc->oc_to, s, argc, argv); +} + + /* get the outlet's declared symbol */ +t_symbol *outlet_getsymbol(t_outlet *x) +{ + return (x->o_sym); +} + +void outlet_free(t_outlet *x) +{ + t_object *y = x->o_owner; + t_outlet *x2; + if (y->ob_outlet == x) y->ob_outlet = x->o_next; + else for (x2 = y->ob_outlet; x2; x2 = x2->o_next) + if (x2->o_next == x) + { + x2->o_next = x->o_next; + break; + } + t_freebytes(x, sizeof(*x)); +} + +t_outconnect *obj_connect(t_object *source, int outno, + t_object *sink, int inno) +{ + t_inlet *i; + t_outlet *o; + t_pd *to; + t_outconnect *oc, *oc2; + + for (o = source->ob_outlet; o && outno; o = o->o_next, outno--) ; + if (!o) return (0); + + if (sink->ob_pd->c_firstin) + { + if (!inno) + { + to = &sink->ob_pd; + goto doit; + } + else inno--; + } + for (i = sink->ob_inlet; i && inno; i = i->i_next, inno--) ; + if (!i) return (0); + to = &i->i_pd; +doit: + oc = (t_outconnect *)t_getbytes(sizeof(*oc)); + oc->oc_next = 0; + oc->oc_to = to; + /* append it to the end of the list */ + /* LATER we might cache the last "oc" to make this faster. */ + if ((oc2 = o->o_connections)) + { + while (oc2->oc_next) oc2 = oc2->oc_next; + oc2->oc_next = oc; + } + else o->o_connections = oc; + if (o->o_sym == &s_signal) canvas_update_dsp(); + + return (oc); +} + +void obj_disconnect(t_object *source, int outno, t_object *sink, int inno) +{ + t_inlet *i; + t_outlet *o; + t_pd *to; + t_outconnect *oc, *oc2; + + for (o = source->ob_outlet; o && outno; o = o->o_next, outno--) + if (!o) return; + if (sink->ob_pd->c_firstin) + { + if (!inno) + { + to = &sink->ob_pd; + goto doit; + } + else inno--; + } + for (i = sink->ob_inlet; i && inno; i = i->i_next, inno--) ; + if (!i) return; + to = &i->i_pd; +doit: + if (!(oc = o->o_connections)) return; + if (oc->oc_to == to) + { + o->o_connections = oc->oc_next; + freebytes(oc, sizeof(*oc)); + goto done; + } + while (oc2 = oc->oc_next) + { + if (oc2->oc_to == to) + { + oc->oc_next = oc2->oc_next; + freebytes(oc2, sizeof(*oc2)); + goto done; + } + oc = oc2; + } +done: + if (o->o_sym == &s_signal) canvas_update_dsp(); +} + +/* ------ traversal routines for code that can't see our structures ------ */ + +int obj_noutlets(t_object *x) +{ + int n; + t_outlet *o; + for (o = x->ob_outlet, n = 0; o; o = o->o_next) n++; + return (n); +} + +int obj_ninlets(t_object *x) +{ + int n; + t_inlet *i; + for (i = x->ob_inlet, n = 0; i; i = i->i_next) n++; + if (x->ob_pd->c_firstin) n++; + return (n); +} + +t_outconnect *obj_starttraverseoutlet(t_object *x, t_outlet **op, int nout) +{ + t_outlet *o = x->ob_outlet; + while (nout-- && o) o = o->o_next; + *op = o; + if (o) return (o->o_connections); + else return (0); +} + +t_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect, + t_object **destp, t_inlet **inletp, int *whichp) +{ + t_pd *y; + y = lastconnect->oc_to; + if (ISINLET(y)) + { + int n; + t_inlet *i = (t_inlet *)y, *i2; + t_object *dest = i->i_owner; + for (n = dest->ob_pd->c_firstin, i2 = dest->ob_inlet; + i2 && i2 != i; i2 = i2->i_next) n++; + *whichp = n; + *destp = dest; + *inletp = i; + } + else + { + *whichp = 0; + *inletp = 0; + *destp = ((t_object *)y); + } + return (lastconnect->oc_next); +} + + /* this one checks that a pd is indeed a patchable object, and returns + it, correctly typed, or zero if the check failed. */ +t_object *pd_checkobject(t_pd *x) +{ + if ((*x)->c_patchable) return ((t_object *)x); + else return (0); +} + + /* move an inlet or outlet to the head of the list */ +void obj_moveinletfirst(t_object *x, t_inlet *i) +{ + t_inlet *i2; + if (x->ob_inlet == i) return; + else for (i2 = x->ob_inlet; i2; i2 = i2->i_next) + if (i2->i_next == i) + { + i2->i_next = i->i_next; + i->i_next = x->ob_inlet; + x->ob_inlet = i; + return; + } +} + +void obj_moveoutletfirst(t_object *x, t_outlet *o) +{ + t_outlet *o2; + if (x->ob_outlet == o) return; + else for (o2 = x->ob_outlet; o2; o2 = o2->o_next) + if (o2->o_next == o) + { + o2->o_next = o->o_next; + o->o_next = x->ob_outlet; + x->ob_outlet = o; + return; + } +} + + /* routines for DSP sorting, which are used in d_ugen.c and g_canvas.c */ + /* LATER try to consolidate all the slightly different routines. */ + +int obj_nsiginlets(t_object *x) +{ + int n; + t_inlet *i; + for (i = x->ob_inlet, n = 0; i; i = i->i_next) + if (i->i_symfrom == &s_signal) n++; + if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin) n++; + return (n); +} + + /* get the index, among signal inlets, of the mth inlet overall */ +int obj_siginletindex(t_object *x, int m) +{ + int n = 0; + t_inlet *i; + if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin) + { + if (!m--) return (0); + n++; + } + for (i = x->ob_inlet; i; i = i->i_next, m--) + if (i->i_symfrom == &s_signal) + { + if (m == 0) return (n); + n++; + } + return (-1); +} + +int obj_issignalinlet(t_object *x, int m) +{ + t_inlet *i; + if (x->ob_pd->c_firstin) + { + if (!m) + return (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin); + else m--; + } + for (i = x->ob_inlet; i && m; i = i->i_next, m--) + ; + return (i && (i->i_symfrom == &s_signal)); +} + +int obj_nsigoutlets(t_object *x) +{ + int n; + t_outlet *o; + for (o = x->ob_outlet, n = 0; o; o = o->o_next) + if (o->o_sym == &s_signal) n++; + return (n); +} + +int obj_sigoutletindex(t_object *x, int m) +{ + int n; + t_outlet *o2; + for (o2 = x->ob_outlet, n = 0; o2; o2 = o2->o_next, m--) + if (o2->o_sym == &s_signal) + { + if (m == 0) return (n); + n++; + } + return (-1); +} + +int obj_issignaloutlet(t_object *x, int m) +{ + int n; + t_outlet *o2; + for (o2 = x->ob_outlet, n = 0; o2 && m--; o2 = o2->o_next); + return (o2 && (o2->o_sym == &s_signal)); +} + +t_sample *obj_findsignalscalar(t_object *x, int m) +{ + int n = 0; + t_inlet *i; + if (x->ob_pd->c_firstin && x->ob_pd->c_floatsignalin) + { + if (!m--) + return (x->ob_pd->c_floatsignalin > 0 ? + (t_sample *)(((char *)x) + x->ob_pd->c_floatsignalin) : 0); + n++; + } + for (i = x->ob_inlet; i; i = i->i_next, m--) + if (i->i_symfrom == &s_signal) + { + if (m == 0) + return (&i->i_un.iu_floatsignalvalue); + n++; + } + return (0); +} + +/* and these are only used in g_io.c... */ + +int inlet_getsignalindex(t_inlet *x) +{ + int n = 0; + t_inlet *i; + for (i = x->i_owner->ob_inlet, n = 0; i && i != x; i = i->i_next) + if (i->i_symfrom == &s_signal) n++; + return (n); +} + +int outlet_getsignalindex(t_outlet *x) +{ + int n = 0; + t_outlet *o; + for (o = x->o_owner->ob_outlet, n = 0; o && o != x; o = o->o_next) + if (o->o_sym == &s_signal) n++; + return (n); +} + diff --git a/apps/plugins/pdbox/PDa/src/m_pd.c b/apps/plugins/pdbox/PDa/src/m_pd.c new file mode 100644 index 0000000..321574b --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/m_pd.c @@ -0,0 +1,612 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include +#include "m_pd.h" +#include "m_imp.h" + + /* FIXME no out-of-memory testing yet! */ + +t_pd *pd_new(t_class *c) +{ + t_pd *x; + if (!c) + bug ("pd_new: apparently called before setup routine"); + x = (t_pd *)t_getbytes(c->c_size); + *x = c; + if (c->c_patchable) + { + ((t_object *)x)->ob_inlet = 0; + ((t_object *)x)->ob_outlet = 0; + } + return (x); +} + +void pd_free(t_pd *x) +{ + t_class *c = *x; + if (c->c_freemethod) (*(t_gotfn)(c->c_freemethod))(x); + if (c->c_patchable) + { + while (((t_object *)x)->ob_outlet) + outlet_free(((t_object *)x)->ob_outlet); + while (((t_object *)x)->ob_inlet) + inlet_free(((t_object *)x)->ob_inlet); + if (((t_object *)x)->ob_binbuf) + binbuf_free(((t_object *)x)->ob_binbuf); + } + if (c->c_size) t_freebytes(x, c->c_size); +} + +void gobj_save(t_gobj *x, t_binbuf *b) +{ + t_class *c = x->g_pd; + if (c->c_savefn) + (c->c_savefn)(x, b); +} + +/* deal with several objects bound to the same symbol. If more than one, we +actually bind a collection object to the symbol, which forwards messages sent +to the symbol. */ + +static t_class *bindlist_class; + +typedef struct _bindelem +{ + t_pd *e_who; + struct _bindelem *e_next; +} t_bindelem; + +typedef struct _bindlist +{ + t_pd b_pd; + t_bindelem *b_list; +} t_bindlist; + +static void bindlist_bang(t_bindlist *x) +{ + t_bindelem *e; + for (e = x->b_list; e; e = e->e_next) + pd_bang(e->e_who); +} + +static void bindlist_float(t_bindlist *x, t_float f) +{ + t_bindelem *e; + for (e = x->b_list; e; e = e->e_next) { + pd_float(e->e_who, f); + } +} + +static void bindlist_symbol(t_bindlist *x, t_symbol *s) +{ + t_bindelem *e; + for (e = x->b_list; e; e = e->e_next) + pd_symbol(e->e_who, s); +} + +static void bindlist_pointer(t_bindlist *x, t_gpointer *gp) +{ + t_bindelem *e; + for (e = x->b_list; e; e = e->e_next) + pd_pointer(e->e_who, gp); +} + +static void bindlist_list(t_bindlist *x, t_symbol *s, + int argc, t_atom *argv) +{ + t_bindelem *e; + for (e = x->b_list; e; e = e->e_next) + pd_list(e->e_who, s, argc, argv); +} + +static void bindlist_anything(t_bindlist *x, t_symbol *s, + int argc, t_atom *argv) +{ + t_bindelem *e; + for (e = x->b_list; e; e = e->e_next) + pd_typedmess(e->e_who, s, argc, argv); +} + +void m_pd_setup(void) +{ + bindlist_class = class_new(gensym("bindlist"), 0, 0, + sizeof(t_bindlist), CLASS_PD, 0); + class_addbang(bindlist_class, bindlist_bang); + class_addfloat(bindlist_class, (t_method)bindlist_float); + class_addsymbol(bindlist_class, bindlist_symbol); + class_addpointer(bindlist_class, bindlist_pointer); + class_addlist(bindlist_class, bindlist_list); + class_addanything(bindlist_class, bindlist_anything); +} + +void pd_bind(t_pd *x, t_symbol *s) +{ + pd_checkgui(x,s); + if (s->s_thing) + { + if (*s->s_thing == bindlist_class) + { + t_bindlist *b = (t_bindlist *)s->s_thing; + t_bindelem *e = (t_bindelem *)getbytes(sizeof(t_bindelem)); + e->e_next = b->b_list; + e->e_who = x; + b->b_list = e; + } + else + { + t_bindlist *b = (t_bindlist *)pd_new(bindlist_class); + t_bindelem *e1 = (t_bindelem *)getbytes(sizeof(t_bindelem)); + t_bindelem *e2 = (t_bindelem *)getbytes(sizeof(t_bindelem)); + b->b_list = e1; + e1->e_who = x; + e1->e_next = e2; + e2->e_who = s->s_thing; + e2->e_next = 0; + s->s_thing = &b->b_pd; + } + } + else s->s_thing = x; +} + +void pd_unbind(t_pd *x, t_symbol *s) +{ + if (s->s_thing == x) s->s_thing = 0; + else if (s->s_thing && *s->s_thing == bindlist_class) + { + /* bindlists always have at least two elements... if the number + goes down to one, get rid of the bindlist and bind the symbol + straight to the remaining element. */ + + t_bindlist *b = (t_bindlist *)s->s_thing; + t_bindelem *e, *e2; + if ((e = b->b_list)->e_who == x) + { + b->b_list = e->e_next; + freebytes(e, sizeof(t_bindelem)); + } + else for (e = b->b_list; e2 = e->e_next; e = e2) + if (e2->e_who == x) + { + e->e_next = e2->e_next; + freebytes(e2, sizeof(t_bindelem)); + break; + } + if (!b->b_list->e_next) + { + s->s_thing = b->b_list->e_who; + freebytes(b->b_list, sizeof(t_bindelem)); + pd_free(&b->b_pd); + } + } + else pd_error(x, "%s: couldn't unbind", s->s_name); +} + +void zz(void) {} + +t_pd *pd_findbyclass(t_symbol *s, t_class *c) +{ + t_pd *x = 0; + + if (!s->s_thing) return (0); + if (*s->s_thing == c) return (s->s_thing); + if (*s->s_thing == bindlist_class) + { + t_bindlist *b = (t_bindlist *)s->s_thing; + t_bindelem *e, *e2; + int warned = 0; + for (e = b->b_list; e; e = e->e_next) + if (*e->e_who == c) + { + if (x && !warned) + { + zz(); + post("warning: %s: multiply defined", s->s_name); + warned = 1; + } + x = e->e_who; + } + } + return x; +} + +/* stack for maintaining bindings for the #X symbol during nestable loads. +*/ + +typedef struct _gstack +{ + t_pd *g_what; + t_symbol *g_loadingabstraction; + struct _gstack *g_next; +} t_gstack; + +static t_gstack *gstack_head = 0; +static t_pd *lastpopped; +static t_symbol *pd_loadingabstraction; + +int pd_setloadingabstraction(t_symbol *sym) +{ + t_gstack *foo = gstack_head; + for (foo = gstack_head; foo; foo = foo->g_next) + if (foo->g_loadingabstraction == sym) + return (1); + pd_loadingabstraction = sym; + return (0); +} + +void pd_pushsym(t_pd *x) +{ + t_gstack *y = (t_gstack *)t_getbytes(sizeof(*y)); + y->g_what = s__X.s_thing; + y->g_next = gstack_head; + y->g_loadingabstraction = pd_loadingabstraction; + pd_loadingabstraction = 0; + gstack_head = y; + s__X.s_thing = x; +} + +void pd_popsym(t_pd *x) +{ + if (!gstack_head || s__X.s_thing != x) bug("gstack_pop"); + else + { + t_gstack *headwas = gstack_head; + s__X.s_thing = headwas->g_what; + gstack_head = headwas->g_next; + t_freebytes(headwas, sizeof(*headwas)); + lastpopped = x; + } +} + +void pd_doloadbang(void) +{ + if (lastpopped) + pd_vmess(lastpopped, gensym("loadbang"), ""); + lastpopped = 0; +} + +void pd_bang(t_pd *x) +{ + (*(*x)->c_bangmethod)(x); +} + +void pd_float(t_pd *x, t_float f) +{ + (*(*x)->c_floatmethod)(x, f); +} + +void pd_pointer(t_pd *x, t_gpointer *gp) +{ + (*(*x)->c_pointermethod)(x, gp); +} + +void pd_symbol(t_pd *x, t_symbol *s) +{ + (*(*x)->c_symbolmethod)(x, s); +} + +void pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv) +{ + (*(*x)->c_listmethod)(x, &s_list, argc, argv); +} + +void mess_init(void); +void obj_init(void); +void conf_init(void); +void glob_init(void); + +void pd_init(void) +{ + mess_init(); + obj_init(); + conf_init(); + glob_init(); +} + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#include +#include "m_pd.h" +#include "m_imp.h" + + /* FIXME no out-of-memory testing yet! */ + +t_pd *pd_new(t_class *c) +{ + t_pd *x; + if (!c) + bug ("pd_new: apparently called before setup routine"); + x = (t_pd *)t_getbytes(c->c_size); + *x = c; + if (c->c_patchable) + { + ((t_object *)x)->ob_inlet = 0; + ((t_object *)x)->ob_outlet = 0; + } + return (x); +} + +void pd_free(t_pd *x) +{ + t_class *c = *x; + if (c->c_freemethod) (*(t_gotfn)(c->c_freemethod))(x); + if (c->c_patchable) + { + while (((t_object *)x)->ob_outlet) + outlet_free(((t_object *)x)->ob_outlet); + while (((t_object *)x)->ob_inlet) + inlet_free(((t_object *)x)->ob_inlet); + if (((t_object *)x)->ob_binbuf) + binbuf_free(((t_object *)x)->ob_binbuf); + } + if (c->c_size) t_freebytes(x, c->c_size); +} + +void gobj_save(t_gobj *x, t_binbuf *b) +{ + t_class *c = x->g_pd; + if (c->c_savefn) + (c->c_savefn)(x, b); +} + +/* deal with several objects bound to the same symbol. If more than one, we +actually bind a collection object to the symbol, which forwards messages sent +to the symbol. */ + +static t_class *bindlist_class; + +typedef struct _bindelem +{ + t_pd *e_who; + struct _bindelem *e_next; +} t_bindelem; + +typedef struct _bindlist +{ + t_pd b_pd; + t_bindelem *b_list; +} t_bindlist; + +static void bindlist_bang(t_bindlist *x) +{ + t_bindelem *e; + for (e = x->b_list; e; e = e->e_next) + pd_bang(e->e_who); +} + +static void bindlist_float(t_bindlist *x, t_float f) +{ + t_bindelem *e; + for (e = x->b_list; e; e = e->e_next) { + pd_float(e->e_who, f); + } +} + +static void bindlist_symbol(t_bindlist *x, t_symbol *s) +{ + t_bindelem *e; + for (e = x->b_list; e; e = e->e_next) + pd_symbol(e->e_who, s); +} + +static void bindlist_pointer(t_bindlist *x, t_gpointer *gp) +{ + t_bindelem *e; + for (e = x->b_list; e; e = e->e_next) + pd_pointer(e->e_who, gp); +} + +static void bindlist_list(t_bindlist *x, t_symbol *s, + int argc, t_atom *argv) +{ + t_bindelem *e; + for (e = x->b_list; e; e = e->e_next) + pd_list(e->e_who, s, argc, argv); +} + +static void bindlist_anything(t_bindlist *x, t_symbol *s, + int argc, t_atom *argv) +{ + t_bindelem *e; + for (e = x->b_list; e; e = e->e_next) + pd_typedmess(e->e_who, s, argc, argv); +} + +void m_pd_setup(void) +{ + bindlist_class = class_new(gensym("bindlist"), 0, 0, + sizeof(t_bindlist), CLASS_PD, 0); + class_addbang(bindlist_class, bindlist_bang); + class_addfloat(bindlist_class, (t_method)bindlist_float); + class_addsymbol(bindlist_class, bindlist_symbol); + class_addpointer(bindlist_class, bindlist_pointer); + class_addlist(bindlist_class, bindlist_list); + class_addanything(bindlist_class, bindlist_anything); +} + +void pd_bind(t_pd *x, t_symbol *s) +{ + pd_checkgui(x,s); + if (s->s_thing) + { + if (*s->s_thing == bindlist_class) + { + t_bindlist *b = (t_bindlist *)s->s_thing; + t_bindelem *e = (t_bindelem *)getbytes(sizeof(t_bindelem)); + e->e_next = b->b_list; + e->e_who = x; + b->b_list = e; + } + else + { + t_bindlist *b = (t_bindlist *)pd_new(bindlist_class); + t_bindelem *e1 = (t_bindelem *)getbytes(sizeof(t_bindelem)); + t_bindelem *e2 = (t_bindelem *)getbytes(sizeof(t_bindelem)); + b->b_list = e1; + e1->e_who = x; + e1->e_next = e2; + e2->e_who = s->s_thing; + e2->e_next = 0; + s->s_thing = &b->b_pd; + } + } + else s->s_thing = x; +} + +void pd_unbind(t_pd *x, t_symbol *s) +{ + if (s->s_thing == x) s->s_thing = 0; + else if (s->s_thing && *s->s_thing == bindlist_class) + { + /* bindlists always have at least two elements... if the number + goes down to one, get rid of the bindlist and bind the symbol + straight to the remaining element. */ + + t_bindlist *b = (t_bindlist *)s->s_thing; + t_bindelem *e, *e2; + if ((e = b->b_list)->e_who == x) + { + b->b_list = e->e_next; + freebytes(e, sizeof(t_bindelem)); + } + else for (e = b->b_list; e2 = e->e_next; e = e2) + if (e2->e_who == x) + { + e->e_next = e2->e_next; + freebytes(e2, sizeof(t_bindelem)); + break; + } + if (!b->b_list->e_next) + { + s->s_thing = b->b_list->e_who; + freebytes(b->b_list, sizeof(t_bindelem)); + pd_free(&b->b_pd); + } + } + else pd_error(x, "%s: couldn't unbind", s->s_name); +} + +void zz(void) {} + +t_pd *pd_findbyclass(t_symbol *s, t_class *c) +{ + t_pd *x = 0; + + if (!s->s_thing) return (0); + if (*s->s_thing == c) return (s->s_thing); + if (*s->s_thing == bindlist_class) + { + t_bindlist *b = (t_bindlist *)s->s_thing; + t_bindelem *e, *e2; + int warned = 0; + for (e = b->b_list; e; e = e->e_next) + if (*e->e_who == c) + { + if (x && !warned) + { + zz(); + post("warning: %s: multiply defined", s->s_name); + warned = 1; + } + x = e->e_who; + } + } + return x; +} + +/* stack for maintaining bindings for the #X symbol during nestable loads. +*/ + +typedef struct _gstack +{ + t_pd *g_what; + t_symbol *g_loadingabstraction; + struct _gstack *g_next; +} t_gstack; + +static t_gstack *gstack_head = 0; +static t_pd *lastpopped; +static t_symbol *pd_loadingabstraction; + +int pd_setloadingabstraction(t_symbol *sym) +{ + t_gstack *foo = gstack_head; + for (foo = gstack_head; foo; foo = foo->g_next) + if (foo->g_loadingabstraction == sym) + return (1); + pd_loadingabstraction = sym; + return (0); +} + +void pd_pushsym(t_pd *x) +{ + t_gstack *y = (t_gstack *)t_getbytes(sizeof(*y)); + y->g_what = s__X.s_thing; + y->g_next = gstack_head; + y->g_loadingabstraction = pd_loadingabstraction; + pd_loadingabstraction = 0; + gstack_head = y; + s__X.s_thing = x; +} + +void pd_popsym(t_pd *x) +{ + if (!gstack_head || s__X.s_thing != x) bug("gstack_pop"); + else + { + t_gstack *headwas = gstack_head; + s__X.s_thing = headwas->g_what; + gstack_head = headwas->g_next; + t_freebytes(headwas, sizeof(*headwas)); + lastpopped = x; + } +} + +void pd_doloadbang(void) +{ + if (lastpopped) + pd_vmess(lastpopped, gensym("loadbang"), ""); + lastpopped = 0; +} + +void pd_bang(t_pd *x) +{ + (*(*x)->c_bangmethod)(x); +} + +void pd_float(t_pd *x, t_float f) +{ + (*(*x)->c_floatmethod)(x, f); +} + +void pd_pointer(t_pd *x, t_gpointer *gp) +{ + (*(*x)->c_pointermethod)(x, gp); +} + +void pd_symbol(t_pd *x, t_symbol *s) +{ + (*(*x)->c_symbolmethod)(x, s); +} + +void pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv) +{ + (*(*x)->c_listmethod)(x, &s_list, argc, argv); +} + +void mess_init(void); +void obj_init(void); +void conf_init(void); +void glob_init(void); + +void pd_init(void) +{ + mess_init(); + obj_init(); + conf_init(); + glob_init(); +} + diff --git a/apps/plugins/pdbox/PDa/src/m_pd.h b/apps/plugins/pdbox/PDa/src/m_pd.h new file mode 100644 index 0000000..67c569c --- /dev/null +++ b/apps/plugins/pdbox/PDa/src/m_pd.h @@ -0,0 +1,1300 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +#ifndef __m_pd_h_ + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +extern "C" { +#endif + +#define PD_VERSION 0.37 /* oops, don't use this... */ */ +#define PD_MAJOR_VERSION 0 /* ... use these two instead. */ +#define PD_MINOR_VERSION 37 + +/* old name for "MSW" flag -- we have to take it for the sake of many old +"nmakefiles" for externs, which will define NT and not MSW */ +#if defined(NT) && !defined(MSW) +#define MSW +#endif + +#ifdef MSW +// #pragma warning( disable : 4091 ) +#pragma warning( disable : 4305 ) /* uncast const double to float */ +#pragma warning( disable : 4244 ) /* uncast float/int conversion etc. */ +#pragma warning( disable : 4101 ) /* unused automatic variables */ +#endif /* MSW */ + + /* the external storage class is "extern" in UNIX; in MSW it's ugly. */ +#ifdef MSW +#ifdef PD_INTERNAL +#define EXTERN __declspec(dllexport) extern +#else +#define EXTERN __declspec(dllimport) extern +#endif /* PD_INTERNAL */ +#else +#define EXTERN extern +#endif /* MSW */ + + /* and depending on the compiler, hidden data structures are + declared differently: */ +#if defined( __GNUC__) || defined( __BORLANDC__ ) || defined( __MWERKS__ ) +#define EXTERN_STRUCT struct +#else +#define EXTERN_STRUCT extern struct +#endif + + +#if !defined(_SIZE_T) && !defined(_SIZE_T_) +#include /* just for size_t -- how lame! */ +#endif + +#define MAXPDSTRING 1000 /* use this for anything you want */ +#define MAXPDARG 5 /* max number of args we can typecheck today */ + + /* signed and unsigned integer types the size of a pointer: */ +#ifdef __alpha__ +typedef long t_int; +#else +typedef int t_int; +#endif + +typedef float t_float; /* a floating-point number at most the same size */ +typedef float t_floatarg; /* floating-point type for function calls */ + +typedef struct _symbol +{ + char *s_name; + struct _class **s_thing; + struct _symbol *s_next; +} t_symbol; + +EXTERN_STRUCT _array; +#define t_array struct _array /* g_canvas.h */ + +/* pointers to glist and array elements go through a "stub" which sticks +around after the glist or array is freed. The stub itself is deleted when +both the glist/array is gone and the refcount is zero, ensuring that no +gpointers are pointing here. */ + +#define GP_NONE 0 /* the stub points nowhere (has been cut off) */ +#define GP_GLIST 1 /* the stub points to a glist element */ +#define GP_ARRAY 2 /* ... or array */ + +typedef struct _gstub +{ + union + { + struct _glist *gs_glist; /* glist we're in */ + struct _array *gs_array; /* array we're in */ + } gs_un; + int gs_which; /* GP_GLIST/GP_ARRAY */ + int gs_refcount; /* number of gpointers pointing here */ +} t_gstub; + +typedef struct _gpointer /* pointer to a gobj in a glist */ +{ + union + { + struct _scalar *gp_scalar; /* scalar we're in (if glist) */ + union word *gp_w; /* raw data (if array) */ + } gp_un; + int gp_valid; /* number which must match gpointee */ + t_gstub *gp_stub; /* stub which points to glist/array */ +} t_gpointer; + +typedef union word +{ + t_float w_float; + t_symbol *w_symbol; + t_gpointer *w_gpointer; + t_array *w_array; + struct _glist *w_list; + int w_index; +} t_word; + +typedef enum +{ + A_NULL, + A_FLOAT, + A_SYMBOL, + A_POINTER, + A_SEMI, + A_COMMA, + A_DEFFLOAT, + A_DEFSYM, + A_DOLLAR, + A_DOLLSYM, + A_GIMME, + A_CANT +} t_atomtype; + +#define A_DEFSYMBOL A_DEFSYM /* better name for this */ + +typedef struct _atom +{ + t_atomtype a_type; + union word a_w; +} t_atom; + +EXTERN_STRUCT _class; +#define t_class struct _class + +EXTERN_STRUCT _outlet; +#define t_outlet struct _outlet + +EXTERN_STRUCT _inlet; +#define t_inlet struct _inlet + +EXTERN_STRUCT _binbuf; +#define t_binbuf struct _binbuf + +EXTERN_STRUCT _clock; +#define t_clock struct _clock + +EXTERN_STRUCT _outconnect; +#define t_outconnect struct _outconnect + +EXTERN_STRUCT _glist; +#define t_glist struct _glist +#define t_canvas struct _glist /* LATER lose this */ + +typedef t_class *t_pd; /* pure datum: nothing but a class pointer */ + +typedef struct _gobj /* a graphical object */ +{ + t_pd g_pd; /* pure datum header (class) */ + struct _gobj *g_next; /* next in list */ +} t_gobj; + +typedef struct _scalar /* a graphical object holding data */ +{ + t_gobj sc_gobj; /* header for graphical object */ + t_symbol *sc_template; /* template name (LATER replace with pointer) */ + t_word sc_vec[1]; /* indeterminate-length array of words */ +} t_scalar; + +typedef struct _text /* patchable object - graphical, with text */ +{ + t_gobj te_g; /* header for graphical object */ + t_binbuf *te_binbuf; /* holder for the text */ + t_outlet *te_outlet; /* linked list of outlets */ + t_inlet *te_inlet; /* linked list of inlets */ + short te_xpix; /* x&y location (within the toplevel) */ + short te_ypix; + short te_width; /* requested width in chars, 0 if auto */ + unsigned int te_type:2; /* from defs below */ +} t_text; + +#define T_TEXT 0 /* just a textual comment */ +#define T_OBJECT 1 /* a MAX style patchable object */ +#define T_MESSAGE 2 /* a MAX stype message */ +#define T_ATOM 3 /* a cell to display a number or symbol */ + +#define te_pd te_g.g_pd + + /* t_object is synonym for t_text (LATER unify them) */ + +typedef struct _text t_object; + +#define ob_outlet te_outlet +#define ob_inlet te_inlet +#define ob_binbuf te_binbuf +#define ob_pd te_g.g_pd +#define ob_g te_g + +typedef void (*t_method)(void); +typedef void *(*t_newmethod)( void); +typedef void (*t_gotfn)(void *x, ...); + +/* ---------------- pre-defined objects and symbols --------------*/ +EXTERN t_pd pd_objectmaker; /* factory for creating "object" boxes */ +EXTERN t_pd pd_canvasmaker; /* factory for creating canvases */ +EXTERN t_symbol s_pointer; +EXTERN t_symbol s_float; +EXTERN t_symbol s_symbol; +EXTERN t_symbol s_bang; +EXTERN t_symbol s_list; +EXTERN t_symbol s_anything; +EXTERN t_symbol s_signal; +EXTERN t_symbol s__N; +EXTERN t_symbol s__X; +EXTERN t_symbol s_x; +EXTERN t_symbol s_y; +EXTERN t_symbol s_; + +/* --------- prototypes from the central message system ----------- */ +EXTERN void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv); +EXTERN void pd_forwardmess(t_pd *x, int argc, t_atom *argv); +EXTERN t_symbol *gensym(char *s); +EXTERN t_gotfn getfn(t_pd *x, t_symbol *s); +EXTERN t_gotfn zgetfn(t_pd *x, t_symbol *s); +EXTERN void nullfn(void); +EXTERN void pd_vmess(t_pd *x, t_symbol *s, char *fmt, ...); +#define mess0(x, s) ((*getfn((x), (s)))((x))) +#define mess1(x, s, a) ((*getfn((x), (s)))((x), (a))) +#define mess2(x, s, a,b) ((*getfn((x), (s)))((x), (a),(b))) +#define mess3(x, s, a,b,c) ((*getfn((x), (s)))((x), (a),(b),(c))) +#define mess4(x, s, a,b,c,d) ((*getfn((x), (s)))((x), (a),(b),(c),(d))) +#define mess5(x, s, a,b,c,d,e) ((*getfn((x), (s)))((x), (a),(b),(c),(d),(e))) +EXTERN void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv); +EXTERN t_pd *pd_newest(void); + +/* --------------- memory management -------------------- */ +EXTERN void *getbytes(size_t nbytes); +EXTERN void *getzbytes(size_t nbytes); +EXTERN void *copybytes(void *src, size_t nbytes); +EXTERN void freebytes(void *x, size_t nbytes); +EXTERN void *resizebytes(void *x, size_t oldsize, size_t newsize); + +/* -------------------- atoms ----------------------------- */ + +#define SETSEMI(atom) ((atom)->a_type = A_SEMI, (atom)->a_w.w_index = 0) +#define SETCOMMA(atom) ((atom)->a_type = A_COMMA, (atom)->a_w.w_index = 0) +#define SETPOINTER(atom, gp) ((atom)->a_type = A_POINTER, \ + (atom)->a_w.w_gpointer = (gp)) +#define SETFLOAT(atom, f) ((atom)->a_type = A_FLOAT, (atom)->a_w.w_float = (f)) +#define SETSYMBOL(atom, s) ((atom)->a_type = A_SYMBOL, \ + (atom)->a_w.w_symbol = (s)) +#define SETDOLLAR(atom, n) ((atom)->a_type = A_DOLLAR, \ + (atom)->a_w.w_index = (n)) +#define SETDOLLSYM(atom, s) ((atom)->a_type = A_DOLLSYM, \ + (atom)->a_w.w_symbol= (s)) + +EXTERN t_float atom_getfloat(t_atom *a); +EXTERN t_int atom_getint(t_atom *a); +EXTERN t_symbol *atom_getsymbol(t_atom *a); +EXTERN t_symbol *atom_gensym(t_atom *a); +EXTERN t_float atom_getfloatarg(int which, int argc, t_atom *argv); +EXTERN t_int atom_getintarg(int which, int argc, t_atom *argv); +EXTERN t_symbol *atom_getsymbolarg(int which, int argc, t_atom *argv); + +EXTERN void atom_string(t_atom *a, char *buf, unsigned int bufsize); + +/* ------------------ binbufs --------------- */ + +EXTERN t_binbuf *binbuf_new(void); +EXTERN void binbuf_free(t_binbuf *x); +EXTERN t_binbuf *binbuf_duplicate(t_binbuf *y); + +EXTERN void binbuf_text(t_binbuf *x, char *text, size_t size); +EXTERN void binbuf_gettext(t_binbuf *x, char **bufp, int *lengthp); +EXTERN void binbuf_clear(t_binbuf *x); +EXTERN void binbuf_add(t_binbuf *x, int argc, t_atom *argv); +EXTERN void binbuf_addv(t_binbuf *x, char *fmt, ...); +EXTERN void binbuf_addbinbuf(t_binbuf *x, t_binbuf *y); +EXTERN void binbuf_addsemi(t_binbuf *x); +EXTERN void binbuf_restore(t_binbuf *x, int argc, t_atom *argv); +EXTERN void binbuf_print(t_binbuf *x); +EXTERN int binbuf_getnatom(t_binbuf *x); +EXTERN t_atom *binbuf_getvec(t_binbuf *x); +EXTERN void binbuf_eval(t_binbuf *x, t_pd *target, int argc, t_atom *argv); +EXTERN int binbuf_read(t_binbuf *b, char *filename, char *dirname, + int crflag); +EXTERN int binbuf_read_via_path(t_binbuf *b, char *filename, char *dirname, + int crflag); +EXTERN int binbuf_write(t_binbuf *x, char *filename, char *dir, + int crflag); +EXTERN void binbuf_evalfile(t_symbol *name, t_symbol *dir); +EXTERN t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, t_atom *av, + int tonew); + +/* ------------------ clocks --------------- */ + +typedef long long t_time; +EXTERN t_clock *clock_new(void *owner, t_method fn); +EXTERN void clock_set(t_clock *x, t_time systime); +EXTERN void clock_delay(t_clock *x, t_time delaytime); +EXTERN void clock_unset(t_clock *x); +EXTERN t_time clock_getlogicaltime(void); +EXTERN t_time clock_getsystime(void); /* OBSOLETE; use clock_getlogicaltime() */ +EXTERN t_time clock_gettimesince(t_time prevsystime); +EXTERN t_time clock_getsystimeafter(t_time delaytime); +EXTERN void clock_free(t_clock *x); + +/* ----------------- pure data ---------------- */ +EXTERN t_pd *pd_new(t_class *cls); +EXTERN void pd_free(t_pd *x); +EXTERN void pd_bind(t_pd *x, t_symbol *s); +EXTERN void pd_unbind(t_pd *x, t_symbol *s); +EXTERN t_pd *pd_findbyclass(t_symbol *s, t_class *c); +EXTERN void pd_pushsym(t_pd *x); +EXTERN void pd_popsym(t_pd *x); +EXTERN t_symbol *pd_getfilename(void); +EXTERN t_symbol *pd_getdirname(void); +EXTERN void pd_bang(t_pd *x); +EXTERN void pd_pointer(t_pd *x, t_gpointer *gp); +EXTERN void pd_float(t_pd *x, t_float f); +EXTERN void pd_symbol(t_pd *x, t_symbol *s); +EXTERN void pd_list(t_pd *x, t_symbol *s, int argc, t_atom *argv); +EXTERN void pd_anything(t_pd *x, t_symbol *s, int argc, t_atom *argv); +#define pd_class(x) (*(x)) + +/* ----------------- pointers ---------------- */ +EXTERN void gpointer_init(t_gpointer *gp); +EXTERN void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto); +EXTERN void gpointer_unset(t_gpointer *gp); +EXTERN int gpointer_check(const t_gpointer *gp, int headok); + +/* ----------------- patchable "objects" -------------- */ +EXTERN_STRUCT _inlet; +#define t_inlet struct _inlet +EXTERN_STRUCT _outlet; +#define t_outlet struct _outlet + +EXTERN t_inlet *inlet_new(t_object *owner, t_pd *dest, t_symbol *s1, + t_symbol *s2); +EXTERN t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp); +EXTERN t_inlet *floatinlet_new(t_object *owner, t_float *fp); +EXTERN t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp); +EXTERN void inlet_free(t_inlet *x); + +EXTERN t_outlet *outlet_new(t_object *owner, t_symbol *s); +EXTERN void outlet_bang(t_outlet *x); +EXTERN void outlet_pointer(t_outlet *x, t_gpointer *gp); +EXTERN void outlet_float(t_outlet *x, t_float f); +EXTERN void outlet_symbol(t_outlet *x, t_symbol *s); +EXTERN void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv); +EXTERN void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv); +EXTERN t_symbol *outlet_getsymbol(t_outlet *x); +EXTERN void outlet_free(t_outlet *x); +EXTERN t_object *pd_checkobject(t_pd *x); + + +/* -------------------- canvases -------------- */ + +EXTERN void glob_setfilename(void *dummy, t_symbol *name, t_symbol *dir); + +EXTERN void canvas_setargs(int argc, t_atom *argv); +EXTERN void canvas_getargs(int *argcp, t_atom **argvp); +EXTERN t_symbol *canvas_getcurrentdir(void); +EXTERN t_glist *canvas_getcurrent(void); +EXTERN void canvas_makefilename(t_glist *c, char *file, + char *result,int resultsize); +EXTERN t_symbol *canvas_getdir(t_glist *x); +EXTERN int sys_fontwidth(int fontsize); +EXTERN int sys_fontheight(int fontsize); +EXTERN void canvas_dataproperties(t_glist *x, t_scalar *sc, t_binbuf *b); + +/* ---------------- widget behaviors ---------------------- */ + +EXTERN_STRUCT _widgetbehavior; +#define t_widgetbehavior struct _widgetbehavior + +EXTERN_STRUCT _parentwidgetbehavior; +#define t_parentwidgetbehavior struct _parentwidgetbehavior +EXTERN t_parentwidgetbehavior *pd_getparentwidget(t_pd *x); + +/* -------------------- classes -------------- */ + +#define CLASS_DEFAULT 0 /* flags for new classes below */ +#define CLASS_PD 1 +#define CLASS_GOBJ 2 +#define CLASS_PATCHABLE 3 +#define CLASS_NOINLET 8 + +#define CLASS_TYPEMASK 3 + + +EXTERN t_class *class_new(t_symbol *name, t_newmethod newmethod, + t_method freemethod, size_t size, int flags, t_atomtype arg1, ...); +EXTERN void class_addcreator(t_newmethod newmethod, t_symbol *s, + t_atomtype type1, ...); +EXTERN void class_addmethod(t_class *c, t_method fn, t_symbol *sel, + t_atomtype arg1, ...); +EXTERN void class_addbang(t_class *c, t_method fn); +EXTERN void class_addpointer(t_class *c, t_method fn); +EXTERN void class_doaddfloat(t_class *c, t_method fn); +EXTERN void class_addsymbol(t_class *c, t_method fn); +EXTERN void class_addlist(t_class *c, t_method fn); +EXTERN void class_addanything(t_class *c, t_method fn); +EXTERN void class_sethelpsymbol(t_class *c, t_symbol *s); +EXTERN void class_setwidget(t_class *c, t_widgetbehavior *w); +EXTERN void class_setparentwidget(t_class *c, t_parentwidgetbehavior *w); +EXTERN t_parentwidgetbehavior *class_parentwidget(t_class *c); +EXTERN char *class_getname(t_class *c); +EXTERN char *class_gethelpname(t_class *c); +EXTERN void class_setdrawcommand(t_class *c); +EXTERN int class_isdrawcommand(t_class *c); +EXTERN void class_domainsignalin(t_class *c, int onset); +#define CLASS_MAINSIGNALIN(c, type, field) \ + class_domainsignalin(c, (char *)(&((type *)0)->field) - (char *)0) + + /* prototype for functions to save Pd's to a binbuf */ +typedef void (*t_savefn)(t_gobj *x, t_binbuf *b); +EXTERN void class_setsavefn(t_class *c, t_savefn f); +EXTERN t_savefn class_getsavefn(t_class *c); + /* prototype for functions to open properties dialogs */ +typedef void (*t_propertiesfn)(t_gobj *x, struct _glist *glist); +EXTERN void class_setpropertiesfn(t_class *c, t_propertiesfn f); +EXTERN t_propertiesfn class_getpropertiesfn(t_class *c); + +#ifndef PD_CLASS_DEF +#define class_addbang(x, y) class_addbang((x), (t_method)(y)) +#define class_addpointer(x, y) class_addpointer((x), (t_method)(y)) +#define class_addfloat(x, y) class_doaddfloat((x), (t_method)(y)) +#define class_addsymbol(x, y) class_addsymbol((x), (t_method)(y)) +#define class_addlist(x, y) class_addlist((x), (t_method)(y)) +#define class_addanything(x, y) class_addanything((x), (t_method)(y)) +#endif + +/* ------------ printing --------------------------------- */ +EXTERN void post(char *fmt, ...); +EXTERN void startpost(char *fmt, ...); +EXTERN void poststring(char *s); +EXTERN void postfloat(float f); +EXTERN void postatom(int argc, t_atom *argv); +EXTERN void endpost(void); +EXTERN void error(char *fmt, ...); +EXTERN void bug(char *fmt, ...); +EXTERN void pd_error(void *object, char *fmt, ...); +EXTERN void sys_logerror(char *object, char *s); +EXTERN void sys_unixerror(char *object); +EXTERN void sys_ouch(void); + +#ifdef __linux__ +EXTERN char* sys_get_path( void); +#endif +EXTERN void sys_addpath(const char* p); + + +/* ------------ system interface routines ------------------- */ +EXTERN int sys_isreadablefile(const char *name); +EXTERN void sys_bashfilename(const char *from, char *to); +EXTERN void sys_unbashfilename(const char *from, char *to); +EXTERN int open_via_path(const char *name, const char *ext, const char *dir, + char *dirresult, char **nameresult, unsigned int size, int bin); +EXTERN int sched_geteventno(void); +EXTERN t_time sys_getrealtime(void); + + +/* ------------ threading ------------------- */ +/* T.Grill - see m_sched.c */ + +EXTERN void sys_lock(void); +EXTERN void sys_unlock(void); +EXTERN int sys_trylock(void); + + +/* --------------- signals ----------------------------------- */ + +#define MAXLOGSIG 32 +#define MAXSIGSIZE (1 << MAXLOGSIG) +#ifndef FIXEDPOINT +typedef float t_sample; +#else +#include "m_fixed.h" +#endif + + +typedef struct _signal +{ + int s_n; /* number of points in the array */ + t_sample *s_vec; /* the array */ + float s_sr; /* sample rate */ + int s_refcount; /* number of times used */ + int s_isborrowed; /* whether we're going to borrow our array */ + struct _signal *s_borrowedfrom; /* signal to borrow it from */ + struct _signal *s_nextfree; /* next in freelist */ + struct _signal *s_nextused; /* next in used list */ +} t_signal; + + +typedef t_int *(*t_perfroutine)(t_int *args); + +EXTERN t_int *plus_perform(t_int *args); +EXTERN t_int *zero_perform(t_int *args); +EXTERN t_int *copy_perform(t_int *args); + +EXTERN void dsp_add_plus(t_sample *in1, t_sample *in2, t_sample *out, int n); +EXTERN void dsp_add_copy(t_sample *in, t_sample *out, int n); +EXTERN void dsp_add_scalarcopy(t_sample *in, t_sample *out, int n); +EXTERN void dsp_add_zero(t_sample *out, int n); + +EXTERN int sys_getblksize(void); +EXTERN float sys_getsr(void); +EXTERN int sys_get_inchannels(void); +EXTERN int sys_get_outchannels(void); + +EXTERN void dsp_add(t_perfroutine f, int n, ...); +EXTERN void dsp_addv(t_perfroutine f, int n, t_int *vec); +EXTERN void pd_fft(float *buf, int npoints, int inverse); +EXTERN int ilog2(int n); + +EXTERN void mayer_fht(t_sample *fz, int n); +EXTERN void mayer_fft(int n, t_sample *real, t_sample *imag); +EXTERN void mayer_ifft(int n, t_sample *real, t_sample *imag); +EXTERN void mayer_realfft(int n, t_sample *real); +EXTERN void mayer_realifft(int n, t_sample *real); + +//EXTERN t_sample cos_table[]; + +#define LOGCOSTABSIZE 9 +#define COSTABSIZE (1<