From 57045df5fe3ec557e57dc7434ac1a07b5521bffc Mon Sep 17 00:00:00 2001 From: Guenter Geiger Date: Mon, 29 Jul 2002 17:06:19 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r58, which included commits to RCS files with non-trunk default branches. svn path=/trunk/; revision=59 --- pd/src/configure | 3021 +++++++++++++++++++++++++++++++++++++++++ pd/src/configure.in | 192 +++ pd/src/d_arithmetic.c | 842 ++++++++++++ pd/src/d_array.c | 1075 +++++++++++++++ pd/src/d_ctl.c | 496 +++++++ pd/src/d_dac.c | 183 +++ pd/src/d_delay.c | 314 +++++ pd/src/d_fft.c | 342 +++++ pd/src/d_fftroutine.c | 1001 ++++++++++++++ pd/src/d_filter.c | 534 ++++++++ pd/src/d_global.c | 312 +++++ pd/src/d_math.c | 573 ++++++++ pd/src/d_mayer_fft.c | 419 ++++++ pd/src/d_misc.c | 260 ++++ pd/src/d_osc.c | 531 ++++++++ pd/src/d_resample.c | 225 +++ pd/src/d_soundfile.c | 2200 ++++++++++++++++++++++++++++++ pd/src/d_ugen.c | 1078 +++++++++++++++ pd/src/g_all_guis.c | 937 +++++++++++++ pd/src/g_all_guis.h | 320 +++++ pd/src/g_array.c | 1358 +++++++++++++++++++ pd/src/g_bang.c | 600 ++++++++ pd/src/g_canvas.c | 1482 ++++++++++++++++++++ pd/src/g_canvas.h | 564 ++++++++ pd/src/g_editor.c | 1658 +++++++++++++++++++++++ pd/src/g_graph.c | 1119 +++++++++++++++ pd/src/g_guiconnect.c | 94 ++ pd/src/g_hdial.c | 675 +++++++++ pd/src/g_hslider.c | 708 ++++++++++ pd/src/g_io.c | 612 +++++++++ pd/src/g_mycanvas.c | 439 ++++++ pd/src/g_numbox.c | 979 ++++++++++++++ pd/src/g_readwrite.c | 722 ++++++++++ pd/src/g_rtext.c | 478 +++++++ pd/src/g_scalar.c | 373 +++++ pd/src/g_template.c | 1673 +++++++++++++++++++++++ pd/src/g_text.c | 1070 +++++++++++++++ pd/src/g_toggle.c | 528 ++++++++ pd/src/g_traversal.c | 1084 +++++++++++++++ pd/src/g_vdial.c | 672 +++++++++ pd/src/g_vslider.c | 688 ++++++++++ pd/src/g_vumeter.c | 762 +++++++++++ pd/src/install-sh | 251 ++++ pd/src/m_atom.c | 129 ++ pd/src/m_binbuf.c | 1138 ++++++++++++++++ pd/src/m_class.c | 772 +++++++++++ pd/src/m_conf.c | 100 ++ pd/src/m_glob.c | 121 ++ pd/src/m_imp.h | 223 +++ pd/src/m_memory.c | 88 ++ pd/src/m_obj.c | 676 +++++++++ pd/src/m_pd.c | 296 ++++ pd/src/m_pd.h | 594 ++++++++ pd/src/m_sched.c | 462 +++++++ pd/src/makefile | 3 + pd/src/makefile.clean | 3 + pd/src/makefile.dependencies | 0 pd/src/makefile.in | 233 ++++ pd/src/makefile.irix | 65 + pd/src/makefile.nt | 95 ++ pd/src/notes.txt | 264 ++++ pd/src/s_entry.c | 10 + pd/src/s_file.c | 54 + pd/src/s_freebsd.c | 3072 +++++++++++++++++++++++++++++++++++++++++ pd/src/s_inter.c | 794 +++++++++++ pd/src/s_linux.c | 3087 ++++++++++++++++++++++++++++++++++++++++++ pd/src/s_loader.c | 142 ++ pd/src/s_mac.c | 356 +++++ pd/src/s_main.c | 803 +++++++++++ pd/src/s_nt.c | 1586 ++++++++++++++++++++++ pd/src/s_path.c | 279 ++++ pd/src/s_portaudio.c | 197 +++ pd/src/s_print.c | 150 ++ pd/src/s_sgi.c | 433 ++++++ pd/src/s_unix.c | 445 ++++++ pd/src/s_watchdog.c | 47 + pd/src/t_main.c | 123 ++ pd/src/t_tk.h | 10 + pd/src/t_tkcmd.c | 367 +++++ pd/src/u_main.tk | 2570 +++++++++++++++++++++++++++++++++++ pd/src/u_pdreceive.c | 305 +++++ pd/src/u_pdsend.c | 150 ++ pd/src/x_acoustics.c | 193 +++ pd/src/x_arithmetic.c | 898 ++++++++++++ pd/src/x_connective.c | 1452 ++++++++++++++++++++ pd/src/x_gui.c | 377 ++++++ pd/src/x_interface.c | 78 ++ pd/src/x_midi.c | 1314 ++++++++++++++++++ pd/src/x_misc.c | 319 +++++ pd/src/x_net.c | 362 +++++ pd/src/x_qlist.c | 345 +++++ pd/src/x_time.c | 519 +++++++ pd/src/z.pd | 41 + pd/src/z2.pd | 12 + pd/src/z3.pd | 3 + pd/src/z4.pd | 2 + pd/src/z5.pd | 8 + pd/src/z6.pd | 4 + 98 files changed, 59613 insertions(+) create mode 100755 pd/src/configure create mode 100644 pd/src/configure.in create mode 100644 pd/src/d_arithmetic.c create mode 100644 pd/src/d_array.c create mode 100644 pd/src/d_ctl.c create mode 100644 pd/src/d_dac.c create mode 100644 pd/src/d_delay.c create mode 100644 pd/src/d_fft.c create mode 100644 pd/src/d_fftroutine.c create mode 100644 pd/src/d_filter.c create mode 100644 pd/src/d_global.c create mode 100644 pd/src/d_math.c create mode 100644 pd/src/d_mayer_fft.c create mode 100644 pd/src/d_misc.c create mode 100644 pd/src/d_osc.c create mode 100644 pd/src/d_resample.c create mode 100644 pd/src/d_soundfile.c create mode 100644 pd/src/d_ugen.c create mode 100644 pd/src/g_all_guis.c create mode 100644 pd/src/g_all_guis.h create mode 100644 pd/src/g_array.c create mode 100644 pd/src/g_bang.c create mode 100644 pd/src/g_canvas.c create mode 100644 pd/src/g_canvas.h create mode 100644 pd/src/g_editor.c create mode 100644 pd/src/g_graph.c create mode 100644 pd/src/g_guiconnect.c create mode 100644 pd/src/g_hdial.c create mode 100644 pd/src/g_hslider.c create mode 100644 pd/src/g_io.c create mode 100644 pd/src/g_mycanvas.c create mode 100644 pd/src/g_numbox.c create mode 100644 pd/src/g_readwrite.c create mode 100644 pd/src/g_rtext.c create mode 100644 pd/src/g_scalar.c create mode 100644 pd/src/g_template.c create mode 100644 pd/src/g_text.c create mode 100644 pd/src/g_toggle.c create mode 100644 pd/src/g_traversal.c create mode 100644 pd/src/g_vdial.c create mode 100644 pd/src/g_vslider.c create mode 100644 pd/src/g_vumeter.c create mode 100644 pd/src/install-sh create mode 100644 pd/src/m_atom.c create mode 100644 pd/src/m_binbuf.c create mode 100644 pd/src/m_class.c create mode 100644 pd/src/m_conf.c create mode 100644 pd/src/m_glob.c create mode 100644 pd/src/m_imp.h create mode 100644 pd/src/m_memory.c create mode 100644 pd/src/m_obj.c create mode 100644 pd/src/m_pd.c create mode 100644 pd/src/m_pd.h create mode 100644 pd/src/m_sched.c create mode 100644 pd/src/makefile create mode 100644 pd/src/makefile.clean create mode 100644 pd/src/makefile.dependencies create mode 100644 pd/src/makefile.in create mode 100644 pd/src/makefile.irix create mode 100644 pd/src/makefile.nt create mode 100644 pd/src/notes.txt create mode 100644 pd/src/s_entry.c create mode 100644 pd/src/s_file.c create mode 100644 pd/src/s_freebsd.c create mode 100644 pd/src/s_inter.c create mode 100644 pd/src/s_linux.c create mode 100644 pd/src/s_loader.c create mode 100644 pd/src/s_mac.c create mode 100644 pd/src/s_main.c create mode 100644 pd/src/s_nt.c create mode 100644 pd/src/s_path.c create mode 100644 pd/src/s_portaudio.c create mode 100644 pd/src/s_print.c create mode 100644 pd/src/s_sgi.c create mode 100644 pd/src/s_unix.c create mode 100644 pd/src/s_watchdog.c create mode 100644 pd/src/t_main.c create mode 100644 pd/src/t_tk.h create mode 100644 pd/src/t_tkcmd.c create mode 100644 pd/src/u_main.tk create mode 100644 pd/src/u_pdreceive.c create mode 100644 pd/src/u_pdsend.c create mode 100644 pd/src/x_acoustics.c create mode 100644 pd/src/x_arithmetic.c create mode 100644 pd/src/x_connective.c create mode 100644 pd/src/x_gui.c create mode 100644 pd/src/x_interface.c create mode 100644 pd/src/x_midi.c create mode 100644 pd/src/x_misc.c create mode 100644 pd/src/x_net.c create mode 100644 pd/src/x_qlist.c create mode 100644 pd/src/x_time.c create mode 100644 pd/src/z.pd create mode 100644 pd/src/z2.pd create mode 100644 pd/src/z3.pd create mode 100644 pd/src/z4.pd create mode 100644 pd/src/z5.pd create mode 100644 pd/src/z6.pd (limited to 'pd/src') diff --git a/pd/src/configure b/pd/src/configure new file mode 100755 index 00000000..88e26a18 --- /dev/null +++ b/pd/src/configure @@ -0,0 +1,3021 @@ +#! /bin/sh + +# Guess values for system-dependent variables and create Makefiles. +# Generated automatically using autoconf version 2.13 +# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. + +# Defaults: +ac_help= +ac_default_prefix=/usr/local +# Any additions from configure.in: +ac_help="$ac_help + --enable-alsa compile ALSA support" +ac_help="$ac_help + --enable-old-alsa ALSA 0.5x support" +ac_help="$ac_help + --enable-rme compile RME support" +ac_help="$ac_help + --enable-debug debugging support" +ac_help="$ac_help + --with-x use the X Window System" + +# Initialize some variables set by options. +# The variables have the same names as the options, with +# dashes changed to underlines. +build=NONE +cache_file=./config.cache +exec_prefix=NONE +host=NONE +no_create= +nonopt=NONE +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +target=NONE +verbose= +x_includes=NONE +x_libraries=NONE +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datadir='${prefix}/share' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +libdir='${exec_prefix}/lib' +includedir='${prefix}/include' +oldincludedir='/usr/include' +infodir='${prefix}/info' +mandir='${prefix}/man' + +# Initialize some other variables. +subdirs= +MFLAGS= MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} +# Maximum number of lines to put in a shell here document. +ac_max_here_lines=12 + +ac_prev= +for ac_option +do + + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval "$ac_prev=\$ac_option" + ac_prev= + continue + fi + + case "$ac_option" in + -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) ac_optarg= ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case "$ac_option" in + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir="$ac_optarg" ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build="$ac_optarg" ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file="$ac_optarg" ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir="$ac_optarg" ;; + + -disable-* | --disable-*) + ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + eval "enable_${ac_feature}=no" ;; + + -enable-* | --enable-*) + ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } + fi + ac_feature=`echo $ac_feature| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "enable_${ac_feature}='$ac_optarg'" ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix="$ac_optarg" ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he) + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat << EOF +Usage: configure [options] [host] +Options: [defaults in brackets after descriptions] +Configuration: + --cache-file=FILE cache test results in FILE + --help print this message + --no-create do not create output files + --quiet, --silent do not print \`checking...' messages + --version print the version of autoconf that created configure +Directory and file names: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [same as prefix] + --bindir=DIR user executables in DIR [EPREFIX/bin] + --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] + --libexecdir=DIR program executables in DIR [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data in DIR + [PREFIX/share] + --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data in DIR + [PREFIX/com] + --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] + --libdir=DIR object code libraries in DIR [EPREFIX/lib] + --includedir=DIR C header files in DIR [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] + --infodir=DIR info documentation in DIR [PREFIX/info] + --mandir=DIR man documentation in DIR [PREFIX/man] + --srcdir=DIR find the sources in DIR [configure dir or ..] + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM + run sed PROGRAM on installed program names +EOF + cat << EOF +Host type: + --build=BUILD configure for building on BUILD [BUILD=HOST] + --host=HOST configure for HOST [guessed] + --target=TARGET configure for TARGET [TARGET=HOST] +Features and packages: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --x-includes=DIR X include files are in DIR + --x-libraries=DIR X library files are in DIR +EOF + if test -n "$ac_help"; then + echo "--enable and --with options recognized:$ac_help" + fi + exit 0 ;; + + -host | --host | --hos | --ho) + ac_prev=host ;; + -host=* | --host=* | --hos=* | --ho=*) + host="$ac_optarg" ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir="$ac_optarg" ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir="$ac_optarg" ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir="$ac_optarg" ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir="$ac_optarg" ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst \ + | --locals | --local | --loca | --loc | --lo) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* \ + | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) + localstatedir="$ac_optarg" ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir="$ac_optarg" ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir="$ac_optarg" ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix="$ac_optarg" ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix="$ac_optarg" ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix="$ac_optarg" ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name="$ac_optarg" ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir="$ac_optarg" ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir="$ac_optarg" ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site="$ac_optarg" ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir="$ac_optarg" ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir="$ac_optarg" ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target="$ac_optarg" ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers) + echo "configure generated by autoconf version 2.13" + exit 0 ;; + + -with-* | --with-*) + ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + case "$ac_option" in + *=*) ;; + *) ac_optarg=yes ;; + esac + eval "with_${ac_package}='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`echo $ac_option|sed -e 's/-*without-//'` + # Reject names that are not valid shell variable names. + if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then + { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } + fi + ac_package=`echo $ac_package| sed 's/-/_/g'` + eval "with_${ac_package}=no" ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes="$ac_optarg" ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries="$ac_optarg" ;; + + -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } + ;; + + *) + if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then + echo "configure: warning: $ac_option: invalid host type" 1>&2 + fi + if test "x$nonopt" != xNONE; then + { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } + fi + nonopt="$ac_option" + ;; + + esac +done + +if test -n "$ac_prev"; then + { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } +fi + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +# File descriptor usage: +# 0 standard input +# 1 file creation +# 2 errors and warnings +# 3 some systems may open it to /dev/tty +# 4 used on the Kubota Titan +# 6 checking for... messages and results +# 5 compiler messages saved in config.log +if test "$silent" = yes; then + exec 6>/dev/null +else + exec 6>&1 +fi +exec 5>./config.log + +echo "\ +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. +" 1>&5 + +# Strip out --no-create and --no-recursion so they do not pile up. +# Also quote any args containing shell metacharacters. +ac_configure_args= +for ac_arg +do + case "$ac_arg" in + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c) ;; + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) + ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) ac_configure_args="$ac_configure_args $ac_arg" ;; + esac +done + +# NLS nuisances. +# Only set these to C if already set. These must not be set unconditionally +# because not all systems understand e.g. LANG=C (notably SCO). +# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! +# Non-C LC_CTYPE values break the ctype check. +if test "${LANG+set}" = set; then LANG=C; export LANG; fi +if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi +if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi +if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -rf conftest* confdefs.h +# AIX cpp loses on an empty file, so make sure it contains at least a newline. +echo > confdefs.h + +# A filename unique to this package, relative to the directory that +# configure is in, which we can look for to find out if srcdir is correct. +ac_unique_file=d_arithmetic.c + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then its parent. + ac_prog=$0 + ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` + test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. + srcdir=$ac_confdir + if test ! -r $srcdir/$ac_unique_file; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r $srcdir/$ac_unique_file; then + if test "$ac_srcdir_defaulted" = yes; then + { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } + else + { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } + fi +fi +srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` + +# Prefer explicitly selected file to automatically selected ones. +if test -z "$CONFIG_SITE"; then + if test "x$prefix" != xNONE; then + CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" + else + CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" + fi +fi +for ac_site_file in $CONFIG_SITE; do + if test -r "$ac_site_file"; then + echo "loading site script $ac_site_file" + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + echo "loading cache $cache_file" + . $cache_file +else + echo "creating cache $cache_file" + > $cache_file +fi + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +ac_exeext= +ac_objext=o +if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then + # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. + if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then + ac_n= ac_c=' +' ac_t=' ' + else + ac_n=-n ac_c= ac_t= + fi +else + ac_n= ac_c='\c' ac_t= +fi + + + + + + + + + + + + + + + + + + + + +# Check whether --enable-alsa or --disable-alsa was given. +if test "${enable_alsa+set}" = set; then + enableval="$enable_alsa" + alsa="yes" +fi + +# Check whether --enable-old-alsa or --disable-old-alsa was given. +if test "${enable_old_alsa+set}" = set; then + enableval="$enable_old_alsa" + alsa="old" +fi + +# Check whether --enable-rme or --disable-rme was given. +if test "${enable_rme+set}" = set; then + enableval="$enable_rme" + rme="yes" +fi + +# Check whether --enable-debug or --disable-debug was given. +if test "${enable_debug+set}" = set; then + enableval="$enable_debug" + USE_OPT_CFLAGS="NO" +else + USE_OPT_CFLAGS="YES" +fi + + +# Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:583: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="gcc" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:613: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_prog_rejected=no + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + break + fi + done + IFS="$ac_save_ifs" +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# -gt 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + set dummy "$ac_dir/$ac_word" "$@" + shift + ac_cv_prog_CC="$@" + fi +fi +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + + if test -z "$CC"; then + case "`uname -s`" in + *win32* | *WIN32*) + # Extract the first word of "cl", so it can be a program name with args. +set dummy cl; ac_word=$2 +echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 +echo "configure:664: checking for $ac_word" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" + ac_dummy="$PATH" + for ac_dir in $ac_dummy; do + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$ac_word; then + ac_cv_prog_CC="cl" + break + fi + done + IFS="$ac_save_ifs" +fi +fi +CC="$ac_cv_prog_CC" +if test -n "$CC"; then + echo "$ac_t""$CC" 1>&6 +else + echo "$ac_t""no" 1>&6 +fi + ;; + esac + fi + test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } +fi + +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 +echo "configure:696: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 + +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +cat > conftest.$ac_ext << EOF + +#line 707 "configure" +#include "confdefs.h" + +main(){return(0);} +EOF +if { (eval echo configure:712: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + ac_cv_prog_cc_works=yes + # If we can't run a trivial program, we are probably using a cross compiler. + if (./conftest; exit) 2>/dev/null; then + ac_cv_prog_cc_cross=no + else + ac_cv_prog_cc_cross=yes + fi +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + ac_cv_prog_cc_works=no +fi +rm -fr conftest* +ac_ext=c +# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. +ac_cpp='$CPP $CPPFLAGS' +ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' +ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' +cross_compiling=$ac_cv_prog_cc_cross + +echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 +if test $ac_cv_prog_cc_works = no; then + { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } +fi +echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 +echo "configure:738: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 +echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 +cross_compiling=$ac_cv_prog_cc_cross + +echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 +echo "configure:743: checking whether we are using GNU C" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then + ac_cv_prog_gcc=yes +else + ac_cv_prog_gcc=no +fi +fi + +echo "$ac_t""$ac_cv_prog_gcc" 1>&6 + +if test $ac_cv_prog_gcc = yes; then + GCC=yes +else + GCC= +fi + +ac_test_CFLAGS="${CFLAGS+set}" +ac_save_CFLAGS="$CFLAGS" +CFLAGS= +echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 +echo "configure:771: checking whether ${CC-cc} accepts -g" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + echo 'void f(){}' > conftest.c +if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then + ac_cv_prog_cc_g=yes +else + ac_cv_prog_cc_g=no +fi +rm -f conftest* + +fi + +echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 +if test "$ac_test_CFLAGS" = set; then + CFLAGS="$ac_save_CFLAGS" +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi + +ac_aux_dir= +for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do + if test -f $ac_dir/install-sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f $ac_dir/install.sh; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; } +fi +ac_config_guess=$ac_aux_dir/config.guess +ac_config_sub=$ac_aux_dir/config.sub +ac_configure=$ac_aux_dir/configure # This should be Cygnus configure. + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# ./install, which can be erroneously created by make from ./install.sh. +echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6 +echo "configure:833: checking for a BSD compatible install" >&5 +if test -z "$INSTALL"; then +if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":" + for ac_dir in $PATH; do + # Account for people who put trailing slashes in PATH elements. + case "$ac_dir/" in + /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + if test -f $ac_dir/$ac_prog; then + if test $ac_prog = install && + grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + else + ac_cv_path_install="$ac_dir/$ac_prog -c" + break 2 + fi + fi + done + ;; + esac + done + IFS="$ac_save_IFS" + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL="$ac_cv_path_install" + else + # As a last resort, use the slow shell script. We don't cache a + # path for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the path is relative. + INSTALL="$ac_install_sh" + fi +fi +echo "$ac_t""$INSTALL" 1>&6 + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6 +echo "configure:886: checking whether ${MAKE-make} sets \${MAKE}" >&5 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftestmake <<\EOF +all: + @echo 'ac_maketemp="${MAKE}"' +EOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=` +if test -n "$ac_maketemp"; then + eval ac_cv_prog_make_${ac_make}_set=yes +else + eval ac_cv_prog_make_${ac_make}_set=no +fi +rm -f conftestmake +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$ac_t""yes" 1>&6 + SET_MAKE= +else + echo "$ac_t""no" 1>&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 +echo "configure:913: checking how to run the C preprocessor" >&5 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then +if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # This must be in double quotes, not single quotes, because CPP may get + # substituted into the Makefile and "${CC-cc}" will confuse make. + CPP="${CC-cc} -E" + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:934: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -E -traditional-cpp" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:951: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP="${CC-cc} -nologo -E" + cat > conftest.$ac_ext < +Syntax Error +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:968: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + : +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + CPP=/lib/cpp +fi +rm -f conftest* +fi +rm -f conftest* +fi +rm -f conftest* + ac_cv_prog_CPP="$CPP" +fi + CPP="$ac_cv_prog_CPP" +else + ac_cv_prog_CPP="$CPP" +fi +echo "$ac_t""$CPP" 1>&6 + + +echo $ac_n "checking for working const""... $ac_c" 1>&6 +echo "configure:994: checking for working const" >&5 +if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext <j = 5; +} +{ /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; +} + +; return 0; } +EOF +if { (eval echo configure:1048: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_c_const=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_c_const=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_c_const" 1>&6 +if test $ac_cv_c_const = no; then + cat >> confdefs.h <<\EOF +#define const +EOF + +fi + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:1069: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +#include +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1082: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext < +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:1149: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +echo $ac_n "checking for pid_t""... $ac_c" 1>&6 +echo "configure:1173: checking for pid_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_pid_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])pid_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_pid_t=yes +else + rm -rf conftest* + ac_cv_type_pid_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_pid_t" 1>&6 +if test $ac_cv_type_pid_t = no; then + cat >> confdefs.h <<\EOF +#define pid_t int +EOF + +fi + +echo $ac_n "checking for size_t""... $ac_c" 1>&6 +echo "configure:1206: checking for size_t" >&5 +if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#if STDC_HEADERS +#include +#include +#endif +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_type_size_t=yes +else + rm -rf conftest* + ac_cv_type_size_t=no +fi +rm -f conftest* + +fi +echo "$ac_t""$ac_cv_type_size_t" 1>&6 +if test $ac_cv_type_size_t = no; then + cat >> confdefs.h <<\EOF +#define size_t unsigned +EOF + +fi + +echo $ac_n "checking whether time.h and sys/time.h may both be included""... $ac_c" 1>&6 +echo "configure:1239: checking whether time.h and sys/time.h may both be included" >&5 +if eval "test \"`echo '$''{'ac_cv_header_time'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +int main() { +struct tm *tp; +; return 0; } +EOF +if { (eval echo configure:1253: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_header_time=yes +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_time=no +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_header_time" 1>&6 +if test $ac_cv_header_time = yes; then + cat >> confdefs.h <<\EOF +#define TIME_WITH_SYS_TIME 1 +EOF + +fi + + +echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 +echo "configure:1275: checking for ANSI C header files" >&5 +if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#include +#include +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1288: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + ac_cv_header_stdc=yes +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "memchr" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. +cat > conftest.$ac_ext < +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "free" >/dev/null 2>&1; then + : +else + rm -rf conftest* + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. +if test "$cross_compiling" = yes; then + : +else + cat > conftest.$ac_ext < +#define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int main () { int i; for (i = 0; i < 256; i++) +if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); +exit (0); } + +EOF +if { (eval echo configure:1355: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null +then + : +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -fr conftest* + ac_cv_header_stdc=no +fi +rm -fr conftest* +fi + +fi +fi + +echo "$ac_t""$ac_cv_header_stdc" 1>&6 +if test $ac_cv_header_stdc = yes; then + cat >> confdefs.h <<\EOF +#define STDC_HEADERS 1 +EOF + +fi + +for ac_hdr in fcntl.h limits.h malloc.h sys/ioctl.h sys/time.h unistd.h bstring.h +do +ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 +echo "configure:1382: checking for $ac_hdr" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1392: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + cat >> confdefs.h <&6 +fi +done + + +if test $ac_cv_prog_gcc = yes; then + echo $ac_n "checking whether ${CC-cc} needs -traditional""... $ac_c" 1>&6 +echo "configure:1421: checking whether ${CC-cc} needs -traditional" >&5 +if eval "test \"`echo '$''{'ac_cv_prog_gcc_traditional'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_pattern="Autoconf.*'x'" + cat > conftest.$ac_ext < +Autoconf TIOCGETP +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +else + rm -rf conftest* + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat > conftest.$ac_ext < +Autoconf TCGETA +EOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + egrep "$ac_pattern" >/dev/null 2>&1; then + rm -rf conftest* + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi + +echo "$ac_t""$ac_cv_prog_gcc_traditional" 1>&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +echo $ac_n "checking return type of signal handlers""... $ac_c" 1>&6 +echo "configure:1467: checking return type of signal handlers" >&5 +if eval "test \"`echo '$''{'ac_cv_type_signal'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +#include +#ifdef signal +#undef signal +#endif +#ifdef __cplusplus +extern "C" void (*signal (int, void (*)(int)))(int); +#else +void (*signal ()) (); +#endif + +int main() { +int i; +; return 0; } +EOF +if { (eval echo configure:1489: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then + rm -rf conftest* + ac_cv_type_signal=void +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + ac_cv_type_signal=int +fi +rm -f conftest* +fi + +echo "$ac_t""$ac_cv_type_signal" 1>&6 +cat >> confdefs.h <&6 +echo "configure:1508: checking for vprintf" >&5 +if eval "test \"`echo '$''{'ac_cv_func_vprintf'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char vprintf(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_vprintf) || defined (__stub___vprintf) +choke me +#else +vprintf(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1536: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_vprintf=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_vprintf=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'vprintf`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_VPRINTF 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +if test "$ac_cv_func_vprintf" != yes; then +echo $ac_n "checking for _doprnt""... $ac_c" 1>&6 +echo "configure:1560: checking for _doprnt" >&5 +if eval "test \"`echo '$''{'ac_cv_func__doprnt'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char _doprnt(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub__doprnt) || defined (__stub____doprnt) +choke me +#else +_doprnt(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1588: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func__doprnt=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func__doprnt=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'_doprnt`\" = yes"; then + echo "$ac_t""yes" 1>&6 + cat >> confdefs.h <<\EOF +#define HAVE_DOPRNT 1 +EOF + +else + echo "$ac_t""no" 1>&6 +fi + +fi + +for ac_func in gettimeofday select socket strerror +do +echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 +echo "configure:1615: checking for $ac_func" >&5 +if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +/* Override any gcc2 internal prototype to avoid an error. */ +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char $ac_func(); + +int main() { + +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined (__stub_$ac_func) || defined (__stub___$ac_func) +choke me +#else +$ac_func(); +#endif + +; return 0; } +EOF +if { (eval echo configure:1643: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_func_$ac_func=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_func_$ac_func=no" +fi +rm -f conftest* +fi + +if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` + cat >> confdefs.h <&6 +fi +done + + + +echo $ac_n "checking for dlopen in -ldl""... $ac_c" 1>&6 +echo "configure:1670: checking for dlopen in -ldl" >&5 +ac_lib_var=`echo dl'_'dlopen | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ldl $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + PDLIB="$PDLIB -ldl" +else + echo "$ac_t""no" 1>&6 +echo "dynamic link support required" || exit 1 +fi + + +echo $ac_n "checking for sin in -lffm""... $ac_c" 1>&6 +echo "configure:1712: checking for sin in -lffm" >&5 +ac_lib_var=`echo ffm'_'sin | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lffm $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + PDLIB="$PDLIB -lffm" +else + echo "$ac_t""no" 1>&6 +fi + + +echo $ac_n "checking for sin in -lm""... $ac_c" 1>&6 +echo "configure:1753: checking for sin in -lm" >&5 +ac_lib_var=`echo m'_'sin | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lm $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + PDLIB="$PDLIB -lm" +else + echo "$ac_t""no" 1>&6 +echo "math library required" || exit 1 +fi + + +echo $ac_n "checking for pthread_create in -lpthread""... $ac_c" 1>&6 +echo "configure:1795: checking for pthread_create in -lpthread" >&5 +ac_lib_var=`echo pthread'_'pthread_create | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lpthread $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + PDLIB="$PDLIB -lpthread" +else + echo "$ac_t""no" 1>&6 +echo "pthreads required" || exit 1 +fi + + +if test "$alsa" = yes; then + echo $ac_n "checking for snd_pcm_info in -lasound""... $ac_c" 1>&6 +echo "configure:1838: checking for snd_pcm_info in -lasound" >&5 +ac_lib_var=`echo asound'_'snd_pcm_info | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lasound $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + PDLIB="$PDLIB -lasound" +else + echo "$ac_t""no" 1>&6 +alsa="" +fi + +elif test "$alsa" = old; then + echo $ac_n "checking for snd_pcm_info in -lasound""... $ac_c" 1>&6 +echo "configure:1880: checking for snd_pcm_info in -lasound" >&5 +ac_lib_var=`echo asound'_'snd_pcm_info | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lasound $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + PDLIB="$PDLIB -lasound" +else + echo "$ac_t""no" 1>&6 +alsa="" +fi + +fi + +# If we find X, set shell vars x_includes and x_libraries to the +# paths, otherwise set no_x=yes. +# Uses ac_ vars as temps to allow command line to override cache and checks. +# --without-x overrides everything else, but does not touch the cache. +echo $ac_n "checking for X""... $ac_c" 1>&6 +echo "configure:1927: checking for X" >&5 + +# Check whether --with-x or --without-x was given. +if test "${with_x+set}" = set; then + withval="$with_x" + : +fi + +# $have_x is `yes', `no', `disabled', or empty when we do not yet know. +if test "x$with_x" = xno; then + # The user explicitly disabled X. + have_x=disabled +else + if test "x$x_includes" != xNONE && test "x$x_libraries" != xNONE; then + # Both variables are already set. + have_x=yes + else +if eval "test \"`echo '$''{'ac_cv_have_x'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + # One or both of the vars are not set, and there is no cached value. +ac_x_includes=NO ac_x_libraries=NO +rm -fr conftestdir +if mkdir conftestdir; then + cd conftestdir + # Make sure to not put "make" in the Imakefile rules, since we grep it out. + cat > Imakefile <<'EOF' +acfindx: + @echo 'ac_im_incroot="${INCROOT}"; ac_im_usrlibdir="${USRLIBDIR}"; ac_im_libdir="${LIBDIR}"' +EOF + if (xmkmf) >/dev/null 2>/dev/null && test -f Makefile; then + # GNU make sometimes prints "make[1]: Entering...", which would confuse us. + eval `${MAKE-make} acfindx 2>/dev/null | grep -v make` + # Open Windows xmkmf reportedly sets LIBDIR instead of USRLIBDIR. + for ac_extension in a so sl; do + if test ! -f $ac_im_usrlibdir/libX11.$ac_extension && + test -f $ac_im_libdir/libX11.$ac_extension; then + ac_im_usrlibdir=$ac_im_libdir; break + fi + done + # Screen out bogus values from the imake configuration. They are + # bogus both because they are the default anyway, and because + # using them would break gcc on systems where it needs fixed includes. + case "$ac_im_incroot" in + /usr/include) ;; + *) test -f "$ac_im_incroot/X11/Xos.h" && ac_x_includes="$ac_im_incroot" ;; + esac + case "$ac_im_usrlibdir" in + /usr/lib | /lib) ;; + *) test -d "$ac_im_usrlibdir" && ac_x_libraries="$ac_im_usrlibdir" ;; + esac + fi + cd .. + rm -fr conftestdir +fi + +if test "$ac_x_includes" = NO; then + # Guess where to find include files, by looking for this one X11 .h file. + test -z "$x_direct_test_include" && x_direct_test_include=X11/Intrinsic.h + + # First, try using that file with no special directory specified. +cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:1994: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + # We can compile using X headers with no special include directory. +ac_x_includes= +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + # Look for the header file in a standard set of common directories. +# Check X11 before X11Rn because it is often a symlink to the current release. + for ac_dir in \ + /usr/X11/include \ + /usr/X11R6/include \ + /usr/X11R5/include \ + /usr/X11R4/include \ + \ + /usr/include/X11 \ + /usr/include/X11R6 \ + /usr/include/X11R5 \ + /usr/include/X11R4 \ + \ + /usr/local/X11/include \ + /usr/local/X11R6/include \ + /usr/local/X11R5/include \ + /usr/local/X11R4/include \ + \ + /usr/local/include/X11 \ + /usr/local/include/X11R6 \ + /usr/local/include/X11R5 \ + /usr/local/include/X11R4 \ + \ + /usr/X386/include \ + /usr/x386/include \ + /usr/XFree86/include/X11 \ + \ + /usr/include \ + /usr/local/include \ + /usr/unsupported/include \ + /usr/athena/include \ + /usr/local/x11r5/include \ + /usr/lpp/Xamples/include \ + \ + /usr/openwin/include \ + /usr/openwin/share/include \ + ; \ + do + if test -r "$ac_dir/$x_direct_test_include"; then + ac_x_includes=$ac_dir + break + fi + done +fi +rm -f conftest* +fi # $ac_x_includes = NO + +if test "$ac_x_libraries" = NO; then + # Check for the libraries. + + test -z "$x_direct_test_library" && x_direct_test_library=Xt + test -z "$x_direct_test_function" && x_direct_test_function=XtMalloc + + # See if we find them without any special options. + # Don't add to $LIBS permanently. + ac_save_LIBS="$LIBS" + LIBS="-l$x_direct_test_library $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + LIBS="$ac_save_LIBS" +# We can link X programs with no special library path. +ac_x_libraries= +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + LIBS="$ac_save_LIBS" +# First see if replacing the include by lib works. +# Check X11 before X11Rn because it is often a symlink to the current release. +for ac_dir in `echo "$ac_x_includes" | sed s/include/lib/` \ + /usr/X11/lib \ + /usr/X11R6/lib \ + /usr/X11R5/lib \ + /usr/X11R4/lib \ + \ + /usr/lib/X11 \ + /usr/lib/X11R6 \ + /usr/lib/X11R5 \ + /usr/lib/X11R4 \ + \ + /usr/local/X11/lib \ + /usr/local/X11R6/lib \ + /usr/local/X11R5/lib \ + /usr/local/X11R4/lib \ + \ + /usr/local/lib/X11 \ + /usr/local/lib/X11R6 \ + /usr/local/lib/X11R5 \ + /usr/local/lib/X11R4 \ + \ + /usr/X386/lib \ + /usr/x386/lib \ + /usr/XFree86/lib/X11 \ + \ + /usr/lib \ + /usr/local/lib \ + /usr/unsupported/lib \ + /usr/athena/lib \ + /usr/local/x11r5/lib \ + /usr/lpp/Xamples/lib \ + /lib/usr/lib/X11 \ + \ + /usr/openwin/lib \ + /usr/openwin/share/lib \ + ; \ +do + for ac_extension in a so sl; do + if test -r $ac_dir/lib${x_direct_test_library}.$ac_extension; then + ac_x_libraries=$ac_dir + break 2 + fi + done +done +fi +rm -f conftest* +fi # $ac_x_libraries = NO + +if test "$ac_x_includes" = NO || test "$ac_x_libraries" = NO; then + # Didn't find X anywhere. Cache the known absence of X. + ac_cv_have_x="have_x=no" +else + # Record where we found X for the cache. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$ac_x_includes ac_x_libraries=$ac_x_libraries" +fi +fi + fi + eval "$ac_cv_have_x" +fi # $with_x != no + +if test "$have_x" != yes; then + echo "$ac_t""$have_x" 1>&6 + no_x=yes +else + # If each of the values was on the command line, it overrides each guess. + test "x$x_includes" = xNONE && x_includes=$ac_x_includes + test "x$x_libraries" = xNONE && x_libraries=$ac_x_libraries + # Update the cache value to reflect the command line values. + ac_cv_have_x="have_x=yes \ + ac_x_includes=$x_includes ac_x_libraries=$x_libraries" + echo "$ac_t""libraries $x_libraries, headers $x_includes" 1>&6 +fi + +echo $ac_n "checking for XCreateWindow in -lX11""... $ac_c" 1>&6 +echo "configure:2157: checking for XCreateWindow in -lX11" >&5 +ac_lib_var=`echo X11'_'XCreateWindow | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-lX11 -L$x_libraries $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + LIBS="$LIBS -lX11 -L$x_libraries" +else + echo "$ac_t""no" 1>&6 +echo "no X11 found" || exit 1 +fi + + +ac_safe=`echo "tcl.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for tcl.h""... $ac_c" 1>&6 +echo "configure:2200: checking for tcl.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2210: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + : +else + echo "$ac_t""no" 1>&6 +ac_safe=`echo "tcl8.1/tcl.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for tcl8.1/tcl.h""... $ac_c" 1>&6 +echo "configure:2231: checking for tcl8.1/tcl.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2241: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.1" +else + echo "$ac_t""no" 1>&6 +ac_safe=`echo "tcl8.2/tcl.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for tcl8.2/tcl.h""... $ac_c" 1>&6 +echo "configure:2262: checking for tcl8.2/tcl.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2272: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.2" +else + echo "$ac_t""no" 1>&6 +ac_safe=`echo "tcl8.3/tcl.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for tcl8.3/tcl.h""... $ac_c" 1>&6 +echo "configure:2293: checking for tcl8.3/tcl.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2303: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.3" +else + echo "$ac_t""no" 1>&6 +ac_safe=`echo "tcl8.4/tcl.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for tcl8.4/tcl.h""... $ac_c" 1>&6 +echo "configure:2324: checking for tcl8.4/tcl.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2334: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.4" +else + echo "$ac_t""no" 1>&6 +ac_safe=`echo "tcl8.5/tcl.h" | sed 'y%./+-%__p_%'` +echo $ac_n "checking for tcl8.5/tcl.h""... $ac_c" 1>&6 +echo "configure:2355: checking for tcl8.5/tcl.h" >&5 +if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + cat > conftest.$ac_ext < +EOF +ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" +{ (eval echo configure:2365: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } +ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` +if test -z "$ac_err"; then + rm -rf conftest* + eval "ac_cv_header_$ac_safe=yes" +else + echo "$ac_err" >&5 + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_header_$ac_safe=no" +fi +rm -f conftest* +fi +if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + echo "$ac_t""yes" 1>&6 + GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.5" +else + echo "$ac_t""no" 1>&6 +echo "no tcl/tk header found" || exit 1 +fi + +fi + +fi + +fi + +fi + +fi + + +echo $ac_n "checking for main in -ltcl8.3""... $ac_c" 1>&6 +echo "configure:2399: checking for main in -ltcl8.3" >&5 +ac_lib_var=`echo tcl8.3'_'main | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ltcl8.3 $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo tcl8.3 | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +echo $ac_n "checking for main in -ltcl8.2""... $ac_c" 1>&6 +echo "configure:2440: checking for main in -ltcl8.2" >&5 +ac_lib_var=`echo tcl8.2'_'main | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ltcl8.2 $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo tcl8.2 | sed -e 's/^a-zA-Z0-9_/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +echo $ac_n "checking for main in -ltcl8.0""... $ac_c" 1>&6 +echo "configure:2481: checking for main in -ltcl8.0" >&5 +ac_lib_var=`echo tcl8.0'_'main | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ltcl8.0 $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo tcl8.0 | sed -e 's/^a-zA-Z0-9_/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + +fi + +fi + + +echo $ac_n "checking for main in -ltk8.3""... $ac_c" 1>&6 +echo "configure:2529: checking for main in -ltk8.3" >&5 +ac_lib_var=`echo tk8.3'_'main | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ltk8.3 $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo tk8.3 | sed -e 's/[^a-zA-Z0-9_]/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +echo $ac_n "checking for main in -ltk8.2""... $ac_c" 1>&6 +echo "configure:2570: checking for main in -ltk8.2" >&5 +ac_lib_var=`echo tk8.2'_'main | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ltk8.2 $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo tk8.2 | sed -e 's/^a-zA-Z0-9_/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +echo $ac_n "checking for main in -ltk8.0""... $ac_c" 1>&6 +echo "configure:2611: checking for main in -ltk8.0" >&5 +ac_lib_var=`echo tk8.0'_'main | sed 'y%./+-%__p_%'` +if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then + echo $ac_n "(cached) $ac_c" 1>&6 +else + ac_save_LIBS="$LIBS" +LIBS="-ltk8.0 $LIBS" +cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=yes" +else + echo "configure: failed program was:" >&5 + cat conftest.$ac_ext >&5 + rm -rf conftest* + eval "ac_cv_lib_$ac_lib_var=no" +fi +rm -f conftest* +LIBS="$ac_save_LIBS" + +fi +if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then + echo "$ac_t""yes" 1>&6 + ac_tr_lib=HAVE_LIB`echo tk8.0 | sed -e 's/^a-zA-Z0-9_/_/g' \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` + cat >> confdefs.h <&6 +fi + +fi + +fi + + + + +if test `uname -s` = FreeBSD; +then + LDFLAGS="-Wl,-export-dynamic" + EXT=pd_freebsd + MORECFLAGS=-DDL_OPEN + SYSSRC=s_freebsd.c + STRIPFLAG=-s + GUINAME="pd-gui" + OSNUMBER=0 +fi + +if test `uname -s` = Linux; +then + LDFLAGS="-Wl,-export-dynamic" + EXT=pd_linux + MORECFLAGS=-DDL_OPEN + SYSSRC=s_linux.c + STRIPFLAG=-s + GUINAME="pd-gui" + if test $USE_OPT_CFLAGS == "YES"; + then + OPT_CFLAGS="-O6 -funroll-loops -fomit-frame-pointer" + else + OPT_CFLAGS="-g" + fi + OSNUMBER=0 +fi + +if test `uname -s` = IRIX64; +then + LDFLAGS="-n32 -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \ + -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \ + -shared -rdata_shared" + EXT=pd_irix6 + MORECFLAGS=-DDL_OPEN + SYSSRC=s_sgi.c + STRIPFLAG=-s + GUINAME="pd-gui" + OSNUMBER=0 +fi + +if test `uname -s` = IRIX32; +then + LDFLAGS="-o32 -DUNIX -DIRIX -O2 -shared -rdata_shared" + EXT=pd_irix5 + MORECFLAGS=-DDL_OPEN + SYSSRC=s_sgi.c + STRIPFLAG=-s + GUINAME="pd-gui" + OSNUMBER=0 +fi + +if test `uname -s` = Darwin; +then + LDFLAGS="-Wl -framework Tcl -framework Tk -framework CoreAudio -framework Carbon -framework CoreMIDI" + EXT=pd_darwin + MORECFLAGS="-DMACOSX -I/usr/X11R6/include -I../portaudio/pa_common \ + -I../portaudio/pablio -I../portaudio/portmidi-macosx -Wno-error" + SYSSRC="s_mac.c s_portaudio.c ../portaudio/pa_common/pa_lib.c \ + ../portaudio/pa_common/pa_trace.c \ + ../portaudio/pa_common/pa_convert.c \ + ../portaudio/pablio/pablio_pd.c \ + ../portaudio/pablio/ringbuffer_pd.c \ + ../portaudio/pa_mac_core/pa_mac_core.c \ + ../portaudio/portmidi-macosx/pmdarwin.c \ + ../portaudio/portmidi-macosx/pmmacosx.c \ + ../portaudio/portmidi-macosx/pmutil.c \ + ../portaudio/portmidi-macosx/portmidi.c \ + ../portaudio/portmidi-macosx/ptdarwin.c " + STRIPFLAG="" + GUINAME="pdtcl" + GUIFLAGS="-framework Tcl -framework Tk \ + -I/Library/Frameworks/Tk.framework/Versions/Current/Headers \ + -I/Library/Frameworks/Tcl.framework/Versions/Current/Headers \ + -I/Library/Frameworks/Tcl.framework/Versions/8.4/PrivateHeaders" + if test $USE_OPT_CFLAGS == "YES"; + then + OPT_CFLAGS="-O2" + else + OPT_CFLAGS="-g" + fi + OSNUMBER=2 + EXTERNTARGET=pd_darwin +fi + +trap '' 1 2 15 +cat > confcache <<\EOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs. It is not useful on other systems. +# If it contains results you don't want to keep, you may remove or edit it. +# +# By default, configure uses ./config.cache as the cache file, +# creating it if it does not exist already. You can give configure +# the --cache-file=FILE option to use a different cache file; that is +# what configure does when it calls configure scripts in +# subdirectories, so they share the cache. +# Giving --cache-file=/dev/null disables caching, for debugging configure. +# config.status only pays attention to the cache file if you give it the +# --recheck option to rerun configure. +# +EOF +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, don't put newlines in cache variables' values. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +(set) 2>&1 | + case `(ac_space=' '; set | grep ac_space) 2>&1` in + *ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote substitution + # turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + -e "s/'/'\\\\''/g" \ + -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' + ;; + esac >> confcache +if cmp -s $cache_file confcache; then + : +else + if test -w $cache_file; then + echo "updating cache $cache_file" + cat confcache > $cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Any assignment to VPATH causes Sun make to only execute +# the first set of double-colon rules, so remove it if not needed. +# If there is a colon in the path, we need to keep it. +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' +fi + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +cat > conftest.defs <<\EOF +s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g +s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g +s%\[%\\&%g +s%\]%\\&%g +s%\$%$$%g +EOF +DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '` +rm -f conftest.defs + + +# Without the "./", some shells look in PATH for config.status. +: ${CONFIG_STATUS=./config.status} + +echo creating $CONFIG_STATUS +rm -f $CONFIG_STATUS +cat > $CONFIG_STATUS </dev/null | sed 1q`: +# +# $0 $ac_configure_args +# +# Compiler output produced by configure, useful for debugging +# configure, is in ./config.log if it exists. + +ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" +for ac_option +do + case "\$ac_option" in + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" + exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; + -version | --version | --versio | --versi | --vers | --ver | --ve | --v) + echo "$CONFIG_STATUS generated by autoconf version 2.13" + exit 0 ;; + -help | --help | --hel | --he | --h) + echo "\$ac_cs_usage"; exit 0 ;; + *) echo "\$ac_cs_usage"; exit 1 ;; + esac +done + +ac_given_srcdir=$srcdir +ac_given_INSTALL="$INSTALL" + +trap 'rm -fr `echo "makefile" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 +EOF +cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF +$ac_vpsub +$extrasub +s%@SHELL@%$SHELL%g +s%@CFLAGS@%$CFLAGS%g +s%@CPPFLAGS@%$CPPFLAGS%g +s%@CXXFLAGS@%$CXXFLAGS%g +s%@FFLAGS@%$FFLAGS%g +s%@DEFS@%$DEFS%g +s%@LDFLAGS@%$LDFLAGS%g +s%@LIBS@%$LIBS%g +s%@exec_prefix@%$exec_prefix%g +s%@prefix@%$prefix%g +s%@program_transform_name@%$program_transform_name%g +s%@bindir@%$bindir%g +s%@sbindir@%$sbindir%g +s%@libexecdir@%$libexecdir%g +s%@datadir@%$datadir%g +s%@sysconfdir@%$sysconfdir%g +s%@sharedstatedir@%$sharedstatedir%g +s%@localstatedir@%$localstatedir%g +s%@libdir@%$libdir%g +s%@includedir@%$includedir%g +s%@oldincludedir@%$oldincludedir%g +s%@infodir@%$infodir%g +s%@mandir@%$mandir%g +s%@alsa@%$alsa%g +s%@rme@%$rme%g +s%@PDLIB@%$PDLIB%g +s%@DEFINES@%$DEFINES%g +s%@MORECFLAGS@%$MORECFLAGS%g +s%@EXT@%$EXT%g +s%@OPT_CFLAGS@%$OPT_CFLAGS%g +s%@USE_OPT_CFLAGS@%$USE_OPT_CFLAGS%g +s%@SYSSRC@%$SYSSRC%g +s%@STRIPFLAG@%$STRIPFLAG%g +s%@GUINAME@%$GUINAME%g +s%@GUIFLAGS@%$GUIFLAGS%g +s%@OSNUMBER@%$OSNUMBER%g +s%@EXTERNTARGET@%$EXTERNTARGET%g +s%@CC@%$CC%g +s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g +s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g +s%@INSTALL_DATA@%$INSTALL_DATA%g +s%@SET_MAKE@%$SET_MAKE%g +s%@CPP@%$CPP%g + +CEOF +EOF + +cat >> $CONFIG_STATUS <<\EOF + +# Split the substitutions into bite-sized pieces for seds with +# small command number limits, like on Digital OSF/1 and HP-UX. +ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. +ac_file=1 # Number of current file. +ac_beg=1 # First line for current file. +ac_end=$ac_max_sed_cmds # Line after last line for current file. +ac_more_lines=: +ac_sed_cmds="" +while $ac_more_lines; do + if test $ac_beg -gt 1; then + sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file + else + sed "${ac_end}q" conftest.subs > conftest.s$ac_file + fi + if test ! -s conftest.s$ac_file; then + ac_more_lines=false + rm -f conftest.s$ac_file + else + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f conftest.s$ac_file" + else + ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" + fi + ac_file=`expr $ac_file + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_cmds` + fi +done +if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat +fi +EOF + +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF +for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case "$ac_file" in + *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` + ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; + *) ac_file_in="${ac_file}.in" ;; + esac + + # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. + + # Remove last slash and all that follows it. Not all systems have dirname. + ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` + if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then + # The file is in a subdirectory. + test ! -d "$ac_dir" && mkdir "$ac_dir" + ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" + # A "../" for each directory in $ac_dir_suffix. + ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` + else + ac_dir_suffix= ac_dots= + fi + + case "$ac_given_srcdir" in + .) srcdir=. + if test -z "$ac_dots"; then top_srcdir=. + else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; + /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; + *) # Relative path. + srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" + top_srcdir="$ac_dots$ac_given_srcdir" ;; + esac + + case "$ac_given_INSTALL" in + [/$]*) INSTALL="$ac_given_INSTALL" ;; + *) INSTALL="$ac_dots$ac_given_INSTALL" ;; + esac + + echo creating "$ac_file" + rm -f "$ac_file" + configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." + case "$ac_file" in + *Makefile*) ac_comsub="1i\\ +# $configure_input" ;; + *) ac_comsub= ;; + esac + + ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` + sed -e "$ac_comsub +s%@configure_input@%$configure_input%g +s%@srcdir@%$srcdir%g +s%@top_srcdir@%$top_srcdir%g +s%@INSTALL@%$INSTALL%g +" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file +fi; done +rm -f conftest.s* + +EOF +cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF + +exit 0 +EOF +chmod +x $CONFIG_STATUS +rm -fr confdefs* $ac_clean_files +test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 + + diff --git a/pd/src/configure.in b/pd/src/configure.in new file mode 100644 index 00000000..ad1eba66 --- /dev/null +++ b/pd/src/configure.in @@ -0,0 +1,192 @@ +dnl Process this file with autoconf to produce a configure script. +AC_INIT(d_arithmetic.c) + +AC_SUBST(alsa) +AC_SUBST(rme) +AC_SUBST(PDLIB) +AC_SUBST(DEFINES) +AC_SUBST(MORECFLAGS) +AC_SUBST(EXT) +AC_SUBST(OPT_CFLAGS) +AC_SUBST(USE_OPT_CFLAGS) +AC_SUBST(SYSSRC) +AC_SUBST(STRIPFLAG) +AC_SUBST(GUINAME) +AC_SUBST(GUIFLAGS) +AC_SUBST(OSNUMBER) +AC_SUBST(EXTERNTARGET) + +dnl other defaults + +dnl check for features + +AC_ARG_ENABLE(alsa, [ --enable-alsa compile ALSA support], + alsa="yes") +AC_ARG_ENABLE(old-alsa,[ --enable-old-alsa ALSA 0.5x support], + alsa="old") +AC_ARG_ENABLE(rme, [ --enable-rme compile RME support], + rme="yes") +AC_ARG_ENABLE(debug, [ --enable-debug debugging support], + USE_OPT_CFLAGS="NO", USE_OPT_CFLAGS="YES") + +dnl Checks for programs. +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_MAKE_SET +AC_PROG_CPP + +dnl Checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_TYPE_PID_T +AC_TYPE_SIZE_T +AC_HEADER_TIME + +dnl Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS(fcntl.h limits.h malloc.h sys/ioctl.h sys/time.h unistd.h bstring.h) + +dnl Checks for library functions. +AC_PROG_GCC_TRADITIONAL +AC_TYPE_SIGNAL +AC_FUNC_VPRINTF +AC_CHECK_FUNCS(gettimeofday select socket strerror) + + +dnl Checks for libraries. +dnl Checking for `dlopen' function in -ldl: +AC_CHECK_LIB(dl, dlopen,PDLIB="$PDLIB -ldl", + echo "dynamic link support required" || exit 1) + +dnl Checking for `sin' function in -lffm: +dnl ffm is the fast math library on the alpha +AC_CHECK_LIB(ffm, sin,PDLIB="$PDLIB -lffm") + +dnl Checking for `sin' function in -lm: +AC_CHECK_LIB(m, sin,PDLIB="$PDLIB -lm", + echo "math library required" || exit 1) + +dnl Checking for `pthread_create' function in -pthread +AC_CHECK_LIB(pthread, pthread_create,PDLIB="$PDLIB -lpthread", + echo "pthreads required" || exit 1) + +dnl This should be fixed so Pd can use ALSA shared libraries where appropriate. +if test "$alsa" = yes; then + AC_CHECK_LIB(asound,snd_pcm_info,PDLIB="$PDLIB -lasound",alsa="") +elif test "$alsa" = old; then + AC_CHECK_LIB(asound,snd_pcm_info,PDLIB="$PDLIB -lasound",alsa="") +fi + +dnl Find paths to includes and libraries for X11 +AC_PATH_X +dnl Checking for `XCreateWindow' function in -lX11: +AC_CHECK_LIB(X11, XCreateWindow, LIBS="$LIBS -lX11 -L$x_libraries", + echo "no X11 found" || exit 1, -L$x_libraries) + +AC_CHECK_HEADER(tcl.h,, + AC_CHECK_HEADER(tcl8.1/tcl.h,GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.1", + AC_CHECK_HEADER(tcl8.2/tcl.h,GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.2", + AC_CHECK_HEADER(tcl8.3/tcl.h,GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.3", + AC_CHECK_HEADER(tcl8.4/tcl.h,GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.4", + AC_CHECK_HEADER(tcl8.5/tcl.h,GUIFLAGS="$GUIFLAGS -I /usr/include/tcl8.5", + echo "no tcl/tk header found" || exit 1)))))) + +AC_CHECK_LIB(tcl8.3, main,, + AC_CHECK_LIB(tcl8.2, main,, + AC_CHECK_LIB(tcl8.0, main))) + +AC_CHECK_LIB(tk8.3, main,, + AC_CHECK_LIB(tk8.2, main,, + AC_CHECK_LIB(tk8.0, main))) + +dnl Checking for tk.h or tkstep.h - not used at the moment +dnl AC_CHECK_HEADER(tk.h,DEFINES="$DEFINES -DTKINC=\\\"tk.h\\\"") +dnl AC_CHECK_HEADER(tkstep.h,DEFINES="$DEFINES -DTKINC=\\\"tkstep.h\\\"") + + +if test `uname -s` = FreeBSD; +then + LDFLAGS="-Wl,-export-dynamic" + EXT=pd_freebsd + MORECFLAGS=-DDL_OPEN + SYSSRC=s_freebsd.c + STRIPFLAG=-s + GUINAME="pd-gui" + OSNUMBER=0 +fi + +if test `uname -s` = Linux; +then + LDFLAGS="-Wl,-export-dynamic" + EXT=pd_linux + MORECFLAGS=-DDL_OPEN + SYSSRC=s_linux.c + STRIPFLAG=-s + GUINAME="pd-gui" + if test $USE_OPT_CFLAGS == "YES"; + then + OPT_CFLAGS="-O6 -funroll-loops -fomit-frame-pointer" + else + OPT_CFLAGS="-g" + fi + OSNUMBER=0 +fi + +if test `uname -s` = IRIX64; +then + LDFLAGS="-n32 -DUNIX -DIRIX -DN32 -woff 1080,1064,1185 \ + -OPT:roundoff=3 -OPT:IEEE_arithmetic=3 -OPT:cray_ivdep=true \ + -shared -rdata_shared" + EXT=pd_irix6 + MORECFLAGS=-DDL_OPEN + SYSSRC=s_sgi.c + STRIPFLAG=-s + GUINAME="pd-gui" + OSNUMBER=0 +fi + +if test `uname -s` = IRIX32; +then + LDFLAGS="-o32 -DUNIX -DIRIX -O2 -shared -rdata_shared" + EXT=pd_irix5 + MORECFLAGS=-DDL_OPEN + SYSSRC=s_sgi.c + STRIPFLAG=-s + GUINAME="pd-gui" + OSNUMBER=0 +fi + +if test `uname -s` = Darwin; +then + LDFLAGS="-Wl -framework Tcl -framework Tk -framework CoreAudio -framework Carbon -framework CoreMIDI" + EXT=pd_darwin + MORECFLAGS="-DMACOSX -I/usr/X11R6/include -I../portaudio/pa_common \ + -I../portaudio/pablio -I../portaudio/portmidi-macosx -Wno-error" + SYSSRC="s_mac.c s_portaudio.c ../portaudio/pa_common/pa_lib.c \ + ../portaudio/pa_common/pa_trace.c \ + ../portaudio/pa_common/pa_convert.c \ + ../portaudio/pablio/pablio_pd.c \ + ../portaudio/pablio/ringbuffer_pd.c \ + ../portaudio/pa_mac_core/pa_mac_core.c \ + ../portaudio/portmidi-macosx/pmdarwin.c \ + ../portaudio/portmidi-macosx/pmmacosx.c \ + ../portaudio/portmidi-macosx/pmutil.c \ + ../portaudio/portmidi-macosx/portmidi.c \ + ../portaudio/portmidi-macosx/ptdarwin.c " + STRIPFLAG="" + GUINAME="pdtcl" + GUIFLAGS="-framework Tcl -framework Tk \ + -I/Library/Frameworks/Tk.framework/Versions/Current/Headers \ + -I/Library/Frameworks/Tcl.framework/Versions/Current/Headers \ + -I/Library/Frameworks/Tcl.framework/Versions/8.4/PrivateHeaders" + if test $USE_OPT_CFLAGS == "YES"; + then + OPT_CFLAGS="-O2" + else + OPT_CFLAGS="-g" + fi + OSNUMBER=2 + EXTERNTARGET=pd_darwin +fi + +AC_OUTPUT(makefile) + diff --git a/pd/src/d_arithmetic.c b/pd/src/d_arithmetic.c new file mode 100644 index 00000000..0ca4e846 --- /dev/null +++ b/pd/src/d_arithmetic.c @@ -0,0 +1,842 @@ +/* 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_float *in1 = (t_float *)(w[1]); + t_float *in2 = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in1++ + *in2++; + return (w+5); +} + +t_int *plus_perf8(t_int *w) +{ + t_float *in1 = (t_float *)(w[1]); + t_float *in2 = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + float f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + float f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + float g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + float 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_float *in = (t_float *)(w[1]); + t_float f = *(t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in++ + f; + return (w+5); +} + +t_int *scalarplus_perf8(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float g = *(t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + float f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + float 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_float *in1 = (t_float *)(w[1]); + t_float *in2 = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in1++ - *in2++; + return (w+5); +} + +t_int *minus_perf8(t_int *w) +{ + t_float *in1 = (t_float *)(w[1]); + t_float *in2 = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + float f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + float f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + float g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + float 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_float *in = (t_float *)(w[1]); + t_float f = *(t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in++ - f; + return (w+5); +} + +t_int *scalarminus_perf8(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float g = *(t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + float f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + float 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_float *in1 = (t_float *)(w[1]); + t_float *in2 = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in1++ * *in2++; + return (w+5); +} + +t_int *times_perf8(t_int *w) +{ + t_float *in1 = (t_float *)(w[1]); + t_float *in2 = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + float f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + float f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + float g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + float 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 *scalartimes_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float f = *(t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in++ * f; + return (w+5); +} + +t_int *scalartimes_perf8(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float g = *(t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + float f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + float 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 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_float *in1 = (t_float *)(w[1]); + t_float *in2 = (t_float *)(w[2]); + t_float *out = (t_float *)(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_float *in1 = (t_float *)(w[1]); + t_float *in2 = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + float f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + float f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + float g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + float g4 = in2[4], g5 = in2[5], g6 = in2[6], g7 = in2[7]; + + out[0] = (g0? f0 / g0 : 0); + out[1] = (g1? f1 / g1 : 0); + out[2] = (g2? f2 / g2 : 0); + out[3] = (g3? f3 / g3 : 0); + out[4] = (g4? f4 / g4 : 0); + out[5] = (g5? f5 / g5 : 0); + out[6] = (g6? f6 / g6 : 0); + out[7] = (g7? f7 / g7 : 0); + } + return (w+5); +} + +t_int *scalarover_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float f = 1. / *(t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + while (n--) *out++ = *in++ * f; + return (w+5); +} + +t_int *scalarover_perf8(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float g = *(t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + if (g) g = 1.f / g; + for (; n; n -= 8, in += 8, out += 8) + { + float f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + float 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 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_float *in1 = (t_float *)(w[1]); + t_float *in2 = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + float f = *in1++, g = *in2++; + *out++ = (f > g ? f : g); + } + return (w+5); +} + +t_int *max_perf8(t_int *w) +{ + t_float *in1 = (t_float *)(w[1]); + t_float *in2 = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + float f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + float f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + float g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + float 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_float *in = (t_float *)(w[1]); + t_float f = *(t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + t_float g = *in++; + *out++ = (f > g ? f : g); + } + return (w+5); +} + +t_int *scalarmax_perf8(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float g = *(t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + float f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + float 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_float *in1 = (t_float *)(w[1]); + t_float *in2 = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + float f = *in1++, g = *in2++; + *out++ = (f < g ? f : g); + } + return (w+5); +} + +t_int *min_perf8(t_int *w) +{ + t_float *in1 = (t_float *)(w[1]); + t_float *in2 = (t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) + { + float f0 = in1[0], f1 = in1[1], f2 = in1[2], f3 = in1[3]; + float f4 = in1[4], f5 = in1[5], f6 = in1[6], f7 = in1[7]; + + float g0 = in2[0], g1 = in2[1], g2 = in2[2], g3 = in2[3]; + float 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_float *in = (t_float *)(w[1]); + t_float f = *(t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + while (n--) + { + t_float g = *in++; + *out++ = (f < g ? f : g); + } + return (w+5); +} + +t_int *scalarmin_perf8(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float g = *(t_float *)(w[2]); + t_float *out = (t_float *)(w[3]); + int n = (int)(w[4]); + for (; n; n -= 8, in += 8, out += 8) + { + float f0 = in[0], f1 = in[1], f2 = in[2], f3 = in[3]; + float 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/pd/src/d_array.c b/pd/src/d_array.c new file mode 100644 index 00000000..3491ad35 --- /dev/null +++ b/pd/src/d_array.c @@ -0,0 +1,1075 @@ +/* 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_imp.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++; + /* bash NANs and underflow/overflow hazards to zero */ + if (!((f > 1.0e-20f && f < 1.0e20f) || (f < -1e-20f && f > -1e20))) + 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)) + { + error("%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)) + { + error("%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) + error("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)) + { + error("%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.5f * (frac-1.) * ( + (a - d + 3.0f * cminusb) * frac + (b - a - cminusb) + ) + ); + } + 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) + error("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)) + { + error("%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 NT + /* 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 /* NT */ +#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.5f * (frac-1.) * ( + (a - d + 3.0f * cminusb) * frac + (b - a - cminusb) + ) + ); + } +#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++; + /* bash NANs and underflow/overflow hazards to zero */ + if (!((f > 1.0e-20f && f < 1.0e20f) || (f < -1e-20f && f > -1e20))) + 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) + error("tabsend~: %s: no such array", x->x_arrayname->s_name); + } + else if (!garray_getfloatarray(a, &vecsize, &x->x_vec)) + error("%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) + error("tabsend~: %s: no such array", x->x_arrayname->s_name); + } + else if (!garray_getfloatarray(a, &vecsize, &x->x_vec)) + error("%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))) + error("%s: no such array", x->x_arrayname->s_name); + else if (!garray_getfloatarray(a, &npoints, &vec)) + error("%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))) + error("%s: no such array", x->x_arrayname->s_name); + else if (!garray_getfloatarray(a, &npoints, &vec)) + error("%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.5f * (frac-1.) * ( + (a - d + 3.0f * cminusb) * frac + (b - a - cminusb)))); + } +} + +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))) + error("%s: no such array", x->x_arrayname->s_name); + else if (!garray_getfloatarray(a, &vecsize, &vec)) + error("%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/pd/src/d_ctl.c b/pd/src/d_ctl.c new file mode 100644 index 00000000..e143a067 --- /dev/null +++ b/pd/src/d_ctl.c @@ -0,0 +1,496 @@ +/* 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 sig~ and line~ routines; possibly fancier envelope generators to + come later. +*/ + +#include "m_pd.h" +#include "math.h" + +/* -------------------------- sig~ ------------------------------ */ +static t_class *sig_class; + +typedef struct _sig +{ + t_object x_obj; + float x_f; +} t_sig; + +static t_int *sig_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_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_perform, 3, in, out, n); + else + dsp_add(sig_perf8, 3, in, out, n); +} + +static void sig_float(t_sig *x, t_float f) +{ + x->x_f = f; +} + +static void sig_dsp(t_sig *x, t_signal **sp) +{ + dsp_add(sig_perform, 3, &x->x_f, sp[0]->s_vec, sp[0]->s_n); +} + +static void *sig_new(t_floatarg f) +{ + t_sig *x = (t_sig *)pd_new(sig_class); + x->x_f = f; + outlet_new(&x->x_obj, gensym("signal")); + return (x); +} + +static void sig_setup(void) +{ + sig_class = class_new(gensym("sig~"), (t_newmethod)sig_new, 0, + sizeof(t_sig), 0, A_DEFFLOAT, 0); + class_addfloat(sig_class, (t_method)sig_float); + class_addmethod(sig_class, (t_method)sig_dsp, gensym("dsp"), 0); +} + +/* -------------------------- line~ ------------------------------ */ +static t_class *line_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_msectodsptick; + float x_inletvalue; + float x_inletwas; + int x_ticksleft; + int x_retarget; +} t_line; + +static t_int *line_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; + /* bash NANs and underflow/overflow hazards to zero */ + if (!((f > 1.0e-20f && f < 1.0e20f) || (f < -1e-20f && f > -1e20))) + x->x_value = f = 0; + if (x->x_retarget) + { + int nticks = x->x_inletwas * x->x_msectodsptick; + 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_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_stop(t_line *x) +{ + x->x_target = x->x_value; + x->x_ticksleft = x->x_retarget = 0; +} + +static void line_dsp(t_line *x, t_signal **sp) +{ + dsp_add(line_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); + x->x_1overn = 1./sp[0]->s_n; + x->x_msectodsptick = sp[0]->s_sr / (1000 * sp[0]->s_n); +} + +static void *line_new(void) +{ + t_line *x = (t_line *)pd_new(line_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_setup(void) +{ + line_class = class_new(gensym("line~"), line_new, 0, + sizeof(t_line), 0, 0); + class_addfloat(line_class, (t_method)line_float); + class_addmethod(line_class, (t_method)line_dsp, gensym("dsp"), 0); + class_addmethod(line_class, (t_method)line_stop, gensym("stop"), 0); +} + +/* -------------------------- snapshot~ ------------------------------ */ +static t_class *snapshot_class; + +typedef struct _snapshot +{ + t_object x_obj; + t_sample x_value; + float x_f; +} t_snapshot; + +static void *snapshot_new(void) +{ + t_snapshot *x = (t_snapshot *)pd_new(snapshot_class); + x->x_value = 0; + outlet_new(&x->x_obj, &s_float); + x->x_f = 0; + return (x); +} + +static t_int *snapshot_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_dsp(t_snapshot *x, t_signal **sp) +{ + dsp_add(snapshot_perform, 2, sp[0]->s_vec + (sp[0]->s_n-1), &x->x_value); +} + +static void snapshot_bang(t_snapshot *x) +{ + outlet_float(x->x_obj.ob_outlet, x->x_value); +} + +static void snapshot_setup(void) +{ + snapshot_class = class_new(gensym("snapshot~"), snapshot_new, 0, + sizeof(t_snapshot), 0, 0); + CLASS_MAINSIGNALIN(snapshot_class, t_snapshot, x_f); + class_addmethod(snapshot_class, (t_method)snapshot_dsp, gensym("dsp"), 0); + class_addbang(snapshot_class, snapshot_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 *sigenv_class; +static void sigenv_tick(t_sigenv *x); + +static void *sigenv_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(sigenv_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)sigenv_tick); + x->x_outlet = outlet_new(&x->x_obj, gensym("float")); + x->x_f = 0; + return (x); +} + +static t_int *sigenv_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 sigenv_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(sigenv_perform, 3, x, sp[0]->s_vec, sp[0]->s_n); + if (sp[0]->s_n > MAXVSTAKEN) bug("sigenv_dsp"); +} + +static void sigenv_tick(t_sigenv *x) /* callback function for the clock */ +{ + outlet_float(x->x_outlet, powtodb(x->x_result)); +} + +static void sigenv_ff(t_sigenv *x) /* cleanup on free */ +{ + clock_free(x->x_clock); + freebytes(x->x_buf, (x->x_npoints + MAXVSTAKEN) * sizeof(float)); +} + + +void sigenv_setup(void ) +{ + sigenv_class = class_new(gensym("env~"), (t_newmethod)sigenv_new, + (t_method)sigenv_ff, sizeof(t_sigenv), 0, A_DEFFLOAT, A_DEFFLOAT, 0); + CLASS_MAINSIGNALIN(sigenv_class, t_sigenv, x_f); + class_addmethod(sigenv_class, (t_method)sigenv_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_setup(); + line_setup(); + snapshot_setup(); + sigenv_setup(); + threshold_tilde_setup(); +} + diff --git a/pd/src/d_dac.c b/pd/src/d_dac.c new file mode 100644 index 00000000..620adba5 --- /dev/null +++ b/pd/src/d_dac.c @@ -0,0 +1,183 @@ +/* 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_imp.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 != DACBLKSIZE) + error("dac~: bad vector size"); + else if (ch >= 0 && ch < sys_get_outchannels()) + dsp_add(plus_perform, 4, sys_soundout + DACBLKSIZE*ch, + (*sp2)->s_vec, sys_soundout + DACBLKSIZE*ch, DACBLKSIZE); + } +} + +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 != DACBLKSIZE) + error("adc~: bad vector size"); + else if (ch >= 0 && ch < sys_get_inchannels()) + dsp_add_copy(sys_soundin + DACBLKSIZE*ch, + (*sp2)->s_vec, DACBLKSIZE); + else dsp_add_zero((*sp2)->s_vec, DACBLKSIZE); + } +} + +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/pd/src/d_delay.c b/pd/src/d_delay.c new file mode 100644 index 00000000..f14bb112 --- /dev/null +++ b/pd/src/d_delay.c @@ -0,0 +1,314 @@ +/* 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) +{ + 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"); +} + +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++; + /* bash NANs and underflow/overflow hazards to zero */ + if (!((f > 1.0e-20f && f < 1.0e20f) || (f < -1e-20f && f > -1e20))) + 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-4; + 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 + 3); + 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.5f * (frac-1.) * ( + (a - d + 3.0f * cminusb) * frac + (b - a - cminusb) + ) + ); + } + 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/pd/src/d_fft.c b/pd/src/d_fft.c new file mode 100644 index 00000000..7e5a95a3 --- /dev/null +++ b/pd/src/d_fft.c @@ -0,0 +1,342 @@ +/* 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) +{ + float *in1 = (t_float *)(w[1]); + float *in2 = (t_float *)(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) +{ + float *in1 = (t_float *)(w[1]); + float *in2 = (t_float *)(w[2]); + int n = w[3]; + mayer_fft(n, in1, in2); + return (w+4); +} + +static t_int *sigifft_perform(t_int *w) +{ + float *in1 = (t_float *)(w[1]); + float *in2 = (t_float *)(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; + float *in1 = sp[0]->s_vec; + float *in2 = sp[1]->s_vec; + float *out1 = sp[2]->s_vec; + float *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) +{ + float *in = (t_float *)(w[1]); + float *out = (t_float *)(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) +{ + float *in = (t_float *)(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); + float *in1 = sp[0]->s_vec; + float *out1 = sp[1]->s_vec; + float *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) +{ + float *in = (t_float *)(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); + float *in1 = sp[0]->s_vec; + float *in2 = sp[1]->s_vec; + float *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~ -------------------------------- */ + +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); +} + +/* ------------------------ global setup routine ------------------------- */ + +void d_fft_setup(void) +{ + sigfft_setup(); + sigrfft_setup(); + sigrifft_setup(); + sigframp_setup(); +} diff --git a/pd/src/d_fftroutine.c b/pd/src/d_fftroutine.c new file mode 100644 index 00000000..7f0d2523 --- /dev/null +++ b/pd/src/d_fftroutine.c @@ -0,0 +1,1001 @@ +/*****************************************************************************/ +/* */ +/* 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 NT */ +#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/pd/src/d_filter.c b/pd/src/d_filter.c new file mode 100644 index 00000000..1693cd85 --- /dev/null +++ b/pd/src/d_filter.c @@ -0,0 +1,534 @@ +/* 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.001) f = 10; + 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; +} + +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; + for (i = 0; i < n; i++) + { + float new = *in++ + coef * last; + *out++ = new - last; + last = new; + } + /* NAN protect */ + if (!((last <= 0) || (last >= 0))) + last = 0; + c->c_x = last; + 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.001) f = 10; + 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; +} + +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; + /* NAN protect */ + if (!((last <= 0) || (last >= 0))) + 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; + } + /* NAN protect */ + if (!((last <= 0) || (last >= 0))) + last = 0; + if (!((prev <= 0) || (prev >= 0))) + 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; + *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/pd/src/d_global.c b/pd/src/d_global.c new file mode 100644 index 00000000..633eba1c --- /dev/null +++ b/pd/src/d_global.c @@ -0,0 +1,312 @@ +/* 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; + float *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 = (float *)getbytes(DEFSENDVS * sizeof(float)); + memset((char *)(x->x_vec), 0, DEFSENDVS * sizeof(float)); + x->x_f = 0; + return (x); +} + +static t_int *sigsend_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + while (n--) + { + float f = *in++; + /* bash NANs and underflow/overflow hazards to zero */ + if (!((f > 1.0e-20f && f < 1.0e20f) || (f < -1e-20f && f > -1e20))) + f = 0; + *out++ = f; + } + 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_float *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_float *out = (t_float *)(w[2]); + int n = (int)(w[3]); + t_float *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; + float *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 = (float *)getbytes(DEFSENDVS * sizeof(float)); + memset((char *)(x->x_vec), 0, DEFSENDVS * sizeof(float)); + outlet_new(&x->x_obj, &s_signal); + return (x); +} + +static t_int *sigcatch_perform(t_int *w) +{ + t_float *in = (t_float *)(w[1]); + t_float *out = (t_float *)(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_float *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_float *in = (t_float *)(w[2]); + int n = (int)(w[3]); + t_float *out = x->x_whereto; + if (out) + { + while (n--) + { + float f = *in++; + /* bash NANs and underflow/overflow hazards to zero */ + if (!((f > 1.0e-20f && f < 1.0e20f) || (f < -1e-20f && f > -1e20))) + f = 0; + *out++ += f; + } + } + 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/pd/src/d_math.c b/pd/src/d_math.c new file mode 100644 index 00000000..d64e2e34 --- /dev/null +++ b/pd/src/d_math.c @@ -0,0 +1,573 @@ +/* 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/pd/src/d_mayer_fft.c b/pd/src/d_mayer_fft.c new file mode 100644 index 00000000..c221e33e --- /dev/null +++ b/pd/src/d_mayer_fft.c @@ -0,0 +1,419 @@ +/* +** 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 NT +#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 (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) +{ + print_setup(); + scope_setup(); + bang_tilde_setup(); + samplerate_tilde_setup(); +} + + + + diff --git a/pd/src/d_osc.c b/pd/src/d_osc.c new file mode 100644 index 00000000..27dceae9 --- /dev/null +++ b/pd/src/d_osc.c @@ -0,0 +1,531 @@ +/* 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 NT + /* 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 /* NT */ +#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; + } + 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/pd/src/d_resample.c b/pd/src/d_resample.c new file mode 100644 index 00000000..83ed7498 --- /dev/null +++ b/pd/src/d_resample.c @@ -0,0 +1,225 @@ +/* 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_float *in = (t_float *)(w[1]); /* original signal */ + t_float *out = (t_float *)(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_float *in = (t_float *)(w[1]); /* original signal */ + t_float *out = (t_float *)(w[2]); /* upsampled signal */ + int up = (int)(w[3]); /* upsampling factor */ + int parent = (int)(w[4]); /* original vectorsize */ + + int n=parent*up; + t_float *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_float *in = (t_float *)(w[1]); /* original signal */ + t_float *out = (t_float *)(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_float *dum_out = out; + t_float *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_float *in = (t_float *)(w[2]); /* original signal */ + t_float *out = (t_float *)(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_float *fp; + t_float 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_float *buf=x->s_vec; + t_freebytes(buf, x->s_n * sizeof(*buf)); + buf = (t_float *)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_float *buf=x->s_vec; + t_freebytes(buf, x->s_n * sizeof(*buf)); + buf = (t_float *)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/pd/src/d_soundfile.c b/pd/src/d_soundfile.c new file mode 100644 index 00000000..33de90b7 --- /dev/null +++ b/pd/src/d_soundfile.c @@ -0,0 +1,2200 @@ +/* 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 real-time disk access +objects are available for linux only so far, although they could be compiled +for Windows if someone were willing to find a Pthreads package for it. */ + +#ifdef UNIX +#include +#include +#endif +#include +#ifdef NT +#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, 1 for PCM */ + 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; + +/* 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 NT +#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 **********************/ + +void readsf_banana( void); /* debugging */ + +/* 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); + /* 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, float **vecs, + long itemsread, unsigned char *buf, int nitems, int bytespersamp, + int bigendian) +{ + int i, j; + unsigned char *sp, *sp2; + float *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 = SCALE * ((sp2[1] << 24) | (sp2[0] << 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) +{ + int argc = *p_argc; + t_atom *argv = *p_argv; + int bytespersamp = 2, bigendian = 0, + endianness = -1, swap, filetype = FORMAT_WAVE, normalize = 0; + long onset = 0, nframes = 0x7fffffff; + t_symbol *filesym; + 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 = 1; + argc -= 1; argv += 1; + } + else goto usage; + } + /* only NextStep handles floating point samples */ + if (bytespersamp == 4) + filetype = FORMAT_NEXT; + + /* 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(); + } + swap = (bigendian != garray_ambigendian()); + if (!argc || argv->a_type != A_SYMBOL) + goto usage; + filesym = argv->a_w.w_symbol; + 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; + 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) +{ + 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(44100, swap); /* lie */ + 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(1, swap); + wavehdr->w_nchannels = swap2(nchannels, swap); + wavehdr->w_samplespersec = swap4(44100, swap); + wavehdr->w_navgbytespersec = swap4(44100 * nchannels * bytespersamp, swap); + wavehdr->w_nblockalign = swap2(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; + } + } + return; +baddonewrite: + post("%s: %s", filename, strerror(errno)); +} + +static void soundfile_xferout(int nchannels, float **vecs, + unsigned char *buf, int nitems, long onset, int bytespersamp, + int bigendian, float normalfactor) +{ + int i, j; + unsigned char *sp, *sp2; + float *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 >> 24); + sp2[2] = (xx >> 24); 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 >> 24); + sp2[1] = (xx >> 24); 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_float *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 / bytespersamp) + framesinfile = bytelimit / 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 / bytespersamp) + finalsize = bytelimit / 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; + float *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_float *vecs[MAXSFCHANS]; + char sampbuf[SAMPBUFSIZE]; + int bufframes, nitems; + int fd = -1; + float normfactor, biggest = 0; + t_symbol *filesym; + + if (soundfiler_writeargparse(obj, &argc, &argv, &filesym, &filetype, + &bytespersamp, &swap, &bigendian, &normalize, &onset, &nframes)) + goto usage; + nchannels = argc; + if (nchannels < 1 || nchannels > MAXSFCHANS) + goto usage; + + 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)) < 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); +} + + +/************************* 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 (NT?) 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 */ + /* 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 */ + 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)); +} +#else +#define pute(x) +#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); +} + +void readsf_banana( void) +{ + struct timeval timout; + timout.tv_sec = 0; + timout.tv_usec = 200000; + pute("banana1\n"); + select(0, 0, 0, 0, &timout); + pute("banana2\n"); +} + + +#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; + pute("1\n"); + pthread_mutex_lock(&x->x_mutex); + while (1) + { + int fd, fifohead; + char *buf; + pute("0\n"); + if (x->x_requestcode == REQUEST_NOTHING) + { + pute("wait 2\n"); + sfread_cond_signal(&x->x_answercondition); + sfread_cond_wait(&x->x_requestcondition, &x->x_mutex); + pute("3\n"); + } + 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. */ + pute("4\n"); + 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); + + pute("5\n"); + /* 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; + pute("open failed\n"); + pute(filename); + pute(dirname); + goto lost; + } + /* check if another request has been made; if so, field it */ + if (x->x_requestcode != REQUEST_BUSY) + goto lost; + pute("6\n"); + 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 */ + sprintf(boo, "fifosize %d\n", + x->x_fifosize); + pute(boo); + 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; + pute("77\n"); + 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; + sprintf(boo, "head %d, tail %d, size %d\n", + x->x_fifohead, x->x_fifotail, wantbytes); + pute(boo); + } + else + { + pute("wait 7a ...\n"); + sfread_cond_signal(&x->x_answercondition); + pute("signalled\n"); + sfread_cond_wait(&x->x_requestcondition, + &x->x_mutex); + pute("7a done\n"); + 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) + { + pute("wait 7...\n"); + sfread_cond_signal(&x->x_answercondition); + sfread_cond_wait(&x->x_requestcondition, + &x->x_mutex); + pute("7 done\n"); + continue; + } + else wantbytes = READSIZE; + } + pute("8\n"); + 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) + { + pute("fileerror\n"); + 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; + } + sprintf(boo, "after: head %d, tail %d\n", + x->x_fifohead, x->x_fifotail); + pute(boo); + /* 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 + { + pute("13\n"); + } + } + pute("thread exit\n"); + 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) + { + pute("wait...\n"); + sfread_cond_signal(&x->x_requestcondition); + sfread_cond_wait(&x->x_answercondition, &x->x_mutex); + pute("done\n"); + } + if (x->x_eof && x->x_fifohead >= x->x_fifotail && + x->x_fifohead < x->x_fifotail + wantbytes-1) + { + 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; + sfread_cond_signal(&x->x_requestcondition); + pthread_mutex_unlock(&x->x_mutex); + goto idle; + } + + 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; + post("stopping readsf 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("readsf_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); + 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; + pute("1\n"); + pthread_mutex_lock(&x->x_mutex); + while (1) + { + pute("0\n"); + if (x->x_requestcode == REQUEST_NOTHING) + { + pute("wait 2\n"); + sfread_cond_signal(&x->x_answercondition); + sfread_cond_wait(&x->x_requestcondition, &x->x_mutex); + pute("3\n"); + } + 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; + + /* alter the request code so that an ensuing "open" will get + noticed. */ + pute("4\n"); + 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); + pthread_mutex_lock(&x->x_mutex); + + pute("5\n"); + + if (fd < 0) + { + x->x_fd = -1; + x->x_eof = 1; + x->x_fileerror = errno; + pute("open failed\n"); + pute(filename); + x->x_requestcode = REQUEST_NOTHING; + continue; + } + /* check if another request has been made; if so, field it */ + if (x->x_requestcode != REQUEST_BUSY) + continue; + pute("6\n"); + 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; + pute("77\n"); + + /* 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 + { + pute("wait 7a ...\n"); + sfread_cond_signal(&x->x_answercondition); + pute("signalled\n"); + sfread_cond_wait(&x->x_requestcondition, + &x->x_mutex); + pute("7a done\n"); + continue; + } + pute("8\n"); + 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) + { + pute("fileerror\n"); + 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); + pute(boo); + /* 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 + { + pute("13\n"); + } + } + pute("thread exit\n"); + 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_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) + { + pute("wait...\n"); + sfread_cond_signal(&x->x_requestcondition); + sfread_cond_wait(&x->x_answercondition, &x->x_mutex); + pute("done\n"); + } + + 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) + { + pute("signal 1\n"); + 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; + pute("signal 2\n"); + 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; + if (soundfiler_writeargparse(x, &argc, + &argv, &filesym, &filetype, &bytespersamp, &swap, &bigendian, + &normalize, &onset, &nframes)) + { + pd_error(x, + "writesf~: usage: open [-bytes [234]] [-wave,-nextstep,-aiff] ..."); + post("... [-big,-little] 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); + /* 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; + 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); +} + +/* ------------------------ global setup routine ------------------------- */ + +void d_soundfile_setup(void) +{ + soundfiler_setup(); + readsf_setup(); + writesf_setup(); +} + diff --git a/pd/src/d_ugen.c b/pd/src/d_ugen.c new file mode 100644 index 00000000..68e931e6 --- /dev/null +++ b/pd/src/d_ugen.c @@ -0,0 +1,1078 @@ +/* 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 +#include + +extern t_class *vinlet_class, *voutlet_class, *canvas_class; +int obj_nsiginlets(t_object *x); +int obj_siginletindex(t_object *x, int m); +int obj_nsigoutlets(t_object *x); +int obj_sigoutletindex(t_object *x, int m); +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_new(t_floatarg fvecsize, t_floatarg foverlap, + t_floatarg fupsample) /* IOhannes */ +{ + int vecsize = fvecsize; + int overlap = foverlap; + int upsample, downsample; /* IOhannes */ + t_block *x = (t_block *)pd_new(block_class); + 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; + x->x_phase = 0; + x->x_period = 1; + x->x_frequency = 1; + x->x_switched = 0; + x->x_switchon = 1; + /* IOhannes { */ + x->x_upsample = upsample; + x->x_downsample = downsample; + /* } IOhannes */ + return (x); +} + +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 */ +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 borowee'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, 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"); + 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, int nextjump) +{ + 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 *)); +} + + /* 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. The + output signals will be filled by the outlet epilog code. */ + + if (reblock || switched) + { + t_signal **iosigs = dc->dc_iosigs; + if (iosigs) + { + t_signal **sigp; + int noutlets = dc->dc_noutlets; + for (i = 0, sigp = iosigs + dc->dc_ninlets; i < noutlets; + i++, sigp++) + { + 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: ; + } + + 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_dsp, gensym("dsp"), 0); + class_addfloat(block_class, block_float); +} + diff --git a/pd/src/g_all_guis.c b/pd/src/g_all_guis.c new file mode 100644 index 00000000..5bbf2f4a --- /dev/null +++ b/pd/src/g_all_guis.c @@ -0,0 +1,937 @@ +/* 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_imp.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef NT +#include +#else +#include +#endif + +/* #define GGEE_HSLIDER_COMPATIBLE */ + +/*------------------ global varaibles -------------------------*/ + +t_symbol *iemgui_key_sym=0; + +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); +} + +void iemgui_raute2dollar(t_symbol *s) +{ + if(s->s_name[0] == '#') + s->s_name[0] = '$'; +} + +void iemgui_dollar2raute(t_symbol *s) +{ + if(s->s_name[0] == '$') + s->s_name[0] = '#'; +} + +t_symbol *iemgui_unique2dollarzero(t_symbol *s, int unique_num, int and_unique_flag) +{ + if(and_unique_flag) + { + int l=0; + char *b, str[144]; + + sprintf(str, "%d", unique_num); + while(str[l]) + { + if(s->s_name[l] != str[l]) + return(s); + else + l++; + } + str[0] = '$'; + str[1] = '0'; + str[2] = 0; + b = s->s_name + l; + if(strlen(b) >= IEM_MAX_SYM_LEN) + strncat(str, b, IEM_MAX_SYM_LEN-1); + else + strcat(str, b); + return(gensym(str)); + } + else + return(s); +} + +t_symbol *iemgui_sym2dollararg(t_symbol *s, int nth_arg, int tail_len) +{ + if(nth_arg) + { + char *b, str[144]; + int i=(int)strlen(s->s_name) - tail_len; + + sprintf(str, "_%d", nth_arg); + str[0] = '$'; + if(i < 0) i = 0; + b = s->s_name + i; + strcat(str, b); + return(gensym(str)); + } + else + return(s); +} + +t_symbol *iemgui_dollarzero2unique(t_symbol *s, int unique_num) +{ + int l; + char *b, str[144]; + + sprintf(str, "%d", unique_num); + l = (int)strlen(s->s_name); + b = s->s_name + 2; + if(l < 2) + strcat(str, "shorty"); + else if(l >= IEM_MAX_SYM_LEN) + strncat(str, b, IEM_MAX_SYM_LEN-1); + else + strcat(str, b); + return(gensym(str)); +} + +t_symbol *iemgui_dollararg2sym(t_symbol *s, int nth_arg, int tail_len, int pargc, t_atom *pargv) +{ + int l=(int)strlen(s->s_name); + char *b, str[288]="0"; + t_symbol *s2; + + if(pargc <= 0){} + else if(nth_arg < 1){} + else if(nth_arg > pargc){} + else if(IS_A_FLOAT(pargv, nth_arg-1)) + sprintf(str, "%d", atom_getintarg(nth_arg-1, pargc, pargv)); + else if(IS_A_SYMBOL(pargv, nth_arg-1)) + { + s2 = atom_getsymbolarg(nth_arg-1, pargc, pargv); + strcpy(str, s2->s_name); + } + b = s->s_name + (l - tail_len); + if(l <= tail_len) + strcat(str, "shorty"); + else if(l >= IEM_MAX_SYM_LEN) + strncat(str, b, IEM_MAX_SYM_LEN-1); + else + strcat(str, b); + return(gensym(str)); +} + +int iemgui_is_dollarzero(t_symbol *s) +{ + char *name=s->s_name; + + if((int)strlen(name) >= 2) + { + if((name[0] == '$') && (name[1] == '0') && ((name[2] < '0') || (name[2] > '9'))) + return(1); + } + return(0); +} + +int iemgui_is_dollararg(t_symbol *s, int *tail_len) +{ + char *name=s->s_name; + + *tail_len = (int)strlen(name); + if(*tail_len >= 2) + { + if((name[0] == '$') && (name[1] >= '1') && (name[1] <= '9')) + { + int i=2, arg=(int)(name[1]-'0'); + + (*tail_len) -= 2; + while(name[i] && (name[i] >= '0') && (name[i] <= '9')) + { + arg *= 10; + arg += (int)(name[i]-'0'); + i++; + (*tail_len)--; + } + return(arg); + } + } + return(0); +} + +void iemgui_fetch_unique(t_iemgui *iemgui) +{ + if(!iemgui->x_unique_num) + { + pd_bind(&iemgui->x_glist->gl_gobj.g_pd, gensym("#X")); + iemgui->x_unique_num = canvas_getdollarzero(); + pd_unbind(&iemgui->x_glist->gl_gobj.g_pd, gensym("#X")); + } +} + +void iemgui_fetch_parent_args(t_iemgui *iemgui, int *pargc, t_atom **pargv) +{ + t_canvas *canvas=glist_getcanvas(iemgui->x_glist); + + canvas_setcurrent(canvas); + canvas_getargs(pargc, pargv); + canvas_unsetcurrent(canvas); +} + +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; + } +} + +void iemgui_all_unique2dollarzero(t_iemgui *iemgui, t_symbol **srlsym) +{ + iemgui_fetch_unique(iemgui); + srlsym[0] = iemgui_unique2dollarzero(srlsym[0], iemgui->x_unique_num, + iemgui->x_fsf.x_snd_is_unique); + srlsym[1] = iemgui_unique2dollarzero(srlsym[1], iemgui->x_unique_num, + iemgui->x_fsf.x_rcv_is_unique); + srlsym[2] = iemgui_unique2dollarzero(srlsym[2], iemgui->x_unique_num, + iemgui->x_fsf.x_lab_is_unique); +} + +void iemgui_all_sym2dollararg(t_iemgui *iemgui, t_symbol **srlsym) +{ + srlsym[0] = iemgui_sym2dollararg(srlsym[0], iemgui->x_isa.x_snd_is_arg_num, + iemgui->x_isa.x_snd_arg_tail_len); + srlsym[1] = iemgui_sym2dollararg(srlsym[1], iemgui->x_isa.x_rcv_is_arg_num, + iemgui->x_isa.x_rcv_arg_tail_len); + srlsym[2] = iemgui_sym2dollararg(srlsym[2], iemgui->x_fsf.x_lab_is_arg_num, + iemgui->x_fsf.x_lab_arg_tail_len); +} + +void iemgui_all_dollarzero2unique(t_iemgui *iemgui, t_symbol **srlsym) +{ + iemgui_fetch_unique(iemgui); + if(iemgui_is_dollarzero(srlsym[0])) + { + iemgui->x_fsf.x_snd_is_unique = 1; + iemgui->x_isa.x_snd_is_arg_num = 0; + iemgui->x_isa.x_snd_arg_tail_len = 0; + srlsym[0] = iemgui_dollarzero2unique(srlsym[0], iemgui->x_unique_num); + } + else + iemgui->x_fsf.x_snd_is_unique = 0; + if(iemgui_is_dollarzero(srlsym[1])) + { + iemgui->x_fsf.x_rcv_is_unique = 1; + iemgui->x_isa.x_rcv_is_arg_num = 0; + iemgui->x_isa.x_rcv_arg_tail_len = 0; + srlsym[1] = iemgui_dollarzero2unique(srlsym[1], iemgui->x_unique_num); + } + else + iemgui->x_fsf.x_rcv_is_unique = 0; + if(iemgui_is_dollarzero(srlsym[2])) + { + iemgui->x_fsf.x_lab_is_unique = 1; + iemgui->x_fsf.x_lab_is_arg_num = 0; + iemgui->x_fsf.x_lab_arg_tail_len = 0; + srlsym[2] = iemgui_dollarzero2unique(srlsym[2], iemgui->x_unique_num); + } + else + iemgui->x_fsf.x_lab_is_unique = 0; +} + +void iemgui_all_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym) +{ + int pargc, tail_len, nth_arg; + t_atom *pargv; + + iemgui_fetch_parent_args(iemgui, &pargc, &pargv); + if(nth_arg = iemgui_is_dollararg(srlsym[0], &tail_len)) + { + iemgui->x_isa.x_snd_is_arg_num = nth_arg; + iemgui->x_isa.x_snd_arg_tail_len = tail_len; + iemgui->x_fsf.x_snd_is_unique = 0; + srlsym[0] = iemgui_dollararg2sym(srlsym[0], nth_arg, tail_len, pargc, pargv); + } + else + { + iemgui->x_isa.x_snd_is_arg_num = 0; + iemgui->x_isa.x_snd_arg_tail_len = 0; + } + if(nth_arg = iemgui_is_dollararg(srlsym[1], &tail_len)) + { + iemgui->x_isa.x_rcv_is_arg_num = nth_arg; + iemgui->x_isa.x_rcv_arg_tail_len = tail_len; + iemgui->x_fsf.x_rcv_is_unique = 0; + srlsym[1] = iemgui_dollararg2sym(srlsym[1], nth_arg, tail_len, pargc, pargv); + } + else + { + iemgui->x_isa.x_rcv_is_arg_num = 0; + iemgui->x_isa.x_rcv_arg_tail_len = 0; + } + if(nth_arg = iemgui_is_dollararg(srlsym[2], &tail_len)) + { + iemgui->x_fsf.x_lab_is_arg_num = nth_arg; + iemgui->x_fsf.x_lab_arg_tail_len = tail_len; + iemgui->x_fsf.x_lab_is_unique = 0; + srlsym[2] = iemgui_dollararg2sym(srlsym[2], nth_arg, tail_len, pargc, pargv); + } + else + { + iemgui->x_fsf.x_lab_is_arg_num = 0; + iemgui->x_fsf.x_lab_arg_tail_len = 0; + } +} + +void iemgui_first_dollararg2sym(t_iemgui *iemgui, t_symbol **srlsym) +{ + int pargc=0, tail_len, nth_arg; + t_atom pargv; + char *name; + + SETFLOAT(&pargv, 0.0); + name = srlsym[0]->s_name; + if(iemgui->x_isa.x_snd_is_arg_num && (name[0] == '$') + && (name[1] >= '1') && (name[1] <= '9')) + { + srlsym[0] = iemgui_dollararg2sym(srlsym[0], iemgui->x_isa.x_snd_is_arg_num, + iemgui->x_isa.x_snd_arg_tail_len, pargc, &pargv); + } + name = srlsym[1]->s_name; + if(iemgui->x_isa.x_rcv_is_arg_num && (name[0] == '$') + && (name[1] >= '1') && (name[1] <= '9')) + { + srlsym[1] = iemgui_dollararg2sym(srlsym[1], iemgui->x_isa.x_rcv_is_arg_num, + iemgui->x_isa.x_rcv_arg_tail_len, pargc, &pargv); + } + name = srlsym[2]->s_name; + if(iemgui->x_fsf.x_lab_is_arg_num && (name[0] == '$') + && (name[1] >= '1') && (name[1] <= '9')) + { + srlsym[2] = iemgui_dollararg2sym(srlsym[2], iemgui->x_fsf.x_lab_is_arg_num, + iemgui->x_fsf.x_lab_arg_tail_len, pargc, &pargv); + } +} + +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) +{ + if(srlsym[0]->s_name[0] == '$') + srlsym[0]->s_name[0] = '#'; + if(srlsym[1]->s_name[0] == '$') + srlsym[1]->s_name[0] = '#'; + if(srlsym[2]->s_name[0] == '$') + srlsym[2]->s_name[0] = '#'; +} + +void iemgui_all_raute2dollar(t_symbol **srlsym) +{ + if(srlsym[0]->s_name[0] == '#') + srlsym[0]->s_name[0] = '$'; + if(srlsym[1]->s_name[0] == '#') + srlsym[1]->s_name[0] = '$'; + if(srlsym[2]->s_name[0] == '#') + srlsym[2]->s_name[0] = '$'; +} + +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; + iemgui_raute2dollar(s); + iemgui_fetch_unique(iemgui); + snd = s; + if(iemgui_is_dollarzero(snd)) + { + iemgui->x_fsf.x_snd_is_unique = 1; + iemgui->x_isa.x_snd_is_arg_num = 0; + iemgui->x_isa.x_snd_arg_tail_len = 0; + snd = iemgui_dollarzero2unique(snd, iemgui->x_unique_num); + } + else + iemgui->x_fsf.x_snd_is_unique = 0; + iemgui_fetch_parent_args(iemgui, &pargc, &pargv); + if(nth_arg = iemgui_is_dollararg(snd, &tail_len)) + { + iemgui->x_isa.x_snd_is_arg_num = nth_arg; + iemgui->x_isa.x_snd_arg_tail_len = tail_len; + iemgui->x_fsf.x_snd_is_unique = 0; + snd = iemgui_dollararg2sym(snd, nth_arg, tail_len, pargc, pargv); + } + else + { + iemgui->x_isa.x_snd_is_arg_num = 0; + iemgui->x_isa.x_snd_arg_tail_len = 0; + } + iemgui->x_snd = snd; + 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; + iemgui_raute2dollar(s); + rcv = s; + iemgui_fetch_unique(iemgui); + if(iemgui_is_dollarzero(rcv)) + { + iemgui->x_fsf.x_rcv_is_unique = 1; + iemgui->x_isa.x_rcv_is_arg_num = 0; + iemgui->x_isa.x_rcv_arg_tail_len = 0; + rcv = iemgui_dollarzero2unique(rcv, iemgui->x_unique_num); + } + else + iemgui->x_fsf.x_rcv_is_unique = 0; + iemgui_fetch_parent_args(iemgui, &pargc, &pargv); + if(nth_arg = iemgui_is_dollararg(rcv, &tail_len)) + { + iemgui->x_isa.x_rcv_is_arg_num = nth_arg; + iemgui->x_isa.x_rcv_arg_tail_len = tail_len; + iemgui->x_fsf.x_rcv_is_unique = 0; + rcv = iemgui_dollararg2sym(rcv, nth_arg, tail_len, pargc, pargv); + } + else + { + iemgui->x_isa.x_rcv_is_arg_num = 0; + iemgui->x_isa.x_rcv_arg_tail_len = 0; + } + 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; + + iemgui_raute2dollar(s); + lab = s; + iemgui_fetch_unique(iemgui); + + if(iemgui_is_dollarzero(lab)) + { + iemgui->x_fsf.x_lab_is_unique = 1; + iemgui->x_fsf.x_lab_is_arg_num = 0; + iemgui->x_fsf.x_lab_arg_tail_len = 0; + lab = iemgui_dollarzero2unique(lab, iemgui->x_unique_num); + } + else + iemgui->x_fsf.x_lab_is_unique = 0; + + iemgui_fetch_parent_args(iemgui, &pargc, &pargv); + if(nth_arg = iemgui_is_dollararg(lab, &tail_len)) + { + iemgui->x_fsf.x_lab_is_arg_num = nth_arg; + iemgui->x_fsf.x_lab_arg_tail_len = tail_len; + iemgui->x_fsf.x_lab_is_unique = 0; + lab = iemgui_dollararg2sym(lab, nth_arg, tail_len, pargc, pargv); + } + else + { + iemgui->x_fsf.x_lab_is_arg_num = 0; + iemgui->x_fsf.x_lab_arg_tail_len = 0; + } + iemgui->x_lab = 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); +} + +int iemgui_list(void *x, t_iemgui *iemgui, t_symbol *s, int ac, t_atom *av) +{ + if(iemgui->x_fsf.x_selected) + { + if((ac == 2)&&IS_A_FLOAT(av,0)&&IS_A_SYMBOL(av,1)) + { + t_symbol *key = atom_getsymbolarg(1, ac, av); + int keydown = atom_getintarg(0, ac, av); + + if(keydown) + { + int refresh = 1,i,d=1; + static char buf[20]; + + buf[0] = 0; + if(!strcmp(key->s_name, "Shift_L")||!strcmp(key->s_name, "Shift_R")) + iemgui->x_fsf.x_shiftdown = 1; + else + { + if(iemgui->x_fsf.x_shiftdown) + d = 10; + if(!strcmp(key->s_name, "Up")) + iemgui->x_obj.te_ypix -= d; + else if(!strcmp(key->s_name, "Down")) + iemgui->x_obj.te_ypix += d; + else if(!strcmp(key->s_name, "Left")) + iemgui->x_obj.te_xpix -= d; + else if(!strcmp(key->s_name, "Right")) + iemgui->x_obj.te_xpix += d; + else + refresh = 0; + if(refresh) + return(1); + } + return(0); + } + else + { + if(!strcmp(key->s_name, "Shift_L")||!strcmp(key->s_name, "Shift_R")) + iemgui->x_fsf.x_shiftdown = 0; + return(0); + } + } + else + return(-1); + } + else + return(-1); +} + +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; + t_rtext *y; + + if(vis) + { + y = rtext_new_without_senditup(glist, (t_text *)z, glist->gl_editor->e_rtext); + (*x->x_gui.x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_NEW); + } + else + { + y = glist_findrtext(glist, (t_text *)z); + (*x->x_gui.x_draw)((void *)z, glist, IEM_GUI_DRAW_MODE_ERASE); + rtext_free(y); + } +} + +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_unique2dollarzero(iemgui, srl); + 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_unique2dollarzero(iemgui, srl); + 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_dollarzero2unique(iemgui, 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); +} diff --git a/pd/src/g_all_guis.h b/pd/src/g_all_guis.h new file mode 100644 index 00000000..54f9cef4 --- /dev/null +++ b/pd/src/g_all_guis.h @@ -0,0 +1,320 @@ +/* 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 20 +#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; + int x_unique_num; + t_symbol *x_snd; + t_symbol *x_rcv; + t_symbol *x_lab; +} 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; + +extern int sys_noloadbang; +extern t_symbol *iemgui_key_sym; +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 void iemgui_raute2dollar(t_symbol *s); +EXTERN void iemgui_dollar2raute(t_symbol *s); +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 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 t_rtext *rtext_new_without_senditup(t_glist *glist, t_text *who, t_rtext *next); +EXTERN int canvas_getdollarzero(void); +EXTERN void canvas_getargs(int *argcp, t_atom **argvp); + diff --git a/pd/src/g_array.c b/pd/src/g_array.c new file mode 100644 index 00000000..2cdb3d8e --- /dev/null +++ b/pd/src/g_array.c @@ -0,0 +1,1358 @@ +/* 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 array_free(t_array *x) +{ + /* we don't unset our gpointer here since it was never "set." */ + /* gpointer_unset(&x->a_gp); */ + gstub_cutoff(x->a_stub); + 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 = *(float *)(elem + xonset); + else xval = indx * xinc; + if (yonset >= 0) + yval = *(float *)(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; + 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, 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) * elemsize); + i++; + (array->a_n)++; + } + 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_xperpix *= 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 = *(float *)(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 = *(float *)(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); + 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", ((float *)(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, + garray_save, + 0 +}; + +/* ----------------------- 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_float **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 = (float *)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++) + *(float *)((x->x_vec + sizeof(t_word) * (i + firstindex)) + yonset) = + 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; + float *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); +} + + diff --git a/pd/src/g_bang.c b/pd/src/g_bang.c new file mode 100644 index 00000000..cb92c685 --- /dev/null +++ b/pd/src/g_bang.c @@ -0,0 +1,600 @@ +/* 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_imp.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef NT +#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) + { + pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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 + { + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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], *ip1, *ip2; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + ip1 = (int *)(&x->x_gui.x_isa); + ip2 = (int *)(&x->x_gui.x_fsf); + 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, + (*ip1)&IEM_INIT_ARGS_ALL, + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + (*ip2)&IEM_FSTYLE_FLAGS_ALL, 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) +{ + int l=iemgui_list((void *)x, &x->x_gui, s, ac, av); + + if(l < 0) + { + bng_bang2(x); + } + else if(l > 0) + { + (*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_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}; + t_symbol *srl[3]; + int a=IEM_GUI_DEFAULTSIZE; + int ldx=0, ldy=-6; + int fs=8, iinit=0, ifstyle=0; + int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, fthold=IEM_BNG_DEFAULTHOLDFLASHTIME; + t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit); + t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle); + char str[144]; + + srl[0] = gensym("empty"); + srl[1] = gensym("empty"); + srl[2] = gensym("empty"); + + 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); + iinit = (int)(atom_getintarg(3, argc, argv)); + if(IS_A_SYMBOL(argv,4)) + srl[0] = atom_getsymbolarg(4, argc, argv); + else if(IS_A_FLOAT(argv,4)) + { + sprintf(str, "%d", (int)atom_getintarg(4, argc, argv)); + srl[0] = gensym(str); + } + if(IS_A_SYMBOL(argv,5)) + srl[1] = atom_getsymbolarg(5, argc, argv); + else if(IS_A_FLOAT(argv,5)) + { + sprintf(str, "%d", (int)atom_getintarg(5, argc, argv)); + srl[1] = gensym(str); + } + if(IS_A_SYMBOL(argv,6)) + srl[2] = atom_getsymbolarg(6, argc, argv); + else if(IS_A_FLOAT(argv,6)) + { + sprintf(str, "%d", (int)atom_getintarg(6, argc, argv)); + srl[2] = gensym(str); + } + ldx = (int)atom_getintarg(7, argc, argv); + ldy = (int)atom_getintarg(8, argc, argv); + ifstyle = (int)(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); + } + + x->x_gui.x_draw = (t_iemfunptr)bng_draw; + iinit &= IEM_INIT_ARGS_ALL; + ifstyle &= IEM_FSTYLE_FLAGS_ALL; + + fstyle->x_snd_able = 1; + fstyle->x_rcv_able = 1; + x->x_flashed = 0; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + x->x_gui.x_isa = *init; + if(!strcmp(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0; + if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0; + x->x_gui.x_unique_num = 0; + if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { fstyle->x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + x->x_gui.x_fsf = *fstyle; + iemgui_first_dollararg2sym(&x->x_gui, srl); + + if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]); + x->x_gui.x_snd = srl[0]; + x->x_gui.x_rcv = srl[1]; + x->x_gui.x_lab = srl[2]; + 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_selected) + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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); + if(!iemgui_key_sym) + iemgui_key_sym = gensym("#keyname"); + 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; + bng_widgetbehavior.w_propertiesfn = bng_properties; + bng_widgetbehavior.w_savefn = bng_save; + class_setwidget(bng_class, &bng_widgetbehavior); + class_sethelpsymbol(bng_class, gensym("bng")); +} diff --git a/pd/src/g_canvas.c b/pd/src/g_canvas.c new file mode 100644 index 00000000..c63b2c0b --- /dev/null +++ b/pd/src/g_canvas.c @@ -0,0 +1,1482 @@ +/* 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 */ + +/* improvement: line-delete-protection, look for "protect" */ +/* 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_imp.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 450 +#define GLIST_DEFCANVASHEIGHT 300 + +#ifdef MACOSX +#define GLIST_DEFCANVASYLOC 20 +#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); + strcat(sbuf, "}\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 *realizedollsym(t_symbol *s, int ac, t_atom *av, int tonew); + +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 = 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 ----------- */ + +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; + + 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 = 450; + 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) +{ + x->gl_screenx1 = x1; + x->gl_screeny1 = y1; + x->gl_screenx2 = x2; + x->gl_screeny2 = 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; + x->gl_y1 = x->gl_screeny2 * diff; + x->gl_y2 = x->gl_y1 - diff; + 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); + } +} + +extern t_gobj *canvas_selectme; /* HACK */ + + /* 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; + if (canvas_selectme) + { + glist_noselect(x); + glist_select(x, canvas_selectme); + canvas_selectme = 0; + } + canvas_drawlines(x); + } + } + else + { + if (glist_isvisible(x)) + { + for (y = x->gl_list; y; y = y->g_next) + gobj_vis(y, x, 0); + 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; + if (createit) + { + if (x->gl_editor) + bug("canvas_create_editor"); + else x->gl_editor = editor_new(x); + } + else + { + if (!x->gl_editor) + bug("canvas_create_editor"); + else 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 NT + 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\n", x, + (int)(x->gl_screenx2 - x->gl_screenx1), + (int)(x->gl_screeny2 - x->gl_screeny1), + (int)(x->gl_screenx1), (int)(x->gl_screeny1) + ); + canvas_reflecttitle(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); + 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 lost 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.menu 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(); + + 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 -tags l%x\n", + glist_getcanvas(x), + t.tr_lx1, t.tr_ly1, t.tr_lx2, t.tr_ly2, 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, 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: */ + +#ifdef NT +#define HORIZBORDER 2 +#define VERTBORDER 2 +#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); + +int obj_issignaloutlet(t_object *x, int outno); +int obj_nsiginlets(t_object *x); +int obj_nsigoutlets(t_object *x); + + /* 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) + canvas_start_dsp(); + else if (!newstate && canvas_dspstate) + canvas_stop_dsp(); + } + 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 *template) +{ + t_canvas *x; + if (!template->gl_imatemplate) return; + /* find all root canvases */ + for (x = canvas_list; x; x = x->gl_next) + glist_redrawall(x); +} + + /* Same as above but just zap them. Call this if a template + is changed by adding or removing a field. LATER we'll just + modify all the items appropriately. */ +static void glist_zapall(t_glist *gl) +{ + t_gobj *g; + for (g = gl->gl_list; g; g = g->g_next) + { + t_class *cl; + if (g->g_pd == canvas_class) + glist_zapall((t_glist *)g); + } + /* do we have any scalars? */ + for (g = gl->gl_list; g; g = g->g_next) + { + if (g->g_pd == scalar_class) + break; + } + if (!g) return; + /* delete all the scalars. This is inefficient if for some reason + you've mixed scalars with other items in a single glist. */ + while (1) + { + for (g = gl->gl_list; g; g = g->g_next) + { + if (g->g_pd == scalar_class) + { + glist_delete(gl, g); + break; + } + } + if (!g) break; + } +} + + /* public interface for above */ +void canvas_zapallfortemplate(t_canvas *template) +{ + t_canvas *x; + if (!template->gl_imatemplate) return; + /* find all root canvases */ + for (x = canvas_list; x; x = x->gl_next) + glist_zapall(x); +} + + /* warn a canvas that some datum has used it as a template. If a + canvas has no data associated with it (at load time, for instance) + we don't have to search through the world for instances as it changes. */ +void canvas_setusedastemplate(t_canvas *x) +{ + x->gl_imatemplate = 1; +} + +/* ------------------------------- 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); +extern void canvas_hdial(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); + +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_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); + +/* ---------------------- 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/pd/src/g_canvas.h b/pd/src/g_canvas.h new file mode 100644 index 00000000..fb567ea5 --- /dev/null +++ b/pd/src/g_canvas.h @@ -0,0 +1,564 @@ +/* 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. + +*/ + +/* --------------------- 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 + +EXTERN_STRUCT _linetraverser; +#define t_linetraverser struct _linetraverser + +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 */ + 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 */ +} 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_imatemplate:1; /* someone needs me as template */ + unsigned int gl_isdeleting:1; /* we're inside glist_delete -- hack! */ + unsigned int gl_protect:1; /* don't delete connections on click */ + 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; + +typedef struct _template +{ + t_pd t_pd; /* 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 */ +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; +}; + +/* 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); + /* save to a binbuf */ +typedef void (*t_savefn)(t_gobj *x, t_binbuf *b); + /* open properties dialog */ +typedef void (*t_propertiesfn)(t_gobj *x, struct _glist *glist); + /* ... 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; + t_savefn w_savefn; + t_propertiesfn w_propertiesfn; +}; + +/* -------- 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 *template, 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 *template, 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 *template, 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 *template, 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 *template, 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 *template, 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); + +/* -------------------- 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); + +/* -------------------- 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, t_rtext *next, + int sendipup); +EXTERN t_rtext *rtext_remove(t_rtext *first, t_rtext *x); +EXTERN t_rtext *glist_findrtext(t_glist *gl, t_text *who); +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 *template); +EXTERN void canvas_zapallfortemplate(t_canvas *template); +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); + +/* ---- 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 *template, + 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 *template, 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 *template, t_gpointer *gp); +EXTERN void word_restore(t_word *wp, t_template *template, + 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 *template); + +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); diff --git a/pd/src/g_editor.c b/pd/src/g_editor.c new file mode 100644 index 00000000..4e6783ff --- /dev/null +++ b/pd/src/g_editor.c @@ -0,0 +1,1658 @@ +/* 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_imp.h" +#include "g_canvas.h" +#include + +void glist_readfrombinbuf(t_glist *x, t_binbuf *b, char *filename, + int selectem); + +/* ------------------ forward function 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); + +/* ---------------- 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); +} + +void gobj_save(t_gobj *x, t_binbuf *b) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_savefn) + (*x->g_pd->c_wb->w_savefn)(x, b); +} + +void gobj_properties(t_gobj *x, struct _glist *glist) +{ + if (x->g_pd->c_wb && x->g_pd->c_wb->w_propertiesfn) + (*x->g_pd->c_wb->w_propertiesfn)(x, glist); +} + +/* ------------------------ managing the selection ----------------- */ + +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)); + /* 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); +} + +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; + } + } +} + + +/* ------------------------ 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 NT + "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 && y->g_pd->c_wb && y->g_pd->c_wb->w_propertiesfn)); + 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 (!y->g_pd->c_wb || !y->g_pd->c_wb->w_propertiesfn) + continue; + gobj_properties(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 *s = class_gethelpname(pd_class(&y->g_pd)); + strcpy(pathbuf, sys_libdir->s_name); + strcat(pathbuf, "/doc/5.reference"); + strcpy(namebuf, s); + if (strcmp(namebuf + strlen(namebuf) - 3, ".pd")) + strcat(namebuf, ".pd"); + glob_evalfile(0, gensym(namebuf), gensym(pathbuf)); + 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, protectmod; + 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); + protectmod = x->gl_protect; + +#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) + { + 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 -tags x\n", + x, xpos, ypos, xpos, ypos); + } + 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 && !protectmod) + { + t_linetraverser t; + t_outconnect *oc; + float fx = xpos, fy = ypos; + linetraverser_start(&t, glist_getcanvas(x)); + 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) + { + 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); + } + else 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_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); +} + +static 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 (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 -tags l%x\n", + glist_getcanvas(x), + lx1, ly1, lx2, ly2, oc); + } + else canvas_setcursor(x, CURSOR_EDITMODE_CONNECT); + return; + } + } + canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); +} + +void canvas_doregion(t_canvas *x, int xpos, int ypos, int doit) +{ + if (doit) + { + t_gobj *y; + 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; + 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); + } + 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) +{ + int xpos = fxpos, ypos = fypos, which = fwhich; + /* post("mouseup %d %d %d", xpos, ypos, which); */ + 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; +} + + /* 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; + float keynum, fflag; + if (ac < 2) + return; + 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'; + /* post("key %c", keynum); */ + 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"); + } + if (keynumsym->s_thing && (fflag != 0)) + pd_float(keynumsym->s_thing, keynum); + if (keyupsym->s_thing && (fflag == 0)) + pd_float(keyupsym->s_thing, keynum); + if (keynamesym->s_thing) + { + t_atom at[2]; + at[0] = av[0]; + if (av[1].a_type == A_SYMBOL) + at[1] = av[1]; + else + { + char buf[3]; + sprintf(buf, "%c", (int)(av[1].a_w.w_float)); + SETSYMBOL(at+1, gensym(buf)); + } + pd_list(keynamesym->s_thing, 0, 2, at); + } + if (x && (fflag != 0)) + { + if (!x->gl_editor) + { + bug("editor"); + return; + } + /* if an object has "grabbed" keys just send them on */ + if (x->gl_editor->e_grab && (keynum != 0) + && x->gl_editor->e_keyfn) + (* x->gl_editor->e_keyfn) + (x->gl_editor->e_grab, keynum); + /* if a text editor is open send it on */ + else if (x->gl_editor->e_textedfor) + { + rtext_key(x->gl_editor->e_textedfor, + (int)keynum, + (av[1].a_type == A_SYMBOL ? av[1].a_w.w_symbol : &s_)); + if (x->gl_editor->e_textdirty) + canvas_dirty(x, 1); + } + /* otherwise check for backspace or clear and do so */ + else if (keynum == 8 || keynum == 127) + canvas_doclear(x); + } +} + +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) + { + t_selection *y; + int resortin = 0, resortout = 0; + 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, + xpos - x->gl_editor->e_xwas, + ypos - x->gl_editor->e_ywas); + if (cl == vinlet_class) resortin = 1; + else if (cl == voutlet_class) resortout = 1; + } + x->gl_editor->e_xwas = xpos; + x->gl_editor->e_ywas = ypos; + if (resortin) canvas_resortinlets(x); + if (resortout) canvas_resortoutlets(x); + canvas_dirty(x, 1); + } + 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) + { + (*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); +} + + +#if 0 /* LATER fix this to re-load abstractions when the patch changes. + The best way to do this is probably to rewrite "stowconnections" + so that it can operate either on the selection as now or on + another replacement criterion. Could do the same trick for + externs if we wish. */ + /* recursively check for abstractions to reload as result of a save. */ +static void glist_doreload(t_glist *gl, t_symbol *name, t_symbol *dir) +{ + t_gobj *g; + for (g = gl->gl_list; g; g = g->g_next) + { + if (pd_class(&g->g_pd) == canvas_class && + canvas_isabstraction(&g->g_pd) && + ((t_canvas *)g)->gl_name == name && + ((t_canvas *)g)->gl_env->ce_dir == dir) + { + ..... + glist_noselect(gl); + canvas_vis(glist_getcanvas(gl), 1); + canvas_editmode(glist_getcanvas(gl), 1.); + glist_select(gl, g); + return (1); + } + else if (pd_class(&g->g_pd) == graph_class) + glist_doreload((t_glist *)g, name, dir); + else if (pd_class(&g->g_pd) == canvas_class) + glist_doreload(&((t_canvas *)g), + name, dir); + } + } + return (0); +} + +static void canvas_reload(t_symbol *name, t_symbol *dir) +{ + t_canvas *x; + /* find all root canvases */ + for (x = canvas_list; x; x = x->gl_next) + glist_doreload(x, name, dir); +} + +#endif /* 0 -- also uncomment call to canvas_reload below */ + +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); +t_gobj *canvas_selectme; /* HACK */ + + /* 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); + if (glist_isvisible(x)) + { + canvas_vis(x, 1); + canvas_editmode(x, 1.); + glist_select(x, y); + } + else + { + /* LATER fix so we can select it right here. + ERight now, HACK it so that canvas_map selects it. + We can't select earlier because the rtexts aren't + created in time. Should create the rtexts in + canvas_vis() but we don't so that yet. */ + + canvas_selectme = y; + canvas_vis(x, 1); + } + 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.); + /* we can't just select here ala glist_select(gl, g); instead, + as in "find", set "selectme" for when "map" function is called. */ + canvas_selectme = 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 srcno = 0, sinkno = 0; + int s1 = glist_isselected(x, &t.tr_ob->ob_g); + int s2 = glist_isselected(x, &t.tr_ob2->ob_g); + if (s1 != s2) + { + for (y = x->gl_list; y && y != &t.tr_ob->ob_g; y = y->g_next) + srcno++; + for (y = x->gl_list; y && y != &t.tr_ob2->ob_g; y = y->g_next) + sinkno++; + binbuf_addv(x->gl_editor->e_connectbuf, "ssiiii;", + gensym("#X"), gensym("connect"), + srcno, t.tr_outno, sinkno, 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 *copy_binbuf; + +static void canvas_copy(t_canvas *x) +{ + t_gobj *y; + t_linetraverser t; + t_outconnect *oc; + if (!x->gl_editor || !x->gl_editor->e_selection) + return; + binbuf_clear(copy_binbuf); + for (y = x->gl_list; y; y = y->g_next) + { + if (glist_isselected(x, y)) + gobj_save(y, copy_binbuf); + } + linetraverser_start(&t, x); + while (oc = linetraverser_next(&t)) + { + int srcno = 0, sinkno = 0; + if (glist_isselected(x, &t.tr_ob->ob_g) + && glist_isselected(x, &t.tr_ob2->ob_g)) + { + for (y = x->gl_list; y && y != &t.tr_ob->ob_g; y = y->g_next) + if (glist_isselected(x, y)) srcno++; + for (y = x->gl_list; y && y != &t.tr_ob2->ob_g; y = y->g_next) + if (glist_isselected(x, y)) sinkno++; + binbuf_addv(copy_binbuf, "ssiiii;", gensym("#X"), + gensym("connect"), srcno, t.tr_outno, sinkno, t.tr_inno); + } + } +} + +extern t_pd *newest; +static void canvas_doclear(t_canvas *x) +{ + t_gobj *y, *y2; + int dspstate; + + dspstate = canvas_suspend_dsp(); + /* 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) +{ + 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_paste(t_canvas *x) +{ + 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(copy_binbuf, 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_duplicate(t_canvas *x) +{ + if (x->gl_editor->e_onmotion == MA_NONE) + { + t_selection *y; + canvas_copy(x); + canvas_paste(x); + 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); + } +} + +static 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 -tags l%x\n", + glist_getcanvas(x), 0, 0, 0, 0, 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; + /* tidy horizontally */ + for (y = x->gl_list; y; y = y->g_next) + { + gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2); + + for (y2 = x->gl_list; y2; y2 = y2->g_next) + { + 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) + { + 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) + { + gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2); + for (y2 = x->gl_list; y2; y2 = y2->g_next) + { + 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) + { + int keep = 1; + gobj_getrect(y, x, &ax1, &ay1, &ax2, &ay2); + for (y2 = x->gl_list; y2; y2 = y2->g_next) + { + 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) + { + 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; + if (x->gl_edit = !x->gl_edit) + canvas_setcursor(x, CURSOR_EDITMODE_NOTHING); + else + { + glist_noselect(x); + canvas_setcursor(x, CURSOR_RUNMODE_NOTHING); + } + sys_vgui("pdtk_canvas_editval .x%x %d\n", + glist_getcanvas(x), x->gl_edit); + if (yesplease) canvas_dirty(x, 1); +} + +static void canvas_protectmode(t_canvas *x, t_floatarg fyesplease) +{ + int yesplease = fyesplease; + + if (yesplease && x->gl_protect) + return; + x->gl_protect = !x->gl_protect; + sys_vgui("pdtk_canvas_protectval .x%x %d\n", + glist_getcanvas(x), x->gl_protect); +} + + /* 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) + { + 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_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_protectmode, + gensym("protectmode"), 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); + +/* -------------- copy buffer ------------------ */ + copy_binbuf = binbuf_new(); +} diff --git a/pd/src/g_graph.c b/pd/src/g_graph.c new file mode 100644 index 00000000..65a5d056 --- /dev/null +++ b/pd/src/g_graph.c @@ -0,0 +1,1119 @@ +/* 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) +{ + 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 (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_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); + /* LATER decide whether all visible glists must have an editor? */ + if (glist_isvisible(canvas) && 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_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); + x2->gl_editor->e_onmotion = MA_PASSOUT; + 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_deletelinesforio(t_canvas *x, t_text *text, + t_inlet *inp, t_outlet *outp); + +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); + } + } +} + +t_class *graph_class; + +/* --------------------------- 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) + rtext_new(parent_glist, &x->gl_obj, + parent_glist->gl_editor->e_rtext, + canvas_showtext(x)); + graph_getrect(gr, parent_glist, &x1, &y1, &x2, &y2); + if (!vis) + rtext_free(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_float *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); + } +} + +static void graph_save(t_gobj *z, t_binbuf *b) +{ + t_glist *x = (t_glist *)z; + text_widgetbehavior.w_savefn(z, b); +} + +void garray_properties(t_garray *x); + +static 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); + } +} + +t_widgetbehavior graph_widgetbehavior = +{ + graph_getrect, + graph_displace, + graph_select, + graph_activate, + graph_delete, + graph_vis, + graph_click, + graph_save, + graph_properties, +}; + + /* 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/pd/src/g_guiconnect.c b/pd/src/g_guiconnect.c new file mode 100644 index 00000000..aef8acb6 --- /dev/null +++ b/pd/src/g_guiconnect.c @@ -0,0 +1,94 @@ +/* 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/pd/src/g_hdial.c b/pd/src/g_hdial.c new file mode 100644 index 00000000..6e9f08a7 --- /dev/null +++ b/pd/src/g_hdial.c @@ -0,0 +1,675 @@ +/* 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_imp.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef NT +#include +#else +#include +#endif + +/* ------------- hdl gui-horicontal dial ---------------------- */ + +t_widgetbehavior hdial_widgetbehavior; +static t_class *hdial_class; + +/* widget helper functions */ + +void hdial_draw_update(t_hdial *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 hdial_draw_new(t_hdial *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 hdial_draw_move(t_hdial *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 hdial_draw_erase(t_hdial* 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 hdial_draw_config(t_hdial* 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 hdial_draw_io(t_hdial* 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 hdial_draw_select(t_hdial* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i; + + if(x->x_gui.x_fsf.x_selected) + { + pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + for(i=0; ix_gui.x_obj.ob_pd, iemgui_key_sym); + for(i=0; ix_gui.x_lcol); + } +} + +void hdial_draw(t_hdial *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + hdial_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + hdial_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + hdial_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + hdial_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + hdial_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + hdial_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + hdial_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ hdl widgetbehaviour----------------------------- */ + +static void hdial_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_hdial *x = (t_hdial *)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 hdial_save(t_gobj *z, t_binbuf *b) +{ + t_hdial *x = (t_hdial *)z; + int bflcol[3], *ip1, *ip2; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + ip1 = (int *)(&x->x_gui.x_isa); + ip2 = (int *)(&x->x_gui.x_fsf); + 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), + gensym("hdl"), x->x_gui.x_w, + x->x_change, (*ip1)&IEM_INIT_ARGS_ALL, x->x_number, + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + (*ip2)&IEM_FSTYLE_FLAGS_ALL, x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2], x->x_on); + binbuf_addv(b, ";"); +} + +static void hdial_properties(t_gobj *z, t_glist *owner) +{ + t_hdial *x = (t_hdial *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + sprintf(buf, "pdtk_iemgui_dialog %%s HDIAL \ + ----------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*/ + x->x_change, 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 hdial_dialog(t_hdial *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 hdial_set(t_hdial *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 hdial_bang(t_hdial *x) +{ + 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); +} + +static void hdial_fout(t_hdial *x, t_floatarg f) +{ + int i=(int)f; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + + 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); +} + +static void hdial_float(t_hdial *x, t_floatarg f) +{ + int i=(int)f; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + + 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); + } +} + +static void hdial_click(t_hdial *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); + + hdial_fout(x, (float)(xx / x->x_gui.x_w)); +} + +static int hdial_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + if(doit) + hdial_click((t_hdial *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt); + return (1); +} + +static void hdial_loadbang(t_hdial *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + hdial_bang(x); +} + +static void hdial_number(t_hdial *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 hdial_size(t_hdial *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 hdial_delta(t_hdial *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void hdial_pos(t_hdial *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void hdial_color(t_hdial *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void hdial_send(t_hdial *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void hdial_receive(t_hdial *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void hdial_label(t_hdial *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void hdial_label_pos(t_hdial *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void hdial_label_font(t_hdial *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void hdial_init(t_hdial *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void hdial_double_change(t_hdial *x) +{x->x_change = 1;} + +static void hdial_single_change(t_hdial *x) +{x->x_change = 0;} + +static void hdial_list(t_hdial *x, t_symbol *s, int ac, t_atom *av) +{ + int l=iemgui_list((void *)x, &x->x_gui, s, ac, av); + + if(l < 0) + { + if(IS_A_FLOAT(av,0)) + hdial_float(x, atom_getfloatarg(0, ac, av)); + } + else if(l > 0) + { + (*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 *hdial_new(t_symbol *s, int argc, t_atom *argv) +{ + t_hdial *x = (t_hdial *)pd_new(hdial_class); + int bflcol[]={-262144, -1, -1}; + t_symbol *srl[3]; + int a=IEM_GUI_DEFAULTSIZE, on=0, f=0; + int ldx=0, ldy=-6, chg=1, num=8; + int fs=8, iinit=0, ifstyle=0; + int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, fthold=IEM_BNG_DEFAULTHOLDFLASHTIME; + t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit); + t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle); + char str[144]; + + srl[0] = gensym("empty"); + srl[1] = gensym("empty"); + srl[2] = gensym("empty"); + + 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); + iinit = (int)atom_getintarg(2, argc, argv); + num = (int)atom_getintarg(3, argc, argv); + if(IS_A_SYMBOL(argv,4)) + srl[0] = atom_getsymbolarg(4, argc, argv); + else if(IS_A_FLOAT(argv,4)) + { + sprintf(str, "%d", (int)atom_getintarg(4, argc, argv)); + srl[0] = gensym(str); + } + if(IS_A_SYMBOL(argv,5)) + srl[1] = atom_getsymbolarg(5, argc, argv); + else if(IS_A_FLOAT(argv,5)) + { + sprintf(str, "%d", (int)atom_getintarg(5, argc, argv)); + srl[1] = gensym(str); + } + if(IS_A_SYMBOL(argv,6)) + srl[2] = atom_getsymbolarg(6, argc, argv); + else if(IS_A_FLOAT(argv,6)) + { + sprintf(str, "%d", (int)atom_getintarg(6, argc, argv)); + srl[2] = gensym(str); + } + ldx = (int)atom_getintarg(7, argc, argv); + ldy = (int)atom_getintarg(8, argc, argv); + ifstyle = (int)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); + } + x->x_gui.x_draw = (t_iemfunptr)hdial_draw; + iinit &= IEM_INIT_ARGS_ALL; + ifstyle &= IEM_FSTYLE_FLAGS_ALL; + fstyle->x_snd_able = 1; + fstyle->x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + x->x_gui.x_isa = *init; + if(!strcmp(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0; + if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0; + if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { fstyle->x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + x->x_gui.x_fsf = *fstyle; + x->x_gui.x_unique_num = 0; + 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; + iemgui_first_dollararg2sym(&x->x_gui, srl); + if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]); + x->x_gui.x_snd = srl[0]; + x->x_gui.x_rcv = srl[1]; + x->x_gui.x_lab = srl[2]; + 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 hdial_ff(t_hdial *x) +{ + if(x->x_gui.x_fsf.x_selected) + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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_hdial_setup(void) +{ + hdial_class = class_new(gensym("hdl"), (t_newmethod)hdial_new, + (t_method)hdial_ff, sizeof(t_hdial), 0, A_GIMME, 0); + class_addcreator((t_newmethod)hdial_new, gensym("rdb"), A_GIMME, 0); + class_addcreator((t_newmethod)hdial_new, gensym("radiobut"), A_GIMME, 0); + class_addcreator((t_newmethod)hdial_new, gensym("radiobutton"), A_GIMME, 0); + class_addbang(hdial_class, hdial_bang); + class_addfloat(hdial_class, hdial_float); + class_addlist(hdial_class, hdial_list); + class_addmethod(hdial_class, (t_method)hdial_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(hdial_class, (t_method)hdial_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(hdial_class, (t_method)hdial_loadbang, gensym("loadbang"), 0); + class_addmethod(hdial_class, (t_method)hdial_set, gensym("set"), A_FLOAT, 0); + class_addmethod(hdial_class, (t_method)hdial_size, gensym("size"), A_GIMME, 0); + class_addmethod(hdial_class, (t_method)hdial_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(hdial_class, (t_method)hdial_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(hdial_class, (t_method)hdial_color, gensym("color"), A_GIMME, 0); + class_addmethod(hdial_class, (t_method)hdial_send, gensym("send"), A_DEFSYM, 0); + class_addmethod(hdial_class, (t_method)hdial_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(hdial_class, (t_method)hdial_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(hdial_class, (t_method)hdial_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(hdial_class, (t_method)hdial_label_font, gensym("label_font"), A_GIMME, 0); + class_addmethod(hdial_class, (t_method)hdial_init, gensym("init"), A_FLOAT, 0); + class_addmethod(hdial_class, (t_method)hdial_number, gensym("number"), A_FLOAT, 0); + class_addmethod(hdial_class, (t_method)hdial_single_change, gensym("single_change"), 0); + class_addmethod(hdial_class, (t_method)hdial_double_change, gensym("double_change"), 0); + if(!iemgui_key_sym) + iemgui_key_sym = gensym("#keyname"); + hdial_widgetbehavior.w_getrectfn = hdial_getrect; + hdial_widgetbehavior.w_displacefn = iemgui_displace; + hdial_widgetbehavior.w_selectfn = iemgui_select; + hdial_widgetbehavior.w_activatefn = NULL; + hdial_widgetbehavior.w_deletefn = iemgui_delete; + hdial_widgetbehavior.w_visfn = iemgui_vis; + hdial_widgetbehavior.w_clickfn = hdial_newclick; + hdial_widgetbehavior.w_propertiesfn = hdial_properties; + hdial_widgetbehavior.w_savefn = hdial_save; + class_setwidget(hdial_class, &hdial_widgetbehavior); + class_sethelpsymbol(hdial_class, gensym("hdial")); +} diff --git a/pd/src/g_hslider.c b/pd/src/g_hslider.c new file mode 100644 index 00000000..0e5415a9 --- /dev/null +++ b/pd/src/g_hslider.c @@ -0,0 +1,708 @@ +/* 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_imp.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef NT +#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) + { + pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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 + { + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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], *ip1, *ip2; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + ip1 = (int *)(&x->x_gui.x_isa); + ip2 = (int *)(&x->x_gui.x_fsf); + 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, (*ip1)&IEM_INIT_ARGS_ALL, + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + (*ip2)&IEM_FSTYLE_FLAGS_ALL, 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_list(t_hslider *x, t_symbol *s, int ac, t_atom *av) +{ + int l=iemgui_list((void *)x, &x->x_gui, s, ac, av); + + if(l < 0) + { + if(IS_A_FLOAT(av,0)) + hslider_float(x, atom_getfloatarg(0, ac, av)); + } + else if(l > 0) + { + (*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_new(t_symbol *s, int argc, t_atom *argv) +{ + t_hslider *x = (t_hslider *)pd_new(hslider_class); + int bflcol[]={-262144, -1, -1}; + t_symbol *srl[3]; + 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, iinit=0, ifstyle=0; + double min=0.0, max=(double)(IEM_SL_DEFAULTSIZE-1); + t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit); + t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle); + char str[144]; + + srl[0] = gensym("empty"); + srl[1] = gensym("empty"); + srl[2] = gensym("empty"); + + 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); + iinit = (int)atom_getintarg(5, argc, argv); + if(IS_A_SYMBOL(argv,6)) + srl[0] = atom_getsymbolarg(6, argc, argv); + else if(IS_A_FLOAT(argv,6)) + { + sprintf(str, "%d", (int)atom_getintarg(6, argc, argv)); + srl[0] = gensym(str); + } + if(IS_A_SYMBOL(argv,7)) + srl[1] = atom_getsymbolarg(7, argc, argv); + else if(IS_A_FLOAT(argv,7)) + { + sprintf(str, "%d", (int)atom_getintarg(7, argc, argv)); + srl[1] = gensym(str); + } + if(IS_A_SYMBOL(argv,8)) + srl[2] = atom_getsymbolarg(8, argc, argv); + else if(IS_A_FLOAT(argv,8)) + { + sprintf(str, "%d", (int)atom_getintarg(8, argc, argv)); + srl[2] = gensym(str); + } + ldx = (int)atom_getintarg(9, argc, argv); + ldy = (int)atom_getintarg(10, argc, argv); + ifstyle = (int)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); + } + if((argc == 18)&&IS_A_FLOAT(argv,17)) + steady = (int)atom_getintarg(17, argc, argv); + + x->x_gui.x_draw = (t_iemfunptr)hslider_draw; + iinit &= IEM_INIT_ARGS_ALL; + ifstyle &= IEM_FSTYLE_FLAGS_ALL; + + fstyle->x_snd_able = 1; + fstyle->x_rcv_able = 1; + + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + x->x_gui.x_isa = *init; + 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(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0; + if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0; + x->x_gui.x_unique_num = 0; + if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { fstyle->x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + x->x_gui.x_fsf = *fstyle; + iemgui_first_dollararg2sym(&x->x_gui, srl); + if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]); + x->x_gui.x_snd = srl[0]; + x->x_gui.x_rcv = srl[1]; + x->x_gui.x_lab = srl[2]; + 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_selected) + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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_addlist(hslider_class, hslider_list); + 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); + if(!iemgui_key_sym) + iemgui_key_sym = gensym("#keyname"); + 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; + hslider_widgetbehavior.w_propertiesfn = hslider_properties; + hslider_widgetbehavior.w_savefn = hslider_save; + class_setwidget(hslider_class, &hslider_widgetbehavior); + class_sethelpsymbol(hslider_class, gensym("hslider")); +} diff --git a/pd/src/g_io.c b/pd/src/g_io.c new file mode 100644 index 00000000..487be350 --- /dev/null +++ b/pd/src/g_io.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. */ + +/* 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_float *x_buf; /* signal buffer; zero if not a signal */ + t_float *x_endbuf; + t_float *x_empty; /* next to read out of buffer in epilog code */ + t_float *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_float *in = (t_float *)(w[2]); + int n = (int)(w[3]); + t_float *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_float *out = (t_float *)(w[2]); /* IOhannes */ + + int n = (int)(w[3]); + t_float *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_float *in = x->x_empty; + t_float *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_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; + } + /* 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_float *)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/pd/src/g_mycanvas.c b/pd/src/g_mycanvas.c new file mode 100644 index 00000000..edddb568 --- /dev/null +++ b/pd/src/g_mycanvas.c @@ -0,0 +1,439 @@ +/* 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_imp.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef NT +#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) + { + pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + sys_vgui(".x%x.c itemconfigure %xBASE -outline #%6.6x\n", canvas, x, IEM_GUI_COLOR_SELECTED); + } + else + { + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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], *ip1, *ip2; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + ip1 = (int *)(&x->x_gui.x_isa); + ip2 = (int *)(&x->x_gui.x_fsf); + 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, + (*ip2)&IEM_FSTYLE_FLAGS_ALL, x->x_gui.x_fontsize, + bflcol[0], bflcol[2], (*ip1)&IEM_INIT_ARGS_ALL); + 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_list(t_my_canvas *x, t_symbol *s, int ac, t_atom *av) +{ + int l=iemgui_list((void *)x, &x->x_gui, s, ac, av); + + /*if(l < 0) + { + post("error: my_canvas: no method for 'list'"); + } + else */if(l > 0) + { + (*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_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}; + t_symbol *srl[3]; + int a=IEM_GUI_DEFAULTSIZE, w=100, h=60; + int ldx=20, ldy=12, f=2, i=0; + int fs=14, iinit=0, ifstyle=0; + t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit); + t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle); + char str[144]; + + srl[0] = gensym("empty"); + srl[1] = gensym("empty"); + srl[2] = gensym("empty"); + + 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; + if(IS_A_SYMBOL(argv,3)) + srl[0] = atom_getsymbolarg(3, argc, argv); + else if(IS_A_FLOAT(argv,3)) + { + sprintf(str, "%d", (int)atom_getintarg(3, argc, argv)); + srl[0] = gensym(str); + } + if(IS_A_SYMBOL(argv,4)) + srl[1] = atom_getsymbolarg(4, argc, argv); + else if(IS_A_FLOAT(argv,4)) + { + sprintf(str, "%d", (int)atom_getintarg(4, argc, argv)); + srl[1] = gensym(str); + } + } + else if((argc == 11)&&(IS_A_SYMBOL(argv,3)||IS_A_FLOAT(argv,3))) + { + i = 1; + if(IS_A_SYMBOL(argv,3)) + srl[1] = atom_getsymbolarg(3, argc, argv); + else if(IS_A_FLOAT(argv,3)) + { + sprintf(str, "%d", (int)atom_getintarg(3, argc, argv)); + srl[1] = gensym(str); + } + } + + 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)) + { + if(IS_A_SYMBOL(argv,i+3)) + srl[2] = atom_getsymbolarg(i+3, argc, argv); + else if(IS_A_FLOAT(argv,i+3)) + { + sprintf(str, "%d", (int)atom_getintarg(i+3, argc, argv)); + srl[2] = gensym(str); + } + ldx = (int)atom_getintarg(i+4, argc, argv); + ldy = (int)atom_getintarg(i+5, argc, argv); + ifstyle = (int)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)) + { + iinit = (int)(atom_getintarg(i+10, argc, argv)); + } + x->x_gui.x_draw = (t_iemfunptr)my_canvas_draw; + iinit &= IEM_INIT_ARGS_ALL; + ifstyle &= IEM_FSTYLE_FLAGS_ALL; + fstyle->x_snd_able = 1; + fstyle->x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + x->x_gui.x_isa = *init; + if(!strcmp(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0; + if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0; + x->x_gui.x_unique_num = 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(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { fstyle->x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + x->x_gui.x_fsf = *fstyle; + iemgui_first_dollararg2sym(&x->x_gui, srl); + if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]); + x->x_gui.x_snd = srl[0]; + x->x_gui.x_rcv = srl[1]; + x->x_gui.x_lab = srl[2]; + 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_selected) + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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_addlist(my_canvas_class, my_canvas_list); + 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); + + if(!iemgui_key_sym) + iemgui_key_sym = gensym("#keyname"); + 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; + my_canvas_widgetbehavior.w_propertiesfn = my_canvas_properties; + my_canvas_widgetbehavior.w_savefn = my_canvas_save; + class_setwidget(my_canvas_class, &my_canvas_widgetbehavior); + class_sethelpsymbol(my_canvas_class, gensym("my_canvas")); +} diff --git a/pd/src/g_numbox.c b/pd/src/g_numbox.c new file mode 100644 index 00000000..e0967b5b --- /dev/null +++ b/pd/src/g_numbox.c @@ -0,0 +1,979 @@ +/* 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_imp.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef NT +#include +#else +#include +#endif + +/*------------------ global varaibles -------------------------*/ + + +/*------------------ global functions -------------------------*/ + + +/* ------------ 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; + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + (*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) + { + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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); + } + pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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 + { + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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], *ip1, *ip2; + 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); + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + + } + ip1 = (int *)(&x->x_gui.x_isa); + ip2 = (int *)(&x->x_gui.x_fsf); + 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, (*ip1)&IEM_INIT_ARGS_ALL, + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + (*ip2)&IEM_FSTYLE_FLAGS_ALL, 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); + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + (*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); + if(x->x_gui.x_fsf.x_change) + { + x->x_gui.x_fsf.x_change = 0; + clock_unset(x->x_clock_reset); + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + + } + (*x->x_gui.x_draw)(x, x->x_gui.x_glist, IEM_GUI_DRAW_MODE_UPDATE); + my_numbox_bang(x); +} + +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, + 0, 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); + pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + x->x_buf[0] = 0; + } + else + { + x->x_gui.x_fsf.x_change = 0; + clock_unset(x->x_clock_reset); + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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_list(t_my_numbox *x, t_symbol *s, int ac, t_atom *av) +{ + int l=-1; + + if(x->x_gui.x_fsf.x_selected) + { + if((ac == 2)&&IS_A_FLOAT(av,0)&&IS_A_SYMBOL(av,1)) + { + t_symbol *key = atom_getsymbolarg(1, ac, av); + int keydown = atom_getintarg(0, ac, av); + + if(keydown) + { + int refresh = 1,i,d=1; + static char buf[20]; + + buf[0] = 0; + if(!strcmp(key->s_name, "Shift_L")||!strcmp(key->s_name, "Shift_R")) + x->x_gui.x_fsf.x_shiftdown = 1; + else + { + if(x->x_gui.x_fsf.x_shiftdown) + d = 10; + if(!strcmp(key->s_name, "Up")) + x->x_gui.x_obj.te_ypix -= d; + else if(!strcmp(key->s_name, "Down")) + x->x_gui.x_obj.te_ypix += d; + else if(!strcmp(key->s_name, "Left")) + x->x_gui.x_obj.te_xpix -= d; + else if(!strcmp(key->s_name, "Right")) + x->x_gui.x_obj.te_xpix += d; + else + refresh = 0; + if(refresh) + l = 1; + } + l = 0; + } + else + { + if(!strcmp(key->s_name, "Shift_L")||!strcmp(key->s_name, "Shift_R")) + x->x_gui.x_fsf.x_shiftdown = 0; + l = 0; + } + } + } + else + { + if(x->x_gui.x_fsf.x_change) + { + if((ac == 2)&&IS_A_FLOAT(av,0)&&IS_A_SYMBOL(av,1)) + { + t_symbol *key = atom_getsymbolarg(1, ac, av); + int keydown = atom_getintarg(0, ac, av); + char buf[3]; + + buf[1] = 0; + if(keydown) + { + char *c=key->s_name; + + l = 0; + 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)) + { + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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); + } + else + { + l = 0; + } + } + } + } + if(l < 0) + { + if(IS_A_FLOAT(av,0)) + { + my_numbox_set(x, atom_getfloatarg(0, ac, av)); + my_numbox_bang(x); + } + } + else if(l > 0) + { + (*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_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}; + t_symbol *srl[3]; + int w=5, h=14; + int lilo=0, f=0, ldx=0, ldy=-6; + int fs=10, iinit=0, ifstyle=0; + int log_height=256; + double min=-1.0e+37, max=1.0e+37,v=0.0; + t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit); + t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle); + char str[144]; + + srl[0] = gensym("empty"); + srl[1] = gensym("empty"); + srl[2] = gensym("empty"); + + + 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); + iinit = (int)atom_getintarg(5, argc, argv); + srl[0] = atom_getsymbolarg(6, argc, argv); + srl[1] = atom_getsymbolarg(7, argc, argv); + srl[2] = atom_getsymbolarg(8, argc, argv); + if(IS_A_SYMBOL(argv,6)) + srl[0] = atom_getsymbolarg(6, argc, argv); + else if(IS_A_FLOAT(argv,6)) + { + sprintf(str, "%d", (int)atom_getintarg(6, argc, argv)); + srl[0] = gensym(str); + } + if(IS_A_SYMBOL(argv,7)) + srl[1] = atom_getsymbolarg(7, argc, argv); + else if(IS_A_FLOAT(argv,7)) + { + sprintf(str, "%d", (int)atom_getintarg(7, argc, argv)); + srl[1] = gensym(str); + } + if(IS_A_SYMBOL(argv,8)) + srl[2] = atom_getsymbolarg(8, argc, argv); + else if(IS_A_FLOAT(argv,8)) + { + sprintf(str, "%d", (int)atom_getintarg(8, argc, argv)); + srl[2] = gensym(str); + } + ldx = (int)atom_getintarg(9, argc, argv); + ldy = (int)atom_getintarg(10, argc, argv); + ifstyle = (int)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); + } + 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; + iinit &= IEM_INIT_ARGS_ALL; + ifstyle &= IEM_FSTYLE_FLAGS_ALL; + fstyle->x_snd_able = 1; + fstyle->x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + x->x_gui.x_isa = *init; + 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(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0; + if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0; + x->x_gui.x_unique_num = 0; + if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { fstyle->x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + x->x_gui.x_fsf = *fstyle; + iemgui_first_dollararg2sym(&x->x_gui, srl); + if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]); + x->x_gui.x_snd = srl[0]; + x->x_gui.x_rcv = srl[1]; + x->x_gui.x_lab = srl[2]; + 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_selected) + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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); + if(!iemgui_key_sym) + iemgui_key_sym = gensym("#keyname"); + 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; + my_numbox_widgetbehavior.w_propertiesfn = my_numbox_properties;; + my_numbox_widgetbehavior.w_savefn = my_numbox_save; + class_setwidget(my_numbox_class, &my_numbox_widgetbehavior); + class_sethelpsymbol(my_numbox_class, gensym("numbox2")); +} diff --git a/pd/src/g_readwrite.c b/pd/src/g_readwrite.c new file mode 100644 index 00000000..ab380971 --- /dev/null +++ b/pd/src/g_readwrite.c @@ -0,0 +1,722 @@ +/* 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_imp.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, 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, sinkno; + for (srcno = 0, y = x->gl_list; y && y != &t.tr_ob->ob_g; y = y->g_next) + srcno++; + for (sinkno = 0, y = x->gl_list; y && y != &t.tr_ob2->ob_g; y = y->g_next) + sinkno++; + binbuf_addv(b, "ssiiii;", gensym("#X"), gensym("connect"), + srcno, t.tr_outno, sinkno, t.tr_inno); + } + if (x->gl_isgraph) + 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); + } +} + + /* 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); +#if 0 /* not yet written */ + canvas_reload(filename, dir); +#endif + } + 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/pd/src/g_rtext.c b/pd/src/g_rtext.c new file mode 100644 index 00000000..4b48dcc0 --- /dev/null +++ b/pd/src/g_rtext.c @@ -0,0 +1,478 @@ +/* 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_imp.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 + +static void rtext_senditup(t_rtext *x, int action, int *widthp, int *heightp, + int *indexp); + +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 *next, int senditup) +{ + 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); + if (senditup) + rtext_senditup(x, SEND_FIRST, &w, &h, &indx); + return (x); +} + +/* iemlib version (now incorporated into rtext_new() via the "senditup" arg) */ + +t_rtext *rtext_new_without_senditup(t_glist *glist, t_text *who, t_rtext *next) +{ + return (rtext_new(glist, who, next, 0)); +} + +static t_rtext *rtext_entered; + +void rtext_free(t_rtext *x) +{ + sys_vgui(".x%x.c delete %s\n", glist_getcanvas(x->x_glist), + x->x_tag); + 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 + +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_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_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') + { + if ((!x->x_selstart) && (x->x_selend == x->x_bufsize)) + { + /* LATER delete the box... this causes reentrancy + problems now. */ + /* glist_delete(x->x_glist, &x->x_text->te_g); */ + return; + } + else if (x->x_selstart && (x->x_selstart == x->x_selend)) + x->x_selstart--; + } + 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/pd/src/g_scalar.c b/pd/src/g_scalar.c new file mode 100644 index 00000000..362fb108 --- /dev/null +++ b/pd/src/g_scalar.c @@ -0,0 +1,373 @@ +/* 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"); +} + + /* 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); + 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); + 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; + if (!templatecanvas) + { + bug("scalar_vis"); /* it's still a bug, have to fix this for real... */ + return; /* proposed by Krzysztof Czaja to avoid crashing */ + } + 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; + (*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); + 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, + scalar_save, + scalar_properties, +}; + +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; + } + for (dt = template->t_vec, i = 0; i < template->t_n; i++, dt++) + { + if (dt->ds_type == DT_ARRAY) + array_free(x->sc_vec[i].w_array); + else if (dt->ds_type == DT_LIST) + canvas_free(x->sc_vec[i].w_list); + } + 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); +} diff --git a/pd/src/g_template.c b/pd/src/g_template.c new file mode 100644 index 00000000..5e11cc5a --- /dev/null +++ b/pd/src/g_template.c @@ -0,0 +1,1673 @@ +/* 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_imp.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). +*/ + + /* 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_pd, 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; + float val = 0; + if (template_find_field(x, fieldname, &onset, &type, &arraytype)) + { + if (type == DT_FLOAT) + val = *(t_float *)(((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 (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_float *)(((char *)wp) + onset) = 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; + post("conform scalar"); + /* possibly replace the scalar */ + if (scfrom->sc_template == tfrom->t_sym) + { + post("match"); + /* 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; + /* convert all array elements and sublists */ + for (i = 0; i < nto; i++) + { + if (tto->t_vec[i].ds_type == DT_LIST) + { + t_glist *gl2 = x->sc_vec[i].w_list; + template_conformglist(tfrom, tto, gl2, conformaction); + } + else if (tto->t_vec[i].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_pd); + template_new(templatesym, argc, argv); + } + } + pd_free(&y->t_pd); + } + /* 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_pd, 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_pd); + t = template_new(sym, argc, argv); + } + pd_free(&y->t_pd); + 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' is obsolete; replace with 'struct'"); + warned = 1; + } + post("name: %s", sym->s_name); + 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_pd); + pd_free(&z->t_pd); + 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 = (n << 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_pd = template_class; + curve_setup(); + plot_setup(); + drawnumber_setup(); +} + diff --git a/pd/src/g_text.c b/pd/src/g_text.c new file mode 100644 index 00000000..226ddc7a --- /dev/null +++ b/pd/src/g_text.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. */ + +/* 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_imp.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; +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); + 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_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); + canvas_startmotion(glist_getcanvas(gl)); + } +} + +/* ---------------------- the "atom" text item ------------------------ */ + +#define ATOMBUFSIZE 40 + +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 */ + char a_buf[ATOMBUFSIZE]; + char a_shift; +} t_gatom; + +static void gatom_set(t_gatom *x, t_symbol *s, int argc, t_atom *argv) +{ + if (!argc) return; + if (x->a_atom.a_type == A_FLOAT) + x->a_atom.a_w.w_float = atom_getfloat(argv); + else if (x->a_atom.a_type == A_SYMBOL) + x->a_atom.a_w.w_symbol = atom_getsymbol(argv); + binbuf_clear(x->a_text.te_binbuf); + binbuf_add(x->a_text.te_binbuf, 1, &x->a_atom); + glist_retext(x->a_glist, &x->a_text); + x->a_buf[0] = 0; +} + +static void gatom_bang(t_gatom *x) +{ + if (x->a_atom.a_type == A_FLOAT) + outlet_float(x->a_text.te_outlet, x->a_atom.a_w.w_float); + else if (x->a_atom.a_type == A_SYMBOL) + outlet_symbol(x->a_text.te_outlet, 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 l = strlen(x->a_buf); + t_atom at; + char sbuf[ATOMBUFSIZE + 4]; + if (c == ' ') return; + else if (c == '\b') + { + if (l > 0) + { + x->a_buf[l-1] = 0; + goto redraw; + } + } + else if (c == '\n') + { + if (x->a_atom.a_type == A_FLOAT) + gatom_float(x, atof(x->a_buf)); + else if (x->a_atom.a_type == A_SYMBOL) + gatom_symbol(x, gensym(x->a_buf)); + else bug("gatom_key"); + } + else if (l < (ATOMBUFSIZE-1)) + { + x->a_buf[l] = c; + x->a_buf[l+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); + } +} + +static void gatom_param(t_gatom *x, t_floatarg width, t_floatarg draglo, + t_floatarg draghi) +{ + 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; + glist_retext(x->a_glist, &x->a_text); +} + +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; + if (type == A_FLOAT) + { + x->a_atom.a_w.w_float = 0; + x->a_text.te_width = 5; + outlet_new(&x->a_text, &s_float); + SETFLOAT(&at, 0); + } + else + { + x->a_atom.a_w.w_symbol = &s_symbol; + x->a_text.te_width = 10; + outlet_new(&x->a_text, &s_symbol); + SETSYMBOL(&at, &s_symbol); + } + binbuf_add(x->a_text.te_binbuf, 1, &at); + if (argc > 1) + { + 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); + glist_add(gl, &x->a_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->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) +{ + 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\n", + x->a_text.te_width, x->a_draglo, x->a_draghi); + 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); + 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 = rtext_new(glist, x, glist->gl_editor->e_rtext, 1); + if (x->te_type == T_ATOM) + glist_retext(glist, x); + text_drawborder(x, glist, rtext_gettag(y), + rtext_width(y), rtext_height(y), 1); + } + else rtext_new(glist, x, glist->gl_editor->e_rtext, 0); + } + else + { + t_rtext *y = glist_findrtext(glist, x); + if (text_shouldvis(x, glist)) + text_eraseborder(x, glist, rtext_gettag(y)); + rtext_free(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); +} + +static 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"))); + binbuf_addv(b, "ssiiiff", 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); + 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, + text_save, + 0, +}; + +static t_widgetbehavior gatom_widgetbehavior = +{ + text_getrect, + text_displace, + text_select, + text_activate, + text_delete, + text_vis, + text_click, + text_save, + gatom_properties, +}; + +/* -------------------- 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, y1, x2+4, y1+4, x2+4, 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_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_DEFFLOAT, A_DEFFLOAT, A_DEFFLOAT, 0); + class_setwidget(gatom_class, &gatom_widgetbehavior); +} + + diff --git a/pd/src/g_toggle.c b/pd/src/g_toggle.c new file mode 100644 index 00000000..6eba64d2 --- /dev/null +++ b/pd/src/g_toggle.c @@ -0,0 +1,528 @@ +/* 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_imp.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef NT +#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) + { + pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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 + { + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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], *ip1, *ip2; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + ip1 = (int *)(&x->x_gui.x_isa); + ip2 = (int *)(&x->x_gui.x_fsf); + 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, + (*ip1)&IEM_INIT_ARGS_ALL, + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + (*ip2)&IEM_FSTYLE_FLAGS_ALL, 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_list(t_toggle *x, t_symbol *s, int ac, t_atom *av) +{ + int l=iemgui_list((void *)x, &x->x_gui, s, ac, av); + + if(l < 0) + { + if(IS_A_FLOAT(av,0)) + toggle_float(x, atom_getfloatarg(0, ac, av)); + } + else if(l > 0) + { + (*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_new(t_symbol *s, int argc, t_atom *argv) +{ + t_toggle *x = (t_toggle *)pd_new(toggle_class); + int bflcol[]={-262144, -1, -1}; + t_symbol *srl[3]; + int a=IEM_GUI_DEFAULTSIZE, f=0; + int ldx=0, ldy=-6; + int fs=8, iinit=0, ifstyle=0; + float on=0.0, nonzero=1.0; + t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit); + t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle); + char str[144]; + + srl[0] = gensym("empty"); + srl[1] = gensym("empty"); + srl[2] = gensym("empty"); + + 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); + iinit = (int)atom_getintarg(1, argc, argv); + if(IS_A_SYMBOL(argv,2)) + srl[0] = atom_getsymbolarg(2, argc, argv); + else if(IS_A_FLOAT(argv,2)) + { + sprintf(str, "%d", (int)atom_getintarg(2, argc, argv)); + srl[0] = gensym(str); + } + if(IS_A_SYMBOL(argv,3)) + srl[1] = atom_getsymbolarg(3, argc, argv); + else if(IS_A_FLOAT(argv,3)) + { + sprintf(str, "%d", (int)atom_getintarg(3, argc, argv)); + srl[1] = gensym(str); + } + if(IS_A_SYMBOL(argv,4)) + srl[2] = atom_getsymbolarg(4, argc, argv); + else if(IS_A_FLOAT(argv,4)) + { + sprintf(str, "%d", (int)atom_getintarg(4, argc, argv)); + srl[2] = gensym(str); + } + ldx = (int)atom_getintarg(5, argc, argv); + ldy = (int)atom_getintarg(6, argc, argv); + ifstyle = (int)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); + } + if((argc == 14)&&IS_A_FLOAT(argv,13)) + nonzero = (float)atom_getfloatarg(13, argc, argv); + x->x_gui.x_draw = (t_iemfunptr)toggle_draw; + iinit &= IEM_INIT_ARGS_ALL; + ifstyle &= IEM_FSTYLE_FLAGS_ALL; + + fstyle->x_snd_able = 1; + fstyle->x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + x->x_gui.x_isa = *init; + if(!strcmp(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0; + if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0; + x->x_gui.x_unique_num = 0; + if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { fstyle->x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + x->x_gui.x_fsf = *fstyle; + 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; + iemgui_first_dollararg2sym(&x->x_gui, srl); + if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]); + x->x_gui.x_snd = srl[0]; + x->x_gui.x_rcv = srl[1]; + x->x_gui.x_lab = srl[2]; + 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_selected) + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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_addlist(toggle_class, toggle_list); + 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); + if(!iemgui_key_sym) + iemgui_key_sym = gensym("#keyname"); + 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; + toggle_widgetbehavior.w_propertiesfn = toggle_properties; + toggle_widgetbehavior.w_savefn = toggle_save; + class_setwidget(toggle_class, &toggle_widgetbehavior); + class_sethelpsymbol(toggle_class, gensym("toggle")); +} diff --git a/pd/src/g_traversal.c b/pd/src/g_traversal.c new file mode 100644 index 00000000..a9e8ce03 --- /dev/null +++ b/pd/src/g_traversal.c @@ -0,0 +1,1084 @@ +/* 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/pd/src/g_vdial.c b/pd/src/g_vdial.c new file mode 100644 index 00000000..49c8d0d4 --- /dev/null +++ b/pd/src/g_vdial.c @@ -0,0 +1,672 @@ +/* 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 */ + +#include +#include +#include +#include +#include "m_imp.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +/*------------------ global varaibles -------------------------*/ + + +/*------------------ global functions -------------------------*/ + + + + +/* ------------- vdl gui-vertical dial ---------------------- */ + +t_widgetbehavior vdial_widgetbehavior; +static t_class *vdial_class; + +/* widget helper functions */ + +void vdial_draw_update(t_vdial *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 vdial_draw_new(t_vdial *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 vdial_draw_move(t_vdial *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 vdial_draw_erase(t_vdial* 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 vdial_draw_config(t_vdial* 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 vdial_draw_io(t_vdial* 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 vdial_draw_select(t_vdial* x, t_glist* glist) +{ + t_canvas *canvas=glist_getcanvas(glist); + int n=x->x_number, i; + + if(x->x_gui.x_fsf.x_selected) + { + pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + for(i=0; ix_gui.x_obj.ob_pd, iemgui_key_sym); + for(i=0; ix_gui.x_lcol); + } +} + +void vdial_draw(t_vdial *x, t_glist *glist, int mode) +{ + if(mode == IEM_GUI_DRAW_MODE_UPDATE) + vdial_draw_update(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_MOVE) + vdial_draw_move(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_NEW) + vdial_draw_new(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_SELECT) + vdial_draw_select(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_ERASE) + vdial_draw_erase(x, glist); + else if(mode == IEM_GUI_DRAW_MODE_CONFIG) + vdial_draw_config(x, glist); + else if(mode >= IEM_GUI_DRAW_MODE_IO) + vdial_draw_io(x, glist, mode - IEM_GUI_DRAW_MODE_IO); +} + +/* ------------------------ vdl widgetbehaviour----------------------------- */ + +static void vdial_getrect(t_gobj *z, t_glist *glist, int *xp1, int *yp1, int *xp2, int *yp2) +{ + t_vdial *x = (t_vdial *)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 vdial_save(t_gobj *z, t_binbuf *b) +{ + t_vdial *x = (t_vdial *)z; + int bflcol[3], *ip1, *ip2; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + ip1 = (int *)(&x->x_gui.x_isa); + ip2 = (int *)(&x->x_gui.x_fsf); + 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, + gensym("vdl"), x->x_gui.x_w, + x->x_change, (*ip1)&IEM_INIT_ARGS_ALL, x->x_number, + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + (*ip2)&IEM_FSTYLE_FLAGS_ALL, x->x_gui.x_fontsize, + bflcol[0], bflcol[1], bflcol[2], x->x_on); + binbuf_addv(b, ";"); +} + +static void vdial_properties(t_gobj *z, t_glist *owner) +{ + t_vdial *x = (t_vdial *)z; + char buf[800]; + t_symbol *srl[3]; + + iemgui_properties(&x->x_gui, srl); + sprintf(buf, "pdtk_iemgui_dialog %%s VDIAL \ + ----------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*/ + x->x_change, 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 vdial_dialog(t_vdial *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 vdial_set(t_vdial *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 vdial_bang(t_vdial *x) +{ + 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); +} + +static void vdial_fout(t_vdial *x, t_floatarg f) +{ + int i=(int)f; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + + 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); +} + +static void vdial_float(t_vdial *x, t_floatarg f) +{ + int i=(int)f; + + if(i < 0) + i = 0; + if(i >= x->x_number) + i = x->x_number-1; + + 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); + } +} + +static void vdial_click(t_vdial *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); + + vdial_fout(x, (float)(yy / x->x_gui.x_h)); +} + +static int vdial_newclick(t_gobj *z, struct _glist *glist, int xpix, int ypix, int shift, int alt, int dbl, int doit) +{ + if(doit) + vdial_click((t_vdial *)z, (t_floatarg)xpix, (t_floatarg)ypix, (t_floatarg)shift, 0, (t_floatarg)alt); + return (1); +} + +static void vdial_loadbang(t_vdial *x) +{ + if(!sys_noloadbang && x->x_gui.x_isa.x_loadinit) + vdial_bang(x); +} + +static void vdial_number(t_vdial *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 vdial_size(t_vdial *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 vdial_delta(t_vdial *x, t_symbol *s, int ac, t_atom *av) +{iemgui_delta((void *)x, &x->x_gui, s, ac, av);} + +static void vdial_pos(t_vdial *x, t_symbol *s, int ac, t_atom *av) +{iemgui_pos((void *)x, &x->x_gui, s, ac, av);} + +static void vdial_color(t_vdial *x, t_symbol *s, int ac, t_atom *av) +{iemgui_color((void *)x, &x->x_gui, s, ac, av);} + +static void vdial_send(t_vdial *x, t_symbol *s) +{iemgui_send(x, &x->x_gui, s);} + +static void vdial_receive(t_vdial *x, t_symbol *s) +{iemgui_receive(x, &x->x_gui, s);} + +static void vdial_label(t_vdial *x, t_symbol *s) +{iemgui_label((void *)x, &x->x_gui, s);} + +static void vdial_label_pos(t_vdial *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_pos((void *)x, &x->x_gui, s, ac, av);} + +static void vdial_label_font(t_vdial *x, t_symbol *s, int ac, t_atom *av) +{iemgui_label_font((void *)x, &x->x_gui, s, ac, av);} + +static void vdial_init(t_vdial *x, t_floatarg f) +{ + x->x_gui.x_isa.x_loadinit = (f==0.0)?0:1; +} + +static void vdial_double_change(t_vdial *x) +{x->x_change = 1;} + +static void vdial_single_change(t_vdial *x) +{x->x_change = 0;} + +static void vdial_list(t_vdial *x, t_symbol *s, int ac, t_atom *av) +{ + int l=iemgui_list((void *)x, &x->x_gui, s, ac, av); + + if(l < 0) + { + if(IS_A_FLOAT(av,0)) + vdial_float(x, atom_getfloatarg(0, ac, av)); + } + else if(l > 0) + { + (*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 *vdial_new(t_symbol *s, int argc, t_atom *argv) +{ + t_vdial *x = (t_vdial *)pd_new(vdial_class); + int bflcol[]={-262144, -1, -1}; + t_symbol *srl[3]; + int a=IEM_GUI_DEFAULTSIZE, on=0, f=0; + int ldx=0, ldy=-6, chg=1, num=8; + int fs=8, iinit=0, ifstyle=0; + int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, fthold=IEM_BNG_DEFAULTHOLDFLASHTIME; + t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit); + t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle); + char str[144]; + + srl[0] = gensym("empty"); + srl[1] = gensym("empty"); + srl[2] = gensym("empty"); + + 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); + iinit = (int)atom_getintarg(2, argc, argv); + num = (int)atom_getintarg(3, argc, argv); + if(IS_A_SYMBOL(argv,4)) + srl[0] = atom_getsymbolarg(4, argc, argv); + else if(IS_A_FLOAT(argv,4)) + { + sprintf(str, "%d", (int)atom_getintarg(4, argc, argv)); + srl[0] = gensym(str); + } + if(IS_A_SYMBOL(argv,5)) + srl[1] = atom_getsymbolarg(5, argc, argv); + else if(IS_A_FLOAT(argv,5)) + { + sprintf(str, "%d", (int)atom_getintarg(5, argc, argv)); + srl[1] = gensym(str); + } + if(IS_A_SYMBOL(argv,6)) + srl[2] = atom_getsymbolarg(6, argc, argv); + else if(IS_A_FLOAT(argv,6)) + { + sprintf(str, "%d", (int)atom_getintarg(6, argc, argv)); + srl[2] = gensym(str); + } + ldx = (int)atom_getintarg(7, argc, argv); + ldy = (int)atom_getintarg(8, argc, argv); + ifstyle = (int)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); + } + x->x_gui.x_draw = (t_iemfunptr)vdial_draw; + iinit &= IEM_INIT_ARGS_ALL; + ifstyle &= IEM_FSTYLE_FLAGS_ALL; + fstyle->x_snd_able = 1; + fstyle->x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + x->x_gui.x_isa = *init; + if(!strcmp(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0; + if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0; + if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { fstyle->x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + x->x_gui.x_fsf = *fstyle; + x->x_gui.x_unique_num = 0; + 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; + iemgui_first_dollararg2sym(&x->x_gui, srl); + if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]); + x->x_gui.x_snd = srl[0]; + x->x_gui.x_rcv = srl[1]; + x->x_gui.x_lab = srl[2]; + 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 vdial_ff(t_vdial *x) +{ + if(x->x_gui.x_fsf.x_selected) + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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_vdial_setup(void) +{ + vdial_class = class_new(gensym("vdl"), (t_newmethod)vdial_new, + (t_method)vdial_ff, sizeof(t_vdial), 0, A_GIMME, 0); + class_addbang(vdial_class, vdial_bang); + class_addfloat(vdial_class, vdial_float); + class_addlist(vdial_class, vdial_list); + class_addmethod(vdial_class, (t_method)vdial_click, gensym("click"), + A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, 0); + class_addmethod(vdial_class, (t_method)vdial_dialog, gensym("dialog"), + A_GIMME, 0); + class_addmethod(vdial_class, (t_method)vdial_loadbang, gensym("loadbang"), 0); + class_addmethod(vdial_class, (t_method)vdial_set, gensym("set"), A_FLOAT, 0); + class_addmethod(vdial_class, (t_method)vdial_size, gensym("size"), A_GIMME, 0); + class_addmethod(vdial_class, (t_method)vdial_delta, gensym("delta"), A_GIMME, 0); + class_addmethod(vdial_class, (t_method)vdial_pos, gensym("pos"), A_GIMME, 0); + class_addmethod(vdial_class, (t_method)vdial_color, gensym("color"), A_GIMME, 0); + class_addmethod(vdial_class, (t_method)vdial_send, gensym("send"), A_DEFSYM, 0); + class_addmethod(vdial_class, (t_method)vdial_receive, gensym("receive"), A_DEFSYM, 0); + class_addmethod(vdial_class, (t_method)vdial_label, gensym("label"), A_DEFSYM, 0); + class_addmethod(vdial_class, (t_method)vdial_label_pos, gensym("label_pos"), A_GIMME, 0); + class_addmethod(vdial_class, (t_method)vdial_label_font, gensym("label_font"), A_GIMME, 0); + class_addmethod(vdial_class, (t_method)vdial_init, gensym("init"), A_FLOAT, 0); + class_addmethod(vdial_class, (t_method)vdial_number, gensym("number"), A_FLOAT, 0); + class_addmethod(vdial_class, (t_method)vdial_single_change, gensym("single_change"), 0); + class_addmethod(vdial_class, (t_method)vdial_double_change, gensym("double_change"), 0); + if(!iemgui_key_sym) + iemgui_key_sym = gensym("#keyname"); + vdial_widgetbehavior.w_getrectfn = vdial_getrect; + vdial_widgetbehavior.w_displacefn = iemgui_displace; + vdial_widgetbehavior.w_selectfn = iemgui_select; + vdial_widgetbehavior.w_activatefn = NULL; + vdial_widgetbehavior.w_deletefn = iemgui_delete; + vdial_widgetbehavior.w_visfn = iemgui_vis; + vdial_widgetbehavior.w_clickfn = vdial_newclick; + vdial_widgetbehavior.w_propertiesfn = vdial_properties; + vdial_widgetbehavior.w_savefn = vdial_save; + class_setwidget(vdial_class, &vdial_widgetbehavior); + class_sethelpsymbol(vdial_class, gensym("vdial")); +} diff --git a/pd/src/g_vslider.c b/pd/src/g_vslider.c new file mode 100644 index 00000000..12cc4213 --- /dev/null +++ b/pd/src/g_vslider.c @@ -0,0 +1,688 @@ +/* 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_imp.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef NT +#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) + { + pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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 + { + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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], *ip1, *ip2; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + ip1 = (int *)(&x->x_gui.x_isa); + ip2 = (int *)(&x->x_gui.x_fsf); + 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, (*ip1)&IEM_INIT_ARGS_ALL, + srl[0], srl[1], srl[2], + x->x_gui.x_ldx, x->x_gui.x_ldy, + (*ip2)&IEM_FSTYLE_FLAGS_ALL, 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_list(t_vslider *x, t_symbol *s, int ac, t_atom *av) +{ + int l=iemgui_list((void *)x, &x->x_gui, s, ac, av); + + if(l < 0) + { + if(IS_A_FLOAT(av,0)) + vslider_float(x, atom_getfloatarg(0, ac, av)); + } + else if(l > 0) + { + (*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_new(t_symbol *s, int argc, t_atom *argv) +{ + t_vslider *x = (t_vslider *)pd_new(vslider_class); + int bflcol[]={-262144, -1, -1}; + t_symbol *srl[3]; + int w=IEM_GUI_DEFAULTSIZE, h=IEM_SL_DEFAULTSIZE; + int lilo=0, f=0, ldx=0, ldy=-8; + int fs=8, iinit=0, ifstyle=0, v=0, steady=1; + double min=0.0, max=(double)(IEM_SL_DEFAULTSIZE-1); + t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit); + t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle); + char str[144]; + + srl[0] = gensym("empty"); + srl[1] = gensym("empty"); + srl[2] = gensym("empty"); + + + 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); + iinit = (int)atom_getintarg(5, argc, argv); + srl[0] = atom_getsymbolarg(6, argc, argv); + srl[1] = atom_getsymbolarg(7, argc, argv); + srl[2] = atom_getsymbolarg(8, argc, argv); + if(IS_A_SYMBOL(argv,6)) + srl[0] = atom_getsymbolarg(6, argc, argv); + else if(IS_A_FLOAT(argv,6)) + { + sprintf(str, "%d", (int)atom_getintarg(6, argc, argv)); + srl[0] = gensym(str); + } + if(IS_A_SYMBOL(argv,7)) + srl[1] = atom_getsymbolarg(7, argc, argv); + else if(IS_A_FLOAT(argv,7)) + { + sprintf(str, "%d", (int)atom_getintarg(7, argc, argv)); + srl[1] = gensym(str); + } + if(IS_A_SYMBOL(argv,8)) + srl[2] = atom_getsymbolarg(8, argc, argv); + else if(IS_A_FLOAT(argv,8)) + { + sprintf(str, "%d", (int)atom_getintarg(8, argc, argv)); + srl[2] = gensym(str); + } + ldx = (int)atom_getintarg(9, argc, argv); + ldy = (int)atom_getintarg(10, argc, argv); + ifstyle = (int)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); + } + if((argc == 18)&&IS_A_FLOAT(argv,17)) + steady = (int)atom_getintarg(17, argc, argv); + x->x_gui.x_draw = (t_iemfunptr)vslider_draw; + iinit &= IEM_INIT_ARGS_ALL; + ifstyle &= IEM_FSTYLE_FLAGS_ALL; + fstyle->x_snd_able = 1; + fstyle->x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + x->x_gui.x_isa = *init; + 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(srl[0]->s_name, "empty")) fstyle->x_snd_able = 0; + if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0; + x->x_gui.x_unique_num = 0; + if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { fstyle->x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + x->x_gui.x_fsf = *fstyle; + iemgui_first_dollararg2sym(&x->x_gui, srl); + if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]); + x->x_gui.x_snd = srl[0]; + x->x_gui.x_rcv = srl[1]; + x->x_gui.x_lab = srl[2]; + 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_selected) + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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_addlist(vslider_class, vslider_list); + 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); + if(!iemgui_key_sym) + iemgui_key_sym = gensym("#keyname"); + 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; + vslider_widgetbehavior.w_propertiesfn = vslider_properties;; + vslider_widgetbehavior.w_savefn = vslider_save; + class_setwidget(vslider_class, &vslider_widgetbehavior); + class_sethelpsymbol(vslider_class, gensym("vslider")); +} diff --git a/pd/src/g_vumeter.c b/pd/src/g_vumeter.c new file mode 100644 index 00000000..b257c588 --- /dev/null +++ b/pd/src/g_vumeter.c @@ -0,0 +1,762 @@ +/* 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_imp.h" +#include "g_canvas.h" +#include "t_tk.h" +#include "g_all_guis.h" +#include + +#ifdef NT +#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) + { + pd_bind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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 + { + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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], *ip1, *ip2; + t_symbol *srl[3]; + + iemgui_save(&x->x_gui, srl, bflcol); + ip1 = (int *)(&x->x_gui.x_isa); + ip2 = (int *)(&x->x_gui.x_fsf); + 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, + (*ip2)&IEM_FSTYLE_FLAGS_ALL, x->x_gui.x_fontsize, + bflcol[0], bflcol[2], x->x_scale, (*ip1)&IEM_INIT_ARGS_ALL); + 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_list(t_vu *x, t_symbol *s, int ac, t_atom *av) +{ + int l=iemgui_list((void *)x, &x->x_gui, s, ac, av); + + if(l < 0) + { + if((IS_A_FLOAT(av,0))&&(IS_A_FLOAT(av,1))) + { + vu_ft1(x, atom_getfloatarg(1, ac, av)); + vu_float(x, atom_getfloatarg(0, ac, av)); + } + } + else if(l > 0) + { + (*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_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}; + t_symbol *srl[3]; + int w=IEM_GUI_DEFAULTSIZE, h=IEM_VU_STEPS*IEM_VU_DEFAULTSIZE; + int ldx=-1, ldy=-8, f=0, fs=8, scale=1; + int iinit=0, ifstyle=0; + int ftbreak=IEM_BNG_DEFAULTBREAKFLASHTIME, fthold=IEM_BNG_DEFAULTHOLDFLASHTIME; + t_iem_init_symargs *init=(t_iem_init_symargs *)(&iinit); + t_iem_fstyle_flags *fstyle=(t_iem_fstyle_flags *)(&ifstyle); + char str[144]; + + srl[0] = gensym("empty"); + srl[1] = gensym("empty"); + srl[2] = gensym("empty"); + + 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); + if(IS_A_SYMBOL(argv,2)) + srl[1] = atom_getsymbolarg(2, argc, argv); + else if(IS_A_FLOAT(argv,2)) + { + sprintf(str, "%d", (int)atom_getintarg(2, argc, argv)); + srl[1] = gensym(str); + } + if(IS_A_SYMBOL(argv,3)) + srl[2] = atom_getsymbolarg(3, argc, argv); + else if(IS_A_FLOAT(argv,3)) + { + sprintf(str, "%d", (int)atom_getintarg(3, argc, argv)); + srl[2] = gensym(str); + } + ldx = (int)atom_getintarg(4, argc, argv); + ldy = (int)atom_getintarg(5, argc, argv); + ifstyle = (int)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); + } + if((argc == 12)&&IS_A_FLOAT(argv,11)) + iinit = (int)atom_getintarg(11, argc, argv); + x->x_gui.x_draw = (t_iemfunptr)vu_draw; + iinit &= IEM_INIT_ARGS_ALL; + ifstyle &= IEM_FSTYLE_FLAGS_ALL; + + fstyle->x_snd_able = 0; + fstyle->x_rcv_able = 1; + x->x_gui.x_glist = (t_glist *)canvas_getcurrent(); + x->x_gui.x_isa = *init; + if(!strcmp(srl[1]->s_name, "empty")) fstyle->x_rcv_able = 0; + x->x_gui.x_unique_num = 0; + if(fstyle->x_font_style == 1) strcpy(x->x_gui.x_font, "helvetica"); + else if(fstyle->x_font_style == 2) strcpy(x->x_gui.x_font, "times"); + else { fstyle->x_font_style = 0; + strcpy(x->x_gui.x_font, "courier"); } + x->x_gui.x_fsf = *fstyle; + iemgui_first_dollararg2sym(&x->x_gui, srl); + if(x->x_gui.x_fsf.x_rcv_able) pd_bind(&x->x_gui.x_obj.ob_pd, srl[1]); + x->x_gui.x_snd = srl[0]; + x->x_gui.x_rcv = srl[1]; + x->x_gui.x_lab = srl[2]; + 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_selected) + pd_unbind(&x->x_gui.x_obj.ob_pd, iemgui_key_sym); + 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_addlist(vu_class, vu_list); + 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); + if(!iemgui_key_sym) + iemgui_key_sym = gensym("#keyname"); + 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; + vu_widgetbehavior.w_propertiesfn = vu_properties; + vu_widgetbehavior.w_savefn = vu_save; + class_setwidget(vu_class,&vu_widgetbehavior); + class_sethelpsymbol(vu_class, gensym("vu")); +} diff --git a/pd/src/install-sh b/pd/src/install-sh new file mode 100644 index 00000000..e9de2384 --- /dev/null +++ b/pd/src/install-sh @@ -0,0 +1,251 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# This comes from X11R5 (mit/util/scripts/install.sh). +# +# Copyright 1991 by the Massachusetts Institute of Technology +# +# Permission to use, copy, modify, distribute, and sell this software and its +# documentation for any purpose is hereby granted without fee, provided that +# the above copyright notice appear in all copies and that both that +# copyright notice and this permission notice appear in supporting +# documentation, and that the name of M.I.T. not be used in advertising or +# publicity pertaining to distribution of the software without specific, +# written prior permission. M.I.T. makes no representations about the +# suitability of this software for any purpose. It is provided "as is" +# without express or implied warranty. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +else + true +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d $dst ]; then + instcmd=: + chmodcmd="" + else + instcmd=mkdir + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f $src -o -d $src ] + then + true + else + echo "install: $src does not exist" + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "install: no destination specified" + exit 1 + else + true + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d $dst ] + then + dst="$dst"/`basename $src` + else + true + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' +' +IFS="${IFS-${defaultIFS}}" + +oIFS="${IFS}" +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS="${oIFS}" + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp="${pathcomp}${1}" + shift + + if [ ! -d "${pathcomp}" ] ; + then + $mkdirprog "${pathcomp}" + else + true + fi + + pathcomp="${pathcomp}/" +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd $dst && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename $dst` + else + dstfile=`basename $dst $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename $dst` + else + true + fi + +# Make a temp file name in the proper directory. + + dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + + $doit $instcmd $src $dsttmp && + + trap "rm -f ${dsttmp}" 0 && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && + +# Now rename the file to the real destination. + + $doit $rmcmd -f $dstdir/$dstfile && + $doit $mvcmd $dsttmp $dstdir/$dstfile + +fi && + + +exit 0 diff --git a/pd/src/m_atom.c b/pd/src/m_atom.c new file mode 100644 index 00000000..c9c4c284 --- /dev/null +++ b/pd/src/m_atom.c @@ -0,0 +1,129 @@ +/* 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_imp.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/pd/src/m_binbuf.c b/pd/src/m_binbuf.c new file mode 100644 index 00000000..6ff79e93 --- /dev/null +++ b/pd/src/m_binbuf.c @@ -0,0 +1,1138 @@ +/* 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 +#ifdef UNIX +#include +#endif +#ifdef NT +#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)); +} + +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 *realizedollsym(t_symbol *s, int ac, t_atom *av, int tonew) /* IOhannes: not static any more */ +{ + 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 = 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 = 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 NT + 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 || 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), + 12.); + } + } + if (!strcmp(first, "#P")) + { + 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"); + } + SETSYMBOL(outmess, gensym("#X")); + SETSYMBOL(outmess + 1, gensym("obj")); + 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, "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("msg"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess), + gensym("bang")); + nobj++; + } + else if (!strcmp(second, "slider") || !strcmp(second, "number") + || !strcmp(second, "flonum") || !strcmp(second, "toggle")) + { + binbuf_addv(newb, "ssff;", + gensym("#X"), gensym("floatatom"), + atom_getfloatarg(2, natom, nextmess), + atom_getfloatarg(3, natom, nextmess)); + 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, "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 + { + 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 1 + 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/pd/src/m_class.c b/pd/src/m_class.c new file mode 100644 index 00000000..9d6329d6 --- /dev/null +++ b/pd/src/m_class.c @@ -0,0 +1,772 @@ +/* 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_imp.h" +#include +#ifdef UNIX +#include +#endif +#ifdef NT +#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 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); + + /* 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; + +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; +#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) = 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; +} + +/* ---------------- 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 absteaction. */ +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) + { + 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; + + /* 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/pd/src/m_conf.c b/pd/src/m_conf.c new file mode 100644 index 00000000..04cddeda --- /dev/null +++ b/pd/src/m_conf.c @@ -0,0 +1,100 @@ +/* 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_imp.h" + +void g_array_setup(void); +void g_canvas_setup(void); +void g_guiconnect_setup(void); +/* iemlib */ +void g_bang_setup(void); +void g_hdial_setup(void); +void g_hslider_setup(void); +void g_mycanvas_setup(void); +void g_numbox_setup(void); +void g_toggle_setup(void); +void g_vdial_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_hdial_setup(); + g_hslider_setup(); + g_mycanvas_setup(); + g_numbox_setup(); + g_toggle_setup(); + g_vdial_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_array_setup(); + d_ctl_setup(); + d_dac_setup(); + d_delay_setup(); + d_fft_setup(); + d_filter_setup(); + d_global_setup(); + d_math_setup(); + d_misc_setup(); + d_osc_setup(); + d_soundfile_setup(); + d_ugen_setup(); +} + diff --git a/pd/src/m_glob.c b/pd/src/m_glob.c new file mode 100644 index 00000000..3be209d6 --- /dev/null +++ b/pd/src/m_glob.c @@ -0,0 +1,121 @@ +/* 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_imp.h" + +static t_class *pdclass; +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_ping(t_pd *dummy); + +#if 0 /* this doesn't work for OSS or for ALSA... discouraged... */ + +#ifdef UNIX +#include +#endif + +void glob_restart_audio(t_pd *dummy) +{ + int inchans = sys_get_inchannels(); + int outchans = sys_get_outchannels(); + post("1"); + sys_close_audio(); + post("2"); +#ifdef UNIX + sleep(2); +#endif + post("3"); + sys_open_audio(1, 0, 1, 0, /* IOhannes */ + 1, &inchans, 1, &outchans, sys_dacsr); + post("4"); +} +#endif + +void alsa_resync( void); + + +#ifdef ALSA01 +void glob_restart_alsa(t_pd *dummy) +{ + alsa_resync(); +} +#endif + +#ifdef NT +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")); + + pdclass = class_new(gensym("pd"), 0, 0, sizeof(t_pd), + CLASS_DEFAULT, A_NULL); + class_addmethod(pdclass, (t_method)glob_initfromgui, gensym("init"), + A_GIMME, 0); + class_addmethod(pdclass, (t_method)glob_setfilename, gensym("filename"), + A_SYMBOL, A_SYMBOL, 0); + class_addmethod(pdclass, (t_method)glob_evalfile, gensym("open"), + A_SYMBOL, A_SYMBOL, 0); + class_addmethod(pdclass, (t_method)glob_quit, gensym("quit"), 0); + class_addmethod(pdclass, (t_method)glob_foo, gensym("foo"), A_GIMME, 0); + class_addmethod(pdclass, (t_method)glob_dsp, gensym("dsp"), A_GIMME, 0); + class_addmethod(pdclass, (t_method)glob_meters, gensym("meters"), + A_FLOAT, 0); + class_addmethod(pdclass, (t_method)glob_key, gensym("key"), A_GIMME, 0); + class_addmethod(pdclass, (t_method)glob_audiostatus, + gensym("audiostatus"), 0); + class_addmethod(pdclass, (t_method)glob_finderror, + gensym("finderror"), 0); +#ifdef UNIX + class_addmethod(pdclass, (t_method)glob_ping, gensym("ping"), 0); +#endif +#ifdef NT + class_addmethod(pdclass, (t_method)glob_audio, gensym("audio"), + A_DEFFLOAT, A_DEFFLOAT, 0); +#endif +#ifdef ALSA01 + class_addmethod(pdclass, (t_method)glob_restart_alsa, + gensym("restart-audio"), 0); +#endif + class_addanything(pdclass, max_default); + pd_bind(&pdclass, gensym("pd")); +} diff --git a/pd/src/m_imp.h b/pd/src/m_imp.h new file mode 100644 index 00000000..7a6bd69e --- /dev/null +++ b/pd/src/m_imp.h @@ -0,0 +1,223 @@ +/* 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. */ + +#include "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 */ + 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 */ + 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 */ +}; + +/* s_file.c */ +typedef struct _namelist +{ + struct _namelist *nl_next; + char *nl_string; +} t_namelist; + +t_namelist *namelist_append(t_namelist *listwas, const char *s); +void namelist_free(t_namelist *listwas); + +extern int sys_debuglevel; +extern int sys_verbose; + +#define DEBUG_MESSUP 1 /* messages up from pd to pd-gui */ +#define DEBUG_MESSDOWN 2 /* messages down from pd-gui to pd */ + +extern int sys_noloadbang; +extern int sys_nogui; +extern char *sys_guicmd; + +/* in s_main.c */ +EXTERN int sys_nearestfontsize(int fontsize); +EXTERN int sys_hostfontsize(int fontsize); + +extern int sys_defaultfont; +extern t_symbol *sys_libdir; /* library directory for auxilliary files */ +/* s_loader.c */ +int sys_load_lib(char *dirname, char *filename); + +/* s_unix.c */ +EXTERN void sys_microsleep(int microsec); + +/* s_sgi.c, s_nt.c, s_linux.c each implement the same API for audio +and MIDI I/O as follows: */ + +#define DACBLKSIZE 64 + +#define SENDDACS_NO 0 /* return values for sys_send_dacs() */ +#define SENDDACS_YES 1 +#define SENDDACS_SLEPT 2 + +#define API_OSS 0 /* API choices */ +#define API_ALSA 1 +#define API_RME 2 +#define API_MMIO 3 +#define API_PORTAUDIO 4 + + + /* MIDI input and output */ +#define MAXMIDIINDEV 16 /* max. number of input ports */ +#define MAXMIDIOUTDEV 16 /* max. number of output ports */ +extern int sys_nmidiin; +extern int sys_nmidiout; +extern int sys_midiindevlist[]; +extern int sys_midioutdevlist[]; + +EXTERN void sys_putmidimess(int portno, int a, int b, int c); +EXTERN void sys_putmidibyte(int portno, int a); +EXTERN void sys_poll_midi(void); +EXTERN void sys_setmiditimediff(double inbuftime, double outbuftime); +EXTERN void sys_midibytein(int portno, int byte); + +extern int sys_hipriority; /* real-time flag, true if priority boosted */ +extern t_sample *sys_soundout; +extern t_sample *sys_soundin; +extern float sys_dacsr; +extern int sys_schedadvance; +extern int sys_sleepgrain; +EXTERN void sys_open_audio(int naudioindev, int *audioindev, int nchindev, int *chindev, + int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, + int srate); /* IOhannes */ + +EXTERN void sys_close_audio(void); + +EXTERN void sys_open_midi(int nmidiin, int *midiinvec, + int nmidiout, int *midioutvec); +EXTERN void sys_close_midi(void); + +EXTERN int sys_send_dacs(void); +EXTERN void sys_reportidle(void); +EXTERN void sys_set_priority(int higher); +EXTERN void sys_audiobuf(int nbufs); +EXTERN void sys_getmeters(float *inmax, float *outmax); +EXTERN void sys_listdevs(void); +EXTERN void sys_setblocksize(int n); + + /* for NT and Linux, there are additional bits of fluff as follows. */ +#ifdef NT +EXTERN void nt_soundindev(int which); +EXTERN void nt_soundoutdev(int which); +EXTERN void nt_midiindev(int which); +EXTERN void nt_midioutdev(int which); +EXTERN void nt_noresync( void); +EXTERN void nt_set_sound_api(int which); +#endif + +#ifdef __linux__ + /* the following definitions allow you to switch at run time + between audio APIs in Linux and later in NT. */ +void linux_set_sound_api(int which); + +void linux_setfrags(int n); +void linux_streammode( void); +void linux_32bit( void); +void rme_soundindev(int which); +void rme_soundoutdev(int which); +void linux_alsa_queue_size(int size); +#ifdef ALSA99 /* old fashioned ALSA */ +void linux_alsa_devno(int devno); +#else +void linux_alsa_devname(char *devname); +#endif +#endif /* __linux__ */ + +/* portaudio, used in Windows and Mac versions... */ + +int pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin, + t_sample *soundout, int framesperbuf, int nbuffers, + int indeviceno, int outdeviceno); +void pa_close_audio(void); +int pa_send_dacs(void); +void pa_reportidle(void); +void pa_listdevs(void); + +/* m_sched.c */ +EXTERN void sys_log_error(int type); +#define ERR_NOTHING 0 +#define ERR_ADCSLEPT 1 +#define ERR_DACSLEPT 2 +#define ERR_RESYNC 3 +#define ERR_DATALATE 4 + +/* s_inter.c */ + +EXTERN void sys_bail(int exitcode); +EXTERN int sys_pollgui(void); + +EXTERN_STRUCT _socketreceiver; +#define t_socketreceiver struct _socketreceiver + +typedef void (*t_socketnotifier)(void *x); +typedef void (*t_socketreceivefn)(void *x, t_binbuf *b); + +EXTERN t_socketreceiver *socketreceiver_new(void *owner, + t_socketnotifier notifier, t_socketreceivefn socketreceivefn, int udp); +EXTERN void socketreceiver_read(t_socketreceiver *x, int fd); +EXTERN void sys_sockerror(char *s); +EXTERN void sys_closesocket(int fd); + +typedef void (*t_fdpollfn)(void *ptr, int fd); +EXTERN void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr); +EXTERN void sys_rmpollfn(int fd); +#ifdef UNIX +void sys_setalarm(int microsec); +void sys_setvirtualalarm( void); +#endif + +/* 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); +/* 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/pd/src/m_memory.c b/pd/src/m_memory.c new file mode 100644 index 00000000..cb94e4b1 --- /dev/null +++ b/pd/src/m_memory.c @@ -0,0 +1,88 @@ +/* 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_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/pd/src/m_obj.c b/pd/src/m_obj.c new file mode 100644 index 00000000..6b9ea932 --- /dev/null +++ b/pd/src/m_obj.c @@ -0,0 +1,676 @@ +/* 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_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 = 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); +} + +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_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/pd/src/m_pd.c b/pd/src/m_pd.c new file mode 100644 index 00000000..713d65ad --- /dev/null +++ b/pd/src/m_pd.c @@ -0,0 +1,296 @@ +/* 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_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); +} + +/* 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) +{ + 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/pd/src/m_pd.h b/pd/src/m_pd.h new file mode 100644 index 00000000..172bf49d --- /dev/null +++ b/pd/src/m_pd.h @@ -0,0 +1,594 @@ +/* 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. */ + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +extern "C" { +#endif + +#ifdef NT +// #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 /* NT */ + + /* the external storage class is "extern" in UNIX; in NT it's ugly. */ +#ifdef NT +#ifdef PD_INTERNAL +#define EXTERN __declspec(dllexport) extern +#else +#define EXTERN __declspec(dllimport) extern +#endif /* PD_INTERNAL */ +#else +#define EXTERN extern +#endif /* NT */ + + /* and depending on the compiler, hidden data structures are + declared differently: */ +#ifdef __GNUC__ +#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))) +void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv); + +/* --------------- 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 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); + +/* ------------------ clocks --------------- */ + +EXTERN t_clock *clock_new(void *owner, t_method fn); +EXTERN void clock_set(t_clock *x, double systime); +EXTERN void clock_delay(t_clock *x, double delaytime); +EXTERN void clock_unset(t_clock *x); +EXTERN double clock_getlogicaltime(void); +EXTERN double clock_getsystime(void); /* OBSOLETE; use clock_getlogicaltime() */ +EXTERN double clock_gettimesince(double prevsystime); +EXTERN double clock_getsystimeafter(double 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 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 t_atom *canvas_getarg(int which); +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) + +#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 sys_geteventno(void); +EXTERN double sys_getrealtime(void); + +/* --------------- signals ----------------------------------- */ + +typedef float t_sample; +#define MAXLOGSIG 32 +#define MAXSIGSIZE (1 << MAXLOGSIG) + +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(float *fz, int n); +EXTERN void mayer_fft(int n, float *real, float *imag); +EXTERN void mayer_ifft(int n, float *real, float *imag); +EXTERN void mayer_realfft(int n, float *real); +EXTERN void mayer_realifft(int n, float *real); + +EXTERN float *cos_table; +#define LOGCOSTABSIZE 9 +#define COSTABSIZE (1< +#endif + +t_clock *clock_new(void *owner, t_method fn) +{ + t_clock *x = (t_clock *)getbytes(sizeof *x); + x->c_settime = -1; + x->c_owner = owner; + x->c_fn = (t_clockmethod)fn; + x->c_next = 0; + return (x); +} + +void clock_unset(t_clock *x) +{ + if (x->c_settime >= 0) + { + if (x == clock_setlist) clock_setlist = x->c_next; + else + { + t_clock *x2 = clock_setlist; + while (x2->c_next != x) x2 = x2->c_next; + x2->c_next = x->c_next; + } + x->c_settime = -1; + } +} + + /* set the clock to call back at an absolute system time */ +void clock_set(t_clock *x, double setticks) +{ + if (setticks < sys_time) setticks = sys_time; + clock_unset(x); + x->c_settime = setticks; + if (clock_setlist && clock_setlist->c_settime <= setticks) + { + t_clock *cbefore, *cafter; + for (cbefore = clock_setlist, cafter = clock_setlist->c_next; + cbefore; cbefore = cafter, cafter = cbefore->c_next) + { + if (!cafter || cafter->c_settime > setticks) + { + cbefore->c_next = x; + x->c_next = cafter; + return; + } + } + } + else x->c_next = clock_setlist, clock_setlist = x; +} + + /* set the clock to call back after a delay in msec */ +void clock_delay(t_clock *x, double delaytime) +{ + clock_set(x, sys_time + sys_time_per_msec * delaytime); +} + + /* get current logical time. We don't specify what units this is in; + use clock_gettimesince() to measure intervals from time of this call. + This was previously, incorrectly named "clock_getsystime"; the old + name is aliased to the new one in m_pd.h. */ +double clock_getlogicaltime( void) +{ + return (sys_time); +} + /* OBSOLETE NAME */ +double clock_getsystime( void) { return (sys_time); } + + /* elapsed time in milliseconds since the given system time */ +double clock_gettimesince(double prevsystime) +{ + return ((sys_time - prevsystime)/sys_time_per_msec); +} + + /* what value the system clock will have after a delay */ +double clock_getsystimeafter(double delaytime) +{ + return (sys_time + sys_time_per_msec * delaytime); +} + +void clock_free(t_clock *x) +{ + clock_unset(x); + freebytes(x, sizeof *x); +} + +/* the following routines maintain a real-execution-time histogram of the +various phases of real-time execution. */ + +static int sys_bin[] = {0, 2, 5, 10, 20, 30, 50, 100, 1000}; +#define NBIN (sizeof(sys_bin)/sizeof(*sys_bin)) +#define NHIST 10 +static int sys_histogram[NHIST][NBIN]; +static double sys_histtime; +static int sched_diddsp, sched_didmidi, sched_didpoll, sched_didnothing; + +static void sys_clearhist( void) +{ + unsigned int i, j; + for (i = 0; i < NHIST; i++) + for (j = 0; j < NBIN; j++) sys_histogram[i][j] = 0; + sys_histtime = sys_getrealtime(); + sched_diddsp = sched_didmidi = sched_didpoll = sched_didnothing = 0; +} + +void sys_printhist( void) +{ + unsigned int i, j; + for (i = 0; i < NHIST; i++) + { + int doit = 0; + for (j = 0; j < NBIN; j++) if (sys_histogram[i][j]) doit = 1; + if (doit) + { + post("%2d %8d %8d %8d %8d %8d %8d %8d %8d", i, + sys_histogram[i][0], + sys_histogram[i][1], + sys_histogram[i][2], + sys_histogram[i][3], + sys_histogram[i][4], + sys_histogram[i][5], + sys_histogram[i][6], + sys_histogram[i][7]); + } + } + post("dsp %d, midi %d, poll %d, nothing %d", + sched_diddsp, sched_didmidi, sched_didpoll, sched_didnothing); +} + +static int sys_histphase; + +int sys_addhist(int phase) +{ + int i, j, phasewas = sys_histphase; + double newtime = sys_getrealtime(); + int msec = (newtime - sys_histtime) * 1000.; + for (j = NBIN-1; j >= 0; j--) + { + if (msec >= sys_bin[j]) + { + sys_histogram[phasewas][j]++; + break; + } + } + sys_histtime = newtime; + sys_histphase = phase; + return (phasewas); +} + +#define NRESYNC 20 + +typedef struct _resync +{ + int r_ntick; + int r_error; +} t_resync; + +static int oss_resyncphase = 0; +static int oss_nresync = 0; +static t_resync oss_resync[NRESYNC]; + +#ifdef __linux__ +void linux_audiostatus(void); +#endif + +static char *(oss_errornames[]) = { +"unknown", +"ADC blocked", +"DAC blocked", +"A/D/A sync", +"data late" +}; + +void glob_audiostatus(void) +{ + int dev, nresync, nresyncphase, i; +#ifdef __linux__ + linux_audiostatus(); +#endif + nresync = (oss_nresync >= NRESYNC ? NRESYNC : oss_nresync); + nresyncphase = oss_resyncphase - 1; + post("audio I/O error history:"); + post("seconds ago\terror type"); + for (i = 0; i < nresync; i++) + { + int errtype; + if (nresyncphase < 0) + nresyncphase += NRESYNC; + errtype = oss_resync[nresyncphase].r_error; + if (errtype < 0 || errtype > 4) + errtype = 0; + + post("%9.2f\t%s", + (sched_diddsp - oss_resync[nresyncphase].r_ntick) + * ((double)DACBLKSIZE) / sys_dacsr, + oss_errornames[errtype]); + nresyncphase--; + } +} + +static int sched_diored; +static int sched_dioredtime; +static int sched_meterson; + +void sys_log_error(int type) +{ + oss_resync[oss_resyncphase].r_ntick = sched_diddsp; + oss_resync[oss_resyncphase].r_error = type; + oss_nresync++; + if (++oss_resyncphase == NRESYNC) oss_resyncphase = 0; + if (type != ERR_NOTHING && !sched_diored) + { + sys_vgui("pdtk_pd_dio 1\n"); + sched_diored = 1; + } + sched_dioredtime = + sched_diddsp + (int)(sys_dacsr /(double)DACBLKSIZE); +} + +static int sched_lastinclip, sched_lastoutclip, + sched_lastindb, sched_lastoutdb; + +void glob_ping(t_pd *dummy); + +static void sched_pollformeters( void) +{ + int inclip, outclip, indb, outdb; + static int sched_nextmeterpolltime, sched_nextpingtime; + + /* if there's no GUI but we're running in "realtime", here is + where we arrange to ping the watchdog every 2 seconds. */ +#ifdef UNIX + if (sys_nogui && sys_hipriority && (sched_diddsp - sched_nextpingtime > 0)) + { + glob_ping(0); + /* ping every 2 seconds */ + sched_nextpingtime = sched_diddsp + + 2 * (int)(sys_dacsr /(double)DACBLKSIZE); + } +#endif + + if (sched_diddsp - sched_nextmeterpolltime < 0) + return; + if (sched_diored && (sched_diddsp - sched_dioredtime > 0)) + { + sys_vgui("pdtk_pd_dio 0\n"); + sched_diored = 0; + } + if (sched_meterson) + { + float inmax, outmax; + sys_getmeters(&inmax, &outmax); + indb = 0.5 + rmstodb(inmax); + outdb = 0.5 + rmstodb(outmax); + inclip = (inmax > 0.999); + outclip = (outmax >= 1.0); + } + else + { + indb = outdb = 0; + inclip = outclip = 0; + } + if (inclip != sched_lastinclip || outclip != sched_lastoutclip + || indb != sched_lastindb || outdb != sched_lastoutdb) + { + sys_vgui("pdtk_pd_meters %d %d %d %d\n", indb, outdb, inclip, outclip); + sched_lastinclip = inclip; + sched_lastoutclip = outclip; + sched_lastindb = indb; + sched_lastoutdb = outdb; + } + sched_nextmeterpolltime = + sched_diddsp + (int)(sys_dacsr /(double)DACBLKSIZE); +} + +void glob_meters(void *dummy, float f) +{ + if (f == 0) + sys_getmeters(0, 0); + sched_meterson = (f != 0); + sched_lastinclip = sched_lastoutclip = sched_lastindb = sched_lastoutdb = + -1; +} + +#if 1 +void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) +{ + if (argc) sys_clearhist(); + else sys_printhist(); +} +#endif + +void dsp_tick(void); + +static int m_nodacs = 0; + + /* this must be called earlier than any patches are loaded */ +void m_schedsetsr( void) +{ + sys_time_per_dsp_tick = + (TIMEUNITPERSEC) * ((double)DACBLKSIZE) / sys_dacsr; + sys_time_per_msec = + TIMEUNITPERSEC / 1000.; +} + +/* +Here is Pd's "main loop." This routine dispatches clock timeouts and DSP +"ticks" deterministically, and polls for input from MIDI and the GUI. If +we're left idle we also poll for graphics updates; but these are considered +lower priority than the rest. + +The time source is normally the audio I/O subsystem via the "sys_send_dacs()" +call. This call returns true if samples were transferred; false means that +the audio I/O system is still bussy with previous transfers. +The sys_send_dacs call is OS dependent and is variously implemented in +s_linux.c, s_nt.c, and s_sgi.c. +*/ + +void sys_pollmidiqueue( void); +void sys_initmidiqueue( void); + +int m_scheduler(int nodacs) +{ + int lasttimeforward = SENDDACS_YES; + int idlecount = 0; + double lastdactime = 0; + sys_clearhist(); + m_nodacs = nodacs; + if (sys_sleepgrain < 1000) + sys_sleepgrain = (sys_schedadvance >= 4000? + (sys_schedadvance >> 2) : 1000); + sys_initmidiqueue(); + while (1) + { + int didsomething = 0; + int timeforward; + + sys_addhist(0); + if (m_nodacs) + { + double elapsed = sys_getrealtime() - lastdactime; + static double next = 0; + if (elapsed > next) + { + timeforward = SENDDACS_YES; + next += (double)DACBLKSIZE / sys_dacsr; + } + else timeforward = SENDDACS_NO; + } + else + { + timeforward = sys_send_dacs(); + + /* if dacs remain "idle" for 1 sec, they're hung up. */ + if (timeforward != 0) idlecount = 0; + else + { + idlecount++; + if (!(idlecount & 31)) + { + static double idletime; + /* on 32nd idle, start a clock watch; every + 32 ensuing idles, check it */ + if (idlecount == 32) + idletime = sys_getrealtime(); + else if (sys_getrealtime() - idletime > 1.) + { + post("audio I/O stuck... closing audio\n"); + m_nodacs = 1; + sys_close_audio(); + lastdactime = sys_getrealtime(); + } + } + } + } + sys_setmiditimediff(0, 1e-6 * sys_schedadvance); + lasttimeforward = timeforward; + sys_addhist(1); + if (timeforward != SENDDACS_NO) + { + /* time has moved forward. Check MIDI and clocks */ + + double next_sys_time = sys_time + sys_time_per_dsp_tick; + int countdown = 5000; + while (clock_setlist && clock_setlist->c_settime < next_sys_time) + { + t_clock *c = clock_setlist; + sys_time = c->c_settime; + clock_unset(clock_setlist); + outlet_setstacklim(); + (*c->c_fn)(c->c_owner); + if (!countdown--) + { + countdown = 5000; + sys_pollgui(); + } + } + sys_time = next_sys_time; + if (sys_quit) break; + dsp_tick(); + if (timeforward != SENDDACS_SLEPT) + didsomething = 1; + sched_diddsp++; + } + + sys_addhist(2); + sys_pollmidiqueue(); + if (sys_pollgui()) + { + if (!didsomething) + sched_didpoll++; + didsomething = 1; + } + sys_addhist(3); + /* test for idle; if so, do graphics updates. */ + if (!didsomething) + { + sched_pollformeters(); + sys_reportidle(); + if (timeforward != SENDDACS_SLEPT) + sys_microsleep(sys_sleepgrain); + sys_addhist(5); + sched_didnothing++; + } + } + return (0); +} + + diff --git a/pd/src/makefile b/pd/src/makefile new file mode 100644 index 00000000..62e5f34b --- /dev/null +++ b/pd/src/makefile @@ -0,0 +1,3 @@ +all: + ./configure + make diff --git a/pd/src/makefile.clean b/pd/src/makefile.clean new file mode 100644 index 00000000..62e5f34b --- /dev/null +++ b/pd/src/makefile.clean @@ -0,0 +1,3 @@ +all: + ./configure + make diff --git a/pd/src/makefile.dependencies b/pd/src/makefile.dependencies new file mode 100644 index 00000000..e69de29b diff --git a/pd/src/makefile.in b/pd/src/makefile.in new file mode 100644 index 00000000..b7eb1104 --- /dev/null +++ b/pd/src/makefile.in @@ -0,0 +1,233 @@ +# +# +# + +VPATH = ../obj:./ +OBJ_DIR = ../obj +BIN_DIR = ../bin +PDEXEC = $(BIN_DIR)/pd +EXT= @EXT@ +GUINAME= @GUINAME@ + +INSTALL_PREFIX = @prefix@ +GFLAGS = -DINSTALL_PREFIX=\"$(INSTALL_PREFIX)\" + +# ALSA compilation + +SOUND_ALSA = @alsa@ + +# RME compilation + +SOUND_RME = @rme@ + +DEFINES = @DEFINES@ +MORECFLAGS = @MORECFLAGS@ + +INCLUDE = -I. +GINCLUDE = $(INCLUDE) @GUIFLAGS@ +GLIB = @LIBS@ + +LDFLAGS = @LDFLAGS@ +LIB = @PDLIB@ + +#select either the DBG and OPT compiler flags below: +OPT_CFLAGS = @OPT_CFLAGS@ +WARN_CFLAGS = -Wall -W -Wstrict-prototypes -Werror \ + -Wno-unused -Wno-parentheses -Wno-switch +ARCH_CFLAGS = -DPD -DUNIX + +CFLAGS = $(ARCH_CFLAGS) $(WARN_CFLAGS) $(OPT_CFLAGS) $(DEFINES) $(MORECFLAGS) + +# you might want ALSA linked in non-shared because +# many Linux machines don't have the ALSA shared library. To link +# ALSA non-shared, move the # sign below. + +ifeq (${SOUND_ALSA},yes) +CFLAGS += -DALSA01 +#LIB += /usr/lib/libasound.a +#LIB += -lasound +endif + +ifeq (${SOUND_ALSA},old) +CFLAGS += -DALSA99 +#LIB += /usr/lib/libasound.a +#LIB += -lasound +endif + +ifeq (${SOUND_RME},yes) +CFLAGS += -DRME_HAMMERFALL +endif + + +# Which system + +SYSTEM = $(shell uname -m) + +ifeq (${SYSTEM},alpha) +#LIB += -lffm -lm +CFLAGS += -mieee -mcpu=ev56 +endif + +# Which compiler + +ifeq (${CC},ccc) +CFLAGS += -g3 -D__COMPAQC__ -arch host +endif + +# the sources + +SYSSRC = @SYSSRC@ + +SRC = g_canvas.c g_graph.c g_text.c g_rtext.c g_array.c g_template.c g_io.c \ + g_scalar.c g_traversal.c g_guiconnect.c g_readwrite.c g_editor.c \ + g_all_guis.c g_bang.c g_hdial.c g_hslider.c g_mycanvas.c g_numbox.c \ + g_toggle.c g_vdial.c g_vslider.c g_vumeter.c \ + m_pd.c m_class.c m_obj.c m_atom.c m_memory.c m_binbuf.c \ + m_conf.c m_glob.c m_sched.c \ + s_main.c s_inter.c s_unix.c s_file.c s_print.c \ + s_loader.c s_path.c s_entry.c \ + d_ugen.c d_ctl.c d_arithmetic.c d_osc.c d_filter.c d_dac.c d_misc.c \ + d_math.c d_fft.c d_mayer_fft.c d_fftroutine.c d_array.c d_global.c \ + d_delay.c d_resample.c \ + x_arithmetic.c x_connective.c x_interface.c x_midi.c x_misc.c \ + x_time.c x_acoustics.c x_net.c x_qlist.c x_gui.c d_soundfile.c \ + $(SYSSRC) + + +OBJ = $(SRC:.c=.o) +EXTERNS = ../extra/*/*.$(EXT) + +GSRC = t_main.c t_tkcmd.c + +GOBJ = $(GSRC:.c=.o) + +# +# ------------------ targets ------------------------------------ +# + +.PHONY: pd gui externs + +all: $(PDEXEC) $(BIN_DIR)/pd-watchdog $(BIN_DIR)/$(GUINAME) $(BIN_DIR)/pdsend \ + $(BIN_DIR)/pdreceive $(BIN_DIR)/pd.tk externs + +bin: $(PDEXEC) $(BIN_DIR)/pd-watchdog $(BIN_DIR)/$(GUINAME) $(BIN_DIR)/pdsend \ + $(BIN_DIR)/pdreceive $(BIN_DIR)/pd.tk + +$(OBJ) : %.o : %.c + $(CC) $(CFLAGS) $(GFLAGS) $(INCLUDE) -c -o $(OBJ_DIR)/$*.o $*.c + +$(GOBJ) : %.o : %.c + $(CC) $(CFLAGS) $(GFLAGS) $(GINCLUDE) -c -o $(OBJ_DIR)/$*.o $*.c + +pd: $(PDEXEC) + +gui: $(BIN_DIR)/$(GUINAME) + +pd-watchdog: $(BIN_DIR)/pd-watchdog + +$(BIN_DIR)/pd-watchdog: s_watchdog.c + cc -O2 $(STRIPFLAG) -o $(BIN_DIR)/pd-watchdog s_watchdog.c + +$(BIN_DIR)/pdsend: u_pdsend.c + cc $(CFLAGS) $(STRIPFLAG) -o $(BIN_DIR)/pdsend u_pdsend.c + +$(BIN_DIR)/pdreceive: u_pdreceive.c + cc $(CFLAGS) $(STRIPFLAG) -o $(BIN_DIR)/pdreceive u_pdreceive.c + +$(PDEXEC): $(OBJ) + cd ../obj; $(CC) $(LDFLAGS) $(DBG_CFLAGS) -o $(PDEXEC) $(OBJ) \ + $(LIB) + +$(BIN_DIR)/pd-gui: $(GOBJ) $(GSRC) + cd ../obj; $(CC) $(INCLUDE) -o $(BIN_DIR)/$(GUINAME) $(GOBJ) \ + $(GLIB) + +$(BIN_DIR)/pd.tk: u_main.tk + echo set pd_nt @OSNUMBER@ > $(BIN_DIR)/pd.tk + grep -v "set pd_nt" < u_main.tk >> $(BIN_DIR)/pd.tk + +#this is for Max OSX only... +$(BIN_DIR)/pdtcl: $(GOBJ) $(GSRC) + cd ../obj; libtool -dynamic -o $(BIN_DIR)/pdtcl $(GOBJ) \ + /Library/Frameworks/Tk.framework/Versions/Current/Tk \ + /Library/Frameworks/Tcl.framework/Versions/Current/Tcl \ + /usr/lib/libSystem.B.dylib + +externs: + cd ../extra/bonk~;make @EXTERNTARGET@ + cd ../extra/choice;make @EXTERNTARGET@ + cd ../extra/expr~;make @EXTERNTARGET@ + cd ../extra/fiddle~;make @EXTERNTARGET@ + cd ../extra/loop~;make @EXTERNTARGET@ + cd ../extra/lrshift~;make @EXTERNTARGET@ + cd ../extra/paf~;make @EXTERNTARGET@ + cd ../extra/pique;make @EXTERNTARGET@ + +INSTDIR = $(DESTDIR)/$(INSTALL_PREFIX) +install: + install -d $(INSTDIR)/lib/pd/bin + install $(BIN_DIR)/$(GUINAME) $(INSTDIR)/lib/pd/bin/$(GUINAME) + install $(BIN_DIR)/pd-watchdog $(INSTDIR)/lib/pd/bin/pd-watchdog + install -m644 $(BIN_DIR)/pd.tk $(INSTDIR)/lib/pd/bin/pd.tk + install -d $(INSTDIR)/bin + install -m755 $(PDEXEC) $(INSTDIR)/bin/pd + install -m 755 $(BIN_DIR)/pdsend $(INSTDIR)/bin/pdsend + install -m 755 $(BIN_DIR)/pdreceive $(INSTDIR)/bin/pdreceive + install -d $(INSTDIR)/lib/pd/extra + install -d $(INSTDIR)/lib/pd/externs + install -m 644 $(EXTERNS) $(INSTDIR)/lib/pd/extra + cp -r ../doc $(INSTDIR)/lib/pd/ + install -d $(INSTDIR)/include + install -m644 m_pd.h $(INSTDIR)/include/m_pd.h + gzip < ../man/pd.1 > $(INSTDIR)/man/man1/pd.1.gz + chmod 644 $(INSTDIR)/man/man1/pd.1.gz + gzip < ../man/pdsend.1 > $(INSTDIR)/man/man1/pdsend.1.gz + chmod 644 $(INSTDIR)/man/man1/pdsend.1.gz + gzip < ../man/pdreceive.1 > $(INSTDIR)/man/man1/pdreceive.1.gz + chmod 644 $(INSTDIR)/man/man1/pdreceive.1.gz + +local-clean: + -rm -f ../obj/* $(BIN_DIR)/pd $(BIN_DIR)/$(GUINAME) $(BIN_DIR)/pdsend \ + $(BIN_DIR)/pdreceive $(BIN_DIR)/pd-watchdog m_stamp.c + -rm -f *~ + -rm -f $(BIN_DIR)/pdsend $(BIN_DIR)/pdreceive + -(cd ../doc/6.externs; rm -f *.pd_linux) + -rm -f makefile.dependencies + touch makefile.dependencies + chmod 666 makefile.dependencies + +extra-clean: + -rm -f `find ../extra/ -name "*.pd_*"` + -rm -f tags + +clean: extra-clean local-clean + +distclean: clean + -rm config.cache config.log config.status makefile tags + echo all: > makefile + echo -e "\t./configure" >> makefile + echo -e "\tmake" >> makefile + +tags: $(SRC) $(GSRC); ctags *.[ch] + +depend: + $(CC) $(INCLUDE) $(CFLAGS) -M $(SRC) > makefile.dependencies + +uninstall: + -rm -r $(INSTDIR)/lib/pd + -rm $(INSTDIR)/bin/pd + -rm $(INSTDIR)/bin/pdsend + -rm $(INSTDIR)/bin/pdreceive + -rm $(INSTDIR)/include/m_pd.h + -rm $(INSTDIR)/man/man1/pd.1.gz + -rm $(INSTDIR)/man/man1/pdsend.1.gz + -rm $(INSTDIR)/man/man1/pdreceive.1.gz + +include makefile.dependencies + + + + + + + diff --git a/pd/src/makefile.irix b/pd/src/makefile.irix new file mode 100644 index 00000000..07975f04 --- /dev/null +++ b/pd/src/makefile.irix @@ -0,0 +1,65 @@ +# these can be altered from the command line to create an N32 version: +# make EXECUTABLE=../bin/pd-n32 \ +XF1="-n32 -DN32 -woff 1080,1064,1185 -Ofast=ip32" \ +XF2="-OPT:cray_ivdep=true -r10000 -OPT:roundoff=3 -OPT:IEEE_arithmetic=3" pd + +EXECUTABLE=../bin/pd +XF1=-o32 -fullwarn -O2 +XF2= +all: $(EXECUTABLE) ../bin/pd-gui ../bin/pd.tk + +VPATH=../obj + +INCLUDE = -I. -I../../../irix/tk/generic -I../../../irix/tcl/generic +GLIB = ../tk/unix/libtk8.0.a ../tcl/unix/libtcl8.0.a -lm -lX11 +LIB = -laudio -lmd -lm +CFLAGS = -DUNIX -DIRIX -DPD $(XF1) $(XF2) +LDFLAGS = $(XF1) $(XF2) + +SYSSRC = s_sgi.c + +SRC = g_canvas.c g_graph.c g_text.c g_rtext.c g_array.c g_template.c g_io.c \ + g_scalar.c g_traversal.c g_guiconnect.c g_readwrite.c g_editor.c \ + g_all_guis.c g_bang.c g_hdial.c g_hslider.c g_mycanvas.c g_numbox.c \ + g_toggle.c g_vdial.c g_vslider.c g_vumeter.c \ + m_pd.c m_class.c m_obj.c m_atom.c m_memory.c m_binbuf.c \ + m_conf.c m_glob.c m_sched.c \ + s_main.c s_inter.c s_unix.c s_file.c s_print.c \ + s_loader.c s_path.c s_entry.c \ + d_ugen.c d_ctl.c d_arithmetic.c d_osc.c d_filter.c d_dac.c d_misc.c \ + d_math.c d_fft.c d_mayer_fft.c d_fftroutine.c d_array.c d_global.c \ + d_delay.c d_resample.c \ + x_arithmetic.c x_connective.c x_interface.c x_midi.c x_misc.c \ + x_time.c x_acoustics.c x_net.c x_qlist.c x_gui.c d_soundfile.c \ + $(SYSSRC) + +OBJ = $(SRC:.c=.o) + +GSRC = t_main.c t_tkcmd.c + +GOBJ = $(GSRC:.c=.o) +.PHONY: pd gui + +.c.o: + cc $(CFLAGS) $(INCLUDE) -c -o $(VPATH)/$*.o $*.c + +pd: $(EXECUTABLE) + +gui: ../bin/pd-gui + +$(EXECUTABLE): $(OBJ) + cd ../obj; cc $(LDFLAGS) -o $(EXECUTABLE) $(OBJ) \ + $(LIB) + +../bin/pd-gui: $(GOBJ) + cd ../obj; cc $(LDFLAGS) -o ../bin/pd-gui $(GOBJ) \ + $(GLIB) -lm -lX11 + +../bin/pd.tk: u_main.tk; cp u_main.tk ../bin/pd.tk + +tags: $(SRC) $(GSRC); ctags *.[ch] + +depend:; cc -M $(CFLAGS) $(INCLUDE) $(SRC) > makefile.dependencies + +include makefile.dependencies + diff --git a/pd/src/makefile.nt b/pd/src/makefile.nt new file mode 100644 index 00000000..91d34051 --- /dev/null +++ b/pd/src/makefile.nt @@ -0,0 +1,95 @@ +# Makefile for portaudio ASIO driver version of PD + +all: pd gui ..\bin\pd.tk + +VC = "C:\Program Files\Microsoft Visual Studio\VC98" +#VC="\Program Files\DevStudio\Vc" +INCLUDE = -I.\ -I..\Tcl\include -I$(VC)\include + +LDIR = $(VC)\lib + +LIB = /NODEFAULTLIB:libc /NODEFAULTLIB:oldnames /NODEFAULTLIB:kernel \ + /NODEFAULTLIB:uuid \ + $(LDIR)\libc.lib $(LDIR)\oldnames.lib $(LDIR)\kernel32.lib \ + $(LDIR)\wsock32.lib $(LDIR)\winmm.lib ..\bin\pthreadVC.lib + +GLIB = $(LIB) ..\lib\tcl83.lib ..\lib\tk83.lib +CFLAGS = /nologo /W3 /DNT /DPD /DPD_INTERNAL /DWIN32 /DWINDOWS /Ox +LFLAGS = /nologo + +SYSSRC = s_nt.c s_portaudio.c + +SRC = g_canvas.c g_graph.c g_text.c g_rtext.c g_array.c g_template.c g_io.c \ + g_scalar.c g_traversal.c g_guiconnect.c g_readwrite.c g_editor.c \ + g_all_guis.c g_bang.c g_hdial.c g_hslider.c g_mycanvas.c g_numbox.c \ + g_toggle.c g_vdial.c g_vslider.c g_vumeter.c \ + m_pd.c m_class.c m_obj.c m_atom.c m_memory.c m_binbuf.c \ + m_conf.c m_glob.c m_sched.c \ + s_main.c s_inter.c s_unix.c s_file.c s_print.c \ + s_loader.c s_path.c s_entry.c \ + d_ugen.c d_ctl.c d_arithmetic.c d_osc.c d_filter.c d_dac.c d_misc.c \ + d_math.c d_fft.c d_mayer_fft.c d_fftroutine.c d_array.c d_global.c \ + d_delay.c d_resample.c \ + x_arithmetic.c x_connective.c x_interface.c x_midi.c x_misc.c \ + x_time.c x_acoustics.c x_net.c x_qlist.c x_gui.c d_soundfile.c \ + $(SYSSRC) + +PADIR = ..\portaudio +INCPA = -I$(PADIR) -I$(PADIR)\pa_common -I$(PADIR)\pablio -I..\lib\asio +SRCPA = $(PADIR)/pa_common/pa_lib.c $(PADIR)/pa_common/pa_trace.c \ + $(PADIR)/pablio/pablio_pd.c $(PADIR)/pablio/ringbuffer_pd.c +SRCASIO = $(PADIR)/pa_asio/pa_asio.cpp + +ASIOLIB = $(LDIR)\user32.lib $(LDIR)\gdi32.lib $(LDIR)\winspool.lib $(LDIR)\comdlg32.lib \ +$(LDIR)\advapi32.lib $(LDIR)\shell32.lib $(LDIR)\ole32.lib $(LDIR)\oleaut32.lib $(LDIR)\uuid.lib \ +$(LDIR)\odbc32.lib $(LDIR)\odbccp32.lib ..\lib\asio\asiolib.lib + + +PAOBJ = pa_lib.obj pa_trace.obj pablio_pd.obj ringbuffer_pd.obj pa_asio.obj +OBJC = $(SRC:.c=.obj) $(PAOBJ) + +GSRC = t_main.c t_tkcmd.c + +GOBJ = $(GSRC:.c=.obj) +.PHONY: pd gui + +ALLCF = $(CFLAGS) $(INCLUDE) $(INCASIO) $(INCPA) /D_WINDOWS + +.c.obj: + cl /c $(ALLCF) /Tc$*.c + +pd: ..\bin\pd.exe + +gui: ..\bin\pdtcl.dll + +..\bin\pd.exe: s_entry.obj ..\bin\pd.lib + link $(LFLAGS) /out:..\bin\pd.exe /INCREMENTAL:NO s_entry.obj \ + ..\bin\pd.lib $(LIB) $(ASIOLIB) + +..\bin\pd.dll ..\bin\pd.lib: $(OBJC) $(OBJASIO) + link $(LFLAGS) /dll /export:sys_main /out:..\bin\pd.dll $(OBJC) \ + $(OBJASIO) $(LIB) $(ASIOLIB) + +..\bin\pdtcl.dll: t_tkcmd.obj + link $(LFLAGS) /dll /export:Pdtcl_Init /out:..\bin\pdtcl.dll \ + t_tkcmd.obj $(GLIB) + +..\bin\pd.tk: u_main.tk; copy u_main.tk ..\bin\pd.tk + +# explicit rules to compile portaudio sources: +pa_lib.obj: $(PADIR)\pa_common\pa_lib.c + cl /c $(ALLCF) $(PADIR)\pa_common\pa_lib.c +pa_trace.obj: $(PADIR)\pa_common\pa_trace.c + cl /c $(ALLCF) $(PADIR)\pa_common\pa_trace.c +pablio_pd.obj: $(PADIR)\pablio\pablio_pd.c + cl /c $(ALLCF) $(PADIR)\pablio\pablio_pd.c +ringbuffer_pd.obj: $(PADIR)\pablio\ringbuffer_pd.c + cl /c $(ALLCF) $(PADIR)\pablio\ringbuffer_pd.c +pa_asio.obj: $(PADIR)\pa_asio\pa_asio.cpp + cl /c $(ALLCF) $(PADIR)\pa_asio\pa_asio.cpp + +# the following should also clean up "bin" but it doesn't because "bin" holds +# precious stuff from elsewhere. +clean: + del *.obj + diff --git a/pd/src/notes.txt b/pd/src/notes.txt new file mode 100644 index 00000000..6a01dd0b --- /dev/null +++ b/pd/src/notes.txt @@ -0,0 +1,264 @@ +---------------- dolist -------------------- +++portno wierdness in gcc 3 worked around (Burton) +non-power-of-2 channel counts in ASIO (Olaf Matthes) +Bug, David McCallum, Jul. 13 -- find last error crashes + + +last-minute bug fixes: +add flag to select MIDI open to use select()? +check top-of-window problem in OSX +denormal protection + +doc: +fix readme file and recopy to web page (add to README in dist instructions) + +problems: +don't draw in/outlets on gui objects in graphs +Alsa degradation after several hours running on soundblaster +font size should depend on subpatch/abstraction +moving a bang toward top of window creates problem +figure out O_NDELAY for linux audio? +missed Thomas's multi-dialog trick??? +fix iemguis not to bash symbol names +check what happens when going back and forth between graph-on-parent +deal with spaces in iemgui labels and send/receive names +David McCallum, table crashes in 98? +font hack (pix@test.at, 30 Nov 2001 + perl -pi -e 's/-[*%a-z0-9-]*\*[*%a-z0-9-]*/fixed/' u_main.tk +get rid of messages causing renaming; try to prevent patches closing themselves. +Krzysztof's qlist_next reentrancy bug +dac~/ adc~/ block~ incompatibility +is ALSA really checking /proc!? +scofo reports error on reading score1.txt +data copy/paste doesn't check templates aren't changed +rfft~ loses nyquist bin -- see "to hell with it" comment in d_fft.c +soundfile writing gets wrong sample rate; see /* lie */ in d_soundfile.c + +data: +vget, vset traversal objects +cursor to show (x, y) location +better hit detection (getrect is too greedy) +click on points of plot +typing at drawnumbers +fix templates to be loaded on demand and belong to a globally known patch +test and debug list elements of templates +sublists should display on parent if desired? +sublists seem not to handle canvas allocation right (get.pd->pointer.pd bug) +scalar hook to catch the mouse +protect against "plots" going away while you drag on them + +features: +Pd to open html help on windows/mac +flag to hide array names +??? have a way to disambiguate externs from different libs??? +put serial object in main dist (see rat@telecoma, Apr. 25; winfried May 22) +if there's just one array, don't do stringent hit check. +netsend separate thread +netreceive (and netsend?) message to set port number +delete-in-rectangle message to Pds +pasting should look at current mouse location +"regular" numbers/symbols to do send/receive thing ala IEMGUI +make selecting text grab keyboard focus +think about x and y scale preservation when changing between graph and object +show outlines of objects even when graph is "open" +make graph labels persistent and add to dialog +array click protection (Krzysztof's suggestion) +Pd support for jack audio system: http://home.t-online.de/home/pdq808/jack-patch +offer audiooutdev 1,3 feature on Windows +Alsa in data late should carefuly reset DAC/ADC fill&empty pointers +increase MIDIQSIZE to at least 1024 in s_unix.c +add nonblock to linux open calls instead of using alarm +make a hook so objects can specify help windows to open (for scheme object) +graph_vis() to decorate graphs when they're toplevel (parent_glist == 0) +get graphs to expand to hold their contents +writing FLOAT wav files +make "table" rescalable vertically +-compat34 flag to save files so that 0.34 can read them +suita.chopin.edu.pl/~czaja/miXed/externs/xeq.html -- MIDI file reader +in glist_delete, consider why this can't be just "vis 0" -- why do we need it? +abstraction auto-reload +closebang +switching between dac and gettimeofday timing on dsp_start/stop +check that -blocksize really reflects in audiobuf calc for Hammerfall +-version to print version and exit; usage() also to print version +NT and OSX: opening HTML files? +message to change block~ sizes dynamically +MIDI file reading/writing? +makefile to have make install depend on make local. +borrow arrow keys from IEMLIB +pd messages to close and reopen sound driver +Float method for random +figure out list, message objects +separate control over alsaindev and alsaoutdev +pd -version +make "import"/export use IEMLIB objects +object to get/set table size; random; quantile +put in something for tilde order forcing +extensible "toolbar" so people can add external GUI objects +text cut and paste; see XStoreBytes +new objects: nexttick~, extend threshold~ and snapshot~ +allow spaces in paths +gem: try XSetBorderWidth, XMoveWindow, xcopyarea (/usr/share/doc/XF*) +dialog for audio and MIDI settings +prepend help to help filenames and add help search path +read/writesf~ for NT +variable send and receive -- check how max/MSP does it? +number boxes to darken for typing and/or received messages +delayed updates +invisible toplevels +dialog to change lib flag and path +fastedit moves +pique~ and fiddle~ unification (notice pique filtering is different!) +new message box look +figure out what to do when "pd sym" conflicts with window title as in Pluton? + + +LATER +Hammerfall adapt to ALSA ++~ 0 faster than +~ -- detect scalar input; also, order forcing inputs +bonk~ file path handling +unify arrays and garrays +dialog to give values of $1, ... for the canvas +bang at end of line~, tabwrite~, etc. +recording to part of a table +printout to main window +should sys_bail kill all "threads" on the way out? +check a_valid usage +allow backslashes (or else really disallow them) +icon & desktop integration +vreadsf~ +benchmarking +flash menu when accelerator hits? +fix edit mode menu item +"undo" +fancier text editing +tools (reassigns meaning of primary click) +get gui to notice early EOF +rewrite t_getbytes properly +obj_new should do a longjmp on out-of-memory + +--------------------- source notes -------------------------- + +0. structure definition roadmap. First, the containment tree of things +that can be sent messages ("pure data"). (note that t_object and t_text, +and t_graph and t_canvas, should be unified...) + +------------ BFFORE 0.35: --------- +m_pd.h t_pd anything with a class + t_gobj "graphic object" + t_text text object +g_canvas.h + t_glist list of graphic objects +g_canvas.c t_canvas Pd "document" + +------------ AFTER 0.35: --------- +m_pd.h t_pd anything with a class + t_gobj "graphic object" + t_text patchable object, AKA t_object +g_canvas.h t_glist list of graphic objects, AKA t_canvas + +... and other structures: +g_canvas.h t_selection -- linked list of gobjs + t_editor -- editor state, allocated for visible glists +m_imp.h t_methodentry -- method handler + t_widgetbehavior -- class-dependent editing behavior for gobjs + t_parentwidgetbehavior -- objects' behavior on parent window + t_class -- method definitions, instance size, flags, etc. + + +1. C coding style. The source should pass most "warnings" of C compilers +(-Wall on linux, for instance; see the makefile.) Some informalities +are intentional, for instance the loose use of function prototypes (see +below) and uncast conversions from longer to shorter numerical formats. +The code doesn't respect "const" yet. + +1.1. Prefixes in structure elements. The names of structure elements always +have a K&R-style prefix, as in ((t_atom)x)->a_type, where the "a_" prefix +indicates "atom." This is intended to enhance readability (although the +convention arose from a limitation of early C compilers.) Common prefixes are +"w_" (word), "a_" (atom), "s_" (symbol), "ob_" (object), "te_" (text object), +"g_" (graphical object), and "gl_" (glist, a list of graphical objects). Also, +global symbols sometimes get prefixes, as in "s_float" (the symbol whose string +is "float). Typedefs are prefixed by "t_". Most _private_ structures, i.e., +structures whose definitions appear in a ".c" file, are prefixed by "x_". + +1.2. Function arguments. Many functions take as their first +argument a pointer named "x", which is a pointer to a structure suggested +by the function prefix; e.g., canvas_dirty(x, n) where "x" points to a canvas +(t_canvas *x). + +1.3. Function Prototypes. Functions which are used in at least two different +files (besides where they originate) are prototyped in the appropriate include +file. Functions which are provided in one file and used in one other are +prototyped right where they are used. This is just to keep the size of the +".h" files down for readability's sake. + +1.4. Whacko private terminology. Some terms are lifted from other historically +relevant programs, notably "ugen" (which is just a tilde object; see d_ugen.c.) + +1.5. Spacing. Tabs are 8 spaces; indentation is 4 spaces. Indenting +curly brackets are by themselves on their own lines, as in: + + if (x) + { + x = 0; + } + +Lines should fit within 80 spaces. + +2. Max patch-level compatibility. "Import" and "Export" functions are +provided which aspire to strict compatibility with 0.26 patches (ISPW version), +but which don't get anywhere close to that yet. Where possible, features +appearing on the Mac will comeday also be provided; for instance, the connect +message on the Mac offers segmented patch cords; these will devolve into +straight lines in Pd. Many, many UI objects in Opcode Max will not appear in +Pd, at least at first. + +3. Compatibility with Max 0.26 "externs", i.e., source-level compatibility. Pd +objects follow the style of 0.26 objects as closely as possible, making +exceptions in cases where the 0.26 model is clearly deficient. These are: + +3.1. Anything involving the MacIntosh "Handle" data type is changed to use +char * or void * instead. + +3.2. Pd passes true single-precision floating-point arguments to methods; +Max uses double. +Typedefs are provided: + t_floatarg, t_intarg for arguments passed by the message system + t_float, t_int for the "word" union (in atoms, for example.) + +3.3. Badly-named entities got name changes: + + w_long --> w_int (in the "union word" structure) + +3.4. Many library functions are renamed and have different arguments; +I hope to provide an include file to alias them when compiling Max externs. + +4. Function name prefixes. +Many function names have prefixes which indicate what "package" they belong +to. The exceptions are: + typedmess, vmess, getfn, gensym (m_class.c) + getbytes, freebytes, resizebytes (m_memory.c) + post, error, bug (s_print.c) +which are all frequently called and which don't fit into simple categories. +Important packages are: +(pd-gui:) pdgui -- everything +(pd:) pd -- functions common to all "pd" objects + obj -- fuctions common to all "patchable" objects ala Max + sys -- "system" level functions + binbuf -- functions manipulating binbufs + class -- functions manipulating classes + (other) -- functions common to the named Pd class + +5. Source file prefixes. +PD: +s system interface +m message system +g graphics stuff +d DSP objects +x control objects +z other + +PD-GUI: +t TK front end + diff --git a/pd/src/s_entry.c b/pd/src/s_entry.c new file mode 100644 index 00000000..354512e5 --- /dev/null +++ b/pd/src/s_entry.c @@ -0,0 +1,10 @@ +/* In NT, this is all there is to pd; the rest sits in a "pdlib" dll so +that externs can link back to functions defined in pd. */ + + +int sys_main(int argc, char **argv); + +int main(int argc, char **argv) +{ + return (sys_main(argc, argv)); +} diff --git a/pd/src/s_file.c b/pd/src/s_file.c new file mode 100644 index 00000000..32d2fcaa --- /dev/null +++ b/pd/src/s_file.c @@ -0,0 +1,54 @@ +/* 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 file-handling routines. + */ + +#include "m_imp.h" +#include +#include + + /* LATER delete this? -- replaced by find_via_path() in s_path.c */ +int sys_isreadablefile(const char *s) +{ + struct stat statbuf; + int mode; + if (stat(s, &statbuf) < 0) return (0); +#ifdef UNIX + mode = statbuf.st_mode; + if (S_ISDIR(mode)) return (0); +#endif + return (1); +} + + /* change '/' characters to the system's native file separator */ +void sys_bashfilename(const char *from, char *to) +{ + char c; + while (c = *from++) + { +#ifdef NT + if (c == '/') c = '\\'; +#endif + *to++ = c; + } + *to = 0; +} + + + /* change the system's native file separator to '/' characters */ +void sys_unbashfilename(const char *from, char *to) +{ + char c; + while (c = *from++) + { +#ifdef NT + if (c == '\\') c = '/'; +#endif + *to++ = c; + } + *to = 0; +} + diff --git a/pd/src/s_freebsd.c b/pd/src/s_freebsd.c new file mode 100644 index 00000000..4ed4241b --- /dev/null +++ b/pd/src/s_freebsd.c @@ -0,0 +1,3072 @@ +/* Copyright (c) 1997-1999 Guenter Geiger, Miller Puckette, Larry Troxler, +* Winfried Ritsch, Karl MacMillan, 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 implements the sys_ functions profiled in m_imp.h for + audio and MIDI I/O. In Linux there might be several APIs for doing the + audio part; right now there are three (OSS, ALSA, RME); the third is + for the RME 9652 driver by Ritsch (but not for the OSS compatible + one by Geiger; for that one, OSS should work.) + + FUNCTION PREFIXES. + sys_ -- functions which must be exported to Pd on all platforms + linux_ -- linux-specific objects which don't depend on API, + mostly static but some exported. + oss_, alsa_, rme_ -- API-specific functions, all of which are + static. + + ALSA SUPPORT. If ALSA99 is defined we support ALSA 0.5x; if ALSA01, + ALSA 0.9x. (the naming scheme reflects the possibility of further API + changes in the future...) We define "ALSA" for code relevant to both + APIs. + + For MIDI, we only offer the OSS API; ALSA has to emulate OSS for us. +*/ + +/* OSS include (whether we're doing OSS audio or not we need this for MIDI) */ + + +/* IOhannes::: + * hacked this to add advanced multidevice-support + * 1311:forum::für::umläute:2001 + */ + +#include + +#if (defined(ALSA01) || defined(ALSA99)) +#define ALSA +#endif + +#ifdef ALSA99 +#include +#endif +#ifdef ALSA01 +#include +#endif + +#include "m_imp.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* local function prototypes */ + +static void linux_close_midi( void); + +static int oss_open_audio(int naudioindev, int *audioindev, int nchindev, + int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, + int *choutdev, int rate); /* IOhannes */ + +static void oss_close_audio(void); +static int oss_send_dacs(void); +static void oss_reportidle(void); + +#ifdef ALSA +typedef int16_t t_alsa_sample16; +typedef int32_t t_alsa_sample32; +#define ALSA_SAMPLEWIDTH_16 sizeof(t_alsa_sample16) +#define ALSA_SAMPLEWIDTH_32 sizeof(t_alsa_sample32) +#define ALSA_XFERSIZE16 (signed int)(sizeof(t_alsa_sample16) * DACBLKSIZE) +#define ALSA_XFERSIZE32 (signed int)(sizeof(t_alsa_sample32) * DACBLKSIZE) +#define ALSA_MAXDEV 1 +#define ALSA_JITTER 1024 +#define ALSA_EXTRABUFFER 2048 +#define ALSA_DEFFRAGSIZE 64 +#define ALSA_DEFNFRAG 12 + +#ifdef ALSA99 +typedef struct _alsa_dev +{ + snd_pcm_t *handle; + snd_pcm_channel_info_t info; + snd_pcm_channel_setup_t setup; +} t_alsa_dev; + +t_alsa_dev alsa_device[ALSA_MAXDEV]; +static int n_alsa_dev; +static char *alsa_buf; +static int alsa_samplewidth; +#endif /* ALSA99 */ + +#ifdef ALSA01 +typedef struct _alsa_dev +{ + snd_pcm_t *inhandle; + snd_pcm_t *outhandle; +} t_alsa_dev; + +t_alsa_dev alsa_device; +static short *alsa_buf; +static int alsa_samplewidth; +static snd_pcm_status_t* in_status; +static snd_pcm_status_t* out_status; +#endif /* ALSA01 */ + +#if 0 /* early alsa 0.9 beta dists had different names for these: */ +#define SND_PCM_ACCESS_RW_INTERLEAVED SNDRV_PCM_ACCESS_RW_INTERLEAVED +#define SND_PCM_FORMAT_S32 SNDRV_PCM_FORMAT_S32 +#define SND_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16 +#define SND_PCM_SUBFORMAT_STD SNDRV_PCM_SUBFORMAT_STD +#endif + +static int alsa_mode; +static int alsa_open_audio(int inchans, int outchans, int rate); +static void alsa_close_audio(void); +static int alsa_send_dacs(void); +static void alsa_set_params(t_alsa_dev *dev, int dir, int rate, int voices); +static void alsa_reportidle(void); +#endif /* ALSA */ + +#ifdef RME_HAMMERFALL +static int rme9652_open_audio(int inchans, int outchans, int rate); +static void rme9652_close_audio(void); +static int rme9652_send_dacs(void); +static void rme9652_reportidle(void); +#endif /* RME_HAMMERFALL */ + +/* Defines */ +#define DEBUG(x) x +#define DEBUG2(x) {x;} + +#define OSS_MAXCHPERDEV 32 /* max channels per OSS device */ +#define OSS_MAXDEV 4 /* maximum number of input or output devices */ +#define OSS_DEFFRAGSIZE 256 /* default log fragment size (frames) */ +#define OSS_DEFAUDIOBUF 40000 /* default audiobuffer, microseconds */ +#define OSS_DEFAULTCH 2 +#define RME_DEFAULTCH 8 /* need this even if RME undefined */ +typedef int16_t t_oss_int16; +typedef int32_t t_oss_int32; +#define OSS_MAXSAMPLEWIDTH sizeof(t_oss_int32) +#define OSS_BYTESPERCHAN(width) (DACBLKSIZE * (width)) +#define OSS_XFERSAMPS(chans) (DACBLKSIZE* (chans)) +#define OSS_XFERSIZE(chans, width) (DACBLKSIZE * (chans) * (width)) + +#ifdef RME_HAMMERFALL +typedef int32_t t_rme_sample; +#define RME_SAMPLEWIDTH sizeof(t_rme_sample) +#define RME_BYTESPERCHAN (DACBLKSIZE * RME_SAMPLEWIDTH) +#endif /* RME_HAMMERFALL */ + +/* GLOBALS */ +static int linux_whichapi = API_OSS; +static int linux_inchannels; +static int linux_outchannels; +static int linux_advance_samples; /* scheduler advance in samples */ +static int linux_meters; /* true if we're metering */ +static float linux_inmax; /* max input amplitude */ +static float linux_outmax; /* max output amplitude */ +static int linux_fragsize = 0; /* for block mode; block size (sample frames) */ +static int linux_nfragment = 0; /* ... and number of blocks. */ + +#ifdef ALSA99 +static int alsa_devno = 1; +#endif +#ifdef ALSA01 +static char alsa_devname[512] = "hw:0,0"; +static int alsa_use_plugin = 0; +#endif + +/* our device handles */ + +typedef struct _oss_dev +{ + int d_fd; + unsigned int d_space; /* bytes available for writing/reading */ + int d_bufsize; /* total buffer size in blocks for this device */ + int d_dropcount; /* # of buffers to drop for resync (output only) */ + unsigned int d_nchannels; /* number of channels for this device */ + unsigned int d_bytespersamp; /* bytes per sample (2 for 16 bit, 4 for 32) */ +} t_oss_dev; + +static t_oss_dev linux_dacs[OSS_MAXDEV]; +static t_oss_dev linux_adcs[OSS_MAXDEV]; +static int linux_noutdevs = 0; +static int linux_nindevs = 0; + + /* exported variables */ +int sys_schedadvance = OSS_DEFAUDIOBUF; /* scheduler advance in microsecs */ +float sys_dacsr; +int sys_hipriority = 0; +t_sample *sys_soundout; +t_sample *sys_soundin; + + /* OSS-specific private variables */ +static int oss_blockmode = 1; /* flag to use "blockmode" */ +static char ossdsp[] = "/dev/dsp%d"; + +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffff +#endif +#define CLIP32(x) (((x)>INT32_MAX)?INT32_MAX:((x) < -INT32_MAX)?-INT32_MAX:(x)) + + +/* ------------- private routines for all APIS ------------------- */ + +static void linux_flush_all_underflows_to_zero(void) +{ +/* + TODO: Implement similar thing for linux (GGeiger) + + One day we will figure this out, I hope, because it + costs CPU time dearly on Intel - LT + */ + /* union fpc_csr f; + f.fc_word = get_fpc_csr(); + f.fc_struct.flush = 1; + set_fpc_csr(f.fc_word); + */ +} + + /* set sample rate and channels. Must set sample rate before "configuring" + any devices so we know scheduler advance in samples. */ + +static void linux_setsr(int sr) +{ + sys_dacsr = sr; + linux_advance_samples = (sys_schedadvance * sys_dacsr) / (1000000.); + if (linux_advance_samples < 3 * DACBLKSIZE) + linux_advance_samples = 3 * DACBLKSIZE; +} + +static void linux_setch(int chin, int chout) +{ + int nblk; + int inbytes = chin * (DACBLKSIZE*sizeof(float)); + int outbytes = chout * (DACBLKSIZE*sizeof(float)); + + linux_inchannels = chin; + linux_outchannels = chout; + if (sys_soundin) + free(sys_soundin); + sys_soundin = (t_float *)malloc(inbytes); + memset(sys_soundin, 0, inbytes); + + if (sys_soundout) + free(sys_soundout); + sys_soundout = (t_float *)malloc(outbytes); + memset(sys_soundout, 0, outbytes); + + if (sys_verbose) + post("input channels = %d, output channels = %d", + linux_inchannels, linux_outchannels); +} + +/* ---------------- MIDI routines -------------------------- */ + +static int oss_nmidiin; +static int oss_midiinfd[MAXMIDIINDEV]; +static int oss_nmidiout; +static int oss_midioutfd[MAXMIDIOUTDEV]; + +static void oss_midiout(int fd, int n) +{ + char b = n; + if ((write(fd, (char *) &b, 1)) != 1) + perror("midi write"); +} + +#define O_MIDIFLAG O_NDELAY + +void linux_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec) +{ + int i; + for (i = 0; i < nmidiout; i++) + oss_midioutfd[i] = -1; + for (i = 0, oss_nmidiin = 0; i < nmidiin; i++) + { + int fd = -1, j, outdevindex = -1; + char namebuf[80]; + int devno = midiinvec[i]; + + for (j = 0; j < nmidiout; j++) + if (midioutvec[j] == midiinvec[i]) + outdevindex = j; + + /* try to open the device for read/write. */ + if (devno == 1 && fd < 0 && outdevindex >= 0) + { + sys_setalarm(1000000); + fd = open("/dev/midi", O_RDWR | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, + "device 1: tried /dev/midi READ/WRITE; returned %d\n", fd); + if (outdevindex >= 0 && fd >= 0) + oss_midioutfd[outdevindex] = fd; + } + if (fd < 0 && outdevindex >= 0) + { + sys_setalarm(1000000); + sprintf(namebuf, "/dev/midi%2.2d", devno-1); + fd = open(namebuf, O_RDWR | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, + "device %d: tried %s READ/WRITE; returned %d\n", + devno, namebuf, fd); + if (outdevindex >= 0 && fd >= 0) + oss_midioutfd[outdevindex] = fd; + } + if (fd < 0 && outdevindex >= 0) + { + sys_setalarm(1000000); + sprintf(namebuf, "/dev/midi%d", devno-1); + fd = open(namebuf, O_RDWR | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, "device %d: tried %s READ/WRITE; returned %d\n", + devno, namebuf, fd); + if (outdevindex >= 0 && fd >= 0) + oss_midioutfd[outdevindex] = fd; + } + if (devno == 1 && fd < 0) + { + sys_setalarm(1000000); + fd = open("/dev/midi", O_RDONLY | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, + "device 1: tried /dev/midi READONLY; returned %d\n", fd); + } + if (fd < 0) + { + sys_setalarm(1000000); + sprintf(namebuf, "/dev/midi%2.2d", devno-1); + fd = open(namebuf, O_RDONLY | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, "device %d: tried %s READONLY; returned %d\n", + devno, namebuf, fd); + } + if (fd < 0) + { + sys_setalarm(1000000); + sprintf(namebuf, "/dev/midi%d", devno-1); + fd = open(namebuf, O_RDONLY | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, "device %d: tried %s READONLY; returned %d\n", + devno, namebuf, fd); + } + if (fd >= 0) + oss_midiinfd[oss_nmidiin++] = fd; + else post("couldn't open MIDI input device %d", devno); + } + for (i = 0, oss_nmidiout = 0; i < nmidiout; i++) + { + int fd = oss_midioutfd[i]; + char namebuf[80]; + int devno = midioutvec[i]; + if (devno == 1 && fd < 0) + { + sys_setalarm(1000000); + fd = open("/dev/midi", O_WRONLY | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, + "device 1: tried /dev/midi WRITEONLY; returned %d\n", fd); + } + if (fd < 0) + { + sys_setalarm(1000000); + sprintf(namebuf, "/dev/midi%2.2d", devno-1); + fd = open(namebuf, O_WRONLY | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, "device %d: tried %s WRITEONLY; returned %d\n", + devno, namebuf, fd); + } + if (fd < 0) + { + sys_setalarm(1000000); + sprintf(namebuf, "/dev/midi%d", devno-1); + fd = open(namebuf, O_WRONLY | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, "device %d: tried %s WRITEONLY; returned %d\n", + devno, namebuf, fd); + } + if (fd >= 0) + oss_midioutfd[oss_nmidiout++] = fd; + else post("couldn't open MIDI output device %d", devno); + } + + if (oss_nmidiin < nmidiin || oss_nmidiout < nmidiout || sys_verbose) + post("opened %d MIDI input device(s) and %d MIDI output device(s).", + oss_nmidiin, oss_nmidiout); +} + +#define md_msglen(x) (((x)<0xC0)?2:((x)<0xE0)?1:((x)<0xF0)?2:\ + ((x)==0xF2)?2:((x)<0xF4)?1:0) + +void sys_putmidimess(int portno, int a, int b, int c) +{ + if (portno >= 0 && portno < oss_nmidiout) + { + switch (md_msglen(a)) + { + case 2: + oss_midiout(oss_midioutfd[portno],a); + oss_midiout(oss_midioutfd[portno],b); + oss_midiout(oss_midioutfd[portno],c); + return; + case 1: + oss_midiout(oss_midioutfd[portno],a); + oss_midiout(oss_midioutfd[portno],b); + return; + case 0: + oss_midiout(oss_midioutfd[portno],a); + return; + }; + } +} + +void sys_putmidibyte(int portno, int byte) +{ + if (portno >= 0 && portno < oss_nmidiout) + oss_midiout(oss_midioutfd[portno], byte); +} + +#if 0 /* this is the "select" version which doesn't work with OSS + driver for emu10k1 (it doesn't implement select.) */ +void sys_poll_midi(void) +{ + int i, throttle = 100; + struct timeval timout; + int did = 1, maxfd = 0; + while (did) + { + fd_set readset, writeset, exceptset; + did = 0; + if (throttle-- < 0) + break; + timout.tv_sec = 0; + timout.tv_usec = 0; + + FD_ZERO(&writeset); + FD_ZERO(&readset); + FD_ZERO(&exceptset); + for (i = 0; i < oss_nmidiin; i++) + { + if (oss_midiinfd[i] > maxfd) + maxfd = oss_midiinfd[i]; + FD_SET(oss_midiinfd[i], &readset); + } + select(maxfd+1, &readset, &writeset, &exceptset, &timout); + for (i = 0; i < oss_nmidiin; i++) + if (FD_ISSET(oss_midiinfd[i], &readset)) + { + char c; + int ret = read(oss_midiinfd[i], &c, 1); + if (ret <= 0) + fprintf(stderr, "Midi read error\n"); + else sys_midibytein(i, (c & 0xff)); + did = 1; + } + } +} +#else + + /* this version uses the asynchronous "read()" ... */ +void sys_poll_midi(void) +{ + int i, throttle = 100; + struct timeval timout; + int did = 1, maxfd = 0; + while (did) + { + fd_set readset, writeset, exceptset; + did = 0; + if (throttle-- < 0) + break; + for (i = 0; i < oss_nmidiin; i++) + { + char c; + int ret = read(oss_midiinfd[i], &c, 1); + if (ret < 0) + { + if (errno != EAGAIN) + perror("MIDI"); + } + else if (ret != 0) + { + sys_midibytein(i, (c & 0xff)); + did = 1; + } + } + } +} +#endif + +void linux_close_midi() +{ + int i; + for (i = 0; i < oss_nmidiin; i++) + close(oss_midiinfd[i]); + for (i = 0; i < oss_nmidiout; i++) + close(oss_midioutfd[i]); + oss_nmidiin = oss_nmidiout = 0; +} + +#define MAXAUDIODEV 4 +#define DEFAULTINDEV 1 +#define DEFAULTOUTDEV 1 + +/* ----------------------- public routines ----------------------- */ +void sys_listdevs( void) +{ + post("device listing not implemented in Linux yet\n"); +} + +void sys_open_audio(int naudioindev, int *audioindev, int nchindev, + int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, + int *choutdev, int rate) +{ /* IOhannes */ + int i, *ip; + int defaultchannels = + (linux_whichapi == API_RME ? RME_DEFAULTCH : OSS_DEFAULTCH); + if (rate < 1) + rate=44100; + + if (naudioindev == -1) + { /* not set */ + if (nchindev==-1) + { + nchindev=1; + chindev[0]=defaultchannels; + naudioindev=1; + audioindev[0] = DEFAULTINDEV; + } + else + { + for (i = 0; i < MAXAUDIODEV; i++) + audioindev[i]=i+1; + naudioindev = nchindev; + } + } + else + { + if (nchindev == -1) + { + nchindev = naudioindev; + for (i = 0; i < naudioindev; i++) + chindev[i] = defaultchannels; + } + else if (nchindev > naudioindev) + { + for (i = naudioindev; i < nchindev; i++) + { + if (i == 0) + audioindev[0] = DEFAULTINDEV; + else audioindev[i] = audioindev[i-1] + 1; + } + naudioindev = nchindev; + } + else if (nchindev < naudioindev) + { + for (i = nchindev; i < naudioindev; i++) + { + if (i == 0) + chindev[0] = defaultchannels; + else chindev[i] = chindev[i-1]; + } + naudioindev = nchindev; + } + } + + if (naudiooutdev == -1) + { /* not set */ + if (nchoutdev==-1) + { + nchoutdev=1; + choutdev[0]=defaultchannels; + naudiooutdev=1; + audiooutdev[0] = DEFAULTOUTDEV; + } + else + { + for (i = 0; i < MAXAUDIODEV; i++) + audiooutdev[i] = i+1; + naudiooutdev = nchoutdev; + } + } + else + { + if (nchoutdev == -1) + { + nchoutdev = naudiooutdev; + for (i = 0; i < naudiooutdev; i++) + choutdev[i] = defaultchannels; + } + else if (nchoutdev > naudiooutdev) + { + for (i = naudiooutdev; i < nchoutdev; i++) + { + if (i == 0) + audiooutdev[0] = DEFAULTOUTDEV; + else audiooutdev[i] = audiooutdev[i-1] + 1; + } + naudiooutdev = nchoutdev; + } + else if (nchoutdev < naudiooutdev) + { + for (i = nchoutdev; i < naudiooutdev; i++) + { + if (i == 0) + choutdev[0] = defaultchannels; + else choutdev[i] = choutdev[i-1]; + } + naudiooutdev = nchoutdev; + } + } + + linux_flush_all_underflows_to_zero(); +#ifdef ALSA + if (linux_whichapi == API_ALSA) + alsa_open_audio((naudioindev > 0 ? chindev[0] : 0), + (naudiooutdev > 0 ? choutdev[0] : 0), rate); + else +#endif +#ifdef RME_HAMMERFALL + if (linux_whichapi == API_RME) + rme9652_open_audio((naudioindev > 0 ? chindev[0] : 0), + (naudiooutdev > 0 ? choutdev[0] : 0), rate); + else +#endif + oss_open_audio(naudioindev, audioindev, nchindev, chindev, + naudiooutdev, audiooutdev, nchoutdev, choutdev, rate); +} + +void sys_close_audio(void) +{ + /* set timeout to avoid hanging close() call */ + + sys_setalarm(1000000); + +#ifdef ALSA + if (linux_whichapi == API_ALSA) + alsa_close_audio(); + else +#endif +#ifdef RME_HAMMERFALL + if (linux_whichapi == API_RME) + rme9652_close_audio(); + else +#endif + oss_close_audio(); + + sys_setalarm(0); +} + +void sys_open_midi(int nmidiin, int *midiinvec, + int nmidiout, int *midioutvec) +{ + linux_open_midi(nmidiin, midiinvec, nmidiout, midioutvec); +} + +void sys_close_midi( void) +{ + sys_setalarm(1000000); + linux_close_midi(); + sys_setalarm(0); +} + +int sys_send_dacs(void) +{ + if (linux_meters) + { + int i, n; + float maxsamp; + for (i = 0, n = linux_inchannels * DACBLKSIZE, maxsamp = linux_inmax; + i < n; i++) + { + float f = sys_soundin[i]; + if (f > maxsamp) maxsamp = f; + else if (-f > maxsamp) maxsamp = -f; + } + linux_inmax = maxsamp; + for (i = 0, n = linux_outchannels * DACBLKSIZE, maxsamp = linux_outmax; + i < n; i++) + { + float f = sys_soundout[i]; + if (f > maxsamp) maxsamp = f; + else if (-f > maxsamp) maxsamp = -f; + } + linux_outmax = maxsamp; + } +#ifdef ALSA + if (linux_whichapi == API_ALSA) + return alsa_send_dacs(); +#endif +#ifdef RME_HAMMERFALL + if (linux_whichapi == API_RME) + return rme9652_send_dacs(); +#endif + return oss_send_dacs(); +} + +float sys_getsr(void) +{ + return (sys_dacsr); +} + +int sys_get_outchannels(void) +{ + return (linux_outchannels); +} + +int sys_get_inchannels(void) +{ + return (linux_inchannels); +} + +void sys_audiobuf(int n) +{ + /* set the size, in milliseconds, of the audio FIFO */ + if (n < 5) n = 5; + else if (n > 5000) n = 5000; + sys_schedadvance = n * 1000; +} + +void sys_getmeters(float *inmax, float *outmax) +{ + if (inmax) + { + linux_meters = 1; + *inmax = linux_inmax; + *outmax = linux_outmax; + } + else + linux_meters = 0; + linux_inmax = linux_outmax = 0; +} + +void sys_reportidle(void) +{ +} + +void sys_set_priority(int higher) +{ + struct sched_param par; + int p1 ,p2, p3; +#ifdef _POSIX_PRIORITY_SCHEDULING + + p1 = sched_get_priority_min(SCHED_FIFO); + p2 = sched_get_priority_max(SCHED_FIFO); + p3 = (higher ? p2 - 1 : p2 - 3); + par.sched_priority = p3; + + if (sched_setscheduler(0,SCHED_FIFO,&par) != -1) + fprintf(stderr, "priority %d scheduling enabled.\n", p3); +#endif + +#ifdef _POSIX_MEMLOCK + if (mlockall(MCL_FUTURE) != -1) + fprintf(stderr, "memory locking enabled.\n"); +#endif +} + +/* ------------ linux-specific command-line flags -------------- */ + +void linux_setfrags(int n) +{ + linux_nfragment = n; + oss_blockmode = 1; +} + +void linux_setfragsize(int n) +{ + if (n < 1) + n = 1; + linux_fragsize = n; + oss_blockmode = 1; +} + +void linux_streammode( void) +{ + oss_blockmode = 0; +} + +void linux_set_sound_api(int which) +{ + linux_whichapi = which; + if (sys_verbose) + post("linux_whichapi %d", linux_whichapi); +} + +#ifdef ALSA99 +void linux_alsa_devno(int devno) +{ + alsa_devno = devno; +} + +#endif + +#ifdef ALSA01 +void linux_alsa_devname(char *devname) +{ + strncpy(alsa_devname, devname, 511); +} + +void linux_alsa_use_plugin(int t) +{ + if (t == 1) + alsa_use_plugin = 1; + else + alsa_use_plugin = 0; +} + +#endif + +/* -------------- Audio I/O using the OSS API ------------------ */ + +typedef struct _multidev { + int fd; + int channels; + int format; +} t_multidev; + +int oss_reset(int fd) { + int err; + if ((err = ioctl(fd,SNDCTL_DSP_RESET)) < 0) + error("OSS: Could not reset"); + return err; +} + + /* The AFMT_S32_BLOCKED format is not defined in standard linux kernels + but is proposed by Guenter Geiger to support extending OSS to handle + 32 bit sample. This is user in Geiger's OSS driver for RME Hammerfall. + I'm not clear why this isn't called AFMT_S32_[SLN]E... */ + +#ifndef AFMT_S32_BLOCKED +#define AFMT_S32_BLOCKED 0x0000400 +#endif + +void oss_configure(t_oss_dev *dev, int srate, int dac, int skipblocksize) +{ /* IOhannes */ + int orig, param, nblk, fd = dev->d_fd, wantformat; + int nchannels = dev->d_nchannels; + int advwas = sys_schedadvance; + + audio_buf_info ainfo; + + /* IOhannes : + * pd is very likely to crash if different formats are used on + multiple soundcards + */ + + /* set resolution - first try 4 byte samples */ + if ((ioctl(fd,SNDCTL_DSP_GETFMTS,¶m) >= 0) && + (param & AFMT_S32_BLOCKED)) + { + wantformat = AFMT_S32_BLOCKED; + dev->d_bytespersamp = 4; + } + else + { +/* FreeBSD's soundcard.h does not define AFMT_S16_NE */ + wantformat = AFMT_S16_BE; + dev->d_bytespersamp = 2; + } + param = wantformat; + + if (sys_verbose) + post("bytes per sample = %d", dev->d_bytespersamp); + if (ioctl(fd, SNDCTL_DSP_SETFMT, ¶m) == -1) + fprintf(stderr,"OSS: Could not set DSP format\n"); + else if (wantformat != param) + fprintf(stderr,"OSS: DSP format: wanted %d, got %d\n", + wantformat, param); + + /* sample rate */ + orig = param = srate; + if (ioctl(fd, SNDCTL_DSP_SPEED, ¶m) == -1) + fprintf(stderr,"OSS: Could not set sampling rate for device\n"); + else if( orig != param ) + fprintf(stderr,"OSS: sampling rate: wanted %d, got %d\n", + orig, param ); + + if (oss_blockmode && !skipblocksize) + { + int fragbytes, logfragsize, nfragment; + /* setting fragment count and size. */ + if (linux_nfragment) /* if nfrags specified, take literally */ + { + nfragment = linux_nfragment; + if (!linux_fragsize) + linux_fragsize = OSS_DEFFRAGSIZE; + sys_schedadvance = ((nfragment * linux_fragsize) * 1.e6) + / (float)srate; + linux_setsr(srate); + } + else + { + if (!linux_fragsize) + { + linux_fragsize = OSS_DEFFRAGSIZE; + while (linux_fragsize > DACBLKSIZE + && linux_fragsize * 4 > linux_advance_samples) + linux_fragsize = linux_fragsize/2; + } + /* post("adv_samples %d", linux_advance_samples); */ + nfragment = (sys_schedadvance * (44100. * 1.e-6)) / linux_fragsize; + } + fragbytes = linux_fragsize * (dev->d_bytespersamp * nchannels); + logfragsize = ilog2(fragbytes); + + if (fragbytes != (1 << logfragsize)) + post("warning: OSS takes only power of 2 blocksize; using %d", + (1 << logfragsize)/(dev->d_bytespersamp * nchannels)); + if (sys_verbose) + post("setting nfrags = %d, fragsize %d\n", nfragment, fragbytes); + + param = orig = (nfragment<<16) + logfragsize; + if (ioctl(fd,SNDCTL_DSP_SETFRAGMENT, ¶m) == -1) + error("OSS: Could not set or read fragment size\n"); + if (param != orig) + { + nfragment = ((param >> 16) & 0xffff); + logfragsize = (param & 0xffff); + post("warning: actual fragments %d, blocksize %d", + nfragment, (1 << logfragsize)); + } + if (sys_verbose) + post("audiobuffer set to %d msec", (int)(0.001 * sys_schedadvance)); + } + + if (dac) + { + /* use "free space" to learn the buffer size. Normally you + should set this to your own desired value; but this seems not + to be implemented uniformly across different sound cards. LATER + we should figure out what to do if the requested scheduler advance + is greater than this buffer size; for now, we just print something + out. */ + + int defect; + if (ioctl(fd, SOUND_PCM_GETOSPACE,&ainfo) < 0) + fprintf(stderr,"OSS: ioctl on output device failed"); + dev->d_bufsize = ainfo.bytes; + + defect = linux_advance_samples * (dev->d_bytespersamp * nchannels) + - dev->d_bufsize - OSS_XFERSIZE(nchannels, dev->d_bytespersamp); + if (defect > 0) + { + if (sys_verbose || defect > (dev->d_bufsize >> 2)) + fprintf(stderr, + "OSS: requested audio buffer size %d limited to %d\n", + linux_advance_samples * (dev->d_bytespersamp * nchannels), + dev->d_bufsize); + linux_advance_samples = + (dev->d_bufsize - OSS_XFERSAMPS(nchannels)) / + (dev->d_bytespersamp *nchannels); + } + } +} + +static int oss_setchannels(int fd, int wantchannels, char *devname) +{ /* IOhannes */ + int param = wantchannels; + + while (param>1) { + int save = param; + if (ioctl(fd, SNDCTL_DSP_CHANNELS, ¶m) == -1) { + error("OSS: SNDCTL_DSP_CHANNELS failed %s",devname); + } else { + if (param == save) return (param); + } + param=save-1; + } + + return (0); +} + +int oss_open_audio(int nindev, int *indev, int nchin, int *chin, + int noutdev, int *outdev, int nchout, int *chout, int rate) +{ /* IOhannes */ + int capabilities = 0; + int inchannels = 0, outchannels = 0; + char devname[20]; + int n, i, fd; + char buf[OSS_MAXSAMPLEWIDTH * DACBLKSIZE * OSS_MAXCHPERDEV]; + int num_devs = 0; + int wantmore=0; + int spread = 0; + audio_buf_info ainfo; + + linux_nindevs = linux_noutdevs = 0; + + /* set logical sample rate amd calculate linux_advance_samples. */ + linux_setsr(rate); + + /* mark input devices unopened */ + for (i = 0; i < OSS_MAXDEV; i++) + linux_adcs[i].d_fd = -1; + + /* open output devices */ + wantmore=0; + if (noutdev < 0 || nindev < 0) + bug("linux_open_audio"); + + for (n = 0; n < noutdev; n++) + { + int gotchans, j, inindex = -1; + int thisdevice=outdev[n]; + int wantchannels = (nchout>n) ? chout[n] : wantmore; + fd = -1; + if (!wantchannels) + goto end_out_loop; + + if (thisdevice > 1) + sprintf(devname, "/dev/dsp%d", thisdevice-1); + else sprintf(devname, "/dev/dsp"); + + /* search for input request for same device. Succeed only + if the number of channels matches. */ + for (j = 0; j < nindev; j++) + if (indev[j] == thisdevice && chin[j] == wantchannels) + inindex = j; + + /* if the same device is requested for input and output, + try to open it read/write */ + if (inindex >= 0) + { + sys_setalarm(1000000); + if ((fd = open(devname, O_RDWR)) == -1) + { + post("%s (read/write): %s", devname, strerror(errno)); + post("(now will try write-only...)"); + } + else + { + if (sys_verbose) + post("opened %s for reading and writing\n", devname); + linux_adcs[inindex].d_fd = fd; + } + } + /* if that didn't happen or if it failed, try write-only */ + if (fd == -1) + { + sys_setalarm(1000000); + if ((fd = open(devname, O_WRONLY)) == -1) + { + post("%s (writeonly): %s", + devname, strerror(errno)); + break; + } + if (sys_verbose) + post("opened %s for writing only\n", devname); + } + if (ioctl(fd, SNDCTL_DSP_GETCAPS, &capabilities) == -1) + error("OSS: SNDCTL_DSP_GETCAPS failed %s", devname); + + gotchans = oss_setchannels(fd, + (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels, + devname); + + if (sys_verbose) + post("opened audio output on %s; got %d channels", + devname, gotchans); + + if (gotchans < 2) + { + /* can't even do stereo? just give up. */ + close(fd); + } + else + { + linux_dacs[linux_noutdevs].d_nchannels = gotchans; + linux_dacs[linux_noutdevs].d_fd = fd; + oss_configure(linux_dacs+linux_noutdevs, rate, 1, 0); + + linux_noutdevs++; + outchannels += gotchans; + if (inindex >= 0) + { + linux_adcs[inindex].d_nchannels = gotchans; + chin[inindex] = gotchans; + } + } + /* LATER think about spreading large numbers of channels over + various dsp's and vice-versa */ + wantmore = wantchannels - gotchans; + end_out_loop: ; + } + + /* open input devices */ + wantmore = 0; + if (nindev==-1) + nindev=4; /* spread channels over default-devices */ + for (n = 0; n < nindev; n++) + { + int gotchans=0; + int thisdevice=indev[n]; + int wantchannels = (nchin>n)?chin[n]:wantmore; + int alreadyopened = 0; + if (!wantchannels) + goto end_in_loop; + + if (thisdevice > 1) + sprintf(devname, "/dev/dsp%d", thisdevice - 1); + else sprintf(devname, "/dev/dsp"); + + sys_setalarm(1000000); + + /* perhaps it's already open from the above? */ + if (linux_dacs[n].d_fd >= 0) + { + fd = linux_dacs[n].d_fd; + alreadyopened = 1; + } + else + { + /* otherwise try to open it here. */ + if ((fd = open(devname, O_RDONLY)) == -1) + { + post("%s (readonly): %s", devname, strerror(errno)); + goto end_in_loop; + } + if (sys_verbose) + post("opened %s for reading only\n", devname); + } + linux_adcs[linux_nindevs].d_fd = fd; + gotchans = oss_setchannels(fd, + (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels, + devname); + if (sys_verbose) + post("opened audio input device %s; got %d channels", + devname, gotchans); + + if (gotchans < 1) + { + close(fd); + goto end_in_loop; + } + + linux_adcs[linux_nindevs].d_nchannels = gotchans; + + oss_configure(linux_adcs+linux_nindevs, rate, 0, alreadyopened); + + inchannels += gotchans; + linux_nindevs++; + + wantmore = wantchannels-gotchans; + /* LATER think about spreading large numbers of channels over + various dsp's and vice-versa */ + end_in_loop: ; + } + + linux_setch(inchannels, outchannels); + + /* We have to do a read to start the engine. This is + necessary because sys_send_dacs waits until the input + buffer is filled and only reads on a filled buffer. + This is good, because it's a way to make sure that we + will not block. But I wonder why we only have to read + from one of the devices and not all of them??? */ + + if (linux_nindevs) + { + if (sys_verbose) + fprintf(stderr,("OSS: issuing first ADC 'read' ... ")); + read(linux_adcs[0].d_fd, buf, + linux_adcs[0].d_bytespersamp * + linux_adcs[0].d_nchannels * DACBLKSIZE); + if (sys_verbose) + fprintf(stderr, "...done.\n"); + } + sys_setalarm(0); + return (0); +} + +void oss_close_audio( void) +{ + int i; + for (i=0;i + OSS_XFERSIZE(linux_adcs[dev].d_nchannels, + linux_adcs[dev].d_bytespersamp)) + { + linux_adcs_read(linux_adcs[dev].d_fd, buf, + OSS_XFERSIZE(linux_adcs[dev].d_nchannels, + linux_adcs[dev].d_bytespersamp)); + if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE, &ainfo) < 0) + { + fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed", + dev, linux_adcs[dev].d_fd); + break; + } + linux_adcs[dev].d_space = ainfo.bytes; + } + } + + /* 2. if any output devices are behind, feed them zeros to catch them + up */ + for (dev = 0; dev < linux_noutdevs; dev++) + { + while (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize - + linux_advance_samples * (linux_dacs[dev].d_nchannels * + linux_dacs[dev].d_bytespersamp)) + { + if (!zeroed) + { + unsigned int i; + for (i = 0; i < OSS_XFERSAMPS(linux_dacs[dev].d_nchannels); + i++) + buf[i] = 0; + zeroed = 1; + } + linux_dacs_write(linux_dacs[dev].d_fd, buf, + OSS_XFERSIZE(linux_dacs[dev].d_nchannels, + linux_dacs[dev].d_bytespersamp)); + if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0) + { + fprintf(stderr, "OSS: ioctl on output device %d, fd %d failed", + dev, linux_dacs[dev].d_fd); + break; + } + linux_dacs[dev].d_space = ainfo.bytes; + } + } + /* 3. if any DAC devices are too far ahead, plan to drop the + number of frames which will let the others catch up. */ + for (dev = 0; dev < linux_noutdevs; dev++) + { + if (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize - + (linux_advance_samples - 1) * linux_dacs[dev].d_nchannels * + linux_dacs[dev].d_bytespersamp) + { + linux_dacs[dev].d_dropcount = linux_advance_samples - 1 - + (linux_dacs[dev].d_space - linux_dacs[dev].d_bufsize) / + (linux_dacs[dev].d_nchannels * + linux_dacs[dev].d_bytespersamp) ; + } + else linux_dacs[dev].d_dropcount = 0; + } +} + +int oss_send_dacs(void) +{ + float *fp1, *fp2; + long fill; + int i, j, dev, rtnval = SENDDACS_YES; + char buf[OSS_MAXSAMPLEWIDTH * DACBLKSIZE * OSS_MAXCHPERDEV]; + t_oss_int16 *sp; + t_oss_int32 *lp; + /* the maximum number of samples we should have in the ADC buffer */ + int idle = 0; + int thischan; + double timeref, timenow; + + if (!linux_nindevs && !linux_noutdevs) + return (SENDDACS_NO); + + if (!oss_blockmode) + { + /* determine whether we're idle. This is true if either (1) + some input device has less than one buffer to read or (2) some + output device has fewer than (linux_advance_samples) blocks buffered + already. */ + oss_calcspace(); + + for (dev=0; dev < linux_noutdevs; dev++) + if (linux_dacs[dev].d_dropcount || + (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space > + linux_advance_samples * linux_dacs[dev].d_bytespersamp * + linux_dacs[dev].d_nchannels)) + idle = 1; + for (dev=0; dev < linux_nindevs; dev++) + if (linux_adcs[dev].d_space < + OSS_XFERSIZE(linux_adcs[dev].d_nchannels, + linux_adcs[dev].d_bytespersamp)) + idle = 1; + } + + if (idle && !oss_blockmode) + { + /* sometimes---rarely---when the ADC available-byte-count is + zero, it's genuine, but usually it's because we're so + late that the ADC has overrun its entire kernel buffer. We + distinguish between the two by waiting 2 msec and asking again. + There should be an error flag we could check instead; look for this + someday... */ + for (dev = 0;dev < linux_nindevs; dev++) + if (linux_adcs[dev].d_space == 0) + { + audio_buf_info ainfo; + sys_microsleep(2000); + oss_calcspace(); + if (linux_adcs[dev].d_space != 0) continue; + + /* here's the bad case. Give up and resync. */ + sys_log_error(ERR_DATALATE); + oss_doresync(); + return (SENDDACS_NO); + } + /* check for slippage between devices, either because + data got lost in the driver from a previous late condition, or + because the devices aren't synced. When we're idle, no + input device should have more than one buffer readable and + no output device should have less than linux_advance_samples-1 + */ + + for (dev=0; dev < linux_noutdevs; dev++) + if (!linux_dacs[dev].d_dropcount && + (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space < + (linux_advance_samples - 2) * + (linux_dacs[dev].d_bytespersamp * + linux_dacs[dev].d_nchannels))) + goto badsync; + for (dev=0; dev < linux_nindevs; dev++) + if (linux_adcs[dev].d_space > 3 * + OSS_XFERSIZE(linux_adcs[dev].d_nchannels, + linux_adcs[dev].d_bytespersamp)) + goto badsync; + + /* return zero to tell the scheduler we're idle. */ + return (SENDDACS_NO); + badsync: + sys_log_error(ERR_RESYNC); + oss_doresync(); + return (SENDDACS_NO); + + } + + /* do output */ + + timeref = sys_getrealtime(); + for (dev=0, thischan = 0; dev < linux_noutdevs; dev++) + { + int nchannels = linux_dacs[dev].d_nchannels; + if (linux_dacs[dev].d_dropcount) + linux_dacs[dev].d_dropcount--; + else + { + if (linux_dacs[dev].d_bytespersamp == 4) + { + for (i = DACBLKSIZE * nchannels, fp1 = sys_soundout + + DACBLKSIZE*thischan, + lp = (t_oss_int32 *)buf; i--; fp1++, lp++) + { + float f = *fp1 * 2147483648.; + *lp = (f >= 2147483647. ? 2147483647. : + (f < -2147483648. ? -2147483648. : f)); + } + } + else + { + for (i = DACBLKSIZE, fp1 = sys_soundout + + DACBLKSIZE*thischan, + sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) + { + for (j=0, fp2 = fp1; j 32767) s = 32767; + else if (s < -32767) s = -32767; + sp[j] = s; + } + } + } + linux_dacs_write(linux_dacs[dev].d_fd, buf, + OSS_XFERSIZE(nchannels, linux_dacs[dev].d_bytespersamp)); + if ((timenow = sys_getrealtime()) - timeref > 0.002) + { + if (!oss_blockmode) + sys_log_error(ERR_DACSLEPT); + else rtnval = SENDDACS_SLEPT; + } + timeref = timenow; + } + thischan += nchannels; + } + memset(sys_soundout, 0, + linux_outchannels * (sizeof(float) * DACBLKSIZE)); + + /* do input */ + + for (dev = 0, thischan = 0; dev < linux_nindevs; dev++) + { + int nchannels = linux_adcs[dev].d_nchannels; + linux_adcs_read(linux_adcs[dev].d_fd, buf, + OSS_XFERSIZE(nchannels, linux_adcs[dev].d_bytespersamp)); + + if ((timenow = sys_getrealtime()) - timeref > 0.002) + { + if (!oss_blockmode) + sys_log_error(ERR_ADCSLEPT); + else + rtnval = SENDDACS_SLEPT; + } + timeref = timenow; + + if (linux_adcs[dev].d_bytespersamp == 4) + { + for (i = DACBLKSIZE*nchannels, + fp1 = sys_soundin + thischan*DACBLKSIZE, + lp = (t_oss_int32 *)buf; i--; fp1++, lp++) + { + *fp1 = ((float)(*lp))*(float)(1./2147483648.); + } + } + else + { + for (i = DACBLKSIZE,fp1 = sys_soundin + thischan*DACBLKSIZE, + sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) + { + for (j=0;j channelinfo.max_voices) + post("decreasing input channels to maximum of %d\n", + wantinchans = channelinfo.max_voices); + if (alsa_samplewidth == 4 && + !(channelinfo.formats & (1< channelinfo.max_voices) + post("decreasing output channels to maximum of %d\n", + wantoutchans = channelinfo.max_voices); + if (alsa_samplewidth == 4 && + !(channelinfo.formats & (1< linux_outchannels ? linux_inchannels : + linux_outchannels) * DACBLKSIZE; + alsa_buf = malloc(bsize); + if (!alsa_buf) + return (1); + memset(alsa_buf, 0, bsize); + return 0; +} + +void alsa_set_params(t_alsa_dev *dev, int dir, int rate, int voices) +{ + int err; + struct snd_pcm_channel_params params; + + memset(&dev->info, 0, sizeof(dev->info)); + dev->info.channel = dir; + if ((err = snd_pcm_channel_info(dev->handle, &dev->info) < 0)) + { + fprintf(stderr, "PD-ALSA: error getting channel info: %s\n", + snd_strerror(err)); + } + memset(¶ms, 0, sizeof(params)); + params.format.interleave = 1; /* may do non-interleaved later */ + /* format is 2 or 4 bytes per sample depending on what was possible */ + params.format.format = + (alsa_samplewidth == 4 ? SND_PCM_SFMT_S32_LE : SND_PCM_SFMT_S16_LE); + + /*will check this further down -just try for now*/ + params.format.rate = rate; + params.format.voices = voices; + params.start_mode = SND_PCM_START_GO; /* seems most reliable */ + /*do not stop at overrun/underrun*/ + params.stop_mode = SND_PCM_STOP_ROLLOVER; + + params.channel = dir; /* playback|capture */ + params.buf.stream.queue_size = + (ALSA_EXTRABUFFER + linux_advance_samples) + * alsa_samplewidth * voices; + params.buf.stream.fill = SND_PCM_FILL_SILENCE_WHOLE; + params.mode = SND_PCM_MODE_STREAM; + + if ((err = snd_pcm_channel_params(dev->handle, ¶ms)) < 0) + { + printf("PD-ALSA: error setting parameters %s", snd_strerror(err)); + } + + /* This should clear the buffers but does not. There is often noise at + startup that sounds like crap left in the buffers - maybe in the lib + instead of the driver? Some solution needs to be found. + */ + + if ((err = snd_pcm_channel_prepare(dev->handle, dir)) < 0) + { + printf("PD-ALSA: error preparing channel %s", snd_strerror(err)); + } + dev->setup.channel = dir; + + if ((err = snd_pcm_channel_setup(dev->handle, &dev->setup)) < 0) + { + printf("PD-ALSA: error getting setup %s", snd_strerror(err)); + } + /* for some reason, if you don't writesomething before starting the + converters we get trash on startup */ + if (dir == SND_PCM_CHANNEL_PLAYBACK) + { + char foo[1024]; + int xxx = 1024 - (1024 % (linux_outchannels * alsa_samplewidth)); + int i, r; + for (i = 0; i < xxx; i++) + foo[i] = 0; + if ((r = snd_pcm_write(dev->handle, foo, xxx)) < xxx) + fprintf(stderr, "alsa_write: %s\n", snd_strerror(errno)); + } + snd_pcm_channel_go(dev->handle, dir); +} + +void alsa_close_audio(void) +{ + int i; + for(i = 0; i < n_alsa_dev; i++) + snd_pcm_close(alsa_device[i].handle); +} + +/* #define DEBUG_ALSA_XFER */ + +int alsa_send_dacs(void) +{ + static int16_t *sp; + t_sample *fp, *fp1, *fp2; + int i, j, k, err, devno = 0; + int inputcount = 0, outputcount = 0, inputlate = 0, outputlate = 0; + int result; + snd_pcm_channel_status_t stat; + static int callno = 0; + static int xferno = 0; + int countwas = 0; + double timelast; + static double timenow; + int inchannels = linux_inchannels; + int outchannels = linux_outchannels; + int inbytesperframe = inchannels * alsa_samplewidth; + int outbytesperframe = outchannels * alsa_samplewidth; + int intransfersize = DACBLKSIZE * inbytesperframe; + int outtransfersize = DACBLKSIZE * outbytesperframe; + int alsaerror; + int loggederror = 0; + + if (!inchannels && !outchannels) + return (SENDDACS_NO); + timelast = timenow; + timenow = sys_getrealtime(); + +#ifdef DEBUG_ALSA_XFER + if (timenow - timelast > 0.050) + fprintf(stderr, "(%d)", + (int)(1000 * (timenow - timelast))), fflush(stderr); +#endif + + callno++; + /* get input and output channel status */ + if (inchannels > 0) + { + devno = 0; + stat.channel = SND_PCM_CHANNEL_CAPTURE; + if (alsaerror = snd_pcm_channel_status(alsa_device[devno].handle, + &stat)) + { + fprintf(stderr, "snd_pcm_channel_status (input): %s\n", + snd_strerror(alsaerror)); + return (SENDDACS_NO); + } + inputcount = stat.count; + inputlate = (stat.underrun > 0 || stat.overrun > 0); + } + if (outchannels > 0) + { + devno = 0; + stat.channel = SND_PCM_CHANNEL_PLAYBACK; + if (alsaerror = snd_pcm_channel_status(alsa_device[devno].handle, + &stat)) + { + fprintf(stderr, "snd_pcm_channel_status (output): %s\n", + snd_strerror(alsaerror)); + return (SENDDACS_NO); + } + outputcount = stat.count; + outputlate = (stat.underrun > 0 || stat.overrun > 0); + } + + /* check if input not ready */ + if (inputcount < intransfersize) + { + /* fprintf(stderr, "no adc; count %d, free %d, call %d, xfer %d\n", + stat.count, + stat.free, + callno, xferno); */ + if (outchannels > 0) + { + /* if there's no input but output is hungry, feed output. */ + while (outputcount < (linux_advance_samples + ALSA_JITTER) + * outbytesperframe) + { + if (!loggederror) + sys_log_error(ERR_RESYNC), loggederror = 1; + memset(alsa_buf, 0, outtransfersize); + result = snd_pcm_write(alsa_device[devno].handle, + alsa_buf, outtransfersize); + if (result < outtransfersize) + { +#ifdef DEBUG_ALSA_XFER + if (result >= 0 || errno == EAGAIN) + fprintf(stderr, "ALSA: write returned %d of %d\n", + result, outtransfersize); + else fprintf(stderr, "ALSA: write: %s\n", + snd_strerror(errno)); + fprintf(stderr, + "inputcount %d, outputcount %d, outbufsize %d\n", + inputcount, outputcount, + (ALSA_EXTRABUFFER + linux_advance_samples) + * alsa_samplewidth * outchannels); +#endif + return (SENDDACS_NO); + } + stat.channel = SND_PCM_CHANNEL_PLAYBACK; + if (alsaerror = + snd_pcm_channel_status(alsa_device[devno].handle, + &stat)) + { + fprintf(stderr, "snd_pcm_channel_status (output): %s\n", + snd_strerror(alsaerror)); + return (SENDDACS_NO); + } + outputcount = stat.count; + } + } + + return SENDDACS_NO; + } + + /* if output buffer has at least linux_advance_samples in it, we're + not ready for this batch. */ + if (outputcount > linux_advance_samples * outbytesperframe) + { + if (inchannels > 0) + { + while (inputcount > (DACBLKSIZE + ALSA_JITTER) * outbytesperframe) + { + if (!loggederror) + sys_log_error(ERR_RESYNC), loggederror = 1; + devno = 0; + result = snd_pcm_read(alsa_device[devno].handle, alsa_buf, + intransfersize); + if (result < intransfersize) + { +#ifdef DEBUG_ALSA_XFER + if (result < 0) + fprintf(stderr, + "snd_pcm_read %d %d: %s\n", + callno, xferno, snd_strerror(errno)); + else fprintf(stderr, + "snd_pcm_read %d %d returned only %d\n", + callno, xferno, result); + fprintf(stderr, + "inputcount %d, outputcount %d, inbufsize %d\n", + inputcount, outputcount, + (ALSA_EXTRABUFFER + linux_advance_samples) + * alsa_samplewidth * inchannels); +#endif + return (SENDDACS_NO); + } + devno = 0; + stat.channel = SND_PCM_CHANNEL_CAPTURE; + if (alsaerror = + snd_pcm_channel_status(alsa_device[devno].handle, + &stat)) + { + fprintf(stderr, "snd_pcm_channel_status (input): %s\n", + snd_strerror(alsaerror)); + return (SENDDACS_NO); + } + inputcount = stat.count; + inputlate = (stat.underrun > 0 || stat.overrun > 0); + } + return (SENDDACS_NO); + } + } + if (sys_getrealtime() - timenow > 0.002) + { +#ifdef DEBUG_ALSA_XFER + fprintf(stderr, "check %d took %d msec\n", + callno, (int)(1000 * (timenow - timelast))), fflush(stderr); +#endif + sys_log_error(ERR_DACSLEPT); + timenow = sys_getrealtime(); + } + if (inputlate || outputlate) + sys_log_error(ERR_DATALATE); + + /* do output */ + /* this "for" loop won't work for more than one device. */ + for (devno = 0, fp = sys_soundout; devno < (outchannels > 0); devno++, + fp += 128) + { + if (alsa_samplewidth == 4) + { + for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; + j += outchannels, fp2++) + { + float s1 = *fp2 * INT32_MAX; + ((t_alsa_sample32 *)alsa_buf)[j] = CLIP32(s1); + } + } + } + else + { + for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; + j += outchannels, fp2++) + { + int s = *fp2 * 32767.; + if (s > 32767) + s = 32767; + else if (s < -32767) + s = -32767; + ((t_alsa_sample16 *)alsa_buf)[j] = s; + } + } + } + + result = snd_pcm_write(alsa_device[devno].handle, alsa_buf, + outtransfersize); + if (result < outtransfersize) + { +#ifdef DEBUG_ALSA_XFER + if (result >= 0 || errno == EAGAIN) + fprintf(stderr, "ALSA: write returned %d of %d\n", + result, outtransfersize); + else fprintf(stderr, "ALSA: write: %s\n", + snd_strerror(errno)); + fprintf(stderr, + "inputcount %d, outputcount %d, outbufsize %d\n", + inputcount, outputcount, + (ALSA_EXTRABUFFER + linux_advance_samples) + * alsa_samplewidth * outchannels); +#endif + sys_log_error(ERR_DACSLEPT); + return (SENDDACS_NO); + } + } + /* zero out the output buffer */ + memset(sys_soundout, 0, DACBLKSIZE * sizeof(*sys_soundout) * + linux_outchannels); + if (sys_getrealtime() - timenow > 0.002) + { +#if DEBUG_ALSA_XFER + fprintf(stderr, "output %d took %d msec\n", + callno, (int)(1000 * (timenow - timelast))), fflush(stderr); +#endif + timenow = sys_getrealtime(); + sys_log_error(ERR_DACSLEPT); + } + + /* do input */ + for (devno = 0, fp = sys_soundin; devno < (linux_inchannels > 0); devno++, + fp += 128) + { + result = snd_pcm_read(alsa_device[devno].handle, alsa_buf, + intransfersize); + if (result < intransfersize) + { +#ifdef DEBUG_ALSA_XFER + if (result < 0) + fprintf(stderr, + "snd_pcm_read %d %d: %s\n", + callno, xferno, snd_strerror(errno)); + else fprintf(stderr, + "snd_pcm_read %d %d returned only %d\n", + callno, xferno, result); + fprintf(stderr, + "inputcount %d, outputcount %d, inbufsize %d\n", + inputcount, outputcount, + (ALSA_EXTRABUFFER + linux_advance_samples) + * alsa_samplewidth * inchannels); +#endif + sys_log_error(ERR_ADCSLEPT); + return (SENDDACS_NO); + } + if (alsa_samplewidth == 4) + { + for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; + j += inchannels, fp2++) + *fp2 = (float) ((t_alsa_sample32 *)alsa_buf)[j] + * (1./ INT32_MAX); + } + } + else + { + for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; j += inchannels, fp2++) + *fp2 = (float) ((t_alsa_sample16 *)alsa_buf)[j] + * 3.051850e-05; + } + } + } + xferno++; + if (sys_getrealtime() - timenow > 0.002) + { +#ifdef DEBUG_ALSA_XFER + fprintf(stderr, "routine took %d msec\n", + (int)(1000 * (sys_getrealtime() - timenow))); +#endif + sys_log_error(ERR_ADCSLEPT); + } + return SENDDACS_YES; +} + +#endif /* ALSA99 */ + +/* support for ALSA pcmv2 api by Karl MacMillan */ + +#ifdef ALSA01 + +static void check_error(int err, const char *why) +{ + if (err < 0) + fprintf(stderr, "%s: %s\n", why, snd_strerror(err)); +} + +static int alsa_open_audio(int wantinchans, int wantoutchans, int srate) +{ + int err, inchans = 0, outchans = 0, subunitdir; + char devname[512]; + snd_pcm_hw_params_t* hw_params; + snd_pcm_sw_params_t* sw_params; + snd_output_t* out; + int frag_size = (linux_fragsize ? linux_fragsize : ALSA_DEFFRAGSIZE); + int nfrags, i; + short* tmp_buf; + unsigned int tmp_uint; + int advwas = sys_schedadvance; + + if (linux_nfragment) + { + nfrags = linux_nfragment; + sys_schedadvance = (frag_size * linux_nfragment * 1.0e6) / srate; + } + else nfrags = sys_schedadvance * (float)srate / (1e6 * frag_size); + + if (sys_verbose || (sys_schedadvance != advwas)) + post("audio buffer set to %d", (int)(0.001 * sys_schedadvance)); + if (wantinchans || wantoutchans) + alsa_checkversion(); + if (wantinchans) + { + err = snd_pcm_open(&alsa_device.inhandle, alsa_devname, + SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); + + check_error(err, "snd_pcm_open (input)"); + if (err < 0) + inchans = 0; + else + { + inchans = wantinchans; + snd_pcm_nonblock(alsa_device.inhandle, 1); + } + } + if (wantoutchans) + { + err = snd_pcm_open(&alsa_device.outhandle, alsa_devname, + SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + + check_error(err, "snd_pcm_open (output)"); + if (err < 0) + outchans = 0; + else + { + outchans = wantoutchans; + snd_pcm_nonblock(alsa_device.outhandle, 1); + } + } + if (inchans) + { + if (sys_verbose) + post("opening sound input..."); + err = snd_pcm_hw_params_malloc(&hw_params); + check_error(err, "snd_pcm_hw_params_malloc (input)"); + + // get the default params + err = snd_pcm_hw_params_any(alsa_device.inhandle, hw_params); + check_error(err, "snd_pcm_hw_params_any (input)"); + // set interleaved access - FIXME deal with other access types + err = snd_pcm_hw_params_set_access(alsa_device.inhandle, hw_params, + SND_PCM_ACCESS_RW_INTERLEAVED); + check_error(err, "snd_pcm_hw_params_set_access (input)"); + // Try to set 32 bit format first + err = snd_pcm_hw_params_set_format(alsa_device.inhandle, hw_params, + SND_PCM_FORMAT_S32); + if (err < 0) + { + /* fprintf(stderr, + "PD-ALSA: 32 bit format not available - using 16\n"); */ + err = snd_pcm_hw_params_set_format(alsa_device.inhandle, hw_params, + SND_PCM_FORMAT_S16); + check_error(err, "snd_pcm_hw_params_set_format (input)"); + alsa_samplewidth = 2; + } + else + { + alsa_samplewidth = 4; + } + post("Sample width set to %d bytes", alsa_samplewidth); + // set the subformat + err = snd_pcm_hw_params_set_subformat(alsa_device.inhandle, hw_params, + SND_PCM_SUBFORMAT_STD); + check_error(err, "snd_pcm_hw_params_set_subformat (input)"); + // set the number of channels + tmp_uint = inchans; + err = snd_pcm_hw_params_set_channels_min(alsa_device.inhandle, + hw_params, &tmp_uint); + check_error(err, "snd_pcm_hw_params_set_channels (input)"); + if (tmp_uint != (unsigned)inchans) + post("ALSA: set input channels to %d", tmp_uint); + inchans = tmp_uint; + // set the sampling rate + err = snd_pcm_hw_params_set_rate_min(alsa_device.inhandle, hw_params, + &srate, 0); + check_error(err, "snd_pcm_hw_params_set_rate_min (input)"); +#if 0 + err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir); + post("input sample rate %d", err); +#endif + // set the period - ie frag size + // post("fragsize a %d", frag_size); + + /* LATER try this to get a recommended period size... + right now, it trips an assertion failure in ALSA lib */ +#if 0 + post("input period was %d, min %d, max %d\n", + snd_pcm_hw_params_get_period_size(hw_params, 0), + snd_pcm_hw_params_get_period_size_min(hw_params, 0), + snd_pcm_hw_params_get_period_size_max(hw_params, 0)); +#endif + err = snd_pcm_hw_params_set_period_size_near(alsa_device.inhandle, + hw_params, + (snd_pcm_uframes_t) + frag_size, 0); + check_error(err, "snd_pcm_hw_params_set_period_size_near (input)"); + // post("fragsize b %d", frag_size); + // set the number of periods - ie numfrags + // post("nfrags a %d", nfrags); + err = snd_pcm_hw_params_set_periods_near(alsa_device.inhandle, + hw_params, nfrags, 0); + check_error(err, "snd_pcm_hw_params_set_periods_near (input)"); + // set the buffer size + err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.inhandle, + hw_params, nfrags * frag_size); + check_error(err, "snd_pcm_hw_params_set_buffer_size_near (input)"); + + err = snd_pcm_hw_params(alsa_device.inhandle, hw_params); + check_error(err, "snd_pcm_hw_params (input)"); + + snd_pcm_hw_params_free(hw_params); + + err = snd_pcm_sw_params_malloc(&sw_params); + check_error(err, "snd_pcm_sw_params_malloc (input)"); + err = snd_pcm_sw_params_current(alsa_device.inhandle, sw_params); + check_error(err, "snd_pcm_sw_params_current (input)"); +#if 1 + err = snd_pcm_sw_params_set_start_mode(alsa_device.inhandle, sw_params, + SND_PCM_START_EXPLICIT); + check_error(err, "snd_pcm_sw_params_set_start_mode (input)"); + err = snd_pcm_sw_params_set_xrun_mode(alsa_device.inhandle, sw_params, + SND_PCM_XRUN_NONE); + check_error(err, "snd_pcm_sw_params_set_xrun_mode (input)"); +#else + err = snd_pcm_sw_params_set_start_threshold(alsa_device.inhandle, + sw_params, nfrags * frag_size); + check_error(err, "snd_pcm_sw_params_set_start_threshold (input)"); + err = snd_pcm_sw_params_set_stop_threshold(alsa_device.inhandle, + sw_params, 1); + check_error(err, "snd_pcm_sw_params_set_stop_threshold (input)"); +#endif + + err = snd_pcm_sw_params_set_avail_min(alsa_device.inhandle, sw_params, + frag_size); + check_error(err, "snd_pcm_sw_params_set_avail_min (input)"); + err = snd_pcm_sw_params(alsa_device.inhandle, sw_params); + check_error(err, "snd_pcm_sw_params (input)"); + + snd_pcm_sw_params_free(sw_params); + + snd_output_stdio_attach(&out, stderr, 0); +#if 0 + if (sys_verbose) + { + snd_pcm_dump_hw_setup(alsa_device.inhandle, out); + snd_pcm_dump_sw_setup(alsa_device.inhandle, out); + } +#endif + } + + if (outchans) + { + int foo; + if (sys_verbose) + post("opening sound output..."); + err = snd_pcm_hw_params_malloc(&hw_params); + check_error(err, "snd_pcm_sw_params (output)"); + + // get the default params + err = snd_pcm_hw_params_any(alsa_device.outhandle, hw_params); + check_error(err, "snd_pcm_hw_params_any (output)"); + // set interleaved access - FIXME deal with other access types + err = snd_pcm_hw_params_set_access(alsa_device.outhandle, hw_params, + SND_PCM_ACCESS_RW_INTERLEAVED); + check_error(err, "snd_pcm_hw_params_set_access (output)"); + // Try to set 32 bit format first + err = snd_pcm_hw_params_set_format(alsa_device.outhandle, hw_params, + SND_PCM_FORMAT_S32); + if (err < 0) + { + err = snd_pcm_hw_params_set_format(alsa_device.outhandle, + hw_params,SND_PCM_FORMAT_S16); + check_error(err, "snd_pcm_hw_params_set_format (output)"); + /* fprintf(stderr, + "PD-ALSA: 32 bit format not available - using 16\n"); */ + alsa_samplewidth = 2; + } + else + { + alsa_samplewidth = 4; + } + // set the subformat + err = snd_pcm_hw_params_set_subformat(alsa_device.outhandle, hw_params, + SND_PCM_SUBFORMAT_STD); + check_error(err, "snd_pcm_hw_params_set_subformat (output)"); + // set the number of channels + tmp_uint = outchans; + err = snd_pcm_hw_params_set_channels_min(alsa_device.outhandle, + hw_params, &tmp_uint); + check_error(err, "snd_pcm_hw_params_set_channels (output)"); + if (tmp_uint != (unsigned)outchans) + post("alsa: set output channels to %d", tmp_uint); + outchans = tmp_uint; + // set the sampling rate + err = snd_pcm_hw_params_set_rate_min(alsa_device.outhandle, hw_params, + &srate, 0); + check_error(err, "snd_pcm_hw_params_set_rate_min (output)"); +#if 0 + err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir); + post("output sample rate %d", err); +#endif + // set the period - ie frag size +#if 0 + post("output period was %d, min %d, max %d\n", + snd_pcm_hw_params_get_period_size(hw_params, 0), + snd_pcm_hw_params_get_period_size_min(hw_params, 0), + snd_pcm_hw_params_get_period_size_max(hw_params, 0)); +#endif + // post("fragsize c %d", frag_size); + err = snd_pcm_hw_params_set_period_size_near(alsa_device.outhandle, + hw_params, + (snd_pcm_uframes_t) + frag_size, 0); + // post("fragsize d %d", frag_size); + check_error(err, "snd_pcm_hw_params_set_period_size_near (output)"); + // set the number of periods - ie numfrags + err = snd_pcm_hw_params_set_periods_near(alsa_device.outhandle, + hw_params, nfrags, 0); + check_error(err, "snd_pcm_hw_params_set_periods_near (output)"); + // set the buffer size + err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.outhandle, + hw_params, nfrags * frag_size); + + check_error(err, "snd_pcm_hw_params_set_buffer_size_near (output)"); + + err = snd_pcm_hw_params(alsa_device.outhandle, hw_params); + check_error(err, "snd_pcm_hw_params (output)"); + + snd_pcm_hw_params_free(hw_params); + + err = snd_pcm_sw_params_malloc(&sw_params); + check_error(err, "snd_pcm_sw_params_malloc (output)"); + err = snd_pcm_sw_params_current(alsa_device.outhandle, sw_params); + check_error(err, "snd_pcm_sw_params_current (output)"); +#if 1 + err = snd_pcm_sw_params_set_start_mode(alsa_device.outhandle, + sw_params, + SND_PCM_START_EXPLICIT); + check_error(err, "snd_pcm_sw_params_set_start_mode (output)"); + err = snd_pcm_sw_params_set_xrun_mode(alsa_device.outhandle, sw_params, + SND_PCM_XRUN_NONE); + check_error(err, "snd_pcm_sw_params_set_xrun_mode (output)"); +#else + err = snd_pcm_sw_params_set_start_threshold(alsa_device.inhandle, + sw_params, nfrags * frag_size); + check_error(err, "snd_pcm_sw_params_set_start_threshold (output)"); + err = snd_pcm_sw_params_set_stop_threshold(alsa_device.inhandle, + sw_params, 1); + check_error(err, "snd_pcm_sw_params_set_stop_threshold (output)"); +#endif + + err = snd_pcm_sw_params_set_avail_min(alsa_device.outhandle, sw_params, + frag_size); + check_error(err, "snd_pcm_sw_params_set_avail_min (output)"); + err = snd_pcm_sw_params(alsa_device.outhandle, sw_params); + check_error(err, "snd_pcm_sw_params (output)"); + + snd_pcm_sw_params_free(sw_params); + + snd_output_stdio_attach(&out, stderr, 0); +#if 0 + if (sys_verbose) + { + snd_pcm_dump_hw_setup(alsa_device.outhandle, out); + snd_pcm_dump_sw_setup(alsa_device.outhandle, out); + } +#endif + } + + linux_setsr(srate); + linux_setch(inchans, outchans); + + if (inchans) + snd_pcm_prepare(alsa_device.inhandle); + if (outchans) + snd_pcm_prepare(alsa_device.outhandle); + + // if duplex we can link the channels so they start together + if (inchans && outchans) + snd_pcm_link(alsa_device.inhandle, alsa_device.outhandle); + + // set up the buffer + if (outchans > inchans) + alsa_buf = (short *)calloc(sizeof(char) * alsa_samplewidth, DACBLKSIZE + * outchans); + else + alsa_buf = (short *)calloc(sizeof(char) * alsa_samplewidth, DACBLKSIZE + * inchans); + // fill the buffer with silence + if (outchans) + { + i = nfrags + 1; + while (i--) + snd_pcm_writei(alsa_device.outhandle, alsa_buf, frag_size); + } + + // set up the status variables + err = snd_pcm_status_malloc(&in_status); + check_error(err, "snd_pcm_status_malloc"); + err = snd_pcm_status_malloc(&out_status); + check_error(err, "snd_pcm_status_malloc"); + + // start the device +#if 1 + if (outchans) + { + err = snd_pcm_start(alsa_device.outhandle); + check_error(err, "snd_pcm_start"); + } + else if (inchans) + { + err = snd_pcm_start(alsa_device.inhandle); + check_error(err, "snd_pcm_start"); + } +#endif + + return 0; +} + +void alsa_close_audio(void) +{ + int err; + if (linux_inchannels) + { + err = snd_pcm_close(alsa_device.inhandle); + check_error(err, "snd_pcm_close (input)"); + } + if (linux_outchannels) + { + err = snd_pcm_close(alsa_device.outhandle); + check_error(err, "snd_pcm_close (output)"); + } +} + +// #define DEBUG_ALSA_XFER + +int alsa_send_dacs(void) +{ + static int16_t *sp; + static int xferno = 0; + static int callno = 0; + static double timenow; + double timelast; + t_sample *fp, *fp1, *fp2; + int i, j, k, err, devno = 0; + int inputcount = 0, outputcount = 0, inputlate = 0, outputlate = 0; + int result; + int inchannels = linux_inchannels; + int outchannels = linux_outchannels; + unsigned int intransfersize = DACBLKSIZE; + unsigned int outtransfersize = DACBLKSIZE; + + // get the status + if (!inchannels && !outchannels) + { + return SENDDACS_NO; + } + + timelast = timenow; + timenow = sys_getrealtime(); + +#ifdef DEBUG_ALSA_XFER + if (timenow - timelast > 0.050) + fprintf(stderr, "(%d)", + (int)(1000 * (timenow - timelast))), fflush(stderr); +#endif + + callno++; + + if (inchannels) + { + snd_pcm_status(alsa_device.inhandle, in_status); + if (snd_pcm_status_get_avail(in_status) < intransfersize) + return SENDDACS_NO; + } + if (outchannels) + { + snd_pcm_status(alsa_device.outhandle, out_status); + if (snd_pcm_status_get_avail(out_status) < outtransfersize) + return SENDDACS_NO; + } + + /* do output */ + if (outchannels) + { + fp = sys_soundout; + if (alsa_samplewidth == 4) + { + for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; + j += outchannels, fp2++) + { + float s1 = *fp2 * INT32_MAX; + ((t_alsa_sample32 *)alsa_buf)[j] = CLIP32(s1); + } + } + } + else + { + for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; + j += outchannels, fp2++) + { + int s = *fp2 * 32767.; + if (s > 32767) + s = 32767; + else if (s < -32767) + s = -32767; + ((t_alsa_sample16 *)alsa_buf)[j] = s; + } + } + } + + result = snd_pcm_writei(alsa_device.outhandle, alsa_buf, + outtransfersize); + if (result != (int)outtransfersize) + { + #ifdef DEBUG_ALSA_XFER + if (result >= 0 || errno == EAGAIN) + fprintf(stderr, "ALSA: write returned %d of %d\n", + result, outtransfersize); + else fprintf(stderr, "ALSA: write: %s\n", + snd_strerror(errno)); + fprintf(stderr, + "inputcount %d, outputcount %d, outbufsize %d\n", + inputcount, outputcount, + (ALSA_EXTRABUFFER + linux_advance_samples) + * alsa_samplewidth * outchannels); + #endif + sys_log_error(ERR_DACSLEPT); + return (SENDDACS_NO); + } + + /* zero out the output buffer */ + memset(sys_soundout, 0, DACBLKSIZE * sizeof(*sys_soundout) * + linux_outchannels); + if (sys_getrealtime() - timenow > 0.002) + { + #ifdef DEBUG_ALSA_XFER + fprintf(stderr, "output %d took %d msec\n", + callno, (int)(1000 * (timenow - timelast))), fflush(stderr); + #endif + timenow = sys_getrealtime(); + sys_log_error(ERR_DACSLEPT); + } + } + /* do input */ + if (linux_inchannels) + { + result = snd_pcm_readi(alsa_device.inhandle, alsa_buf, intransfersize); + if (result < (int)intransfersize) + { +#ifdef DEBUG_ALSA_XFER + if (result < 0) + fprintf(stderr, + "snd_pcm_read %d %d: %s\n", + callno, xferno, snd_strerror(errno)); + else fprintf(stderr, + "snd_pcm_read %d %d returned only %d\n", + callno, xferno, result); + fprintf(stderr, + "inputcount %d, outputcount %d, inbufsize %d\n", + inputcount, outputcount, + (ALSA_EXTRABUFFER + linux_advance_samples) + * alsa_samplewidth * inchannels); +#endif + sys_log_error(ERR_ADCSLEPT); + return (SENDDACS_NO); + } + fp = sys_soundin; + if (alsa_samplewidth == 4) + { + for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; + j += inchannels, fp2++) + *fp2 = (float) ((t_alsa_sample32 *)alsa_buf)[j] + * (1./ INT32_MAX); + } + } + else + { + for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; j += inchannels, + fp2++) + *fp2 = (float) ((t_alsa_sample16 *)alsa_buf)[j] + * 3.051850e-05; + } + } + } + xferno++; + if (sys_getrealtime() - timenow > 0.002) + { +#ifdef DEBUG_ALSA_XFER + fprintf(stderr, "routine took %d msec\n", + (int)(1000 * (sys_getrealtime() - timenow))); +#endif + sys_log_error(ERR_ADCSLEPT); + } + return SENDDACS_YES; +} + +void alsa_resync( void) +{ + int i, result; + memset(alsa_buf, 0, + sizeof(char) * alsa_samplewidth * DACBLKSIZE * linux_outchannels); + for (i = 0; i < 100; i++) + { + result = snd_pcm_writei(alsa_device.outhandle, alsa_buf, + DACBLKSIZE); + if (result != (int)DACBLKSIZE) + break; + } + post("%d written", i); +} + + +#endif /* ALSA01 */ + +/*************************************************** + * Code using the RME_9652 API + */ + + /* + trying native device for future use of native memory map: + because of busmaster if you dont use the dac, you dont need + CPU Power und also no nearly no CPU-Power is used in device + + since always all DAs and ADs are synced (else they wouldnt work) + we use linux_dacs[0], linux_adcs[0] + */ + +#ifdef RME_HAMMERFALL + +#define RME9652_MAX_CHANNELS 26 + +#define RME9652_CH_PER_NATIVE_DEVICE 1 + +static int rme9652_dac_devices[RME9652_MAX_CHANNELS]; +static int rme9652_adc_devices[RME9652_MAX_CHANNELS]; + +static char rme9652_dsp_dac[] = "/dev/rme9652/C0da%d"; +static char rme9652_dsp_adc[] = "/dev/rme9652/C0ad%d"; + +static int num_of_rme9652_dac = 0; +static int num_of_rme9652_adc = 0; + +static int rme_soundindevonset = 1; +static int rme_soundoutdevonset = 1; + +void rme_soundindev(int which) +{ + rme_soundindevonset = which; +} + +void rme_soundoutdev(int which) +{ + rme_soundoutdevonset = which; +} + +void rme9652_configure(int dev, int fd,int srate, int dac) { + int orig, param, nblk; + audio_buf_info ainfo; + orig = param = srate; + + /* samplerate */ + + fprintf(stderr,"RME9652: configuring %d, fd=%d, sr=%d\n, dac=%d\n", + dev,fd,srate,dac); + + if (ioctl(fd,SNDCTL_DSP_SPEED,¶m) == -1) + fprintf(stderr,"RME9652: Could not set sampling rate for device\n"); + else if( orig != param ) + fprintf(stderr,"RME9652: sampling rate: wanted %d, got %d\n", + orig, param ); + + // setting the correct samplerate (could be different than expected) + srate = param; + + + /* setting resolution */ + + /* use ctrlpanel to change, experiment, channels 1 */ + + orig = param = AFMT_S16_BE; + if (ioctl(fd,SNDCTL_DSP_SETFMT,¶m) == -1) + fprintf(stderr,"RME9652: Could not set DSP format\n"); + else if( orig != param ) + fprintf(stderr,"RME9652: DSP format: wanted %d, got %d\n",orig, param ); + + /* setting channels */ + orig = param = RME9652_CH_PER_NATIVE_DEVICE; + + if (ioctl(fd,SNDCTL_DSP_CHANNELS,¶m) == -1) + fprintf(stderr,"RME9652: Could not set channels\n"); + else if( orig != param ) + fprintf(stderr,"RME9652: num channels: wanted %d, got %d\n",orig, param ); + + if (dac) + { + + /* use "free space" to learn the buffer size. Normally you + should set this to your own desired value; but this seems not + to be implemented uniformly across different sound cards. LATER + we should figure out what to do if the requested scheduler advance + is greater than this buffer size; for now, we just print something + out. */ + + if( ioctl(linux_dacs[0].d_fd, SOUND_PCM_GETOSPACE,&ainfo) < 0 ) + fprintf(stderr,"RME: ioctl on output device %d failed",dev); + + linux_dacs[0].d_bufsize = ainfo.bytes; + + fprintf(stderr,"RME: ioctl SOUND_PCM_GETOSPACE says %d buffsize\n", + linux_dacs[0].d_bufsize); + + + if (linux_advance_samples * (RME_SAMPLEWIDTH * + RME9652_CH_PER_NATIVE_DEVICE) + > linux_dacs[0].d_bufsize - RME_BYTESPERCHAN) + { + fprintf(stderr, + "RME: requested audio buffer size %d limited to %d\n", + linux_advance_samples + * (RME_SAMPLEWIDTH * RME9652_CH_PER_NATIVE_DEVICE), + linux_dacs[0].d_bufsize); + linux_advance_samples = + (linux_dacs[0].d_bufsize - RME_BYTESPERCHAN) + / (RME_SAMPLEWIDTH *RME9652_CH_PER_NATIVE_DEVICE); + } + } +} + + +int rme9652_open_audio(int inchans, int outchans,int srate) +{ + int orig; + int tmp; + int inchannels = 0,outchannels = 0; + char devname[20]; + int i; + char buf[RME_SAMPLEWIDTH*RME9652_CH_PER_NATIVE_DEVICE*DACBLKSIZE]; + int num_devs = 0; + audio_buf_info ainfo; + + linux_nindevs = linux_noutdevs = 0; + + if (sys_verbose) + post("RME open"); + /* First check if we can */ + /* open the write ports */ + + for (num_devs=0; outchannels < outchans; num_devs++) + { + int channels = RME9652_CH_PER_NATIVE_DEVICE; + + sprintf(devname, rme9652_dsp_dac, num_devs + rme_soundoutdevonset); + if ((tmp = open(devname,O_WRONLY)) == -1) + { + DEBUG(fprintf(stderr,"RME9652: failed to open %s writeonly\n", + devname);) + break; + } + DEBUG(fprintf(stderr,"RME9652: out device Nr. %d (%d) on %s\n", + linux_noutdevs+1,tmp,devname);) + + if (outchans > outchannels) + { + rme9652_dac_devices[linux_noutdevs] = tmp; + linux_noutdevs++; + outchannels += channels; + } + else close(tmp); + } + if( linux_noutdevs > 0) + linux_dacs[0].d_fd = rme9652_dac_devices[0]; + + /* Second check if we can */ + /* open the read ports */ + + for (num_devs=0; inchannels < inchans; num_devs++) + { + int channels = RME9652_CH_PER_NATIVE_DEVICE; + + sprintf(devname, rme9652_dsp_adc, num_devs+rme_soundindevonset); + + if ((tmp = open(devname,O_RDONLY)) == -1) + { + DEBUG(fprintf(stderr,"RME9652: failed to open %s readonly\n", + devname);) + break; + } + DEBUG(fprintf(stderr,"RME9652: in device Nr. %d (%d) on %s\n", + linux_nindevs+1,tmp,devname);) + + if (inchans > inchannels) + { + rme9652_adc_devices[linux_nindevs] = tmp; + linux_nindevs++; + inchannels += channels; + } + else + close(tmp); + } + if(linux_nindevs > 0) + linux_adcs[0].d_fd = rme9652_adc_devices[0]; + + /* configure soundcards */ + + rme9652_configure(0, linux_adcs[0].d_fd,srate, 0); + rme9652_configure(0, linux_dacs[0].d_fd,srate, 1); + + /* We have to do a read to start the engine. This is + necessary because sys_send_dacs waits until the input + buffer is filled and only reads on a filled buffer. + This is good, because it's a way to make sure that we + will not block */ + + if (linux_nindevs) + { + fprintf(stderr,("RME9652: starting read engine ... ")); + + + for (num_devs=0; num_devs < linux_nindevs; num_devs++) + read(rme9652_adc_devices[num_devs], + buf, RME_SAMPLEWIDTH* RME9652_CH_PER_NATIVE_DEVICE* + DACBLKSIZE); + + + for (num_devs=0; num_devs < linux_noutdevs; num_devs++) + write(rme9652_dac_devices[num_devs], + buf, RME_SAMPLEWIDTH* RME9652_CH_PER_NATIVE_DEVICE* + DACBLKSIZE); + + if(linux_noutdevs) + ioctl(rme9652_dac_devices[0],SNDCTL_DSP_SYNC); + + fprintf(stderr,"done\n"); + } + + linux_setsr(srate); + linux_setch(linux_nindevs, linux_noutdevs); + + num_of_rme9652_dac = linux_noutdevs; + num_of_rme9652_adc = linux_nindevs; + + if(linux_noutdevs)linux_noutdevs=1; + if(linux_nindevs)linux_nindevs=1; + + /* trick RME9652 behaves as one device fromread write pointers */ + return (0); +} + +void rme9652_close_audio( void) +{ + int i; + for (i=0;i>2;i--;) + { + float s1 = *(fp1+=4) * INT32_MAX; + float s2 = *(fp2+=4) * INT32_MAX; + float s3 = *(fp3+=4) * INT32_MAX; + float s4 = *(fp4+=4) * INT32_MAX; + + *(a+=4) = CLIP32(s1); + *(b+=4) = CLIP32(s2); + *(c+=4) = CLIP32(s3); + *(d+=4) = CLIP32(s4); + } + + linux_dacs_write(rme9652_dac_devices[j],buf,RME_BYTESPERCHAN); + } + } + + if ((timenow = sys_getrealtime()) - timeref > 0.02) + sys_log_error(ERR_DACSLEPT); + timeref = timenow; + } + + memset(sys_soundout, 0, + linux_outchannels * (sizeof(float) * DACBLKSIZE)); + + /* do input */ + + if(linux_nindevs) { + + for(j=0;j 0.02) + sys_log_error(ERR_ADCSLEPT); + timeref = timenow; + { + t_rme_sample *a,*b,*c,*d; + float *fp1,*fp2,*fp3,*fp4; + + fp1 = sys_soundin + j*DACBLKSIZE-4; + fp2 = fp1 + 1; + fp3 = fp1 + 2; + fp4 = fp1 + 3; + a = buf-4; + b=a+1; + c=a+2; + d=a+3; + + for (i = (DACBLKSIZE>>2);i--;) + { + *(fp1+=4) = *(a+=4) * (float)(1./INT32_MAX); + *(fp2+=4) = *(b+=4) * (float)(1./INT32_MAX); + *(fp3+=4) = *(c+=4) * (float)(1./INT32_MAX); + *(fp4+=4) = *(d+=4) * (float)(1./INT32_MAX); + } + } + } + } + /* fprintf(stderr,"ready \n");*/ + + return (1); +} + +#endif /* RME_HAMMERFALL */ diff --git a/pd/src/s_inter.c b/pd/src/s_inter.c new file mode 100644 index 00000000..b9f52d68 --- /dev/null +++ b/pd/src/s_inter.c @@ -0,0 +1,794 @@ +/* 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. */ + +/* Pd side of the Pd/Pd-gui interface. */ + +#include "m_imp.h" +#ifdef UNIX +#include +#include +#include +#include +#include +#include +#include +#endif +#ifdef HAVE_BSTRING_H +#include +#endif +#ifdef NT +#include +#include +#include +#include +typedef int pid_t; +#define EADDRINUSE WSAEADDRINUSE +#endif +#include +#include +#include +#include +#include +#include + +#ifdef MACOSX +#include +#include +#else +#include +#endif + +extern char pd_version[]; + +typedef struct _fdpoll +{ + int fdp_fd; + t_fdpollfn fdp_fn; + void *fdp_ptr; +} t_fdpoll; + +#define INBUFSIZE 4096 + +struct _socketreceiver +{ + char *sr_inbuf; + int sr_inhead; + int sr_intail; + void *sr_owner; + int sr_udp; + t_socketnotifier sr_notifier; + t_socketreceivefn sr_socketreceivefn; +}; + +static int sys_nfdpoll; +static t_fdpoll *sys_fdpoll; +static int sys_maxfd; +static int sys_guisock; + +static t_binbuf *inbinbuf; +static t_socketreceiver *sys_socketreceiver; +extern int sys_addhist(int phase); + +void sys_sockerror(char *s) +{ +#ifdef NT + int err = WSAGetLastError(); + if (err == 10054) return; + else if (err == 10044) + { + fprintf(stderr, + "Warning: you might not have TCP/IP \"networking\" turned on\n"); + fprintf(stderr, "which is needed for Pd to talk to its GUI layer.\n"); + } +#endif +#ifdef UNIX + int err = errno; +#endif + fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err); +} + +void sys_addpollfn(int fd, t_fdpollfn fn, void *ptr) +{ + int nfd = sys_nfdpoll; + int size = nfd * sizeof(t_fdpoll); + t_fdpoll *fp; + sys_fdpoll = (t_fdpoll *)t_resizebytes(sys_fdpoll, size, + size + sizeof(t_fdpoll)); + fp = sys_fdpoll + nfd; + fp->fdp_fd = fd; + fp->fdp_fn = fn; + fp->fdp_ptr = ptr; + sys_nfdpoll = nfd + 1; + if (fd >= sys_maxfd) sys_maxfd = fd + 1; +} + +void sys_rmpollfn(int fd) +{ + int nfd = sys_nfdpoll; + int i, size = nfd * sizeof(t_fdpoll); + t_fdpoll *fp; + for (i = nfd, fp = sys_fdpoll; i--; fp++) + { + if (fp->fdp_fd == fd) + { + while (i--) + { + fp[0] = fp[1]; + fp++; + } + sys_fdpoll = (t_fdpoll *)t_resizebytes(sys_fdpoll, size, + size - sizeof(t_fdpoll)); + sys_nfdpoll = nfd - 1; + return; + } + } + post("warning: %d removed from poll list but not found", fd); +} + +static int sys_domicrosleep(int microsec, int pollem) +{ + struct timeval timout; + int i, didsomething = 0; + t_fdpoll *fp; + timout.tv_sec = 0; + timout.tv_usec = microsec; + if (pollem) + { + fd_set readset, writeset, exceptset; + FD_ZERO(&writeset); + FD_ZERO(&readset); + FD_ZERO(&exceptset); + for (fp = sys_fdpoll, i = sys_nfdpoll; i--; fp++) + FD_SET(fp->fdp_fd, &readset); + select(sys_maxfd+1, &readset, &writeset, &exceptset, &timout); + for (i = 0; i < sys_nfdpoll; i++) + if (FD_ISSET(sys_fdpoll[i].fdp_fd, &readset)) + { + (*sys_fdpoll[i].fdp_fn)(sys_fdpoll[i].fdp_ptr, sys_fdpoll[i].fdp_fd); + didsomething = 1; + } + return (didsomething); + } + else + { + select(0, 0, 0, 0, &timout); + return (0); + } +} + +void sys_microsleep(int microsec) +{ + sys_domicrosleep(microsec, 1); +} + +t_socketreceiver *socketreceiver_new(void *owner, t_socketnotifier notifier, + t_socketreceivefn socketreceivefn, int udp) +{ + t_socketreceiver *x = (t_socketreceiver *)getbytes(sizeof(*x)); + x->sr_inhead = x->sr_intail = 0; + x->sr_owner = owner; + x->sr_notifier = notifier; + x->sr_socketreceivefn = socketreceivefn; + x->sr_udp = udp; + if (!(x->sr_inbuf = malloc(INBUFSIZE))) bug("t_socketreceiver");; + return (x); +} + +void socketreceiver_free(t_socketreceiver *x) +{ + free(x->sr_inbuf); + freebytes(x, sizeof(*x)); +} + + /* this is in a separately called subroutine so that the buffer isn't + sitting on the stack while the messages are getting passed. */ +static int socketreceiver_doread(t_socketreceiver *x) +{ + char messbuf[INBUFSIZE], *bp = messbuf; + int indx; + int inhead = x->sr_inhead; + int intail = x->sr_intail; + char *inbuf = x->sr_inbuf; + if (intail == inhead) return (0); + for (indx = intail; indx != inhead; indx = (indx+1)&(INBUFSIZE-1)) + { + /* if we hit a semi that isn't preceeded by a \, it's a message + boundary. LATER we should deal with the possibility that the + preceeding \ might itself be escaped! */ + char c = *bp++ = inbuf[indx]; + if (c == ';' && (!indx || inbuf[indx-1] != '\\')) + { + intail = (indx+1)&(INBUFSIZE-1); + binbuf_text(inbinbuf, messbuf, bp - messbuf); + if (sys_debuglevel & DEBUG_MESSDOWN) + { + write(2, messbuf, bp - messbuf); + write(2, "\n", 1); + } + x->sr_inhead = inhead; + x->sr_intail = intail; + return (1); + } + } + return (0); +} + +static void socketreceiver_getudp(t_socketreceiver *x, int fd) +{ + char buf[INBUFSIZE+1]; + int ret = recv(fd, buf, INBUFSIZE, 0); + if (ret < 0) + { + sys_sockerror("recv"); + sys_rmpollfn(fd); + sys_closesocket(fd); + } + else if (ret > 0) + { + buf[ret] = 0; +#if 0 + post("%s", buf); +#endif + if (buf[ret-1] != '\n') + { +#if 0 + buf[ret] = 0; + error("dropped bad buffer %s\n", buf); +#endif + } + else + { + char *semi = strchr(buf, ';'); + if (semi) + *semi = 0; + binbuf_text(inbinbuf, buf, strlen(buf)); + outlet_setstacklim(); + if (x->sr_socketreceivefn) + (*x->sr_socketreceivefn)(x->sr_owner, inbinbuf); + else bug("socketreceiver_getudp"); + } + } +} + +void socketreceiver_read(t_socketreceiver *x, int fd) +{ + if (x->sr_udp) /* UDP ("datagram") socket protocol */ + socketreceiver_getudp(x, fd); + else /* TCP ("streaming") socket protocol */ + { + char *semi; + int readto = + (x->sr_inhead >= x->sr_intail ? INBUFSIZE : x->sr_intail-1); + int ret; + + /* the input buffer might be full. If so, drop the whole thing */ + if (readto == x->sr_inhead) + { + fprintf(stderr, "pd: dropped message from gui\n"); + x->sr_inhead = x->sr_intail = 0; + readto = INBUFSIZE; + } + else + { + ret = recv(fd, x->sr_inbuf + x->sr_inhead, + readto - x->sr_inhead, 0); + if (ret < 0) + { + sys_sockerror("recv"); + if (x == sys_socketreceiver) sys_bail(1); + else + { + if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner); + sys_rmpollfn(fd); + sys_closesocket(fd); + } + } + else if (ret == 0) + { + if (x == sys_socketreceiver) + { + fprintf(stderr, "pd: exiting\n"); + sys_bail(0); + } + else + { + post("EOF on socket %d\n", fd); + if (x->sr_notifier) (*x->sr_notifier)(x->sr_owner); + sys_rmpollfn(fd); + sys_closesocket(fd); + } + } + else + { + x->sr_inhead += ret; + if (x->sr_inhead >= INBUFSIZE) x->sr_inhead = 0; + while (socketreceiver_doread(x)) + { + outlet_setstacklim(); + if (x->sr_socketreceivefn) + (*x->sr_socketreceivefn)(x->sr_owner, inbinbuf); + else binbuf_eval(inbinbuf, 0, 0, 0); + } + } + } + } +} + +void sys_closesocket(int fd) +{ +#ifdef UNIX + close(fd); +#endif +#ifdef NT + closesocket(fd); +#endif +} + + +void sys_gui(char *s) +{ + int length = strlen(s), written = 0, res, histwas = sys_addhist(4); + if (sys_debuglevel & DEBUG_MESSUP) + fprintf(stderr, "%s", s); + if (sys_nogui) + return; + while (1) + { + res = send(sys_guisock, s + written, length, 0); + if (res < 0) + { + perror("pd output pipe"); + sys_bail(1); + } + else + { + written += res; + if (written >= length) + break; + } + } + sys_addhist(histwas); +} + +/* LATER should do a bounds check -- but how do you get printf to do that? + See also rtext_senditup() in this regard */ + +void sys_vgui(char *fmt, ...) +{ + int result, i; + char buf[2048]; + va_list ap; + + va_start(ap, fmt); + vsprintf(buf, fmt, ap); + sys_gui(buf); + va_end(ap); +} + + +#define FIRSTPORTNUM 5400 + +/* -------------- signal handling for UNIX -------------- */ + +#ifdef UNIX +typedef void (*sighandler_t)(int); + +static void sys_signal(int signo, sighandler_t sigfun) +{ + struct sigaction action; + action.sa_flags = 0; + action.sa_handler = sigfun; + memset(&action.sa_mask, 0, sizeof(action.sa_mask)); +#ifdef __linux__ + action.sa_restorer = 0; +#endif + if (sigaction(signo, &action, 0) < 0) + perror("sigaction"); +} + +static void sys_exithandler(int n) +{ + static int trouble = 0; + if (!trouble) + { + trouble = 1; + fprintf(stderr, "Pd: signal %d\n", n); + sys_bail(1); + + } + else _exit(1); +} + +static void sys_alarmhandler(int n) +{ + fprintf(stderr, "Pd: system call timed out\n"); +} + +static void sys_huphandler(int n) +{ + struct timeval timout; + timout.tv_sec = 0; + timout.tv_usec = 30000; + select(1, 0, 0, 0, &timout); +} + +void sys_setalarm(int microsec) +{ + struct itimerval gonzo; +#if 0 + fprintf(stderr, "timer %d\n", microsec); +#endif + gonzo.it_interval.tv_sec = 0; + gonzo.it_interval.tv_usec = 0; + gonzo.it_value.tv_sec = 0; + gonzo.it_value.tv_usec = microsec; + if (microsec) + sys_signal(SIGALRM, sys_alarmhandler); + else sys_signal(SIGALRM, SIG_IGN); + setitimer(ITIMER_REAL, &gonzo, 0); +} + +#endif + +static int sys_watchfd; + +void glob_ping(t_pd *dummy) +{ + if (write(sys_watchfd, "\n", 1) < 1) + { + fprintf(stderr, "pd: watchdog process died\n"); + sys_bail(1); + } +} + +static int defaultfontshit[] = { + 8, 5, 9, 10, 6, 10, 12, 7, 13, 14, 9, 17, 16, 10, 19, 24, 15, 28, + 24, 15, 28}; + +int sys_startgui(const char *guidir) +{ + pid_t childpid; + char cmdbuf[4*MAXPDSTRING]; + struct sockaddr_in server; + int msgsock; + char buf[15]; + int len = sizeof(server); + int ntry = 0, portno = FIRSTPORTNUM; + int xsock = -1; +#ifdef NT + short version = MAKEWORD(2, 0); + WSADATA nobby; +#endif +#ifdef UNIX + int stdinpipe[2]; +#endif + /* create an empty FD poll list */ + sys_fdpoll = (t_fdpoll *)t_getbytes(0); + sys_nfdpoll = 0; + inbinbuf = binbuf_new(); + +#ifdef UNIX + signal(SIGHUP, sys_huphandler); + signal(SIGINT, sys_exithandler); + signal(SIGQUIT, sys_exithandler); + signal(SIGILL, sys_exithandler); + signal(SIGIOT, sys_exithandler); + signal(SIGFPE, SIG_IGN); + /* signal(SIGILL, sys_exithandler); + signal(SIGBUS, sys_exithandler); + signal(SIGSEGV, sys_exithandler); */ + signal(SIGPIPE, sys_exithandler); + signal(SIGALRM, SIG_IGN); + signal(SIGTERM, SIG_IGN); +#ifdef __linux__ + signal(SIGSTKFLT, sys_exithandler); +#endif +#endif +#ifdef NT + if (WSAStartup(version, &nobby)) sys_sockerror("WSAstartup"); +#endif + + if (sys_nogui) + { + /* fake the GUI's message giving cwd and font sizes; then + skip starting the GUI up. */ + t_atom zz[19]; + int i; +#ifdef NT + if (GetCurrentDirectory(MAXPDSTRING, cmdbuf) == 0) + strcpy(cmdbuf, "."); +#endif +#ifdef UNIX + if (!getcwd(cmdbuf, MAXPDSTRING)) + strcpy(cmdbuf, "."); + +#endif + SETSYMBOL(zz, gensym(cmdbuf)); + for (i = 1; i < 22; i++) + SETFLOAT(zz + i, defaultfontshit[i-1]); + glob_initfromgui(0, 0, 22, zz); + } + else + { +#ifdef NT + char scriptbuf[MAXPDSTRING+30], wishbuf[MAXPDSTRING+30], portbuf[80]; + int spawnret; + +#endif + int intarg; + + /* create a socket */ + xsock = socket(AF_INET, SOCK_STREAM, 0); + if (xsock < 0) sys_sockerror("socket"); +#if 0 + intarg = 0; + if (setsockopt(xsock, SOL_SOCKET, SO_SNDBUF, + &intarg, sizeof(intarg)) < 0) + post("setsockopt (SO_RCVBUF) failed\n"); + intarg = 0; + if (setsockopt(xsock, SOL_SOCKET, SO_RCVBUF, + &intarg, sizeof(intarg)) < 0) + post("setsockopt (SO_RCVBUF) failed\n"); +#endif + intarg = 1; + if (setsockopt(xsock, IPPROTO_TCP, TCP_NODELAY, + &intarg, sizeof(intarg)) < 0) + post("setsockopt (TCP_NODELAY) failed\n"); + + + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + + /* assign server port number */ + server.sin_port = htons((unsigned short)portno); + + /* name the socket */ + while (bind(xsock, (struct sockaddr *)&server, sizeof(server)) < 0) + { +#ifdef NT + int err = WSAGetLastError(); +#endif +#ifdef UNIX + int err = errno; +#endif + if ((ntry++ > 20) || (err != EADDRINUSE)) + { + perror("bind"); + fprintf(stderr, + "Pd needs your machine to be configured with\n"); + fprintf(stderr, + "'networking' turned on (see Pd's html doc for details.)\n"); + exit(1); + } + portno++; + server.sin_port = htons((unsigned short)(portno)); + } + + if (sys_verbose) fprintf(stderr, "port %d\n", portno); + + sys_socketreceiver = socketreceiver_new(0, 0, 0, 0); + +#ifdef UNIX + childpid = fork(); + if (childpid < 0) + { + if (errno) perror("sys_startgui"); + else fprintf(stderr, "sys_startgui failed\n"); + return (1); + } + else if (!childpid) /* we're the child */ + { + seteuid(getuid()); /* lose setuid priveliges */ +#ifndef MACOSX + /* the wish process in Unix will make a wish shell and + read/write standard in and out unless we close the + file descriptors. Somehow this doesn't make the MAC OSX + version of Wish happy...*/ + if (pipe(stdinpipe) < 0) + sys_sockerror("pipe"); + else + { + if (stdinpipe[0] != 0) + { + close (0); + dup2(stdinpipe[0], 0); + close(stdinpipe[0]); + } + } +#endif + if (!sys_guicmd) + { +#ifdef MACOSX + char *homedir = getenv("HOME"), filename[250]; + struct stat statbuf; + if (!homedir || strlen(homedir) > 150) + goto nexttry; + sprintf(filename, + "%s/Applications/Wish shell.app/Contents/MacOS/Wish Shell", + homedir); + + if (stat(filename, &statbuf) >= 0) + goto foundit; + nexttry: + strcpy(filename, + "/Applications/Wish Shell.app/Contents/MacOS/Wish Shell"); + foundit: + sprintf(cmdbuf, "\"%s\" %s/pd.tk %d\n", filename, guidir, portno); +#else + sprintf(cmdbuf, +"TCL_LIBRARY=\"%s/tcl/library\" TK_LIBRARY=\"%s/tk/library\" \ + \"%s/pd-gui\" %d\n", + sys_libdir->s_name, sys_libdir->s_name, guidir, portno); +#endif + sys_guicmd = cmdbuf; + } + if (sys_verbose) fprintf(stderr, "%s", sys_guicmd); + execl("/bin/sh", "sh", "-c", sys_guicmd, 0); + perror("pd: exec"); + _exit(1); + } +#endif /* UNIX */ + +#ifdef NT + /* in NT land "guipath" is unused; we just do everything from + the libdir. */ + fprintf(stderr, "%s\n", sys_libdir->s_name); + + strcpy(scriptbuf, "\""); + strcat(scriptbuf, sys_libdir->s_name); + strcat(scriptbuf, "/bin/pd.tk\""); + sys_bashfilename(scriptbuf, scriptbuf); + + sprintf(portbuf, "%d", portno); + + strcpy(wishbuf, sys_libdir->s_name); + strcat(wishbuf, "/bin/wish83.exe"); + sys_bashfilename(wishbuf, wishbuf); + + spawnret = _spawnl(P_NOWAIT, wishbuf, "wish83", scriptbuf, portbuf, 0); + if (spawnret < 0) + { + perror("spawnl"); + fprintf(stderr, "%s: couldn't load TCL\n", wishbuf); + exit(1); + } + +#endif /* NT */ + } + +#ifdef UNIX + /* now that we've spun off the child process we can promote + our process's priority, if we happen to be root. */ + if (sys_hipriority) + { + if (!getuid() || !geteuid()) + { + /* To prevent lockup, we fork off a watchdog process with + higher real-time priority than ours. The GUI has to send + a stream of ping messages to the watchdog THROUGH the Pd + process which has to pick them up from the GUI and forward + them. If any of these things aren't happening the watchdog + starts sending "stop" and "cont" signals to the Pd process + to make it timeshare with the rest of the system. (Version + 0.33P2 : if there's no GUI, the watchdog pinging is done + from the scheduler idle routine in this process instead.) */ + + int pipe9[2], watchpid; + if (pipe(pipe9) < 0) + { + seteuid(getuid()); /* lose setuid priveliges */ + sys_sockerror("pipe"); + return (1); + } + watchpid = fork(); + if (watchpid < 0) + { + seteuid(getuid()); /* lose setuid priveliges */ + if (errno) + perror("sys_startgui"); + else fprintf(stderr, "sys_startgui failed\n"); + return (1); + } + else if (!watchpid) /* we're the child */ + { + sys_set_priority(1); + seteuid(getuid()); /* lose setuid priveliges */ + if (pipe9[1] != 0) + { + dup2(pipe9[0], 0); + close(pipe9[0]); + } + close(pipe9[1]); + + sprintf(cmdbuf, "%s/pd-watchdog\n", guidir); + if (sys_verbose) fprintf(stderr, "%s", cmdbuf); + execl("/bin/sh", "sh", "-c", cmdbuf, 0); + perror("pd: exec"); + _exit(1); + } + else /* we're the parent */ + { + sys_set_priority(0); + seteuid(getuid()); /* lose setuid priveliges */ + close(pipe9[0]); + sys_watchfd = pipe9[1]; + /* We also have to start the ping loop in the GUI; + this is done later when the socket is open. */ + } + } + else + { + post("realtime setting failed because not root\n"); + sys_hipriority = 0; + } + } + + seteuid(getuid()); /* lose setuid priveliges */ +#endif /* UNIX */ + +#ifdef NT + if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) + fprintf(stderr, "pd: couldn't set high priority class\n"); +#endif + + if (!sys_nogui) + { + if (sys_verbose) + fprintf(stderr, "Waiting for connection request... \n"); + if (listen(xsock, 5) < 0) sys_sockerror("listen"); + + sys_guisock = accept(xsock, (struct sockaddr *) &server, &len); +#ifdef OOPS + close(xsock); +#endif + if (sys_guisock < 0) sys_sockerror("accept"); + sys_addpollfn(sys_guisock, (t_fdpollfn)socketreceiver_read, + sys_socketreceiver); + + if (sys_verbose) + fprintf(stderr, "... connected\n"); + + /* here is where we start the pinging. */ + if (sys_hipriority) + sys_gui("pdtk_watchdog\n"); + + sys_vgui("pdtk_pd_startup {%s}\n", pd_version); + } + return (0); + +} + + +static int sys_poll_togui(void) +{ + /* LATER use this to flush output buffer to gui */ + return (0); +} + +int sys_pollgui(void) +{ + return (sys_domicrosleep(0, 1) || sys_poll_togui()); +} + +/* LATER try to save dirty documents */ +void sys_bail(int n) +{ + static int reentered = 0; + if (!reentered) + { + reentered = 1; + sys_close_audio(); + sys_close_midi(); + } + _exit(n); +} + +void glob_quit(void *dummy) +{ + sys_vgui("exit\n"); + if (!sys_nogui) + close(sys_guisock); + sys_bail(0); +} + diff --git a/pd/src/s_linux.c b/pd/src/s_linux.c new file mode 100644 index 00000000..5c394674 --- /dev/null +++ b/pd/src/s_linux.c @@ -0,0 +1,3087 @@ +/* Copyright (c) 1997-1999 Guenter Geiger, Miller Puckette, Larry Troxler, +* Winfried Ritsch, Karl MacMillan, 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 implements the sys_ functions profiled in m_imp.h for + audio and MIDI I/O. In Linux there might be several APIs for doing the + audio part; right now there are three (OSS, ALSA, RME); the third is + for the RME 9652 driver by Ritsch (but not for the OSS compatible + one by Geiger; for that one, OSS should work.) + + FUNCTION PREFIXES. + sys_ -- functions which must be exported to Pd on all platforms + linux_ -- linux-specific objects which don't depend on API, + mostly static but some exported. + oss_, alsa_, rme_ -- API-specific functions, all of which are + static. + + ALSA SUPPORT. If ALSA99 is defined we support ALSA 0.5x; if ALSA01, + ALSA 0.9x. (the naming scheme reflects the possibility of further API + changes in the future...) We define "ALSA" for code relevant to both + APIs. + + For MIDI, we only offer the OSS API; ALSA has to emulate OSS for us. +*/ + +/* OSS include (whether we're doing OSS audio or not we need this for MIDI) */ + + +/* IOhannes::: + * hacked this to add advanced multidevice-support + * 1311:forum::für::umläute:2001 + */ + +#include + +#if (defined(ALSA01) || defined(ALSA99)) +#define ALSA +#endif + +#ifdef ALSA99 +#include +#endif +#ifdef ALSA01 +#include +#endif + +#include "m_imp.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* local function prototypes */ + +static void linux_close_midi( void); + +static int oss_open_audio(int naudioindev, int *audioindev, int nchindev, + int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, + int *choutdev, int rate); /* IOhannes */ + +static void oss_close_audio(void); +static int oss_send_dacs(void); +static void oss_reportidle(void); + +#ifdef ALSA +typedef int16_t t_alsa_sample16; +typedef int32_t t_alsa_sample32; +#define ALSA_SAMPLEWIDTH_16 sizeof(t_alsa_sample16) +#define ALSA_SAMPLEWIDTH_32 sizeof(t_alsa_sample32) +#define ALSA_XFERSIZE16 (signed int)(sizeof(t_alsa_sample16) * DACBLKSIZE) +#define ALSA_XFERSIZE32 (signed int)(sizeof(t_alsa_sample32) * DACBLKSIZE) +#define ALSA_MAXDEV 1 +#define ALSA_JITTER 1024 +#define ALSA_EXTRABUFFER 2048 +#define ALSA_DEFFRAGSIZE 64 +#define ALSA_DEFNFRAG 12 + +#ifdef ALSA99 +typedef struct _alsa_dev +{ + snd_pcm_t *handle; + snd_pcm_channel_info_t info; + snd_pcm_channel_setup_t setup; +} t_alsa_dev; + +t_alsa_dev alsa_device[ALSA_MAXDEV]; +static int n_alsa_dev; +static char *alsa_buf; +static int alsa_samplewidth; +#endif /* ALSA99 */ + +#ifdef ALSA01 +typedef struct _alsa_dev +{ + snd_pcm_t *inhandle; + snd_pcm_t *outhandle; +} t_alsa_dev; + +t_alsa_dev alsa_device; +static short *alsa_buf; +static int alsa_samplewidth; +static snd_pcm_status_t* in_status; +static snd_pcm_status_t* out_status; +#endif /* ALSA01 */ + +#if 0 /* early alsa 0.9 beta dists had different names for these: */ +#define SND_PCM_ACCESS_RW_INTERLEAVED SNDRV_PCM_ACCESS_RW_INTERLEAVED +#define SND_PCM_FORMAT_S32 SNDRV_PCM_FORMAT_S32 +#define SND_PCM_FORMAT_S16 SNDRV_PCM_FORMAT_S16 +#define SND_PCM_SUBFORMAT_STD SNDRV_PCM_SUBFORMAT_STD +#endif + +static int alsa_mode; +static int alsa_open_audio(int inchans, int outchans, int rate); +static void alsa_close_audio(void); +static int alsa_send_dacs(void); +static void alsa_set_params(t_alsa_dev *dev, int dir, int rate, int voices); +static void alsa_reportidle(void); +#endif /* ALSA */ + +#ifdef RME_HAMMERFALL +static int rme9652_open_audio(int inchans, int outchans, int rate); +static void rme9652_close_audio(void); +static int rme9652_send_dacs(void); +static void rme9652_reportidle(void); +#endif /* RME_HAMMERFALL */ + +/* Defines */ +#define DEBUG(x) x +#define DEBUG2(x) {x;} + +#define OSS_MAXCHPERDEV 32 /* max channels per OSS device */ +#define OSS_MAXDEV 4 /* maximum number of input or output devices */ +#define OSS_DEFFRAGSIZE 256 /* default log fragment size (frames) */ +#define OSS_DEFAUDIOBUF 40000 /* default audiobuffer, microseconds */ +#define OSS_DEFAULTCH 2 +#define RME_DEFAULTCH 8 /* need this even if RME undefined */ +typedef int16_t t_oss_int16; +typedef int32_t t_oss_int32; +#define OSS_MAXSAMPLEWIDTH sizeof(t_oss_int32) +#define OSS_BYTESPERCHAN(width) (DACBLKSIZE * (width)) +#define OSS_XFERSAMPS(chans) (DACBLKSIZE* (chans)) +#define OSS_XFERSIZE(chans, width) (DACBLKSIZE * (chans) * (width)) + +#ifdef RME_HAMMERFALL +typedef int32_t t_rme_sample; +#define RME_SAMPLEWIDTH sizeof(t_rme_sample) +#define RME_BYTESPERCHAN (DACBLKSIZE * RME_SAMPLEWIDTH) +#endif /* RME_HAMMERFALL */ + +/* GLOBALS */ +static int linux_whichapi = API_OSS; +static int linux_inchannels; +static int linux_outchannels; +static int linux_advance_samples; /* scheduler advance in samples */ +static int linux_meters; /* true if we're metering */ +static float linux_inmax; /* max input amplitude */ +static float linux_outmax; /* max output amplitude */ +static int linux_fragsize = 0; /* for block mode; block size (sample frames) */ +static int linux_nfragment = 0; /* ... and number of blocks. */ + +#ifdef ALSA99 +static int alsa_devno = 1; +#endif +#ifdef ALSA01 +static char alsa_devname[512] = "hw:0,0"; +static int alsa_use_plugin = 0; +#endif + +/* our device handles */ + +typedef struct _oss_dev +{ + int d_fd; + unsigned int d_space; /* bytes available for writing/reading */ + int d_bufsize; /* total buffer size in blocks for this device */ + int d_dropcount; /* # of buffers to drop for resync (output only) */ + unsigned int d_nchannels; /* number of channels for this device */ + unsigned int d_bytespersamp; /* bytes per sample (2 for 16 bit, 4 for 32) */ +} t_oss_dev; + +static t_oss_dev linux_dacs[OSS_MAXDEV]; +static t_oss_dev linux_adcs[OSS_MAXDEV]; +static int linux_noutdevs = 0; +static int linux_nindevs = 0; + + /* exported variables */ +int sys_schedadvance = OSS_DEFAUDIOBUF; /* scheduler advance in microsecs */ +float sys_dacsr; +int sys_hipriority = 0; +t_sample *sys_soundout; +t_sample *sys_soundin; + + /* OSS-specific private variables */ +static int oss_blockmode = 1; /* flag to use "blockmode" */ +static int oss_32bit = 0; /* allow 23 bit transfers in OSS */ +static char ossdsp[] = "/dev/dsp%d"; + +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffff +#endif + /* don't assume we can turn all 31 bits when doing float-to-fix; + otherwise some audio drivers (e.g. Midiman/ALSA) wrap around. */ +#define FMAX 0x7ffff000 +#define CLIP32(x) (((x)>FMAX)?FMAX:((x) < -FMAX)?-FMAX:(x)) + + +/* ------------- private routines for all APIS ------------------- */ + +static void linux_flush_all_underflows_to_zero(void) +{ +/* + TODO: Implement similar thing for linux (GGeiger) + + One day we will figure this out, I hope, because it + costs CPU time dearly on Intel - LT + */ + /* union fpc_csr f; + f.fc_word = get_fpc_csr(); + f.fc_struct.flush = 1; + set_fpc_csr(f.fc_word); + */ +} + + /* set sample rate and channels. Must set sample rate before "configuring" + any devices so we know scheduler advance in samples. */ + +static void linux_setsr(int sr) +{ + sys_dacsr = sr; + linux_advance_samples = (sys_schedadvance * sys_dacsr) / (1000000.); + if (linux_advance_samples < 3 * DACBLKSIZE) + linux_advance_samples = 3 * DACBLKSIZE; +} + +static void linux_setch(int chin, int chout) +{ + int nblk; + int inbytes = chin * (DACBLKSIZE*sizeof(float)); + int outbytes = chout * (DACBLKSIZE*sizeof(float)); + + linux_inchannels = chin; + linux_outchannels = chout; + if (sys_soundin) + free(sys_soundin); + sys_soundin = (t_float *)malloc(inbytes); + memset(sys_soundin, 0, inbytes); + + if (sys_soundout) + free(sys_soundout); + sys_soundout = (t_float *)malloc(outbytes); + memset(sys_soundout, 0, outbytes); + + if (sys_verbose) + post("input channels = %d, output channels = %d", + linux_inchannels, linux_outchannels); +} + +/* ---------------- MIDI routines -------------------------- */ + +static int oss_nmidiin; +static int oss_midiinfd[MAXMIDIINDEV]; +static int oss_nmidiout; +static int oss_midioutfd[MAXMIDIOUTDEV]; + +static void oss_midiout(int fd, int n) +{ + char b = n; + if ((write(fd, (char *) &b, 1)) != 1) + perror("midi write"); +} + +#define O_MIDIFLAG O_NDELAY + +void linux_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec) +{ + int i; + for (i = 0; i < nmidiout; i++) + oss_midioutfd[i] = -1; + for (i = 0, oss_nmidiin = 0; i < nmidiin; i++) + { + int fd = -1, j, outdevindex = -1; + char namebuf[80]; + int devno = midiinvec[i]; + + for (j = 0; j < nmidiout; j++) + if (midioutvec[j] == midiinvec[i]) + outdevindex = j; + + /* try to open the device for read/write. */ + if (devno == 1 && fd < 0 && outdevindex >= 0) + { + sys_setalarm(1000000); + fd = open("/dev/midi", O_RDWR | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, + "device 1: tried /dev/midi READ/WRITE; returned %d\n", fd); + if (outdevindex >= 0 && fd >= 0) + oss_midioutfd[outdevindex] = fd; + } + if (fd < 0 && outdevindex >= 0) + { + sys_setalarm(1000000); + sprintf(namebuf, "/dev/midi%2.2d", devno-1); + fd = open(namebuf, O_RDWR | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, + "device %d: tried %s READ/WRITE; returned %d\n", + devno, namebuf, fd); + if (outdevindex >= 0 && fd >= 0) + oss_midioutfd[outdevindex] = fd; + } + if (fd < 0 && outdevindex >= 0) + { + sys_setalarm(1000000); + sprintf(namebuf, "/dev/midi%d", devno-1); + fd = open(namebuf, O_RDWR | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, "device %d: tried %s READ/WRITE; returned %d\n", + devno, namebuf, fd); + if (outdevindex >= 0 && fd >= 0) + oss_midioutfd[outdevindex] = fd; + } + if (devno == 1 && fd < 0) + { + sys_setalarm(1000000); + fd = open("/dev/midi", O_RDONLY | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, + "device 1: tried /dev/midi READONLY; returned %d\n", fd); + } + if (fd < 0) + { + sys_setalarm(1000000); + sprintf(namebuf, "/dev/midi%2.2d", devno-1); + fd = open(namebuf, O_RDONLY | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, "device %d: tried %s READONLY; returned %d\n", + devno, namebuf, fd); + } + if (fd < 0) + { + sys_setalarm(1000000); + sprintf(namebuf, "/dev/midi%d", devno-1); + fd = open(namebuf, O_RDONLY | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, "device %d: tried %s READONLY; returned %d\n", + devno, namebuf, fd); + } + if (fd >= 0) + oss_midiinfd[oss_nmidiin++] = fd; + else post("couldn't open MIDI input device %d", devno); + } + for (i = 0, oss_nmidiout = 0; i < nmidiout; i++) + { + int fd = oss_midioutfd[i]; + char namebuf[80]; + int devno = midioutvec[i]; + if (devno == 1 && fd < 0) + { + sys_setalarm(1000000); + fd = open("/dev/midi", O_WRONLY | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, + "device 1: tried /dev/midi WRITEONLY; returned %d\n", fd); + } + if (fd < 0) + { + sys_setalarm(1000000); + sprintf(namebuf, "/dev/midi%2.2d", devno-1); + fd = open(namebuf, O_WRONLY | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, "device %d: tried %s WRITEONLY; returned %d\n", + devno, namebuf, fd); + } + if (fd < 0) + { + sys_setalarm(1000000); + sprintf(namebuf, "/dev/midi%d", devno-1); + fd = open(namebuf, O_WRONLY | O_MIDIFLAG); + if (sys_verbose) + fprintf(stderr, "device %d: tried %s WRITEONLY; returned %d\n", + devno, namebuf, fd); + } + if (fd >= 0) + oss_midioutfd[oss_nmidiout++] = fd; + else post("couldn't open MIDI output device %d", devno); + } + + if (oss_nmidiin < nmidiin || oss_nmidiout < nmidiout || sys_verbose) + post("opened %d MIDI input device(s) and %d MIDI output device(s).", + oss_nmidiin, oss_nmidiout); +} + +#define md_msglen(x) (((x)<0xC0)?2:((x)<0xE0)?1:((x)<0xF0)?2:\ + ((x)==0xF2)?2:((x)<0xF4)?1:0) + +void sys_putmidimess(int portno, int a, int b, int c) +{ + if (portno >= 0 && portno < oss_nmidiout) + { + switch (md_msglen(a)) + { + case 2: + oss_midiout(oss_midioutfd[portno],a); + oss_midiout(oss_midioutfd[portno],b); + oss_midiout(oss_midioutfd[portno],c); + return; + case 1: + oss_midiout(oss_midioutfd[portno],a); + oss_midiout(oss_midioutfd[portno],b); + return; + case 0: + oss_midiout(oss_midioutfd[portno],a); + return; + }; + } +} + +void sys_putmidibyte(int portno, int byte) +{ + if (portno >= 0 && portno < oss_nmidiout) + oss_midiout(oss_midioutfd[portno], byte); +} + +#if 0 /* this is the "select" version which doesn't work with OSS + driver for emu10k1 (it doesn't implement select.) */ +void sys_poll_midi(void) +{ + int i, throttle = 100; + struct timeval timout; + int did = 1, maxfd = 0; + while (did) + { + fd_set readset, writeset, exceptset; + did = 0; + if (throttle-- < 0) + break; + timout.tv_sec = 0; + timout.tv_usec = 0; + + FD_ZERO(&writeset); + FD_ZERO(&readset); + FD_ZERO(&exceptset); + for (i = 0; i < oss_nmidiin; i++) + { + if (oss_midiinfd[i] > maxfd) + maxfd = oss_midiinfd[i]; + FD_SET(oss_midiinfd[i], &readset); + } + select(maxfd+1, &readset, &writeset, &exceptset, &timout); + for (i = 0; i < oss_nmidiin; i++) + if (FD_ISSET(oss_midiinfd[i], &readset)) + { + char c; + int ret = read(oss_midiinfd[i], &c, 1); + if (ret <= 0) + fprintf(stderr, "Midi read error\n"); + else sys_midibytein(i, (c & 0xff)); + did = 1; + } + } +} +#else + + /* this version uses the asynchronous "read()" ... */ +void sys_poll_midi(void) +{ + int i, throttle = 100; + struct timeval timout; + int did = 1, maxfd = 0; + while (did) + { + fd_set readset, writeset, exceptset; + did = 0; + if (throttle-- < 0) + break; + for (i = 0; i < oss_nmidiin; i++) + { + char c; + int ret = read(oss_midiinfd[i], &c, 1); + if (ret < 0) + { + if (errno != EAGAIN) + perror("MIDI"); + } + else if (ret != 0) + { + sys_midibytein(i, (c & 0xff)); + did = 1; + } + } + } +} +#endif + +void linux_close_midi() +{ + int i; + for (i = 0; i < oss_nmidiin; i++) + close(oss_midiinfd[i]); + for (i = 0; i < oss_nmidiout; i++) + close(oss_midioutfd[i]); + oss_nmidiin = oss_nmidiout = 0; +} + +#define MAXAUDIODEV 4 +#define DEFAULTINDEV 1 +#define DEFAULTOUTDEV 1 + +/* ----------------------- public routines ----------------------- */ +void sys_listdevs( void) +{ + post("device listing not implemented in Linux yet\n"); +} + +void sys_open_audio(int naudioindev, int *audioindev, int nchindev, + int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, + int *choutdev, int rate) +{ /* IOhannes */ + int i, *ip; + int defaultchannels = + (linux_whichapi == API_RME ? RME_DEFAULTCH : OSS_DEFAULTCH); + if (rate < 1) + rate=44100; + + if (naudioindev == -1) + { /* not set */ + if (nchindev==-1) + { + nchindev=1; + chindev[0]=defaultchannels; + naudioindev=1; + audioindev[0] = DEFAULTINDEV; + } + else + { + for (i = 0; i < MAXAUDIODEV; i++) + audioindev[i]=i+1; + naudioindev = nchindev; + } + } + else + { + if (nchindev == -1) + { + nchindev = naudioindev; + for (i = 0; i < naudioindev; i++) + chindev[i] = defaultchannels; + } + else if (nchindev > naudioindev) + { + for (i = naudioindev; i < nchindev; i++) + { + if (i == 0) + audioindev[0] = DEFAULTINDEV; + else audioindev[i] = audioindev[i-1] + 1; + } + naudioindev = nchindev; + } + else if (nchindev < naudioindev) + { + for (i = nchindev; i < naudioindev; i++) + { + if (i == 0) + chindev[0] = defaultchannels; + else chindev[i] = chindev[i-1]; + } + naudioindev = nchindev; + } + } + + if (naudiooutdev == -1) + { /* not set */ + if (nchoutdev==-1) + { + nchoutdev=1; + choutdev[0]=defaultchannels; + naudiooutdev=1; + audiooutdev[0] = DEFAULTOUTDEV; + } + else + { + for (i = 0; i < MAXAUDIODEV; i++) + audiooutdev[i] = i+1; + naudiooutdev = nchoutdev; + } + } + else + { + if (nchoutdev == -1) + { + nchoutdev = naudiooutdev; + for (i = 0; i < naudiooutdev; i++) + choutdev[i] = defaultchannels; + } + else if (nchoutdev > naudiooutdev) + { + for (i = naudiooutdev; i < nchoutdev; i++) + { + if (i == 0) + audiooutdev[0] = DEFAULTOUTDEV; + else audiooutdev[i] = audiooutdev[i-1] + 1; + } + naudiooutdev = nchoutdev; + } + else if (nchoutdev < naudiooutdev) + { + for (i = nchoutdev; i < naudiooutdev; i++) + { + if (i == 0) + choutdev[0] = defaultchannels; + else choutdev[i] = choutdev[i-1]; + } + naudiooutdev = nchoutdev; + } + } + + linux_flush_all_underflows_to_zero(); +#ifdef ALSA + if (linux_whichapi == API_ALSA) + alsa_open_audio((naudioindev > 0 ? chindev[0] : 0), + (naudiooutdev > 0 ? choutdev[0] : 0), rate); + else +#endif +#ifdef RME_HAMMERFALL + if (linux_whichapi == API_RME) + rme9652_open_audio((naudioindev > 0 ? chindev[0] : 0), + (naudiooutdev > 0 ? choutdev[0] : 0), rate); + else +#endif + oss_open_audio(naudioindev, audioindev, nchindev, chindev, + naudiooutdev, audiooutdev, nchoutdev, choutdev, rate); +} + +void sys_close_audio(void) +{ + /* set timeout to avoid hanging close() call */ + + sys_setalarm(1000000); + +#ifdef ALSA + if (linux_whichapi == API_ALSA) + alsa_close_audio(); + else +#endif +#ifdef RME_HAMMERFALL + if (linux_whichapi == API_RME) + rme9652_close_audio(); + else +#endif + oss_close_audio(); + + sys_setalarm(0); +} + +void sys_open_midi(int nmidiin, int *midiinvec, + int nmidiout, int *midioutvec) +{ + linux_open_midi(nmidiin, midiinvec, nmidiout, midioutvec); +} + +void sys_close_midi( void) +{ + sys_setalarm(1000000); + linux_close_midi(); + sys_setalarm(0); +} + +int sys_send_dacs(void) +{ + if (linux_meters) + { + int i, n; + float maxsamp; + for (i = 0, n = linux_inchannels * DACBLKSIZE, maxsamp = linux_inmax; + i < n; i++) + { + float f = sys_soundin[i]; + if (f > maxsamp) maxsamp = f; + else if (-f > maxsamp) maxsamp = -f; + } + linux_inmax = maxsamp; + for (i = 0, n = linux_outchannels * DACBLKSIZE, maxsamp = linux_outmax; + i < n; i++) + { + float f = sys_soundout[i]; + if (f > maxsamp) maxsamp = f; + else if (-f > maxsamp) maxsamp = -f; + } + linux_outmax = maxsamp; + } +#ifdef ALSA + if (linux_whichapi == API_ALSA) + return alsa_send_dacs(); +#endif +#ifdef RME_HAMMERFALL + if (linux_whichapi == API_RME) + return rme9652_send_dacs(); +#endif + return oss_send_dacs(); +} + +float sys_getsr(void) +{ + return (sys_dacsr); +} + +int sys_get_outchannels(void) +{ + return (linux_outchannels); +} + +int sys_get_inchannels(void) +{ + return (linux_inchannels); +} + +void sys_audiobuf(int n) +{ + /* set the size, in milliseconds, of the audio FIFO */ + if (n < 5) n = 5; + else if (n > 5000) n = 5000; + sys_schedadvance = n * 1000; +} + +void sys_getmeters(float *inmax, float *outmax) +{ + if (inmax) + { + linux_meters = 1; + *inmax = linux_inmax; + *outmax = linux_outmax; + } + else + linux_meters = 0; + linux_inmax = linux_outmax = 0; +} + +void sys_reportidle(void) +{ +} + +void sys_set_priority(int higher) +{ + struct sched_param par; + int p1 ,p2, p3; +#ifdef _POSIX_PRIORITY_SCHEDULING + + p1 = sched_get_priority_min(SCHED_FIFO); + p2 = sched_get_priority_max(SCHED_FIFO); + p3 = (higher ? p2 - 1 : p2 - 3); + par.sched_priority = p3; + + if (sched_setscheduler(0,SCHED_FIFO,&par) != -1) + fprintf(stderr, "priority %d scheduling enabled.\n", p3); +#endif + +#ifdef _POSIX_MEMLOCK + if (mlockall(MCL_FUTURE) != -1) + fprintf(stderr, "memory locking enabled.\n"); +#endif +} + +void sys_setblocksize(int n) +{ + if (n < 1) + n = 1; + linux_fragsize = n; + oss_blockmode = 1; +} + +/* ------------ linux-specific command-line flags -------------- */ + +void linux_setfrags(int n) +{ + linux_nfragment = n; + oss_blockmode = 1; +} + +void linux_streammode( void) +{ + oss_blockmode = 0; +} + +void linux_32bit( void) +{ + oss_32bit = 1; +} + +void linux_set_sound_api(int which) +{ + linux_whichapi = which; + if (sys_verbose) + post("linux_whichapi %d", linux_whichapi); +} + +#ifdef ALSA99 +void linux_alsa_devno(int devno) +{ + alsa_devno = devno; +} + +#endif + +#ifdef ALSA01 +void linux_alsa_devname(char *devname) +{ + strncpy(alsa_devname, devname, 511); +} + +void linux_alsa_use_plugin(int t) +{ + if (t == 1) + alsa_use_plugin = 1; + else + alsa_use_plugin = 0; +} + +#endif + +/* -------------- Audio I/O using the OSS API ------------------ */ + +typedef struct _multidev { + int fd; + int channels; + int format; +} t_multidev; + +int oss_reset(int fd) { + int err; + if ((err = ioctl(fd,SNDCTL_DSP_RESET)) < 0) + error("OSS: Could not reset"); + return err; +} + + /* The AFMT_S32_BLOCKED format is not defined in standard linux kernels + but is proposed by Guenter Geiger to support extending OSS to handle + 32 bit sample. This is user in Geiger's OSS driver for RME Hammerfall. + I'm not clear why this isn't called AFMT_S32_[SLN]E... */ + +#ifndef AFMT_S32_BLOCKED +#define AFMT_S32_BLOCKED 0x0000400 +#endif + +void oss_configure(t_oss_dev *dev, int srate, int dac, int skipblocksize) +{ /* IOhannes */ + int orig, param, nblk, fd = dev->d_fd, wantformat; + int nchannels = dev->d_nchannels; + int advwas = sys_schedadvance; + + audio_buf_info ainfo; + + /* IOhannes : + * pd is very likely to crash if different formats are used on + multiple soundcards + */ + + /* set resolution - first try 4 byte samples */ + if (oss_32bit && (ioctl(fd,SNDCTL_DSP_GETFMTS,¶m) >= 0) && + (param & AFMT_S32_BLOCKED)) + { + wantformat = AFMT_S32_BLOCKED; + dev->d_bytespersamp = 4; + } + else + { + wantformat = AFMT_S16_NE; + dev->d_bytespersamp = 2; + } + param = wantformat; + + if (sys_verbose) + post("bytes per sample = %d", dev->d_bytespersamp); + if (ioctl(fd, SNDCTL_DSP_SETFMT, ¶m) == -1) + fprintf(stderr,"OSS: Could not set DSP format\n"); + else if (wantformat != param) + fprintf(stderr,"OSS: DSP format: wanted %d, got %d\n", + wantformat, param); + + /* sample rate */ + orig = param = srate; + if (ioctl(fd, SNDCTL_DSP_SPEED, ¶m) == -1) + fprintf(stderr,"OSS: Could not set sampling rate for device\n"); + else if( orig != param ) + fprintf(stderr,"OSS: sampling rate: wanted %d, got %d\n", + orig, param ); + + if (oss_blockmode && !skipblocksize) + { + int fragbytes, logfragsize, nfragment; + /* setting fragment count and size. */ + if (linux_nfragment) /* if nfrags specified, take literally */ + { + nfragment = linux_nfragment; + if (!linux_fragsize) + linux_fragsize = OSS_DEFFRAGSIZE; + sys_schedadvance = ((nfragment * linux_fragsize) * 1.e6) + / (float)srate; + linux_setsr(srate); + } + else + { + if (!linux_fragsize) + { + linux_fragsize = OSS_DEFFRAGSIZE; + while (linux_fragsize > DACBLKSIZE + && linux_fragsize * 4 > linux_advance_samples) + linux_fragsize = linux_fragsize/2; + } + /* post("adv_samples %d", linux_advance_samples); */ + nfragment = (sys_schedadvance * (44100. * 1.e-6)) / linux_fragsize; + } + fragbytes = linux_fragsize * (dev->d_bytespersamp * nchannels); + logfragsize = ilog2(fragbytes); + + if (fragbytes != (1 << logfragsize)) + post("warning: OSS takes only power of 2 blocksize; using %d", + (1 << logfragsize)/(dev->d_bytespersamp * nchannels)); + if (sys_verbose) + post("setting nfrags = %d, fragsize %d\n", nfragment, fragbytes); + + param = orig = (nfragment<<16) + logfragsize; + if (ioctl(fd,SNDCTL_DSP_SETFRAGMENT, ¶m) == -1) + error("OSS: Could not set or read fragment size\n"); + if (param != orig) + { + nfragment = ((param >> 16) & 0xffff); + logfragsize = (param & 0xffff); + post("warning: actual fragments %d, blocksize %d", + nfragment, (1 << logfragsize)); + } + if (sys_verbose) + post("audiobuffer set to %d msec", (int)(0.001 * sys_schedadvance)); + } + + if (dac) + { + /* use "free space" to learn the buffer size. Normally you + should set this to your own desired value; but this seems not + to be implemented uniformly across different sound cards. LATER + we should figure out what to do if the requested scheduler advance + is greater than this buffer size; for now, we just print something + out. */ + + int defect; + if (ioctl(fd, SOUND_PCM_GETOSPACE,&ainfo) < 0) + fprintf(stderr,"OSS: ioctl on output device failed"); + dev->d_bufsize = ainfo.bytes; + + defect = linux_advance_samples * (dev->d_bytespersamp * nchannels) + - dev->d_bufsize - OSS_XFERSIZE(nchannels, dev->d_bytespersamp); + if (defect > 0) + { + if (sys_verbose || defect > (dev->d_bufsize >> 2)) + fprintf(stderr, + "OSS: requested audio buffer size %d limited to %d\n", + linux_advance_samples * (dev->d_bytespersamp * nchannels), + dev->d_bufsize); + linux_advance_samples = + (dev->d_bufsize - OSS_XFERSAMPS(nchannels)) / + (dev->d_bytespersamp *nchannels); + } + } +} + +static int oss_setchannels(int fd, int wantchannels, char *devname) +{ /* IOhannes */ + int param = wantchannels; + + while (param>1) { + int save = param; + if (ioctl(fd, SNDCTL_DSP_CHANNELS, ¶m) == -1) { + error("OSS: SNDCTL_DSP_CHANNELS failed %s",devname); + } else { + if (param == save) return (param); + } + param=save-1; + } + + return (0); +} + +#define O_AUDIOFLAG 0 /* O_NDELAY */ + +int oss_open_audio(int nindev, int *indev, int nchin, int *chin, + int noutdev, int *outdev, int nchout, int *chout, int rate) +{ /* IOhannes */ + int capabilities = 0; + int inchannels = 0, outchannels = 0; + char devname[20]; + int n, i, fd; + char buf[OSS_MAXSAMPLEWIDTH * DACBLKSIZE * OSS_MAXCHPERDEV]; + int num_devs = 0; + int wantmore=0; + int spread = 0; + audio_buf_info ainfo; + + linux_nindevs = linux_noutdevs = 0; + + /* set logical sample rate amd calculate linux_advance_samples. */ + linux_setsr(rate); + + /* mark input devices unopened */ + for (i = 0; i < OSS_MAXDEV; i++) + linux_adcs[i].d_fd = -1; + + /* open output devices */ + wantmore=0; + if (noutdev < 0 || nindev < 0) + bug("linux_open_audio"); + + for (n = 0; n < noutdev; n++) + { + int gotchans, j, inindex = -1; + int thisdevice=outdev[n]; + int wantchannels = (nchout>n) ? chout[n] : wantmore; + fd = -1; + if (!wantchannels) + goto end_out_loop; + + if (thisdevice > 1) + sprintf(devname, "/dev/dsp%d", thisdevice-1); + else sprintf(devname, "/dev/dsp"); + + /* search for input request for same device. Succeed only + if the number of channels matches. */ + for (j = 0; j < nindev; j++) + if (indev[j] == thisdevice && chin[j] == wantchannels) + inindex = j; + + /* if the same device is requested for input and output, + try to open it read/write */ + if (inindex >= 0) + { + sys_setalarm(1000000); + if ((fd = open(devname, O_RDWR | O_AUDIOFLAG)) == -1) + { + post("%s (read/write): %s", devname, strerror(errno)); + post("(now will try write-only...)"); + } + else + { + if (sys_verbose) + post("opened %s for reading and writing\n", devname); + linux_adcs[inindex].d_fd = fd; + } + } + /* if that didn't happen or if it failed, try write-only */ + if (fd == -1) + { + sys_setalarm(1000000); + if ((fd = open(devname, O_WRONLY | O_AUDIOFLAG)) == -1) + { + post("%s (writeonly): %s", + devname, strerror(errno)); + break; + } + if (sys_verbose) + post("opened %s for writing only\n", devname); + } + if (ioctl(fd, SNDCTL_DSP_GETCAPS, &capabilities) == -1) + error("OSS: SNDCTL_DSP_GETCAPS failed %s", devname); + + gotchans = oss_setchannels(fd, + (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels, + devname); + + if (sys_verbose) + post("opened audio output on %s; got %d channels", + devname, gotchans); + + if (gotchans < 2) + { + /* can't even do stereo? just give up. */ + close(fd); + } + else + { + linux_dacs[linux_noutdevs].d_nchannels = gotchans; + linux_dacs[linux_noutdevs].d_fd = fd; + oss_configure(linux_dacs+linux_noutdevs, rate, 1, 0); + + linux_noutdevs++; + outchannels += gotchans; + if (inindex >= 0) + { + linux_adcs[inindex].d_nchannels = gotchans; + chin[inindex] = gotchans; + } + } + /* LATER think about spreading large numbers of channels over + various dsp's and vice-versa */ + wantmore = wantchannels - gotchans; + end_out_loop: ; + } + + /* open input devices */ + wantmore = 0; + if (nindev==-1) + nindev=4; /* spread channels over default-devices */ + for (n = 0; n < nindev; n++) + { + int gotchans=0; + int thisdevice=indev[n]; + int wantchannels = (nchin>n)?chin[n]:wantmore; + int alreadyopened = 0; + if (!wantchannels) + goto end_in_loop; + + if (thisdevice > 1) + sprintf(devname, "/dev/dsp%d", thisdevice - 1); + else sprintf(devname, "/dev/dsp"); + + sys_setalarm(1000000); + + /* perhaps it's already open from the above? */ + if (linux_dacs[n].d_fd >= 0) + { + fd = linux_dacs[n].d_fd; + alreadyopened = 1; + } + else + { + /* otherwise try to open it here. */ + if ((fd = open(devname, O_RDONLY | O_AUDIOFLAG)) == -1) + { + post("%s (readonly): %s", devname, strerror(errno)); + goto end_in_loop; + } + if (sys_verbose) + post("opened %s for reading only\n", devname); + } + linux_adcs[linux_nindevs].d_fd = fd; + gotchans = oss_setchannels(fd, + (wantchannels>OSS_MAXCHPERDEV)?OSS_MAXCHPERDEV:wantchannels, + devname); + if (sys_verbose) + post("opened audio input device %s; got %d channels", + devname, gotchans); + + if (gotchans < 1) + { + close(fd); + goto end_in_loop; + } + + linux_adcs[linux_nindevs].d_nchannels = gotchans; + + oss_configure(linux_adcs+linux_nindevs, rate, 0, alreadyopened); + + inchannels += gotchans; + linux_nindevs++; + + wantmore = wantchannels-gotchans; + /* LATER think about spreading large numbers of channels over + various dsp's and vice-versa */ + end_in_loop: ; + } + + linux_setch(inchannels, outchannels); + + /* We have to do a read to start the engine. This is + necessary because sys_send_dacs waits until the input + buffer is filled and only reads on a filled buffer. + This is good, because it's a way to make sure that we + will not block. But I wonder why we only have to read + from one of the devices and not all of them??? */ + + if (linux_nindevs) + { + if (sys_verbose) + fprintf(stderr,("OSS: issuing first ADC 'read' ... ")); + read(linux_adcs[0].d_fd, buf, + linux_adcs[0].d_bytespersamp * + linux_adcs[0].d_nchannels * DACBLKSIZE); + if (sys_verbose) + fprintf(stderr, "...done.\n"); + } + sys_setalarm(0); + return (0); +} + +void oss_close_audio( void) +{ + int i; + for (i=0;i + OSS_XFERSIZE(linux_adcs[dev].d_nchannels, + linux_adcs[dev].d_bytespersamp)) + { + linux_adcs_read(linux_adcs[dev].d_fd, buf, + OSS_XFERSIZE(linux_adcs[dev].d_nchannels, + linux_adcs[dev].d_bytespersamp)); + if (ioctl(linux_adcs[dev].d_fd, SOUND_PCM_GETISPACE, &ainfo) < 0) + { + fprintf(stderr, "OSS: ioctl on input device %d, fd %d failed", + dev, linux_adcs[dev].d_fd); + break; + } + linux_adcs[dev].d_space = ainfo.bytes; + } + } + + /* 2. if any output devices are behind, feed them zeros to catch them + up */ + for (dev = 0; dev < linux_noutdevs; dev++) + { + while (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize - + linux_advance_samples * (linux_dacs[dev].d_nchannels * + linux_dacs[dev].d_bytespersamp)) + { + if (!zeroed) + { + unsigned int i; + for (i = 0; i < OSS_XFERSAMPS(linux_dacs[dev].d_nchannels); + i++) + buf[i] = 0; + zeroed = 1; + } + linux_dacs_write(linux_dacs[dev].d_fd, buf, + OSS_XFERSIZE(linux_dacs[dev].d_nchannels, + linux_dacs[dev].d_bytespersamp)); + if (ioctl(linux_dacs[dev].d_fd, SOUND_PCM_GETOSPACE, &ainfo) < 0) + { + fprintf(stderr, "OSS: ioctl on output device %d, fd %d failed", + dev, linux_dacs[dev].d_fd); + break; + } + linux_dacs[dev].d_space = ainfo.bytes; + } + } + /* 3. if any DAC devices are too far ahead, plan to drop the + number of frames which will let the others catch up. */ + for (dev = 0; dev < linux_noutdevs; dev++) + { + if (linux_dacs[dev].d_space > linux_dacs[dev].d_bufsize - + (linux_advance_samples - 1) * linux_dacs[dev].d_nchannels * + linux_dacs[dev].d_bytespersamp) + { + linux_dacs[dev].d_dropcount = linux_advance_samples - 1 - + (linux_dacs[dev].d_space - linux_dacs[dev].d_bufsize) / + (linux_dacs[dev].d_nchannels * + linux_dacs[dev].d_bytespersamp) ; + } + else linux_dacs[dev].d_dropcount = 0; + } +} + +int oss_send_dacs(void) +{ + float *fp1, *fp2; + long fill; + int i, j, dev, rtnval = SENDDACS_YES; + char buf[OSS_MAXSAMPLEWIDTH * DACBLKSIZE * OSS_MAXCHPERDEV]; + t_oss_int16 *sp; + t_oss_int32 *lp; + /* the maximum number of samples we should have in the ADC buffer */ + int idle = 0; + int thischan; + double timeref, timenow; + + if (!linux_nindevs && !linux_noutdevs) + return (SENDDACS_NO); + + if (!oss_blockmode) + { + /* determine whether we're idle. This is true if either (1) + some input device has less than one buffer to read or (2) some + output device has fewer than (linux_advance_samples) blocks buffered + already. */ + oss_calcspace(); + + for (dev=0; dev < linux_noutdevs; dev++) + if (linux_dacs[dev].d_dropcount || + (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space > + linux_advance_samples * linux_dacs[dev].d_bytespersamp * + linux_dacs[dev].d_nchannels)) + idle = 1; + for (dev=0; dev < linux_nindevs; dev++) + if (linux_adcs[dev].d_space < + OSS_XFERSIZE(linux_adcs[dev].d_nchannels, + linux_adcs[dev].d_bytespersamp)) + idle = 1; + } + + if (idle && !oss_blockmode) + { + /* sometimes---rarely---when the ADC available-byte-count is + zero, it's genuine, but usually it's because we're so + late that the ADC has overrun its entire kernel buffer. We + distinguish between the two by waiting 2 msec and asking again. + There should be an error flag we could check instead; look for this + someday... */ + for (dev = 0;dev < linux_nindevs; dev++) + if (linux_adcs[dev].d_space == 0) + { + audio_buf_info ainfo; + sys_microsleep(2000); + oss_calcspace(); + if (linux_adcs[dev].d_space != 0) continue; + + /* here's the bad case. Give up and resync. */ + sys_log_error(ERR_DATALATE); + oss_doresync(); + return (SENDDACS_NO); + } + /* check for slippage between devices, either because + data got lost in the driver from a previous late condition, or + because the devices aren't synced. When we're idle, no + input device should have more than one buffer readable and + no output device should have less than linux_advance_samples-1 + */ + + for (dev=0; dev < linux_noutdevs; dev++) + if (!linux_dacs[dev].d_dropcount && + (linux_dacs[dev].d_bufsize - linux_dacs[dev].d_space < + (linux_advance_samples - 2) * + (linux_dacs[dev].d_bytespersamp * + linux_dacs[dev].d_nchannels))) + goto badsync; + for (dev=0; dev < linux_nindevs; dev++) + if (linux_adcs[dev].d_space > 3 * + OSS_XFERSIZE(linux_adcs[dev].d_nchannels, + linux_adcs[dev].d_bytespersamp)) + goto badsync; + + /* return zero to tell the scheduler we're idle. */ + return (SENDDACS_NO); + badsync: + sys_log_error(ERR_RESYNC); + oss_doresync(); + return (SENDDACS_NO); + + } + + /* do output */ + + timeref = sys_getrealtime(); + for (dev=0, thischan = 0; dev < linux_noutdevs; dev++) + { + int nchannels = linux_dacs[dev].d_nchannels; + if (linux_dacs[dev].d_dropcount) + linux_dacs[dev].d_dropcount--; + else + { + if (linux_dacs[dev].d_bytespersamp == 4) + { + for (i = DACBLKSIZE * nchannels, fp1 = sys_soundout + + DACBLKSIZE*thischan, + lp = (t_oss_int32 *)buf; i--; fp1++, lp++) + { + float f = *fp1 * 2147483648.; + *lp = (f >= 2147483647. ? 2147483647. : + (f < -2147483648. ? -2147483648. : f)); + } + } + else + { + for (i = DACBLKSIZE, fp1 = sys_soundout + + DACBLKSIZE*thischan, + sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) + { + for (j=0, fp2 = fp1; j 32767) s = 32767; + else if (s < -32767) s = -32767; + sp[j] = s; + } + } + } + linux_dacs_write(linux_dacs[dev].d_fd, buf, + OSS_XFERSIZE(nchannels, linux_dacs[dev].d_bytespersamp)); + if ((timenow = sys_getrealtime()) - timeref > 0.002) + { + if (!oss_blockmode) + sys_log_error(ERR_DACSLEPT); + else rtnval = SENDDACS_SLEPT; + } + timeref = timenow; + } + thischan += nchannels; + } + memset(sys_soundout, 0, + linux_outchannels * (sizeof(float) * DACBLKSIZE)); + + /* do input */ + + for (dev = 0, thischan = 0; dev < linux_nindevs; dev++) + { + int nchannels = linux_adcs[dev].d_nchannels; + linux_adcs_read(linux_adcs[dev].d_fd, buf, + OSS_XFERSIZE(nchannels, linux_adcs[dev].d_bytespersamp)); + + if ((timenow = sys_getrealtime()) - timeref > 0.002) + { + if (!oss_blockmode) + sys_log_error(ERR_ADCSLEPT); + else + rtnval = SENDDACS_SLEPT; + } + timeref = timenow; + + if (linux_adcs[dev].d_bytespersamp == 4) + { + for (i = DACBLKSIZE*nchannels, + fp1 = sys_soundin + thischan*DACBLKSIZE, + lp = (t_oss_int32 *)buf; i--; fp1++, lp++) + { + *fp1 = ((float)(*lp))*(float)(1./2147483648.); + } + } + else + { + for (i = DACBLKSIZE,fp1 = sys_soundin + thischan*DACBLKSIZE, + sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) + { + for (j=0;j channelinfo.max_voices) + post("decreasing input channels to maximum of %d\n", + wantinchans = channelinfo.max_voices); + if (alsa_samplewidth == 4 && + !(channelinfo.formats & (1< channelinfo.max_voices) + post("decreasing output channels to maximum of %d\n", + wantoutchans = channelinfo.max_voices); + if (alsa_samplewidth == 4 && + !(channelinfo.formats & (1< linux_outchannels ? linux_inchannels : + linux_outchannels) * DACBLKSIZE; + alsa_buf = malloc(bsize); + if (!alsa_buf) + return (1); + memset(alsa_buf, 0, bsize); + return 0; +} + +void alsa_set_params(t_alsa_dev *dev, int dir, int rate, int voices) +{ + int err; + struct snd_pcm_channel_params params; + + memset(&dev->info, 0, sizeof(dev->info)); + dev->info.channel = dir; + if ((err = snd_pcm_channel_info(dev->handle, &dev->info) < 0)) + { + fprintf(stderr, "PD-ALSA: error getting channel info: %s\n", + snd_strerror(err)); + } + memset(¶ms, 0, sizeof(params)); + params.format.interleave = 1; /* may do non-interleaved later */ + /* format is 2 or 4 bytes per sample depending on what was possible */ + params.format.format = + (alsa_samplewidth == 4 ? SND_PCM_SFMT_S32_LE : SND_PCM_SFMT_S16_LE); + + /*will check this further down -just try for now*/ + params.format.rate = rate; + params.format.voices = voices; + params.start_mode = SND_PCM_START_GO; /* seems most reliable */ + /*do not stop at overrun/underrun*/ + params.stop_mode = SND_PCM_STOP_ROLLOVER; + + params.channel = dir; /* playback|capture */ + params.buf.stream.queue_size = + (ALSA_EXTRABUFFER + linux_advance_samples) + * alsa_samplewidth * voices; + params.buf.stream.fill = SND_PCM_FILL_SILENCE_WHOLE; + params.mode = SND_PCM_MODE_STREAM; + + if ((err = snd_pcm_channel_params(dev->handle, ¶ms)) < 0) + { + printf("PD-ALSA: error setting parameters %s", snd_strerror(err)); + } + + /* This should clear the buffers but does not. There is often noise at + startup that sounds like crap left in the buffers - maybe in the lib + instead of the driver? Some solution needs to be found. + */ + + if ((err = snd_pcm_channel_prepare(dev->handle, dir)) < 0) + { + printf("PD-ALSA: error preparing channel %s", snd_strerror(err)); + } + dev->setup.channel = dir; + + if ((err = snd_pcm_channel_setup(dev->handle, &dev->setup)) < 0) + { + printf("PD-ALSA: error getting setup %s", snd_strerror(err)); + } + /* for some reason, if you don't writesomething before starting the + converters we get trash on startup */ + if (dir == SND_PCM_CHANNEL_PLAYBACK) + { + char foo[1024]; + int xxx = 1024 - (1024 % (linux_outchannels * alsa_samplewidth)); + int i, r; + for (i = 0; i < xxx; i++) + foo[i] = 0; + if ((r = snd_pcm_write(dev->handle, foo, xxx)) < xxx) + fprintf(stderr, "alsa_write: %s\n", snd_strerror(errno)); + } + snd_pcm_channel_go(dev->handle, dir); +} + +void alsa_close_audio(void) +{ + int i; + for(i = 0; i < n_alsa_dev; i++) + snd_pcm_close(alsa_device[i].handle); +} + +/* #define DEBUG_ALSA_XFER */ + +int alsa_send_dacs(void) +{ + static int16_t *sp; + t_sample *fp, *fp1, *fp2; + int i, j, k, err, devno = 0; + int inputcount = 0, outputcount = 0, inputlate = 0, outputlate = 0; + int result; + snd_pcm_channel_status_t stat; + static int callno = 0; + static int xferno = 0; + int countwas = 0; + double timelast; + static double timenow; + int inchannels = linux_inchannels; + int outchannels = linux_outchannels; + int inbytesperframe = inchannels * alsa_samplewidth; + int outbytesperframe = outchannels * alsa_samplewidth; + int intransfersize = DACBLKSIZE * inbytesperframe; + int outtransfersize = DACBLKSIZE * outbytesperframe; + int alsaerror; + int loggederror = 0; + + if (!inchannels && !outchannels) + return (SENDDACS_NO); + timelast = timenow; + timenow = sys_getrealtime(); + +#ifdef DEBUG_ALSA_XFER + if (timenow - timelast > 0.050) + fprintf(stderr, "(%d)", + (int)(1000 * (timenow - timelast))), fflush(stderr); +#endif + + callno++; + /* get input and output channel status */ + if (inchannels > 0) + { + devno = 0; + stat.channel = SND_PCM_CHANNEL_CAPTURE; + if (alsaerror = snd_pcm_channel_status(alsa_device[devno].handle, + &stat)) + { + fprintf(stderr, "snd_pcm_channel_status (input): %s\n", + snd_strerror(alsaerror)); + return (SENDDACS_NO); + } + inputcount = stat.count; + inputlate = (stat.underrun > 0 || stat.overrun > 0); + } + if (outchannels > 0) + { + devno = 0; + stat.channel = SND_PCM_CHANNEL_PLAYBACK; + if (alsaerror = snd_pcm_channel_status(alsa_device[devno].handle, + &stat)) + { + fprintf(stderr, "snd_pcm_channel_status (output): %s\n", + snd_strerror(alsaerror)); + return (SENDDACS_NO); + } + outputcount = stat.count; + outputlate = (stat.underrun > 0 || stat.overrun > 0); + } + + /* check if input not ready */ + if (inputcount < intransfersize) + { + /* fprintf(stderr, "no adc; count %d, free %d, call %d, xfer %d\n", + stat.count, + stat.free, + callno, xferno); */ + if (outchannels > 0) + { + /* if there's no input but output is hungry, feed output. */ + while (outputcount < (linux_advance_samples + ALSA_JITTER) + * outbytesperframe) + { + if (!loggederror) + sys_log_error(ERR_RESYNC), loggederror = 1; + memset(alsa_buf, 0, outtransfersize); + result = snd_pcm_write(alsa_device[devno].handle, + alsa_buf, outtransfersize); + if (result < outtransfersize) + { +#ifdef DEBUG_ALSA_XFER + if (result >= 0 || errno == EAGAIN) + fprintf(stderr, "ALSA: write returned %d of %d\n", + result, outtransfersize); + else fprintf(stderr, "ALSA: write: %s\n", + snd_strerror(errno)); + fprintf(stderr, + "inputcount %d, outputcount %d, outbufsize %d\n", + inputcount, outputcount, + (ALSA_EXTRABUFFER + linux_advance_samples) + * alsa_samplewidth * outchannels); +#endif + return (SENDDACS_NO); + } + stat.channel = SND_PCM_CHANNEL_PLAYBACK; + if (alsaerror = + snd_pcm_channel_status(alsa_device[devno].handle, + &stat)) + { + fprintf(stderr, "snd_pcm_channel_status (output): %s\n", + snd_strerror(alsaerror)); + return (SENDDACS_NO); + } + outputcount = stat.count; + } + } + + return SENDDACS_NO; + } + + /* if output buffer has at least linux_advance_samples in it, we're + not ready for this batch. */ + if (outputcount > linux_advance_samples * outbytesperframe) + { + if (inchannels > 0) + { + while (inputcount > (DACBLKSIZE + ALSA_JITTER) * outbytesperframe) + { + if (!loggederror) + sys_log_error(ERR_RESYNC), loggederror = 1; + devno = 0; + result = snd_pcm_read(alsa_device[devno].handle, alsa_buf, + intransfersize); + if (result < intransfersize) + { +#ifdef DEBUG_ALSA_XFER + if (result < 0) + fprintf(stderr, + "snd_pcm_read %d %d: %s\n", + callno, xferno, snd_strerror(errno)); + else fprintf(stderr, + "snd_pcm_read %d %d returned only %d\n", + callno, xferno, result); + fprintf(stderr, + "inputcount %d, outputcount %d, inbufsize %d\n", + inputcount, outputcount, + (ALSA_EXTRABUFFER + linux_advance_samples) + * alsa_samplewidth * inchannels); +#endif + return (SENDDACS_NO); + } + devno = 0; + stat.channel = SND_PCM_CHANNEL_CAPTURE; + if (alsaerror = + snd_pcm_channel_status(alsa_device[devno].handle, + &stat)) + { + fprintf(stderr, "snd_pcm_channel_status (input): %s\n", + snd_strerror(alsaerror)); + return (SENDDACS_NO); + } + inputcount = stat.count; + inputlate = (stat.underrun > 0 || stat.overrun > 0); + } + return (SENDDACS_NO); + } + } + if (sys_getrealtime() - timenow > 0.002) + { +#ifdef DEBUG_ALSA_XFER + fprintf(stderr, "check %d took %d msec\n", + callno, (int)(1000 * (timenow - timelast))), fflush(stderr); +#endif + sys_log_error(ERR_DACSLEPT); + timenow = sys_getrealtime(); + } + if (inputlate || outputlate) + sys_log_error(ERR_DATALATE); + + /* do output */ + /* this "for" loop won't work for more than one device. */ + for (devno = 0, fp = sys_soundout; devno < (outchannels > 0); devno++, + fp += 128) + { + if (alsa_samplewidth == 4) + { + for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; + j += outchannels, fp2++) + { + float s1 = *fp2 * INT32_MAX; + ((t_alsa_sample32 *)alsa_buf)[j] = CLIP32(s1); + } + } + } + else + { + for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; + j += outchannels, fp2++) + { + int s = *fp2 * 32767.; + if (s > 32767) + s = 32767; + else if (s < -32767) + s = -32767; + ((t_alsa_sample16 *)alsa_buf)[j] = s; + } + } + } + + result = snd_pcm_write(alsa_device[devno].handle, alsa_buf, + outtransfersize); + if (result < outtransfersize) + { +#ifdef DEBUG_ALSA_XFER + if (result >= 0 || errno == EAGAIN) + fprintf(stderr, "ALSA: write returned %d of %d\n", + result, outtransfersize); + else fprintf(stderr, "ALSA: write: %s\n", + snd_strerror(errno)); + fprintf(stderr, + "inputcount %d, outputcount %d, outbufsize %d\n", + inputcount, outputcount, + (ALSA_EXTRABUFFER + linux_advance_samples) + * alsa_samplewidth * outchannels); +#endif + sys_log_error(ERR_DACSLEPT); + return (SENDDACS_NO); + } + } + /* zero out the output buffer */ + memset(sys_soundout, 0, DACBLKSIZE * sizeof(*sys_soundout) * + linux_outchannels); + if (sys_getrealtime() - timenow > 0.002) + { +#if DEBUG_ALSA_XFER + fprintf(stderr, "output %d took %d msec\n", + callno, (int)(1000 * (timenow - timelast))), fflush(stderr); +#endif + timenow = sys_getrealtime(); + sys_log_error(ERR_DACSLEPT); + } + + /* do input */ + for (devno = 0, fp = sys_soundin; devno < (linux_inchannels > 0); devno++, + fp += 128) + { + result = snd_pcm_read(alsa_device[devno].handle, alsa_buf, + intransfersize); + if (result < intransfersize) + { +#ifdef DEBUG_ALSA_XFER + if (result < 0) + fprintf(stderr, + "snd_pcm_read %d %d: %s\n", + callno, xferno, snd_strerror(errno)); + else fprintf(stderr, + "snd_pcm_read %d %d returned only %d\n", + callno, xferno, result); + fprintf(stderr, + "inputcount %d, outputcount %d, inbufsize %d\n", + inputcount, outputcount, + (ALSA_EXTRABUFFER + linux_advance_samples) + * alsa_samplewidth * inchannels); +#endif + sys_log_error(ERR_ADCSLEPT); + return (SENDDACS_NO); + } + if (alsa_samplewidth == 4) + { + for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; + j += inchannels, fp2++) + *fp2 = (float) ((t_alsa_sample32 *)alsa_buf)[j] + * (1./ INT32_MAX); + } + } + else + { + for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; j += inchannels, fp2++) + *fp2 = (float) ((t_alsa_sample16 *)alsa_buf)[j] + * 3.051850e-05; + } + } + } + xferno++; + if (sys_getrealtime() - timenow > 0.002) + { +#ifdef DEBUG_ALSA_XFER + fprintf(stderr, "routine took %d msec\n", + (int)(1000 * (sys_getrealtime() - timenow))); +#endif + sys_log_error(ERR_ADCSLEPT); + } + return SENDDACS_YES; +} + +#endif /* ALSA99 */ + +/* support for ALSA pcmv2 api by Karl MacMillan */ + +#ifdef ALSA01 + +static void check_error(int err, const char *why) +{ + if (err < 0) + fprintf(stderr, "%s: %s\n", why, snd_strerror(err)); +} + +static int alsa_open_audio(int wantinchans, int wantoutchans, int srate) +{ + int err, inchans = 0, outchans = 0, subunitdir; + char devname[512]; + snd_pcm_hw_params_t* hw_params; + snd_pcm_sw_params_t* sw_params; + snd_output_t* out; + int frag_size = (linux_fragsize ? linux_fragsize : ALSA_DEFFRAGSIZE); + int nfrags, i; + short* tmp_buf; + unsigned int tmp_uint; + int advwas = sys_schedadvance; + + if (linux_nfragment) + { + nfrags = linux_nfragment; + sys_schedadvance = (frag_size * linux_nfragment * 1.0e6) / srate; + } + else nfrags = sys_schedadvance * (float)srate / (1e6 * frag_size); + + if (sys_verbose || (sys_schedadvance != advwas)) + post("audio buffer set to %d", (int)(0.001 * sys_schedadvance)); + if (wantinchans || wantoutchans) + alsa_checkversion(); + if (wantinchans) + { + err = snd_pcm_open(&alsa_device.inhandle, alsa_devname, + SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); + + check_error(err, "snd_pcm_open (input)"); + if (err < 0) + inchans = 0; + else + { + inchans = wantinchans; + snd_pcm_nonblock(alsa_device.inhandle, 1); + } + } + if (wantoutchans) + { + err = snd_pcm_open(&alsa_device.outhandle, alsa_devname, + SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + + check_error(err, "snd_pcm_open (output)"); + if (err < 0) + outchans = 0; + else + { + outchans = wantoutchans; + snd_pcm_nonblock(alsa_device.outhandle, 1); + } + } + if (inchans) + { + if (sys_verbose) + post("opening sound input..."); + err = snd_pcm_hw_params_malloc(&hw_params); + check_error(err, "snd_pcm_hw_params_malloc (input)"); + + // get the default params + err = snd_pcm_hw_params_any(alsa_device.inhandle, hw_params); + check_error(err, "snd_pcm_hw_params_any (input)"); + // set interleaved access - FIXME deal with other access types + err = snd_pcm_hw_params_set_access(alsa_device.inhandle, hw_params, + SND_PCM_ACCESS_RW_INTERLEAVED); + check_error(err, "snd_pcm_hw_params_set_access (input)"); + // Try to set 32 bit format first + err = snd_pcm_hw_params_set_format(alsa_device.inhandle, hw_params, + SND_PCM_FORMAT_S32); + if (err < 0) + { + /* fprintf(stderr, + "PD-ALSA: 32 bit format not available - using 16\n"); */ + err = snd_pcm_hw_params_set_format(alsa_device.inhandle, hw_params, + SND_PCM_FORMAT_S16); + check_error(err, "snd_pcm_hw_params_set_format (input)"); + alsa_samplewidth = 2; + } + else + { + alsa_samplewidth = 4; + } + post("Sample width set to %d bytes", alsa_samplewidth); + // set the subformat + err = snd_pcm_hw_params_set_subformat(alsa_device.inhandle, hw_params, + SND_PCM_SUBFORMAT_STD); + check_error(err, "snd_pcm_hw_params_set_subformat (input)"); + // set the number of channels + tmp_uint = inchans; + err = snd_pcm_hw_params_set_channels_min(alsa_device.inhandle, + hw_params, &tmp_uint); + check_error(err, "snd_pcm_hw_params_set_channels (input)"); + if (tmp_uint != (unsigned)inchans) + post("ALSA: set input channels to %d", tmp_uint); + inchans = tmp_uint; + // set the sampling rate + err = snd_pcm_hw_params_set_rate_min(alsa_device.inhandle, hw_params, + &srate, 0); + check_error(err, "snd_pcm_hw_params_set_rate_min (input)"); +#if 0 + err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir); + post("input sample rate %d", err); +#endif + // set the period - ie frag size + // post("fragsize a %d", frag_size); + + /* LATER try this to get a recommended period size... + right now, it trips an assertion failure in ALSA lib */ +#if 0 + post("input period was %d, min %d, max %d\n", + snd_pcm_hw_params_get_period_size(hw_params, 0), + snd_pcm_hw_params_get_period_size_min(hw_params, 0), + snd_pcm_hw_params_get_period_size_max(hw_params, 0)); +#endif + err = snd_pcm_hw_params_set_period_size_near(alsa_device.inhandle, + hw_params, + (snd_pcm_uframes_t) + frag_size, 0); + check_error(err, "snd_pcm_hw_params_set_period_size_near (input)"); + // post("fragsize b %d", frag_size); + // set the number of periods - ie numfrags + // post("nfrags a %d", nfrags); + err = snd_pcm_hw_params_set_periods_near(alsa_device.inhandle, + hw_params, nfrags, 0); + check_error(err, "snd_pcm_hw_params_set_periods_near (input)"); + // set the buffer size + err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.inhandle, + hw_params, nfrags * frag_size); + check_error(err, "snd_pcm_hw_params_set_buffer_size_near (input)"); + + err = snd_pcm_hw_params(alsa_device.inhandle, hw_params); + check_error(err, "snd_pcm_hw_params (input)"); + + snd_pcm_hw_params_free(hw_params); + + err = snd_pcm_sw_params_malloc(&sw_params); + check_error(err, "snd_pcm_sw_params_malloc (input)"); + err = snd_pcm_sw_params_current(alsa_device.inhandle, sw_params); + check_error(err, "snd_pcm_sw_params_current (input)"); +#if 1 + err = snd_pcm_sw_params_set_start_mode(alsa_device.inhandle, sw_params, + SND_PCM_START_EXPLICIT); + check_error(err, "snd_pcm_sw_params_set_start_mode (input)"); + err = snd_pcm_sw_params_set_xrun_mode(alsa_device.inhandle, sw_params, + SND_PCM_XRUN_NONE); + check_error(err, "snd_pcm_sw_params_set_xrun_mode (input)"); +#else + err = snd_pcm_sw_params_set_start_threshold(alsa_device.inhandle, + sw_params, nfrags * frag_size); + check_error(err, "snd_pcm_sw_params_set_start_threshold (input)"); + err = snd_pcm_sw_params_set_stop_threshold(alsa_device.inhandle, + sw_params, 1); + check_error(err, "snd_pcm_sw_params_set_stop_threshold (input)"); +#endif + + err = snd_pcm_sw_params_set_avail_min(alsa_device.inhandle, sw_params, + frag_size); + check_error(err, "snd_pcm_sw_params_set_avail_min (input)"); + err = snd_pcm_sw_params(alsa_device.inhandle, sw_params); + check_error(err, "snd_pcm_sw_params (input)"); + + snd_pcm_sw_params_free(sw_params); + + snd_output_stdio_attach(&out, stderr, 0); +#if 0 + if (sys_verbose) + { + snd_pcm_dump_hw_setup(alsa_device.inhandle, out); + snd_pcm_dump_sw_setup(alsa_device.inhandle, out); + } +#endif + } + + if (outchans) + { + int foo; + if (sys_verbose) + post("opening sound output..."); + err = snd_pcm_hw_params_malloc(&hw_params); + check_error(err, "snd_pcm_sw_params (output)"); + + // get the default params + err = snd_pcm_hw_params_any(alsa_device.outhandle, hw_params); + check_error(err, "snd_pcm_hw_params_any (output)"); + // set interleaved access - FIXME deal with other access types + err = snd_pcm_hw_params_set_access(alsa_device.outhandle, hw_params, + SND_PCM_ACCESS_RW_INTERLEAVED); + check_error(err, "snd_pcm_hw_params_set_access (output)"); + // Try to set 32 bit format first + err = snd_pcm_hw_params_set_format(alsa_device.outhandle, hw_params, + SND_PCM_FORMAT_S32); + if (err < 0) + { + err = snd_pcm_hw_params_set_format(alsa_device.outhandle, + hw_params,SND_PCM_FORMAT_S16); + check_error(err, "snd_pcm_hw_params_set_format (output)"); + /* fprintf(stderr, + "PD-ALSA: 32 bit format not available - using 16\n"); */ + alsa_samplewidth = 2; + } + else + { + alsa_samplewidth = 4; + } + // set the subformat + err = snd_pcm_hw_params_set_subformat(alsa_device.outhandle, hw_params, + SND_PCM_SUBFORMAT_STD); + check_error(err, "snd_pcm_hw_params_set_subformat (output)"); + // set the number of channels + tmp_uint = outchans; + err = snd_pcm_hw_params_set_channels_min(alsa_device.outhandle, + hw_params, &tmp_uint); + check_error(err, "snd_pcm_hw_params_set_channels (output)"); + if (tmp_uint != (unsigned)outchans) + post("alsa: set output channels to %d", tmp_uint); + outchans = tmp_uint; + // set the sampling rate + err = snd_pcm_hw_params_set_rate_min(alsa_device.outhandle, hw_params, + &srate, 0); + check_error(err, "snd_pcm_hw_params_set_rate_min (output)"); +#if 0 + err = snd_pcm_hw_params_get_rate(hw_params, &subunitdir); + post("output sample rate %d", err); +#endif + // set the period - ie frag size +#if 0 + post("output period was %d, min %d, max %d\n", + snd_pcm_hw_params_get_period_size(hw_params, 0), + snd_pcm_hw_params_get_period_size_min(hw_params, 0), + snd_pcm_hw_params_get_period_size_max(hw_params, 0)); +#endif + // post("fragsize c %d", frag_size); + err = snd_pcm_hw_params_set_period_size_near(alsa_device.outhandle, + hw_params, + (snd_pcm_uframes_t) + frag_size, 0); + // post("fragsize d %d", frag_size); + check_error(err, "snd_pcm_hw_params_set_period_size_near (output)"); + // set the number of periods - ie numfrags + err = snd_pcm_hw_params_set_periods_near(alsa_device.outhandle, + hw_params, nfrags, 0); + check_error(err, "snd_pcm_hw_params_set_periods_near (output)"); + // set the buffer size + err = snd_pcm_hw_params_set_buffer_size_near(alsa_device.outhandle, + hw_params, nfrags * frag_size); + + check_error(err, "snd_pcm_hw_params_set_buffer_size_near (output)"); + + err = snd_pcm_hw_params(alsa_device.outhandle, hw_params); + check_error(err, "snd_pcm_hw_params (output)"); + + snd_pcm_hw_params_free(hw_params); + + err = snd_pcm_sw_params_malloc(&sw_params); + check_error(err, "snd_pcm_sw_params_malloc (output)"); + err = snd_pcm_sw_params_current(alsa_device.outhandle, sw_params); + check_error(err, "snd_pcm_sw_params_current (output)"); +#if 1 + err = snd_pcm_sw_params_set_start_mode(alsa_device.outhandle, + sw_params, + SND_PCM_START_EXPLICIT); + check_error(err, "snd_pcm_sw_params_set_start_mode (output)"); + err = snd_pcm_sw_params_set_xrun_mode(alsa_device.outhandle, sw_params, + SND_PCM_XRUN_NONE); + check_error(err, "snd_pcm_sw_params_set_xrun_mode (output)"); +#else + err = snd_pcm_sw_params_set_start_threshold(alsa_device.inhandle, + sw_params, nfrags * frag_size); + check_error(err, "snd_pcm_sw_params_set_start_threshold (output)"); + err = snd_pcm_sw_params_set_stop_threshold(alsa_device.inhandle, + sw_params, 1); + check_error(err, "snd_pcm_sw_params_set_stop_threshold (output)"); +#endif + + err = snd_pcm_sw_params_set_avail_min(alsa_device.outhandle, sw_params, + frag_size); + check_error(err, "snd_pcm_sw_params_set_avail_min (output)"); + err = snd_pcm_sw_params(alsa_device.outhandle, sw_params); + check_error(err, "snd_pcm_sw_params (output)"); + + snd_pcm_sw_params_free(sw_params); + + snd_output_stdio_attach(&out, stderr, 0); +#if 0 + if (sys_verbose) + { + snd_pcm_dump_hw_setup(alsa_device.outhandle, out); + snd_pcm_dump_sw_setup(alsa_device.outhandle, out); + } +#endif + } + + linux_setsr(srate); + linux_setch(inchans, outchans); + + if (inchans) + snd_pcm_prepare(alsa_device.inhandle); + if (outchans) + snd_pcm_prepare(alsa_device.outhandle); + + // if duplex we can link the channels so they start together + if (inchans && outchans) + snd_pcm_link(alsa_device.inhandle, alsa_device.outhandle); + + // set up the buffer + if (outchans > inchans) + alsa_buf = (short *)calloc(sizeof(char) * alsa_samplewidth, DACBLKSIZE + * outchans); + else + alsa_buf = (short *)calloc(sizeof(char) * alsa_samplewidth, DACBLKSIZE + * inchans); + // fill the buffer with silence + if (outchans) + { + i = nfrags + 1; + while (i--) + snd_pcm_writei(alsa_device.outhandle, alsa_buf, frag_size); + } + + // set up the status variables + err = snd_pcm_status_malloc(&in_status); + check_error(err, "snd_pcm_status_malloc"); + err = snd_pcm_status_malloc(&out_status); + check_error(err, "snd_pcm_status_malloc"); + + // start the device +#if 1 + if (outchans) + { + err = snd_pcm_start(alsa_device.outhandle); + check_error(err, "snd_pcm_start"); + } + else if (inchans) + { + err = snd_pcm_start(alsa_device.inhandle); + check_error(err, "snd_pcm_start"); + } +#endif + + return 0; +} + +void alsa_close_audio(void) +{ + int err; + if (linux_inchannels) + { + err = snd_pcm_close(alsa_device.inhandle); + check_error(err, "snd_pcm_close (input)"); + } + if (linux_outchannels) + { + err = snd_pcm_close(alsa_device.outhandle); + check_error(err, "snd_pcm_close (output)"); + } +} + +// #define DEBUG_ALSA_XFER + +int alsa_send_dacs(void) +{ + static int16_t *sp; + static int xferno = 0; + static int callno = 0; + static double timenow; + double timelast; + t_sample *fp, *fp1, *fp2; + int i, j, k, err, devno = 0; + int inputcount = 0, outputcount = 0, inputlate = 0, outputlate = 0; + int result; + int inchannels = linux_inchannels; + int outchannels = linux_outchannels; + unsigned int intransfersize = DACBLKSIZE; + unsigned int outtransfersize = DACBLKSIZE; + + // get the status + if (!inchannels && !outchannels) + { + return SENDDACS_NO; + } + + timelast = timenow; + timenow = sys_getrealtime(); + +#ifdef DEBUG_ALSA_XFER + if (timenow - timelast > 0.050) + fprintf(stderr, "(%d)", + (int)(1000 * (timenow - timelast))), fflush(stderr); +#endif + + callno++; + + if (inchannels) + { + snd_pcm_status(alsa_device.inhandle, in_status); + if (snd_pcm_status_get_avail(in_status) < intransfersize) + return SENDDACS_NO; + } + if (outchannels) + { + snd_pcm_status(alsa_device.outhandle, out_status); + if (snd_pcm_status_get_avail(out_status) < outtransfersize) + return SENDDACS_NO; + } + + /* do output */ + if (outchannels) + { + fp = sys_soundout; + if (alsa_samplewidth == 4) + { + for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; + j += outchannels, fp2++) + { + float s1 = *fp2 * INT32_MAX; + ((t_alsa_sample32 *)alsa_buf)[j] = CLIP32(s1); + } + } + } + else + { + for (i = 0, fp1 = fp; i < outchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; + j += outchannels, fp2++) + { + int s = *fp2 * 32767.; + if (s > 32767) + s = 32767; + else if (s < -32767) + s = -32767; + ((t_alsa_sample16 *)alsa_buf)[j] = s; + } + } + } + + result = snd_pcm_writei(alsa_device.outhandle, alsa_buf, + outtransfersize); + if (result != (int)outtransfersize) + { + #ifdef DEBUG_ALSA_XFER + if (result >= 0 || errno == EAGAIN) + fprintf(stderr, "ALSA: write returned %d of %d\n", + result, outtransfersize); + else fprintf(stderr, "ALSA: write: %s\n", + snd_strerror(errno)); + fprintf(stderr, + "inputcount %d, outputcount %d, outbufsize %d\n", + inputcount, outputcount, + (ALSA_EXTRABUFFER + linux_advance_samples) + * alsa_samplewidth * outchannels); + #endif + sys_log_error(ERR_DACSLEPT); + return (SENDDACS_NO); + } + + /* zero out the output buffer */ + memset(sys_soundout, 0, DACBLKSIZE * sizeof(*sys_soundout) * + linux_outchannels); + if (sys_getrealtime() - timenow > 0.002) + { + #ifdef DEBUG_ALSA_XFER + fprintf(stderr, "output %d took %d msec\n", + callno, (int)(1000 * (timenow - timelast))), fflush(stderr); + #endif + timenow = sys_getrealtime(); + sys_log_error(ERR_DACSLEPT); + } + } + /* do input */ + if (linux_inchannels) + { + result = snd_pcm_readi(alsa_device.inhandle, alsa_buf, intransfersize); + if (result < (int)intransfersize) + { +#ifdef DEBUG_ALSA_XFER + if (result < 0) + fprintf(stderr, + "snd_pcm_read %d %d: %s\n", + callno, xferno, snd_strerror(errno)); + else fprintf(stderr, + "snd_pcm_read %d %d returned only %d\n", + callno, xferno, result); + fprintf(stderr, + "inputcount %d, outputcount %d, inbufsize %d\n", + inputcount, outputcount, + (ALSA_EXTRABUFFER + linux_advance_samples) + * alsa_samplewidth * inchannels); +#endif + sys_log_error(ERR_ADCSLEPT); + return (SENDDACS_NO); + } + fp = sys_soundin; + if (alsa_samplewidth == 4) + { + for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; + j += inchannels, fp2++) + *fp2 = (float) ((t_alsa_sample32 *)alsa_buf)[j] + * (1./ INT32_MAX); + } + } + else + { + for (i = 0, fp1 = fp; i < inchannels; i++, fp1 += DACBLKSIZE) + { + for (j = i, k = DACBLKSIZE, fp2 = fp1; k--; j += inchannels, + fp2++) + *fp2 = (float) ((t_alsa_sample16 *)alsa_buf)[j] + * 3.051850e-05; + } + } + } + xferno++; + if (sys_getrealtime() - timenow > 0.002) + { +#ifdef DEBUG_ALSA_XFER + fprintf(stderr, "routine took %d msec\n", + (int)(1000 * (sys_getrealtime() - timenow))); +#endif + sys_log_error(ERR_ADCSLEPT); + } + return SENDDACS_YES; +} + +void alsa_resync( void) +{ + int i, result; + if (linux_whichapi != API_ALSA) + { + error("restart-audio: implemented for ALSA only."); + return; + } + memset(alsa_buf, 0, + sizeof(char) * alsa_samplewidth * DACBLKSIZE * linux_outchannels); + for (i = 0; i < 100; i++) + { + result = snd_pcm_writei(alsa_device.outhandle, alsa_buf, + DACBLKSIZE); + if (result != (int)DACBLKSIZE) + break; + } + post("%d written", i); +} + + +#endif /* ALSA01 */ + +/*************************************************** + * Code using the RME_9652 API + */ + + /* + trying native device for future use of native memory map: + because of busmaster if you dont use the dac, you dont need + CPU Power und also no nearly no CPU-Power is used in device + + since always all DAs and ADs are synced (else they wouldnt work) + we use linux_dacs[0], linux_adcs[0] + */ + +#ifdef RME_HAMMERFALL + +#define RME9652_MAX_CHANNELS 26 + +#define RME9652_CH_PER_NATIVE_DEVICE 1 + +static int rme9652_dac_devices[RME9652_MAX_CHANNELS]; +static int rme9652_adc_devices[RME9652_MAX_CHANNELS]; + +static char rme9652_dsp_dac[] = "/dev/rme9652/C0da%d"; +static char rme9652_dsp_adc[] = "/dev/rme9652/C0ad%d"; + +static int num_of_rme9652_dac = 0; +static int num_of_rme9652_adc = 0; + +static int rme_soundindevonset = 1; +static int rme_soundoutdevonset = 1; + +void rme_soundindev(int which) +{ + rme_soundindevonset = which; +} + +void rme_soundoutdev(int which) +{ + rme_soundoutdevonset = which; +} + +void rme9652_configure(int dev, int fd,int srate, int dac) { + int orig, param, nblk; + audio_buf_info ainfo; + orig = param = srate; + + /* samplerate */ + + fprintf(stderr,"RME9652: configuring %d, fd=%d, sr=%d\n, dac=%d\n", + dev,fd,srate,dac); + + if (ioctl(fd,SNDCTL_DSP_SPEED,¶m) == -1) + fprintf(stderr,"RME9652: Could not set sampling rate for device\n"); + else if( orig != param ) + fprintf(stderr,"RME9652: sampling rate: wanted %d, got %d\n", + orig, param ); + + // setting the correct samplerate (could be different than expected) + srate = param; + + + /* setting resolution */ + + /* use ctrlpanel to change, experiment, channels 1 */ + + orig = param = AFMT_S16_NE; + if (ioctl(fd,SNDCTL_DSP_SETFMT,¶m) == -1) + fprintf(stderr,"RME9652: Could not set DSP format\n"); + else if( orig != param ) + fprintf(stderr,"RME9652: DSP format: wanted %d, got %d\n",orig, param ); + + /* setting channels */ + orig = param = RME9652_CH_PER_NATIVE_DEVICE; + + if (ioctl(fd,SNDCTL_DSP_CHANNELS,¶m) == -1) + fprintf(stderr,"RME9652: Could not set channels\n"); + else if( orig != param ) + fprintf(stderr,"RME9652: num channels: wanted %d, got %d\n",orig, param ); + + if (dac) + { + + /* use "free space" to learn the buffer size. Normally you + should set this to your own desired value; but this seems not + to be implemented uniformly across different sound cards. LATER + we should figure out what to do if the requested scheduler advance + is greater than this buffer size; for now, we just print something + out. */ + + if( ioctl(linux_dacs[0].d_fd, SOUND_PCM_GETOSPACE,&ainfo) < 0 ) + fprintf(stderr,"RME: ioctl on output device %d failed",dev); + + linux_dacs[0].d_bufsize = ainfo.bytes; + + fprintf(stderr,"RME: ioctl SOUND_PCM_GETOSPACE says %d buffsize\n", + linux_dacs[0].d_bufsize); + + + if (linux_advance_samples * (RME_SAMPLEWIDTH * + RME9652_CH_PER_NATIVE_DEVICE) + > linux_dacs[0].d_bufsize - RME_BYTESPERCHAN) + { + fprintf(stderr, + "RME: requested audio buffer size %d limited to %d\n", + linux_advance_samples + * (RME_SAMPLEWIDTH * RME9652_CH_PER_NATIVE_DEVICE), + linux_dacs[0].d_bufsize); + linux_advance_samples = + (linux_dacs[0].d_bufsize - RME_BYTESPERCHAN) + / (RME_SAMPLEWIDTH *RME9652_CH_PER_NATIVE_DEVICE); + } + } +} + + +int rme9652_open_audio(int inchans, int outchans,int srate) +{ + int orig; + int tmp; + int inchannels = 0,outchannels = 0; + char devname[20]; + int i; + char buf[RME_SAMPLEWIDTH*RME9652_CH_PER_NATIVE_DEVICE*DACBLKSIZE]; + int num_devs = 0; + audio_buf_info ainfo; + + linux_nindevs = linux_noutdevs = 0; + + if (sys_verbose) + post("RME open"); + /* First check if we can */ + /* open the write ports */ + + for (num_devs=0; outchannels < outchans; num_devs++) + { + int channels = RME9652_CH_PER_NATIVE_DEVICE; + + sprintf(devname, rme9652_dsp_dac, num_devs + rme_soundoutdevonset); + if ((tmp = open(devname,O_WRONLY)) == -1) + { + DEBUG(fprintf(stderr,"RME9652: failed to open %s writeonly\n", + devname);) + break; + } + DEBUG(fprintf(stderr,"RME9652: out device Nr. %d (%d) on %s\n", + linux_noutdevs+1,tmp,devname);) + + if (outchans > outchannels) + { + rme9652_dac_devices[linux_noutdevs] = tmp; + linux_noutdevs++; + outchannels += channels; + } + else close(tmp); + } + if( linux_noutdevs > 0) + linux_dacs[0].d_fd = rme9652_dac_devices[0]; + + /* Second check if we can */ + /* open the read ports */ + + for (num_devs=0; inchannels < inchans; num_devs++) + { + int channels = RME9652_CH_PER_NATIVE_DEVICE; + + sprintf(devname, rme9652_dsp_adc, num_devs+rme_soundindevonset); + + if ((tmp = open(devname,O_RDONLY)) == -1) + { + DEBUG(fprintf(stderr,"RME9652: failed to open %s readonly\n", + devname);) + break; + } + DEBUG(fprintf(stderr,"RME9652: in device Nr. %d (%d) on %s\n", + linux_nindevs+1,tmp,devname);) + + if (inchans > inchannels) + { + rme9652_adc_devices[linux_nindevs] = tmp; + linux_nindevs++; + inchannels += channels; + } + else + close(tmp); + } + if(linux_nindevs > 0) + linux_adcs[0].d_fd = rme9652_adc_devices[0]; + + /* configure soundcards */ + + rme9652_configure(0, linux_adcs[0].d_fd,srate, 0); + rme9652_configure(0, linux_dacs[0].d_fd,srate, 1); + + /* We have to do a read to start the engine. This is + necessary because sys_send_dacs waits until the input + buffer is filled and only reads on a filled buffer. + This is good, because it's a way to make sure that we + will not block */ + + if (linux_nindevs) + { + fprintf(stderr,("RME9652: starting read engine ... ")); + + + for (num_devs=0; num_devs < linux_nindevs; num_devs++) + read(rme9652_adc_devices[num_devs], + buf, RME_SAMPLEWIDTH* RME9652_CH_PER_NATIVE_DEVICE* + DACBLKSIZE); + + + for (num_devs=0; num_devs < linux_noutdevs; num_devs++) + write(rme9652_dac_devices[num_devs], + buf, RME_SAMPLEWIDTH* RME9652_CH_PER_NATIVE_DEVICE* + DACBLKSIZE); + + if(linux_noutdevs) + ioctl(rme9652_dac_devices[0],SNDCTL_DSP_SYNC); + + fprintf(stderr,"done\n"); + } + + linux_setsr(srate); + linux_setch(linux_nindevs, linux_noutdevs); + + num_of_rme9652_dac = linux_noutdevs; + num_of_rme9652_adc = linux_nindevs; + + if(linux_noutdevs)linux_noutdevs=1; + if(linux_nindevs)linux_nindevs=1; + + /* trick RME9652 behaves as one device fromread write pointers */ + return (0); +} + +void rme9652_close_audio( void) +{ + int i; + for (i=0;i>2;i--;) + { + float s1 = *(fp1+=4) * INT32_MAX; + float s2 = *(fp2+=4) * INT32_MAX; + float s3 = *(fp3+=4) * INT32_MAX; + float s4 = *(fp4+=4) * INT32_MAX; + + *(a+=4) = CLIP32(s1); + *(b+=4) = CLIP32(s2); + *(c+=4) = CLIP32(s3); + *(d+=4) = CLIP32(s4); + } + + linux_dacs_write(rme9652_dac_devices[j],buf,RME_BYTESPERCHAN); + } + } + + if ((timenow = sys_getrealtime()) - timeref > 0.02) + sys_log_error(ERR_DACSLEPT); + timeref = timenow; + } + + memset(sys_soundout, 0, + linux_outchannels * (sizeof(float) * DACBLKSIZE)); + + /* do input */ + + if(linux_nindevs) { + + for(j=0;j 0.02) + sys_log_error(ERR_ADCSLEPT); + timeref = timenow; + { + t_rme_sample *a,*b,*c,*d; + float *fp1,*fp2,*fp3,*fp4; + + fp1 = sys_soundin + j*DACBLKSIZE-4; + fp2 = fp1 + 1; + fp3 = fp1 + 2; + fp4 = fp1 + 3; + a = buf-4; + b=a+1; + c=a+2; + d=a+3; + + for (i = (DACBLKSIZE>>2);i--;) + { + *(fp1+=4) = *(a+=4) * (float)(1./INT32_MAX); + *(fp2+=4) = *(b+=4) * (float)(1./INT32_MAX); + *(fp3+=4) = *(c+=4) * (float)(1./INT32_MAX); + *(fp4+=4) = *(d+=4) * (float)(1./INT32_MAX); + } + } + } + } + /* fprintf(stderr,"ready \n");*/ + + return (1); +} + +#endif /* RME_HAMMERFALL */ diff --git a/pd/src/s_loader.c b/pd/src/s_loader.c new file mode 100644 index 00000000..4c0ef972 --- /dev/null +++ b/pd/src/s_loader.c @@ -0,0 +1,142 @@ +/* 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. */ + +#ifdef DL_OPEN +#include +#endif +#ifdef UNIX +#include +#include +#endif +#ifdef NT +#include +#include +#endif +#ifdef MACOSX +#include +#endif +#include +#include "m_imp.h" +#include + +typedef void (*t_xxx)(void); + +static char sys_dllextent[] = +#ifdef __FreeBSD__ + ".pd_freebsd"; +#endif +#ifdef IRIX +#ifdef N32 + ".pd_irix6"; +#else + ".pd_irix5"; +#endif +#endif +#ifdef __linux__ + ".pd_linux"; +#endif +#ifdef MACOSX + ".pd_darwin"; +#endif +#ifdef NT + ".dll"; +#endif + + +int sys_load_lib(char *dirname, char *classname) +{ + char symname[MAXPDSTRING], filename[MAXPDSTRING], dirbuf[MAXPDSTRING], + *nameptr, *lastdot; + void *dlobj; + t_xxx makeout; + int fd; +#ifdef NT + HINSTANCE ntdll; +#endif +#if 0 + fprintf(stderr, "lib %s %s\n", dirname, classname); +#endif + if ((fd = open_via_path(dirname, classname, sys_dllextent, + dirbuf, &nameptr, MAXPDSTRING, 1)) < 0) + { + return (0); + } + else + { + close(fd); + /* refabricate the pathname */ + strcpy(filename, dirbuf); + strcat(filename, "/"); + strcat(filename, nameptr); + /* extract the setup function name */ + if (lastdot = strrchr(nameptr, '.')) + *lastdot = 0; + +#ifdef MACOSX + strcpy(symname, "_"); + strcat(symname, nameptr); +#else + strcpy(symname, nameptr); +#endif + /* if the last character is a tilde, replace with "_tilde" */ + if (symname[strlen(symname) - 1] == '~') + strcpy(symname + (strlen(symname) - 1), "_tilde"); + /* and append _setup to form the C setup function name */ + strcat(symname, "_setup"); +#ifdef DL_OPEN + dlobj = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); + if (!dlobj) + { + post("%s: %s", filename, dlerror()); + return (0); + } + makeout = (t_xxx)dlsym(dlobj, symname); +#endif +#ifdef NT + sys_bashfilename(filename, filename); + ntdll = LoadLibrary(filename); + if (!ntdll) + { + post("%s: couldn't load", filename); + return (0); + } + makeout = (t_xxx)GetProcAddress(ntdll, symname); +#endif +#ifdef MACOSX + { + NSObjectFileImage image; + void *ret; + NSSymbol s; + if ( NSCreateObjectFileImageFromFile( filename, &image) != NSObjectFileImageSuccess ) + { + post("%s: couldn't load", filename); + return 0; + } + ret = NSLinkModule( image, filename, NSLINKMODULE_OPTION_BINDNOW); + + s = NSLookupSymbolInModule(ret, symname); + + if (s) + makeout = (t_xxx)NSAddressOfSymbol( s); + else makeout = 0; + } +#endif + } + if (!makeout) + { + post("load_object: Symbol \"%s\" not found", symname); + return 0; + } + (*makeout)(); + return (1); +} + + + + + + + + + diff --git a/pd/src/s_mac.c b/pd/src/s_mac.c new file mode 100644 index 00000000..a36f192a --- /dev/null +++ b/pd/src/s_mac.c @@ -0,0 +1,356 @@ +/* Copyright (c) 1997-2001 Guenter Geiger, Miller Puckette, Larry Troxler, +* Winfried Ritsch, Karl MacMillan, 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 implements the sys_ functions profiled in m_imp.h for + audio and MIDI I/O on Macintosh OS X. + + Audio simply calls routines in s_portaudio.c, which in turn call the + portaudio package. s_portaudio.c is also intended for use from NT. + + MIDI is handled by "portmidi". +*/ + + +#include "m_imp.h" +#include +#ifdef UNIX +#include +#endif +#ifndef MACOSX +#include +#else +#include +#include "portaudio.h" +#include "portmidi.h" +#include "porttime.h" +#include "pminternal.h" +#endif +#include +#include +#include +#include + +/* Defines */ +#define DEBUG(x) x +#define DEBUG2(x) {x;} + +#define PA_DEFAULTCH 2 /* portaudio specific? */ +#define PA_MAXCH 100 +#define PA_DEFAULTSRATE 44100 +typedef short t_pa_sample; +#define PA_SAMPLEWIDTH sizeof(t_pa_sample) +#define PA_BYTESPERCHAN (DACBLKSIZE * PA_SAMPLEWIDTH) +#define PA_XFERSAMPS (PA_DEFAULTCH*DACBLKSIZE) +#define PA_XFERSIZE (PA_SAMPLEWIDTH * PA_XFERSAMPS) + +static int mac_whichapi = API_PORTAUDIO; +static int mac_inchannels; +static int mac_outchannels; +static int mac_advance_samples; /* scheduler advance in samples */ +static int mac_meters; /* true if we're metering */ +static float mac_inmax; /* max input amplitude */ +static float mac_outmax; /* max output amplitude */ +static int mac_blocksize = 256; /* audio I/O block size in sample frames */ + + /* exported variables */ +int sys_schedadvance = 50000; /* scheduler advance in microseconds */ +float sys_dacsr; +int sys_hipriority = 0; +t_sample *sys_soundout; +t_sample *sys_soundin; + +static PmStream *mac_midiindevlist[MAXMIDIINDEV]; +static PmStream *mac_midioutdevlist[MAXMIDIOUTDEV]; +static int mac_nmidiindev; +static int mac_nmidioutdev; + + /* set channels and sample rate. */ + +static void mac_setchsr(int chin, int chout, int sr) +{ + int nblk; + int inbytes = chin * (DACBLKSIZE*sizeof(float)); + int outbytes = chout * (DACBLKSIZE*sizeof(float)); + + mac_inchannels = chin; + mac_outchannels = chout; + sys_dacsr = sr; + mac_advance_samples = (sys_schedadvance * sys_dacsr) / (1000000.); + if (mac_advance_samples < 3 * DACBLKSIZE) + mac_advance_samples = 3 * DACBLKSIZE; + + if (sys_soundin) + free(sys_soundin); + sys_soundin = (t_float *)malloc(inbytes); + memset(sys_soundin, 0, inbytes); + + if (sys_soundout) + free(sys_soundout); + sys_soundout = (t_float *)malloc(outbytes); + memset(sys_soundout, 0, outbytes); + + if (sys_verbose) + post("input channels = %d, output channels = %d", + mac_inchannels, mac_outchannels); +} + +/* ----------------------- public routines ----------------------- */ + +void sys_open_audio(int naudioindev, int *audioindev, int nchindev, + int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, + int *choutdev, int rate) /* IOhannes */ +{ + int inchans= + (nchindev > 0 ? chindev[0] : (nchindev == 0 ? 0 : PA_DEFAULTCH)); + int outchans= + (nchoutdev > 0 ? choutdev[0] : (nchoutdev == 0 ? 0 : PA_DEFAULTCH)); + int soundindev = (naudioindev <= 0 ? -1 : (audioindev[0]-1)); + int soundoutdev = (naudiooutdev <= 0 ? -1 : (audiooutdev[0]-1)); + int sounddev = (inchans > 0 ? soundindev : soundoutdev); + if (naudioindev > 1 || nchindev > 1 || naudiooutdev > 1 || nchoutdev > 1) + post("sorry, only one portaudio device can be open at once.\n"); + /* post("nindev %d, noutdev %d", naudioindev, naudiooutdev); + post("soundindev %d, soundoutdev %d", soundindev, soundoutdev); */ + if (sys_verbose) + post("channels in %d, out %d", inchans, outchans); + if (rate < 1) + rate = PA_DEFAULTSRATE; + mac_setchsr(inchans, outchans, rate); + pa_open_audio(inchans, outchans, rate, sys_soundin, sys_soundout, + mac_blocksize, mac_advance_samples/mac_blocksize, + soundindev, soundoutdev); +} + +void sys_close_audio(void) +{ + pa_close_audio(); +} + + +int sys_send_dacs(void) +{ + if (mac_meters) + { + int i, n; + float maxsamp; + for (i = 0, n = mac_inchannels * DACBLKSIZE, maxsamp = mac_inmax; + i < n; i++) + { + float f = sys_soundin[i]; + if (f > maxsamp) maxsamp = f; + else if (-f > maxsamp) maxsamp = -f; + } + mac_inmax = maxsamp; + for (i = 0, n = mac_outchannels * DACBLKSIZE, maxsamp = mac_outmax; + i < n; i++) + { + float f = sys_soundout[i]; + if (f > maxsamp) maxsamp = f; + else if (-f > maxsamp) maxsamp = -f; + } + mac_outmax = maxsamp; + } + return pa_send_dacs(); +} + +float sys_getsr(void) +{ + return (sys_dacsr); +} + +int sys_get_outchannels(void) +{ + return (mac_outchannels); +} + +int sys_get_inchannels(void) +{ + return (mac_inchannels); +} + +void sys_audiobuf(int n) +{ + /* set the size, in milliseconds, of the audio FIFO */ + if (n < 5) n = 5; + else if (n > 5000) n = 5000; + sys_schedadvance = n * 1000; +} + +void sys_getmeters(float *inmax, float *outmax) +{ + if (inmax) + { + mac_meters = 1; + *inmax = mac_inmax; + *outmax = mac_outmax; + } + else + mac_meters = 0; + mac_inmax = mac_outmax = 0; +} + +void sys_reportidle(void) +{ +} + +void sys_open_midi(int nmidiin, int *midiinvec, + int nmidiout, int *midioutvec) +{ + int i = 0; + int n = 0; + PmError err; + + Pt_Start(1, 0, 0); /* start a timer with millisecond accuracy */ + mac_nmidiindev = 0; + + for (i = 0; i < nmidiin; i++) + { + err = Pm_OpenInput(&mac_midiindevlist[mac_nmidiindev], midiinvec[i], + NULL, 100, NULL, NULL, NULL); + if (err) + post("could not open midi input device number %d: %s", + midiinvec[i], Pm_GetErrorText(err)); + else + { + if (sys_verbose) + post("Midi Input opened.\n"); + mac_nmidiindev++; + } + } + + mac_nmidioutdev = 0; + for (i = 0; i < nmidiout; i++) + { + err = Pm_OpenOutput(&mac_midioutdevlist[mac_nmidioutdev], midioutvec[i], + NULL, 0, NULL, NULL, 0); + if (err) + post("could not open midi output device number %d: %s", + midioutvec[i], Pm_GetErrorText(err)); + else + { + if (sys_verbose) + post("Midi Output opened.\n"); + mac_nmidioutdev++; + } + } +} + +void sys_close_midi( void) +{ + int i; + for (i = 0; i < mac_nmidiindev; i++) + Pm_Close(mac_midiindevlist[mac_nmidiindev]); + mac_nmidiindev = 0; + for (i = 0; i < mac_nmidioutdev; i++) + Pm_Close(mac_midioutdevlist[mac_nmidioutdev]); + mac_nmidioutdev = 0; +} + +void sys_putmidimess(int portno, int a, int b, int c) +{ + PmEvent buffer; + fprintf(stderr, "put 1 msg %d %d\n", portno, mac_nmidioutdev); + if (portno >= 0 && portno < mac_nmidioutdev) + { + buffer.message = Pm_Message(a, b, c); + buffer.timestamp = 0; + fprintf(stderr, "put msg\n"); + Pm_Write(mac_midioutdevlist[portno], &buffer, 1); + } +} + +void sys_putmidibyte(int portno, int byte) +{ + post("sorry, no byte-by-byte MIDI output implemented in MAC OSX"); +} + +void sys_poll_midi(void) +{ + int i, nmess; + PmEvent buffer; + for (i = 0; i < mac_nmidiindev; i++) + { + int nmess = Pm_Read(mac_midiindevlist[i], &buffer, 1); + if (nmess > 0) + { + int status = Pm_MessageStatus(buffer.message); + int data1 = Pm_MessageData1(buffer.message); + int data2 = Pm_MessageData2(buffer.message); + int msgtype = (status >> 4) - 8; + switch (msgtype) + { + case 0: + case 1: + case 2: + case 3: + case 6: + sys_midibytein(i, status); + sys_midibytein(i, data1); + sys_midibytein(i, data2); + break; + case 4: + case 5: + sys_midibytein(i, status); + sys_midibytein(i, data1); + break; + case 7: + sys_midibytein(i, status); + break; + } + } + } +} + +void sys_set_priority(int higher) +{ + int retval; + errno = 0; + retval = setpriority(PRIO_PROCESS, 0, (higher? -20 : -19)); + if (retval == -1 & errno != 0) + { + perror("setpriority"); + fprintf(stderr, "priority bost faled.\n"); + } +} + +void sys_listdevs(void ) +{ + pa_listdevs(); +} + +void sys_setblocksize(int n) +{ + if (n < 1) + n = 1; + if (n != (1 << ilog2(n))) + warn("blocksize adjusted to power of 2: %d", + (n = (1 << ilog2(n)))); + mac_blocksize = n; +} + + /* dummy stuff that shouldn't he here */ +void nt_soundindev(int which) +{ +} + +void nt_soundoutdev(int which) +{ +} + +void nt_midiindev(int which) +{ +} + +void nt_midioutdev(int which) +{ +} + +void nt_noresync(void ) +{ +} + +void glob_audio(void *dummy, t_floatarg fadc, t_floatarg fdac) +{ +} diff --git a/pd/src/s_main.c b/pd/src/s_main.c new file mode 100644 index 00000000..3c0f4164 --- /dev/null +++ b/pd/src/s_main.c @@ -0,0 +1,803 @@ +/* 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. */ + +/* IOhannes : + * hacked the code to add advanced multidevice-support + * 1311:forum::für::umläute:2001 + */ + +char pd_version[] = "Pd version 0.35\n"; +char pd_compiletime[] = __TIME__; +char pd_compiledate[] = __DATE__; + +#include "m_imp.h" +#include +#include +#include +#include +#include +#include +#include + +#ifdef UNIX +#include +#endif +#ifdef NT +#include +#include +#include +#endif + +void pd_init(void); +int sys_argparse(int argc, char **argv); +void sys_findprogdir(char *progname); +int sys_startgui(const char *guipath); +int sys_rcfile(void); +int m_scheduler(int nodacs); +void m_schedsetsr( void); + +int sys_debuglevel; +int sys_verbose; +int sys_noloadbang; +int sys_nogui; +char *sys_guicmd; +t_symbol *sys_libdir; +static t_symbol *sys_guidir; +static t_namelist *sys_externlist; +static t_namelist *sys_openlist; +static t_namelist *sys_messagelist; + +int sys_nmidiout = 1; +#ifdef NT +int sys_nmidiin = 0; +#define DEFMIDIOUTDEV 0 /* For output, in NT, default to "midi_mapper" */ +#else +int sys_nmidiin = 1; +#define DEFMIDIOUTDEV 1 /* in other OSes, default to first MIDI device */ +#endif +#define DEFMIDIINDEV 1 /* for NT this isn't used since sys_nmidiin is 0. */ +int sys_midiindevlist[MAXMIDIINDEV] = {DEFMIDIINDEV}; +int sys_midioutdevlist[MAXMIDIOUTDEV] = {DEFMIDIOUTDEV}; + +typedef struct _fontinfo +{ + int fi_fontsize; + int fi_maxwidth; + int fi_maxheight; + int fi_hostfontsize; + int fi_width; + int fi_height; +} t_fontinfo; + + /* these give the nominal point size and maximum height of the characters + in the six fonts. */ + +static t_fontinfo sys_fontlist[] = { + {8, 5, 9, 0, 0, 0}, {10, 7, 13, 0, 0, 0}, {12, 9, 16, 0, 0, 0}, + {16, 10, 20, 0, 0, 0}, {24, 15, 25, 0, 0, 0}, {36, 25, 45, 0, 0, 0}}; +#define NFONT (sizeof(sys_fontlist)/sizeof(*sys_fontlist)) + +/* here are the actual font size structs on msp's systems: +NT: +font 8 5 9 8 5 11 +font 10 7 13 10 6 13 +font 12 9 16 14 8 16 +font 16 10 20 16 10 18 +font 24 15 25 16 10 18 +font 36 25 42 36 22 41 + +linux: +font 8 5 9 8 5 9 +font 10 7 13 12 7 13 +font 12 9 16 14 9 15 +font 16 10 20 16 10 19 +font 24 15 25 24 15 24 +font 36 25 42 36 22 41 +*/ + +static t_fontinfo *sys_findfont(int fontsize) +{ + unsigned int i; + t_fontinfo *fi; + for (i = 0, fi = sys_fontlist; i < (NFONT-1); i++, fi++) + if (fontsize < fi[1].fi_fontsize) return (fi); + return (sys_fontlist + (NFONT-1)); +} + +int sys_nearestfontsize(int fontsize) +{ + return (sys_findfont(fontsize)->fi_fontsize); +} + +int sys_hostfontsize(int fontsize) +{ + return (sys_findfont(fontsize)->fi_hostfontsize); +} + +int sys_fontwidth(int fontsize) +{ + return (sys_findfont(fontsize)->fi_width); +} + +int sys_fontheight(int fontsize) +{ + return (sys_findfont(fontsize)->fi_height); +} + +int sys_defaultfont; +#ifdef NT +#define DEFAULTFONT 12 +#else +#define DEFAULTFONT 10 +#endif + + +static int inchannels = -1, outchannels = -1; +static int srate = 44100; + +/* IOhannes { */ +#define MAXSOUNDINDEV 4 +#define MAXSOUNDOUTDEV 4 + +int sys_nsoundin = -1; +int sys_nsoundout = -1; +int sys_soundindevlist[MAXSOUNDINDEV] = {-1}; +int sys_soundoutdevlist[MAXSOUNDOUTDEV] = {-1}; + +int sys_nchin = -1; +int sys_nchout = -1; +int sys_chinlist[MAXSOUNDINDEV] = {-1}; +int sys_choutlist[MAXSOUNDOUTDEV] = {-1}; +/* } IOhannes */ + +static void openit(const char *dirname, const char *filename) +{ + char dirbuf[MAXPDSTRING], *nameptr; + int fd = open_via_path(dirname, filename, "", dirbuf, &nameptr, + MAXPDSTRING, 0); + if (fd) + { + close (fd); + glob_evalfile(0, gensym(nameptr), gensym(dirbuf)); + } + else + error("%s: can't open", filename); +} + +#define NHOSTFONT 7 + +/* this is called from the gui process. The first argument is the cwd, and +succeeding args give the widths and heights of known fonts. We wait until +these are known to open files and send messages specified on the command line. +We ask the GUI to specify the "cwd" in case we don't have a local OS to get it +from; for instance we could be some kind of RT embedded system. However, to +really make this make sense we would have to implement +open(), read(), etc, calls to be served somehow from the GUI too. */ + +void glob_initfromgui(void *dummy, t_symbol *s, int argc, t_atom *argv) +{ + char *cwd = atom_getsymbolarg(0, argc, argv)->s_name; + t_namelist *nl; + unsigned int i, j; + if (argc != 1 + 3 * NHOSTFONT) bug("glob_initfromgui"); + for (i = 0; i < NFONT; i++) + { + int wantheight = sys_fontlist[i].fi_maxheight; + for (j = 0; j < NHOSTFONT-1; j++) + { + if (atom_getintarg(3 * (j + 1) + 3, argc, argv) > wantheight) + break; + } + /* j is now the "real" font index for the desired font index i. */ + sys_fontlist[i].fi_hostfontsize = atom_getintarg(3 * j + 1, argc, argv); + sys_fontlist[i].fi_width = atom_getintarg(3 * j + 2, argc, argv); + sys_fontlist[i].fi_height = atom_getintarg(3 * j + 3, argc, argv); + } +#if 0 + for (i = 0; i < 6; i++) + fprintf(stderr, "font %d %d %d %d %d\n", + sys_fontlist[i].fi_fontsize, + sys_fontlist[i].fi_maxheight, + sys_fontlist[i].fi_hostfontsize, + sys_fontlist[i].fi_width, + sys_fontlist[i].fi_height); +#endif + /* load dynamic libraries specified with "-lib" args */ + for (nl = sys_externlist; nl; nl = nl->nl_next) + if (!sys_load_lib(cwd, nl->nl_string)) + post("%s: can't load library", nl->nl_string); + namelist_free(sys_externlist); + sys_externlist = 0; + /* open patches specifies with "-open" args */ + for (nl = sys_openlist; nl; nl = nl->nl_next) + openit(cwd, nl->nl_string); + namelist_free(sys_openlist); + sys_openlist = 0; + /* send messages specified with "-send" args */ + for (nl = sys_messagelist; nl; nl = nl->nl_next) + { + t_binbuf *b = binbuf_new(); + binbuf_text(b, nl->nl_string, strlen(nl->nl_string)); + binbuf_eval(b, 0, 0, 0); + binbuf_free(b); + } + namelist_free(sys_messagelist); + sys_messagelist = 0; +} + +static void sys_addextrapath(void); + +/* this is called from main() in s_entry.c */ +int sys_main(int argc, char **argv) +{ +#ifdef PD_DEBUG + fprintf(stderr, "Pd: COMPILED FOR DEBUGGING\n"); +#endif + pd_init(); /* start the message system */ + sys_findprogdir(argv[0]); /* set sys_progname, guipath */ +#ifdef __linux__ + sys_rcfile(); /* parse the startup file */ +#endif + if (sys_argparse(argc, argv)) return (1); /* parse cmd line */ + sys_addextrapath(); + if (sys_verbose) fprintf(stderr, "%s compiled %s %s\n", + pd_version, pd_compiletime, pd_compiledate); + /* open audio and MIDI */ + sys_open_midi(sys_nmidiin, sys_midiindevlist, + sys_nmidiout, sys_midioutdevlist); + sys_open_audio(sys_nsoundin, sys_soundindevlist, sys_nchin, sys_chinlist, + sys_nsoundout, sys_soundoutdevlist, sys_nchout, sys_choutlist, srate); + + /* tell scheduler the sample rate */ + m_schedsetsr(); + if (sys_startgui(sys_guidir->s_name)) /* start the gui */ + return(1); + + /* run scheduler until it quits */ + return (m_scheduler(!(inchannels || outchannels))); +} + +static char *(usagemessage[]) = { +"usage: pd [-flags] [file]...\n", +"\naudio configuration flags:\n", +"-r -- specify sample rate\n", +#if defined(__linux__) || defined(NT) +"-inchannels ... -- number of audio in channels (by device, like \"2\" or \"16,8\")\n", +"-outchannels ... -- number of audio out channels (by device)\n", +#else +"-inchannels -- number of audio input channels\n", +"-outchannels -- number of audio output channels\n", +#endif +"-channels ... -- specify both input and output channels\n", +"-audiobuf -- specify size of audio buffer in msec\n", +"-blocksize -- specify audio I/O block size in sample frames\n", +"-sleepgrain -- specify number of milliseconds to sleep when idle\n", +"-nodac -- suppress audio output\n", +"-noadc -- suppress audio input\n", +"-noaudio -- suppress audio input and output (-nosound is synonym) \n", +"-listdev -- list audio and MIDI devices\n", + +#ifdef __linux__ +"-frags -- specify number of audio fragments (defeats audiobuf)\n", +"-fragsize -- specify log of fragment size ('blocksize' is better...)\n", +"-stream -- use stream mode audio (e.g., for es1370 audio cards)\n", +"-32bit -- allow 32 bit OSS audio transfers (for RME Hammerfall)\n", +#endif + +#ifdef ALSA99 +"-alsa -- use ALSA audio drivers\n", +"-alsadev -- specify ALSA I/O device number (counting from 1)\n", +#endif + +#ifdef ALSA01 +"-alsa -- use ALSA audio drivers\n", +"-alsadev -- ALSA device # (counting from 1) or name: default hw:0,0\n", +#endif + +#ifdef RME_HAMMERFALL +"-rme -- use Ritsch's RME 9652 audio driver\n", +#endif +"-audioindev ... -- sound in device list; e.g., \"2,1\" for second and first\n", +"-audiooutdev ... -- sound out device list, same as above \n", +"-audiodev ... -- specify both -audioindev and -audiooutdev together\n", + +#ifdef NT +"-resync -- resynchronize audio (default if more than 2 channels)\n", +"-noresync -- never resynchronize audio I/O (default for stereo)\n", +"-asio -- use ASIO audio driver (and not the 'MMIO' default)\n", +#endif + +"\nMIDI configuration flags:\n", +"-midiindev ... -- midi in device list; e.g., \"1,3\" for first and third\n", +"-midioutdev ... -- midi out device list, same format\n", +"-mididev ... -- specify -midioutdev and -midiindev together\n", +"-nomidiin -- suppress MIDI input\n", +"-nomidiout -- suppress MIDI output\n", +"-nomidi -- suppress MIDI input and output\n", + +"\ngeneral flags:\n", +"-path -- add to file search path\n", +"-open -- open file(s) on startup\n", +"-lib -- load object library(s)\n", +"-font -- specify default font size in points\n", +"-verbose -- extra printout on startup and when searching for files\n", +"-d -- specify debug level\n", +"-noloadbang -- suppress all loadbangs\n", +"-nogui -- suppress starting the GUI\n", +"-guicmd \"cmd...\" -- substitute another GUI program (e.g., rsh)\n", +"-send \"msg...\" -- send a message at startup (after patches are loaded)\n", +#ifdef UNIX +"-rt or -realtime -- use real-time priority (needs root privilege)\n", +#endif +}; + +static void sys_parsedevlist(int *np, int *vecp, int max, char *str) +{ + int n = 0; + while (n < max) + { + if (!*str) break; + else + { + char *endp; + vecp[n] = strtol(str, &endp, 10); + if (endp == str) + break; + n++; + if (!endp) + break; + str = endp + 1; + } + } + *np = n; +} + +static int sys_getmultidevchannels(int n, int *devlist) +{ + int sum = 0; + if (n<0)return(-1); + if (n==0)return 0; + while(n--)sum+=*devlist++; + return sum; +} + + + /* this routine tries to figure out where to find the auxilliary files + Pd will need to run. This is either done by looking at the command line + invokation for Pd, or if htat fails, by consulting the variable + INSTALL_PREFIX. In NT, we don't try to use INSTALL_PREFIX. */ +void sys_findprogdir(char *progname) +{ + char sbuf[MAXPDSTRING], sbuf2[MAXPDSTRING], *sp; + char *lastslash; +#ifdef UNIX + struct stat statbuf; +#endif + + /* find out by what string Pd was invoked; put answer in "sbuf". */ +#ifdef NT + GetModuleFileName(NULL, sbuf2, sizeof(sbuf2)); + sbuf2[MAXPDSTRING-1] = 0; + sys_unbashfilename(sbuf2, sbuf); +#endif /* NT */ +#ifdef UNIX + strncpy(sbuf, progname, MAXPDSTRING); + sbuf[MAXPDSTRING-1] = 0; +#endif + lastslash = strrchr(sbuf, '/'); + if (lastslash) + { + /* bash last slash to zero so that sbuf is directory pd was in, + e.g., ~/pd/bin */ + *lastslash = 0; + /* go back to the parent from there, e.g., ~/pd */ + lastslash = strrchr(sbuf, '/'); + if (lastslash) + { + strncpy(sbuf2, sbuf, lastslash-sbuf); + sbuf2[lastslash-sbuf] = 0; + } + else strcpy(sbuf2, ".."); + } + else + { + /* no slashes found. Try INSTALL_PREFIX. */ +#ifdef INSTALL_PREFIX + strcpy(sbuf2, INSTALL_PREFIX); +#else + strcpy(sbuf2, "."); +#endif + } + /* now we believe sbuf2 holds the parent directory of the directory + pd was found in. We now want to infer the "lib" directory and the + "gui" directory. In "simple" UNIX installations, the layout is + .../bin/pd + .../bin/pd-gui + .../doc + and in "complicated" UNIX installations, it's: + .../bin/pd + .../lib/pd/bin/pd-gui + .../lib/pd/doc + To decide which, we stat .../lib/pd; if that exists, we assume it's + the complicated layout. In NT, it's the "simple" layout, but + the gui program is straight wish80: + .../bin/pd + .../bin/wish80.exe + .../doc + */ +#ifdef UNIX + strncpy(sbuf, sbuf2, MAXPDSTRING-30); + sbuf[MAXPDSTRING-30] = 0; + strcat(sbuf, "/lib/pd"); + if (stat(sbuf, &statbuf) >= 0) + { + /* complicated layout: lib dir is the one we just stat-ed above */ + sys_libdir = gensym(sbuf); + /* gui lives in .../lib/pd/bin */ + strncpy(sbuf, sbuf2, MAXPDSTRING-30); + sbuf[MAXPDSTRING-30] = 0; + strcat(sbuf, "/lib/pd/bin"); + sys_guidir = gensym(sbuf); + } + else + { + /* simple layout: lib dir is the parent */ + sys_libdir = gensym(sbuf2); + /* gui lives in .../bin */ + strncpy(sbuf, sbuf2, MAXPDSTRING-30); + sbuf[MAXPDSTRING-30] = 0; + strcat(sbuf, "/bin"); + sys_guidir = gensym(sbuf); + } +#endif +#ifdef NT + sys_libdir = gensym(sbuf2); + sys_guidir = &s_; /* in NT the guipath just depends on the libdir */ +#endif +} + +int sys_argparse(int argc, char **argv) +{ + char sbuf[MAXPDSTRING]; +#ifdef NT + int resync = -1; +#endif + argc--; argv++; + while ((argc > 0) && **argv == '-') + { + if (!strcmp(*argv, "-r") && argc > 1 && + sscanf(argv[1], "%d", &srate) >= 1) + { + argc -= 2; + argv += 2; + } + else if (!strcmp(*argv, "-inchannels")) + { /* IOhannes */ + sys_parsedevlist(&sys_nchin, sys_chinlist, MAXSOUNDINDEV, argv[1]); + inchannels=sys_getmultidevchannels(sys_nchin, sys_chinlist); + + if (!sys_nchin) + goto usage; + + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-outchannels")) + { /* IOhannes */ + sys_parsedevlist(&sys_nchout, sys_choutlist,MAXSOUNDOUTDEV, argv[1]); + outchannels=sys_getmultidevchannels(sys_nchout, sys_choutlist); + + if (!sys_nchout) + goto usage; + + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-channels")) + { + sys_parsedevlist(&sys_nchin, sys_chinlist,MAXSOUNDINDEV, + argv[1]); + inchannels = sys_getmultidevchannels(sys_nchin, sys_chinlist); + sys_parsedevlist(&sys_nchout, sys_choutlist,MAXSOUNDOUTDEV, + argv[1]); + outchannels = sys_getmultidevchannels(sys_nchout, sys_choutlist); + + if (!sys_nchout) + goto usage; + + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-soundbuf") || !strcmp(*argv, "-audiobuf")) + { + sys_audiobuf(atoi(argv[1])); + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-blocksize")) + { + sys_setblocksize(atoi(argv[1])); + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-sleepgrain")) + { + sys_sleepgrain = 1000 * atoi(argv[1]); + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-nodac")) + { /* IOhannes */ + sys_nsoundout=0; + sys_nchout = 0; + outchannels =0; + argc--; argv++; + } + else if (!strcmp(*argv, "-noadc")) + { /* IOhannes */ + sys_nsoundin=0; + sys_nchin = 0; + inchannels =0; + argc--; argv++; + } + else if (!strcmp(*argv, "-nosound") || !strcmp(*argv, "-noaudio")) + { /* IOhannes */ + sys_nsoundin=sys_nsoundout = 0; + sys_nchin = sys_nchout = 0; + inchannels =outchannels =0; + argc--; argv++; + } + else if (!strcmp(*argv, "-nomidiin")) + { + sys_nmidiin = 0; + argc--; argv++; + } + else if (!strcmp(*argv, "-nomidiout")) + { + sys_nmidiout = 0; + argc--; argv++; + } + else if (!strcmp(*argv, "-nomidi")) + { + sys_nmidiin = sys_nmidiout = 0; + argc--; argv++; + } + else if (!strcmp(*argv, "-midiindev")) + { + sys_parsedevlist(&sys_nmidiin, sys_midiindevlist, MAXMIDIINDEV, + argv[1]); + if (!sys_nmidiin) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-midioutdev")) + { + sys_parsedevlist(&sys_nmidiout, sys_midioutdevlist, MAXMIDIOUTDEV, + argv[1]); + if (!sys_nmidiout) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-mididev")) + { + sys_parsedevlist(&sys_nmidiin, sys_midiindevlist, MAXMIDIINDEV, + argv[1]); + sys_parsedevlist(&sys_nmidiout, sys_midioutdevlist, MAXMIDIOUTDEV, + argv[1]); + if (!sys_nmidiout) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-path")) + { + sys_addpath(argv[1]); + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-open") && argc > 1) + { + sys_openlist = namelist_append(sys_openlist, argv[1]); + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-lib") && argc > 1) + { + sys_externlist = namelist_append(sys_externlist, argv[1]); + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-font") && argc > 1) + { + sys_defaultfont = sys_nearestfontsize(atoi(argv[1])); + argc -= 2; + argv += 2; + } + else if (!strcmp(*argv, "-verbose")) + { + sys_verbose = 1; + argc--; argv++; + } + else if (!strcmp(*argv, "-d") && argc > 1 && + sscanf(argv[1], "%d", &sys_debuglevel) >= 1) + { + argc -= 2; + argv += 2; + } + else if (!strcmp(*argv, "-noloadbang")) + { + sys_noloadbang = 1; + argc--; argv++; + } + else if (!strcmp(*argv, "-nogui")) + { + sys_nogui = 1; + argc--; argv++; + } + else if (!strcmp(*argv, "-guicmd") && argc > 1) + { + sys_guicmd = argv[1]; + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-send") && argc > 1) + { + sys_messagelist = namelist_append(sys_messagelist, argv[1]); + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-listdev")) + { + sys_listdevs(); + argc--; argv++; + } +#ifdef UNIX + else if (!strcmp(*argv, "-rt")) + { + sys_hipriority = 1; + argc--; argv++; + } + else if (!strcmp(*argv, "-realtime")) + { + sys_hipriority = 1; + argc--; argv++; + } +#endif +#ifdef __linux__ + else if (!strcmp(*argv, "-frags")) + { + linux_setfrags(atoi(argv[1])); + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-fragsize")) + { + post("pd: -fragsize argument is obsolete; use '-blocksize %d'\n", + (1 << atoi(argv[1]))); + sys_setblocksize(1 << atoi(argv[1])); + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-stream")) + { + linux_streammode(); + argc--; argv++; + } + else if (!strcmp(*argv, "-32bit")) + { + linux_32bit(); + argc--; argv++; + } +#ifdef ALSA01 + else if (!strcmp(*argv, "-alsa")) + { + linux_set_sound_api(API_ALSA); + argc--; argv++; + } + else if (!strcmp(*argv, "-alsadev")) + { + if (argv[1][0] >= '1' && argv[1][0] <= '9') + { + char buf[80]; + sprintf(buf, "hw:%d,0", atoi(argv[1]) - 1); + linux_alsa_devname(buf); + } + else linux_alsa_devname(argv[1]); + linux_set_sound_api(API_ALSA); + argc -= 2; argv +=2; + } +#endif +#ifdef ALSA99 + else if (!strcmp(*argv, "-alsa")) + { + linux_set_sound_api(API_ALSA); + argc--; argv++; + } + else if (!strcmp(*argv, "-alsadev")) + { + linux_alsa_devno(atoi(argv[1])); + linux_set_sound_api(API_ALSA); + argc -= 2; argv +=2; + } +#endif +#ifdef RME_HAMMERFALL + else if (!strcmp(*argv, "-rme")) + { + linux_set_sound_api(API_RME); + argc--; argv++; + } +#endif +#endif + else if (!strcmp(*argv, "-soundindev") || + !strcmp(*argv, "-audioindev")) + { /* IOhannes */ + sys_parsedevlist(&sys_nsoundin, sys_soundindevlist, + MAXSOUNDINDEV, argv[1]); + if (!sys_nsoundin) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-soundoutdev") || + !strcmp(*argv, "-audiooutdev")) + { /* IOhannes */ + sys_parsedevlist(&sys_nsoundout, sys_soundoutdevlist, + MAXSOUNDOUTDEV, argv[1]); + if (!sys_nsoundout) + goto usage; + argc -= 2; argv += 2; + } + else if (!strcmp(*argv, "-sounddev") || !strcmp(*argv, "-audiodev")) + { + sys_parsedevlist(&sys_nsoundin, sys_soundindevlist, + MAXSOUNDINDEV, argv[1]); + sys_parsedevlist(&sys_nsoundout, sys_soundoutdevlist, + MAXSOUNDOUTDEV, argv[1]); + if (!sys_nsoundout) + goto usage; + argc -= 2; argv += 2; + } +#ifdef NT + else if (!strcmp(*argv, "-asio")) + { + nt_set_sound_api(API_PORTAUDIO); + argc--; argv++; + } + else if (!strcmp(*argv, "-noresync")) + { + resync = 0; + argc--; argv++; + } + else if (!strcmp(*argv, "-resync")) + { + resync = 1; + argc--; argv++; + } + +#endif /* NT */ + else + { + unsigned int i; + usage: + for (i = 0; i < sizeof(usagemessage)/sizeof(*usagemessage); i++) + fprintf(stderr, "%s", usagemessage[i]); + return (1); + } + } +#ifdef NT + /* resynchronization is on by default for mulltichannel, otherwise + off. */ + if (resync == -1) + resync = (inchannels > 2 || outchannels > 2); + if (!resync) + nt_noresync(); +#endif + if (!sys_defaultfont) sys_defaultfont = DEFAULTFONT; + for (; argc > 0; argc--, argv++) + sys_openlist = namelist_append(sys_openlist, *argv); + + + return (0); +} + +int sys_getblksize(void) +{ + return (DACBLKSIZE); +} + +static void sys_addextrapath(void) +{ + char sbuf[MAXPDSTRING]; + /* add "extra" library to path */ + strncpy(sbuf, sys_libdir->s_name, MAXPDSTRING-30); + sbuf[MAXPDSTRING-30] = 0; + strcat(sbuf, "/extra"); + sys_addpath(sbuf); +} + diff --git a/pd/src/s_nt.c b/pd/src/s_nt.c new file mode 100644 index 00000000..99346e7c --- /dev/null +++ b/pd/src/s_nt.c @@ -0,0 +1,1586 @@ +/* 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. */ + +/* modified 2/98 by Winfried Ritsch to deal with up to 4 synchronized +"wave" devices, which is how ADAT boards appear to the WAVE API. */ + +#include "m_imp.h" +#include + +#include + +#include + +/* ------------------------- audio -------------------------- */ + +static void nt_close_midiin(void); + +static void postflags(void); + +#define NAPORTS 16 /* wini hack for multiple ADDA devices */ +#define NT_MAXCH (2 * NAPORTS) +#define CHANNELS_PER_DEVICE 2 +#define DEFAULTCHANS 2 +#define DEFAULTSRATE 44100 +#define SAMPSIZE 2 + +#define REALDACBLKSIZE (4 * DACBLKSIZE) /* larger underlying bufsize */ + +#define MAXBUFFER 100 /* number of buffers in use at maximum advance */ +#define DEFBUFFER 30 /* default is about 30x6 = 180 msec! */ +static int nt_naudiobuffer = DEFBUFFER; +static int nt_advance_samples; + +float sys_dacsr = DEFAULTSRATE; + +static int nt_whichapi = API_MMIO; +static int nt_meters; /* true if we're metering */ +static float nt_inmax; /* max input amplitude */ +static float nt_outmax; /* max output amplitude */ +static int nt_nwavein, nt_nwaveout; /* number of WAVE devices in and out */ +static int nt_blocksize = 0; /* audio I/O block size in sample frames */ +int sys_schedadvance = 20000; /* scheduler advance in microseconds */ + +typedef struct _sbuf +{ + HANDLE hData; + HPSTR lpData; // pointer to waveform data memory + HANDLE hWaveHdr; + WAVEHDR *lpWaveHdr; // pointer to header structure +} t_sbuf; + +t_sbuf ntsnd_outvec[NAPORTS][MAXBUFFER]; /* circular buffer array */ +HWAVEOUT ntsnd_outdev[NAPORTS]; /* output device */ +static int ntsnd_outphase[NAPORTS]; /* index of next buffer to send */ + +t_sbuf ntsnd_invec[NAPORTS][MAXBUFFER]; /* circular buffer array */ +HWAVEIN ntsnd_indev[NAPORTS]; /* input device */ +static int ntsnd_inphase[NAPORTS]; /* index of next buffer to read */ +int sys_hipriority = 0; + +static void nt_waveinerror(char *s, int err) +{ + char t[256]; + waveInGetErrorText(err, t, 256); + fprintf(stderr, s, t); +} + +static void nt_waveouterror(char *s, int err) +{ + char t[256]; + waveOutGetErrorText(err, t, 256); + fprintf(stderr, s, t); +} + +static void wave_prep(t_sbuf *bp) +{ + WAVEHDR *wh; + short *sp; + int i; + /* + * Allocate and lock memory for the waveform data. The memory + * for waveform data must be globally allocated with + * GMEM_MOVEABLE and GMEM_SHARE flags. + */ + + if (!(bp->hData = + GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, + (DWORD) (CHANNELS_PER_DEVICE * REALDACBLKSIZE * SAMPSIZE)))) + printf("alloc 1 failed\n"); + + if (!(bp->lpData = + (HPSTR) GlobalLock(bp->hData))) + printf("lock 1 failed\n"); + + /* Allocate and lock memory for the header. */ + + if (!(bp->hWaveHdr = + GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD) sizeof(WAVEHDR)))) + printf("alloc 2 failed\n"); + + if (!(wh = bp->lpWaveHdr = + (WAVEHDR *) GlobalLock(bp->hWaveHdr))) + printf("lock 2 failed\n"); + + for (i = CHANNELS_PER_DEVICE * REALDACBLKSIZE, + sp = (short *)bp->lpData; i--; ) + *sp++ = 0; + + wh->lpData = bp->lpData; + wh->dwBufferLength = (CHANNELS_PER_DEVICE * REALDACBLKSIZE * SAMPSIZE); + wh->dwFlags = 0; + wh->dwLoops = 0L; + wh->lpNext = 0; + wh->reserved = 0; +} + +static int nt_inalloc[NAPORTS], nt_outalloc[NAPORTS]; +static UINT nt_whichdac = WAVE_MAPPER, nt_whichadc = WAVE_MAPPER; + +int mmio_open_audio(void) +{ + PCMWAVEFORMAT form; + int i; + UINT mmresult; + int nad, nda; + + if (sys_verbose) + post("%d devices in, %d devices out", + nt_nwavein, nt_nwaveout); + + form.wf.wFormatTag = WAVE_FORMAT_PCM; + form.wf.nChannels = CHANNELS_PER_DEVICE; + form.wf.nSamplesPerSec = sys_dacsr; + form.wf.nAvgBytesPerSec = sys_dacsr * (CHANNELS_PER_DEVICE * SAMPSIZE); + form.wf.nBlockAlign = CHANNELS_PER_DEVICE * SAMPSIZE; + form.wBitsPerSample = 8 * SAMPSIZE; + + for (nad=0; nad < nt_nwavein; nad++) + { + /* Open waveform device(s), sucessively numbered, for input */ + + mmresult = waveInOpen(&ntsnd_indev[nad], nt_whichadc+nad, + (WAVEFORMATEX *)(&form), 0L, 0L, CALLBACK_NULL); + + if (sys_verbose) + printf("opened adc device %d with return %d\n", + nt_whichadc+nad,mmresult); + + if (mmresult != MMSYSERR_NOERROR) + { + nt_waveinerror("waveInOpen: %s\n", mmresult); + nt_nwavein = nad; /* nt_nwavein = 0 wini */ + } + else + { + if (!nt_inalloc[nad]) + { + for (i = 0; i < nt_naudiobuffer; i++) + wave_prep(&ntsnd_invec[nad][i]); + nt_inalloc[nad] = 1; + } + for (i = 0; i < nt_naudiobuffer; i++) + { + mmresult = waveInPrepareHeader(ntsnd_indev[nad], + ntsnd_invec[nad][i].lpWaveHdr, sizeof(WAVEHDR)); + if (mmresult != MMSYSERR_NOERROR) + nt_waveinerror("waveinprepareheader: %s\n", mmresult); + mmresult = waveInAddBuffer(ntsnd_indev[nad], + ntsnd_invec[nad][i].lpWaveHdr, sizeof(WAVEHDR)); + if (mmresult != MMSYSERR_NOERROR) + nt_waveinerror("waveInAddBuffer: %s\n", mmresult); + } + } + } + /* quickly start them all together */ + for(nad=0; nad < nt_nwavein; nad++) + waveInStart(ntsnd_indev[nad]); + + for(nda=0; nda < nt_nwaveout; nda++) + { + + /* Open a waveform device for output in sucessiv device numbering*/ + mmresult = waveOutOpen(&ntsnd_outdev[nda], nt_whichdac + nda, + (WAVEFORMATEX *)(&form), 0L, 0L, CALLBACK_NULL); + + if (sys_verbose) + fprintf(stderr,"opened dac device %d, with return %d\n", + nt_whichdac +nda, mmresult); + + if (mmresult != MMSYSERR_NOERROR) + { + fprintf(stderr,"Wave out open device %d + %d\n",nt_whichdac,nda); + nt_waveouterror("waveOutOpen device: %s\n", mmresult); + nt_nwaveout = nda; + } + else + { + if (!(nt_outalloc[nda])) + { + for (i = 0; i < nt_naudiobuffer; i++) + { + wave_prep(&ntsnd_outvec[nda][i]); + /* set DONE flag as if we had queued them */ + ntsnd_outvec[nda][i].lpWaveHdr->dwFlags = WHDR_DONE; + } + nt_outalloc[nda] = 1; + } + } + } + + return (0); +} + +void mmio_close_audio( void) +{ + int errcode; + int nda, nad; + if (sys_verbose) + post("closing audio..."); + + for (nda=0; nda < nt_nwaveout; nda++) /*if (nt_nwaveout) wini */ + { + errcode = waveOutReset(ntsnd_outdev[nda]); + if (errcode != MMSYSERR_NOERROR) + printf("error resetting output %d: %d\n", nda, errcode); + errcode = waveOutClose(ntsnd_outdev[nda]); + if (errcode != MMSYSERR_NOERROR) + printf("error closing output %d: %d\n",nda , errcode); + } + nt_nwaveout = 0; + + for(nad=0; nad < nt_nwavein;nad++) /* if (nt_nwavein) wini */ + { + errcode = waveInReset(ntsnd_indev[nad]); + if (errcode != MMSYSERR_NOERROR) + printf("error resetting input: %d\n", errcode); + errcode = waveInClose(ntsnd_indev[nad]); + if (errcode != MMSYSERR_NOERROR) + printf("error closing input: %d\n", errcode); + } + nt_nwavein = 0; +} + + +#define ADCJITTER 10 /* We tolerate X buffers of jitter by default */ +#define DACJITTER 10 + +static int nt_adcjitterbufsallowed = ADCJITTER; +static int nt_dacjitterbufsallowed = DACJITTER; + + /* ------------- MIDI time stamping from audio clock ------------ */ + +#ifdef MIDI_TIMESTAMP + +static double nt_hibuftime; +static double initsystime = -1; + + /* call this whenever we reset audio */ +static void nt_resetmidisync(void) +{ + initsystime = clock_getsystime(); + nt_hibuftime = sys_getrealtime(); +} + + /* call this whenever we're idled waiting for audio to be ready. + The routine maintains a high and low water point for the difference + between real and DAC time. */ + +static void nt_midisync(void) +{ + double jittersec, diff; + + if (initsystime == -1) nt_resetmidisync(); + jittersec = (nt_dacjitterbufsallowed > nt_adcjitterbufsallowed ? + nt_dacjitterbufsallowed : nt_adcjitterbufsallowed) + * REALDACBLKSIZE / sys_getsr(); + diff = sys_getrealtime() - 0.001 * clock_gettimesince(initsystime); + if (diff > nt_hibuftime) nt_hibuftime = diff; + if (diff < nt_hibuftime - jittersec) + { + post("jitter excess %d %f", dac, diff); + nt_resetmidisync(); + } +} + +static double nt_midigettimefor(LARGE_INTEGER timestamp) +{ + /* this is broken now... used to work when "timestamp" was derived from + QueryPerformanceCounter() instead of the gates approved + timeGetSystemTime() call in the MIDI callback routine below. */ + return (nt_tixtotime(timestamp) - nt_hibuftime); +} +#endif /* MIDI_TIMESTAMP */ + + +static int nt_fill = 0; +#define WRAPFWD(x) ((x) >= nt_naudiobuffer ? (x) - nt_naudiobuffer: (x)) +#define WRAPBACK(x) ((x) < 0 ? (x) + nt_naudiobuffer: (x)) +#define MAXRESYNC 500 + +#if 0 /* this is used for debugging */ +static void nt_printaudiostatus(void) +{ + int nad, nda; + for (nad = 0; nad < nt_nwavein; nad++) + { + int phase = ntsnd_inphase[nad]; + int phase2 = phase, phase3 = WRAPFWD(phase2), count, ntrans = 0; + int firstphasedone = -1, firstphasebusy = -1; + for (count = 0; count < nt_naudiobuffer; count++) + { + int donethis = + (ntsnd_invec[nad][phase2].lpWaveHdr->dwFlags & WHDR_DONE); + int donenext = + (ntsnd_invec[nad][phase3].lpWaveHdr->dwFlags & WHDR_DONE); + if (donethis && !donenext) + { + if (firstphasebusy >= 0) goto multipleadc; + firstphasebusy = count; + } + if (!donethis && donenext) + { + if (firstphasedone >= 0) goto multipleadc; + firstphasedone = count; + } + phase2 = phase3; + phase3 = WRAPFWD(phase2 + 1); + } + post("nad %d phase %d busy %d done %d", nad, phase, firstphasebusy, + firstphasedone); + continue; + multipleadc: + startpost("nad %d phase %d: oops:", nad, phase); + for (count = 0; count < nt_naudiobuffer; count++) + { + char buf[80]; + sprintf(buf, " %d", + (ntsnd_invec[nad][count].lpWaveHdr->dwFlags & WHDR_DONE)); + poststring(buf); + } + endpost(); + } + for (nda = 0; nda < nt_nwaveout; nda++) + { + int phase = ntsnd_outphase[nad]; + int phase2 = phase, phase3 = WRAPFWD(phase2), count, ntrans = 0; + int firstphasedone = -1, firstphasebusy = -1; + for (count = 0; count < nt_naudiobuffer; count++) + { + int donethis = + (ntsnd_outvec[nda][phase2].lpWaveHdr->dwFlags & WHDR_DONE); + int donenext = + (ntsnd_outvec[nda][phase3].lpWaveHdr->dwFlags & WHDR_DONE); + if (donethis && !donenext) + { + if (firstphasebusy >= 0) goto multipledac; + firstphasebusy = count; + } + if (!donethis && donenext) + { + if (firstphasedone >= 0) goto multipledac; + firstphasedone = count; + } + phase2 = phase3; + phase3 = WRAPFWD(phase2 + 1); + } + if (firstphasebusy < 0) post("nda %d phase %d all %d", + nda, phase, (ntsnd_outvec[nad][0].lpWaveHdr->dwFlags & WHDR_DONE)); + else post("nda %d phase %d busy %d done %d", nda, phase, firstphasebusy, + firstphasedone); + continue; + multipledac: + startpost("nda %d phase %d: oops:", nda, phase); + for (count = 0; count < nt_naudiobuffer; count++) + { + char buf[80]; + sprintf(buf, " %d", + (ntsnd_outvec[nad][count].lpWaveHdr->dwFlags & WHDR_DONE)); + poststring(buf); + } + endpost(); + } +} +#endif /* 0 */ + +/* this is a hack to avoid ever resyncing audio pointers in case for whatever +reason the sync testing below gives false positives. */ + +static int nt_resync_cancelled; + +void nt_noresync( void) +{ + nt_resync_cancelled = 1; +} + +static void nt_resyncaudio(void) +{ + UINT mmresult; + int nad, nda, count; + if (nt_resync_cancelled) + return; + /* for each open input device, eat all buffers which are marked + ready. The next one will thus be "busy". */ + post("resyncing audio"); + for (nad = 0; nad < nt_nwavein; nad++) + { + int phase = ntsnd_inphase[nad]; + for (count = 0; count < MAXRESYNC; count++) + { + WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr; + if (!(inwavehdr->dwFlags & WHDR_DONE)) break; + if (inwavehdr->dwFlags & WHDR_PREPARED) + waveInUnprepareHeader(ntsnd_indev[nad], + inwavehdr, sizeof(WAVEHDR)); + inwavehdr->dwFlags = 0L; + waveInPrepareHeader(ntsnd_indev[nad], inwavehdr, sizeof(WAVEHDR)); + mmresult = waveInAddBuffer(ntsnd_indev[nad], inwavehdr, + sizeof(WAVEHDR)); + if (mmresult != MMSYSERR_NOERROR) + nt_waveinerror("waveInAddBuffer: %s\n", mmresult); + ntsnd_inphase[nad] = phase = WRAPFWD(phase + 1); + } + if (count == MAXRESYNC) post("resync error 1"); + } + /* Each output buffer which is "ready" is filled with zeros and + queued. */ + for (nda = 0; nda < nt_nwaveout; nda++) + { + int phase = ntsnd_outphase[nda]; + for (count = 0; count < MAXRESYNC; count++) + { + WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr; + if (!(outwavehdr->dwFlags & WHDR_DONE)) break; + if (outwavehdr->dwFlags & WHDR_PREPARED) + waveOutUnprepareHeader(ntsnd_outdev[nda], + outwavehdr, sizeof(WAVEHDR)); + outwavehdr->dwFlags = 0L; + memset((char *)(ntsnd_outvec[nda][phase].lpData), + 0, (CHANNELS_PER_DEVICE * REALDACBLKSIZE * SAMPSIZE)); + waveOutPrepareHeader(ntsnd_outdev[nda], outwavehdr, + sizeof(WAVEHDR)); + mmresult = waveOutWrite(ntsnd_outdev[nda], outwavehdr, + sizeof(WAVEHDR)); + if (mmresult != MMSYSERR_NOERROR) + nt_waveouterror("waveOutAddBuffer: %s\n", mmresult); + ntsnd_outphase[nda] = phase = WRAPFWD(phase + 1); + } + if (count == MAXRESYNC) post("resync error 2"); + } + +#ifdef MIDI_TIMESTAMP + nt_resetmidisync(); +#endif + +} + +#define LATE 0 +#define RESYNC 1 +#define NOTHING 2 +static int nt_errorcount; +static int nt_resynccount; +static double nt_nextreporttime = -1; + +void nt_logerror(int which) +{ +#if 0 + post("error %d %d", count, which); + if (which < NOTHING) nt_errorcount++; + if (which == RESYNC) nt_resynccount++; + if (sys_getrealtime() > nt_nextreporttime) + { + post("%d audio I/O error%s", nt_errorcount, + (nt_errorcount > 1 ? "s" : "")); + if (nt_resynccount) post("DAC/ADC sync error"); + nt_errorcount = nt_resynccount = 0; + nt_nextreporttime = sys_getrealtime() - 5; + } +#endif +} + +/* system buffer with t_sample types for one tick */ +t_sample *sys_soundout; +t_sample *sys_soundin; +float sys_dacsr; + +int mmio_send_dacs(void) +{ + HMMIO hmmio; + UINT mmresult; + HANDLE hFormat; + int i, j; + short *sp1, *sp2; + float *fp1, *fp2; + int nextfill, doxfer = 0; + int nda, nad; + if (!nt_nwavein && !nt_nwaveout) return (0); + + + if (nt_meters) + { + int i, n; + float maxsamp; + for (i = 0, n = 2 * nt_nwavein * DACBLKSIZE, maxsamp = nt_inmax; + i < n; i++) + { + float f = sys_soundin[i]; + if (f > maxsamp) maxsamp = f; + else if (-f > maxsamp) maxsamp = -f; + } + nt_inmax = maxsamp; + for (i = 0, n = 2 * nt_nwaveout * DACBLKSIZE, maxsamp = nt_outmax; + i < n; i++) + { + float f = sys_soundout[i]; + if (f > maxsamp) maxsamp = f; + else if (-f > maxsamp) maxsamp = -f; + } + nt_outmax = maxsamp; + } + + /* the "fill pointer" nt_fill controls where in the next + I/O buffers we will write and/or read. If it's zero, we + first check whether the buffers are marked "done". */ + + if (!nt_fill) + { + for (nad = 0; nad < nt_nwavein; nad++) + { + int phase = ntsnd_inphase[nad]; + WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr; + if (!(inwavehdr->dwFlags & WHDR_DONE)) goto idle; + } + for (nda = 0; nda < nt_nwaveout; nda++) + { + int phase = ntsnd_outphase[nda]; + WAVEHDR *outwavehdr = + ntsnd_outvec[nda][phase].lpWaveHdr; + if (!(outwavehdr->dwFlags & WHDR_DONE)) goto idle; + } + for (nad = 0; nad < nt_nwavein; nad++) + { + int phase = ntsnd_inphase[nad]; + WAVEHDR *inwavehdr = + ntsnd_invec[nad][phase].lpWaveHdr; + if (inwavehdr->dwFlags & WHDR_PREPARED) + waveInUnprepareHeader(ntsnd_indev[nad], + inwavehdr, sizeof(WAVEHDR)); + } + for (nda = 0; nda < nt_nwaveout; nda++) + { + int phase = ntsnd_outphase[nda]; + WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr; + if (outwavehdr->dwFlags & WHDR_PREPARED) + waveOutUnprepareHeader(ntsnd_outdev[nda], + outwavehdr, sizeof(WAVEHDR)); + } + } + + /* Convert audio output to fixed-point and put it in the output + buffer. */ + for (nda = 0, fp1 = sys_soundout; nda < nt_nwaveout; nda++) + { + int phase = ntsnd_outphase[nda]; + + for (i = 0, sp1 = (short *)(ntsnd_outvec[nda][phase].lpData) + + CHANNELS_PER_DEVICE * nt_fill; + i < 2; i++, fp1 += DACBLKSIZE, sp1++) + { + for (j = 0, fp2 = fp1, sp2 = sp1; j < DACBLKSIZE; + j++, fp2++, sp2 += CHANNELS_PER_DEVICE) + { + int x1 = 32767.f * *fp2; + if (x1 > 32767) x1 = 32767; + else if (x1 < -32767) x1 = -32767; + *sp2 = x1; + } + } + } + memset(sys_soundout, 0, + (DACBLKSIZE*sizeof(t_sample)*CHANNELS_PER_DEVICE)*nt_nwaveout); + + /* vice versa for the input buffer */ + + for (nad = 0, fp1 = sys_soundin; nad < nt_nwavein; nad++) + { + int phase = ntsnd_inphase[nad]; + + for (i = 0, sp1 = (short *)(ntsnd_invec[nad][phase].lpData) + + CHANNELS_PER_DEVICE * nt_fill; + i < 2; i++, fp1 += DACBLKSIZE, sp1++) + { + for (j = 0, fp2 = fp1, sp2 = sp1; j < DACBLKSIZE; + j++, fp2++, sp2 += CHANNELS_PER_DEVICE) + { + *fp2 = ((float)(1./32767.)) * (float)(*sp2); + } + } + } + + nt_fill = nt_fill + DACBLKSIZE; + if (nt_fill == REALDACBLKSIZE) + { + nt_fill = 0; + + for (nad = 0; nad < nt_nwavein; nad++) + { + int phase = ntsnd_inphase[nad]; + HWAVEIN device = ntsnd_indev[nad]; + WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr; + waveInPrepareHeader(device, inwavehdr, sizeof(WAVEHDR)); + mmresult = waveInAddBuffer(device, inwavehdr, sizeof(WAVEHDR)); + if (mmresult != MMSYSERR_NOERROR) + nt_waveinerror("waveInAddBuffer: %s\n", mmresult); + ntsnd_inphase[nad] = WRAPFWD(phase + 1); + } + for (nda = 0; nda < nt_nwaveout; nda++) + { + int phase = ntsnd_outphase[nda]; + HWAVEOUT device = ntsnd_outdev[nda]; + WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr; + waveOutPrepareHeader(device, outwavehdr, sizeof(WAVEHDR)); + mmresult = waveOutWrite(device, outwavehdr, sizeof(WAVEHDR)); + if (mmresult != MMSYSERR_NOERROR) + nt_waveouterror("waveOutWrite: %s\n", mmresult); + ntsnd_outphase[nda] = WRAPFWD(phase + 1); + } + + /* check for DAC underflow or ADC overflow. */ + for (nad = 0; nad < nt_nwavein; nad++) + { + int phase = WRAPBACK(ntsnd_inphase[nad] - 2); + WAVEHDR *inwavehdr = ntsnd_invec[nad][phase].lpWaveHdr; + if (inwavehdr->dwFlags & WHDR_DONE) goto late; + } + for (nda = 0; nda < nt_nwaveout; nda++) + { + int phase = WRAPBACK(ntsnd_outphase[nda] - 2); + WAVEHDR *outwavehdr = ntsnd_outvec[nda][phase].lpWaveHdr; + if (outwavehdr->dwFlags & WHDR_DONE) goto late; + } + } + return (1); + +late: + + nt_logerror(LATE); + nt_resyncaudio(); + return (1); + +idle: + + /* If more than nt_adcjitterbufsallowed ADC buffers are ready + on any input device, resynchronize */ + + for (nad = 0; nad < nt_nwavein; nad++) + { + int phase = ntsnd_inphase[nad]; + WAVEHDR *inwavehdr = + ntsnd_invec[nad] + [WRAPFWD(phase + nt_adcjitterbufsallowed)].lpWaveHdr; + if (inwavehdr->dwFlags & WHDR_DONE) + { + nt_resyncaudio(); + return (0); + } + } + + /* test dac sync the same way */ + for (nda = 0; nda < nt_nwaveout; nda++) + { + int phase = ntsnd_outphase[nda]; + WAVEHDR *outwavehdr = + ntsnd_outvec[nda] + [WRAPFWD(phase + nt_dacjitterbufsallowed)].lpWaveHdr; + if (outwavehdr->dwFlags & WHDR_DONE) + { + nt_resyncaudio(); + return (0); + } + } +#ifdef MIDI_TIMESTAMP + nt_midisync(); +#endif + return (0); +} + + +static void nt_setchsr(int inchannels, int outchannels, int sr) +{ + int inbytes = inchannels * (DACBLKSIZE*sizeof(float)); + int outbytes = outchannels * (DACBLKSIZE*sizeof(float)); + + if (nt_nwavein) + free(sys_soundin); + if (nt_nwaveout) + free(sys_soundout); + + nt_nwavein = inchannels/CHANNELS_PER_DEVICE; + nt_nwaveout = outchannels/CHANNELS_PER_DEVICE; + sys_dacsr = sr; + + sys_soundin = (t_float *)malloc(inbytes); + memset(sys_soundin, 0, inbytes); + + sys_soundout = (t_float *)malloc(outbytes); + memset(sys_soundout, 0, outbytes); + + nt_advance_samples = (sys_schedadvance * sys_dacsr) / (1000000.); + if (nt_advance_samples < 3 * DACBLKSIZE) + nt_advance_samples = 3 * DACBLKSIZE; +} + +/* ------------------------- MIDI output -------------------------- */ +static void nt_midiouterror(char *s, int err) +{ + char t[256]; + midiOutGetErrorText(err, t, 256); + fprintf(stderr, s, t); +} + +static HMIDIOUT hMidiOut[MAXMIDIOUTDEV]; /* output device */ +static int nt_nmidiout; /* number of devices */ + +static void nt_open_midiout(int nmidiout, int *midioutvec) +{ + UINT result, wRtn; + int i; + int dev; + MIDIOUTCAPS midioutcaps; + if (nmidiout > MAXMIDIOUTDEV) + nmidiout = MAXMIDIOUTDEV; + + dev = 0; + + for (i = 0; i < nmidiout; i++) + { + MIDIOUTCAPS mocap; + result = midiOutOpen(&hMidiOut[dev], midioutvec[i]-1, 0, 0, + CALLBACK_NULL); + wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap, + sizeof(mocap)); + if (result != MMSYSERR_NOERROR) + { + fprintf(stderr,"midiOutOpen: %s\n",midioutcaps.szPname); + nt_midiouterror("midiOutOpen: %s\n", result); + } + else + { + if (sys_verbose) + fprintf(stderr,"midiOutOpen: Open %s as Port %d\n", + midioutcaps.szPname, dev); + dev++; + } + } + nt_nmidiout = dev; +} + +void sys_putmidimess(int portno, int a, int b, int c) +{ + DWORD foo; + MMRESULT res; + if (portno >= 0 && portno < nt_nmidiout) + { + foo = (a & 0xff) | ((b & 0xff) << 8) | ((c & 0xff) << 16); + res = midiOutShortMsg(hMidiOut[portno], foo); + if (res != MMSYSERR_NOERROR) + post("MIDI out error %d", res); + } +} + +void sys_putmidibyte(int portno, int byte) +{ + MMRESULT res; + if (portno >= 0 && portno < nt_nmidiout) + { + res = midiOutShortMsg(hMidiOut[portno], byte); + if (res != MMSYSERR_NOERROR) + post("MIDI out error %d", res); + } +} + +static void nt_close_midiout(void) +{ + int i; + for (i = 0; i < nt_nmidiout; i++) + { + midiOutReset(hMidiOut[i]); + midiOutClose(hMidiOut[i]); + } + nt_nmidiout = 0; +} + +/* -------------------------- MIDI input ---------------------------- */ + +#define INPUT_BUFFER_SIZE 1000 // size of input buffer in events + +static void nt_midiinerror(char *s, int err) +{ + char t[256]; + midiInGetErrorText(err, t, 256); + fprintf(stderr, s, t); +} + + +/* Structure to represent a single MIDI event. + */ + +#define EVNT_F_ERROR 0x00000001L + +typedef struct event_tag +{ + DWORD fdwEvent; + DWORD dwDevice; + LARGE_INTEGER timestamp; + DWORD data; +} EVENT; +typedef EVENT FAR *LPEVENT; + +/* Structure to manage the circular input buffer. + */ +typedef struct circularBuffer_tag +{ + HANDLE hSelf; /* handle to this structure */ + HANDLE hBuffer; /* buffer handle */ + WORD wError; /* error flags */ + DWORD dwSize; /* buffer size (in EVENTS) */ + DWORD dwCount; /* byte count (in EVENTS) */ + LPEVENT lpStart; /* ptr to start of buffer */ + LPEVENT lpEnd; /* ptr to end of buffer (last byte + 1) */ + LPEVENT lpHead; /* ptr to head (next location to fill) */ + LPEVENT lpTail; /* ptr to tail (next location to empty) */ +} CIRCULARBUFFER; +typedef CIRCULARBUFFER FAR *LPCIRCULARBUFFER; + + +/* Structure to pass instance data from the application + to the low-level callback function. + */ +typedef struct callbackInstance_tag +{ + HANDLE hSelf; + DWORD dwDevice; + LPCIRCULARBUFFER lpBuf; +} CALLBACKINSTANCEDATA; +typedef CALLBACKINSTANCEDATA FAR *LPCALLBACKINSTANCEDATA; + +/* Function prototypes + */ +LPCALLBACKINSTANCEDATA FAR PASCAL AllocCallbackInstanceData(void); +void FAR PASCAL FreeCallbackInstanceData(LPCALLBACKINSTANCEDATA lpBuf); + +LPCIRCULARBUFFER AllocCircularBuffer(DWORD dwSize); +void FreeCircularBuffer(LPCIRCULARBUFFER lpBuf); +WORD FAR PASCAL GetEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent); + +// Callback instance data pointers +LPCALLBACKINSTANCEDATA lpCallbackInstanceData[MAXMIDIINDEV]; + +UINT wNumDevices = 0; // Number of MIDI input devices opened +BOOL bRecordingEnabled = 1; // Enable/disable recording flag +int nNumBufferLines = 0; // Number of lines in display buffer +RECT rectScrollClip; // Clipping rectangle for scrolling + +LPCIRCULARBUFFER lpInputBuffer; // Input buffer structure +EVENT incomingEvent; // Incoming MIDI event structure + +MIDIINCAPS midiInCaps[MAXMIDIINDEV]; // Device capabilities structures +HMIDIIN hMidiIn[MAXMIDIINDEV]; // MIDI input device handles + + +/* AllocCallbackInstanceData - Allocates a CALLBACKINSTANCEDATA + * structure. This structure is used to pass information to the + * low-level callback function, each time it receives a message. + * + * Because this structure is accessed by the low-level callback + * function, it must be allocated using GlobalAlloc() with the + * GMEM_SHARE and GMEM_MOVEABLE flags and page-locked with + * GlobalPageLock(). + * + * Params: void + * + * Return: A pointer to the allocated CALLBACKINSTANCE data structure. + */ +LPCALLBACKINSTANCEDATA FAR PASCAL AllocCallbackInstanceData(void) +{ + HANDLE hMem; + LPCALLBACKINSTANCEDATA lpBuf; + + /* Allocate and lock global memory. + */ + hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, + (DWORD)sizeof(CALLBACKINSTANCEDATA)); + if(hMem == NULL) + return NULL; + + lpBuf = (LPCALLBACKINSTANCEDATA)GlobalLock(hMem); + if(lpBuf == NULL){ + GlobalFree(hMem); + return NULL; + } + + /* Page lock the memory. + */ + //GlobalPageLock((HGLOBAL)HIWORD(lpBuf)); + + /* Save the handle. + */ + lpBuf->hSelf = hMem; + + return lpBuf; +} + +/* FreeCallbackInstanceData - Frees the given CALLBACKINSTANCEDATA structure. + * + * Params: lpBuf - Points to the CALLBACKINSTANCEDATA structure to be freed. + * + * Return: void + */ +void FAR PASCAL FreeCallbackInstanceData(LPCALLBACKINSTANCEDATA lpBuf) +{ + HANDLE hMem; + + /* Save the handle until we're through here. + */ + hMem = lpBuf->hSelf; + + /* Free the structure. + */ + //GlobalPageUnlock((HGLOBAL)HIWORD(lpBuf)); + GlobalUnlock(hMem); + GlobalFree(hMem); +} + + +/* + * AllocCircularBuffer - Allocates memory for a CIRCULARBUFFER structure + * and a buffer of the specified size. Each memory block is allocated + * with GlobalAlloc() using GMEM_SHARE and GMEM_MOVEABLE flags, locked + * with GlobalLock(), and page-locked with GlobalPageLock(). + * + * Params: dwSize - The size of the buffer, in events. + * + * Return: A pointer to a CIRCULARBUFFER structure identifying the + * allocated display buffer. NULL if the buffer could not be allocated. + */ + + +LPCIRCULARBUFFER AllocCircularBuffer(DWORD dwSize) +{ + HANDLE hMem; + LPCIRCULARBUFFER lpBuf; + LPEVENT lpMem; + + /* Allocate and lock a CIRCULARBUFFER structure. + */ + hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, + (DWORD)sizeof(CIRCULARBUFFER)); + if(hMem == NULL) + return NULL; + + lpBuf = (LPCIRCULARBUFFER)GlobalLock(hMem); + if(lpBuf == NULL) + { + GlobalFree(hMem); + return NULL; + } + + /* Page lock the memory. Global memory blocks accessed by + * low-level callback functions must be page locked. + */ +#ifndef _WIN32 + GlobalSmartPageLock((HGLOBAL)HIWORD(lpBuf)); +#endif + + /* Save the memory handle. + */ + lpBuf->hSelf = hMem; + + /* Allocate and lock memory for the actual buffer. + */ + hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, dwSize * sizeof(EVENT)); + if(hMem == NULL) + { +#ifndef _WIN32 + GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf)); +#endif + GlobalUnlock(lpBuf->hSelf); + GlobalFree(lpBuf->hSelf); + return NULL; + } + + lpMem = (LPEVENT)GlobalLock(hMem); + if(lpMem == NULL) + { + GlobalFree(hMem); +#ifndef _WIN32 + GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf)); +#endif + GlobalUnlock(lpBuf->hSelf); + GlobalFree(lpBuf->hSelf); + return NULL; + } + + /* Page lock the memory. Global memory blocks accessed by + * low-level callback functions must be page locked. + */ +#ifndef _WIN32 + GlobalSmartPageLock((HGLOBAL)HIWORD(lpMem)); +#endif + + /* Set up the CIRCULARBUFFER structure. + */ + lpBuf->hBuffer = hMem; + lpBuf->wError = 0; + lpBuf->dwSize = dwSize; + lpBuf->dwCount = 0L; + lpBuf->lpStart = lpMem; + lpBuf->lpEnd = lpMem + dwSize; + lpBuf->lpTail = lpMem; + lpBuf->lpHead = lpMem; + + return lpBuf; +} + +/* FreeCircularBuffer - Frees the memory for the given CIRCULARBUFFER + * structure and the memory for the buffer it references. + * + * Params: lpBuf - Points to the CIRCULARBUFFER to be freed. + * + * Return: void + */ +void FreeCircularBuffer(LPCIRCULARBUFFER lpBuf) +{ + HANDLE hMem; + + /* Free the buffer itself. + */ +#ifndef _WIN32 + GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf->lpStart)); +#endif + GlobalUnlock(lpBuf->hBuffer); + GlobalFree(lpBuf->hBuffer); + + /* Free the CIRCULARBUFFER structure. + */ + hMem = lpBuf->hSelf; +#ifndef _WIN32 + GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf)); +#endif + GlobalUnlock(hMem); + GlobalFree(hMem); +} + +/* GetEvent - Gets a MIDI event from the circular input buffer. Events + * are removed from the buffer. The corresponding PutEvent() function + * is called by the low-level callback function, so it must reside in + * the callback DLL. PutEvent() is defined in the CALLBACK.C module. + * + * Params: lpBuf - Points to the circular buffer. + * lpEvent - Points to an EVENT structure that is filled with the + * retrieved event. + * + * Return: Returns non-zero if successful, zero if there are no + * events to get. + */ +WORD FAR PASCAL GetEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent) +{ + /* If no event available, return */ + if (!wNumDevices || lpBuf->dwCount <= 0) return (0); + + /* Get the event. + */ + *lpEvent = *lpBuf->lpTail; + + /* Decrement the byte count, bump the tail pointer. + */ + --lpBuf->dwCount; + ++lpBuf->lpTail; + + /* Wrap the tail pointer, if necessary. + */ + if(lpBuf->lpTail >= lpBuf->lpEnd) + lpBuf->lpTail = lpBuf->lpStart; + + return 1; +} + +/* PutEvent - Puts an EVENT in a CIRCULARBUFFER. If the buffer is full, + * it sets the wError element of the CIRCULARBUFFER structure + * to be non-zero. + * + * Params: lpBuf - Points to the CIRCULARBUFFER. + * lpEvent - Points to the EVENT. + * + * Return: void +*/ + +void FAR PASCAL PutEvent(LPCIRCULARBUFFER lpBuf, LPEVENT lpEvent) +{ + /* If the buffer is full, set an error and return. + */ + if(lpBuf->dwCount >= lpBuf->dwSize){ + lpBuf->wError = 1; + return; + } + + /* Put the event in the buffer, bump the head pointer and the byte count. + */ + *lpBuf->lpHead = *lpEvent; + + ++lpBuf->lpHead; + ++lpBuf->dwCount; + + /* Wrap the head pointer, if necessary. + */ + if(lpBuf->lpHead >= lpBuf->lpEnd) + lpBuf->lpHead = lpBuf->lpStart; +} + +/* midiInputHandler - Low-level callback function to handle MIDI input. + * Installed by midiInOpen(). The input handler takes incoming + * MIDI events and places them in the circular input buffer. It then + * notifies the application by posting a MM_MIDIINPUT message. + * + * This function is accessed at interrupt time, so it should be as + * fast and efficient as possible. You can't make any + * Windows calls here, except PostMessage(). The only Multimedia + * Windows call you can make are timeGetSystemTime(), midiOutShortMsg(). + * + * + * Param: hMidiIn - Handle for the associated input device. + * wMsg - One of the MIM_***** messages. + * dwInstance - Points to CALLBACKINSTANCEDATA structure. + * dwParam1 - MIDI data. + * dwParam2 - Timestamp (in milliseconds) + * + * Return: void + */ +void FAR PASCAL midiInputHandler( +HMIDIIN hMidiIn, +WORD wMsg, +DWORD dwInstance, +DWORD dwParam1, +DWORD dwParam2) +{ + EVENT event; + + switch(wMsg) + { + case MIM_OPEN: + break; + + /* The only error possible is invalid MIDI data, so just pass + * the invalid data on so we'll see it. + */ + case MIM_ERROR: + case MIM_DATA: + event.fdwEvent = (wMsg == MIM_ERROR) ? EVNT_F_ERROR : 0; + event.dwDevice = ((LPCALLBACKINSTANCEDATA)dwInstance)->dwDevice; + event.data = dwParam1; +#ifdef MIDI_TIMESTAMP + event.timestamp = timeGetSystemTime(); +#endif + /* Put the MIDI event in the circular input buffer. + */ + + PutEvent(((LPCALLBACKINSTANCEDATA)dwInstance)->lpBuf, + (LPEVENT) &event); + + break; + + default: + break; + } +} + +void nt_open_midiin(int nmidiin, int *midiinvec) +{ + UINT wRtn; + char szErrorText[256]; + unsigned int i; + unsigned int ndev = 0; + /* Allocate a circular buffer for low-level MIDI input. This buffer + * is filled by the low-level callback function and emptied by the + * application. + */ + lpInputBuffer = AllocCircularBuffer((DWORD)(INPUT_BUFFER_SIZE)); + if (lpInputBuffer == NULL) + { + printf("Not enough memory available for input buffer.\n"); + return; + } + + /* Open all MIDI input devices after allocating and setting up + * instance data for each device. The instance data is used to + * pass buffer management information between the application and + * the low-level callback function. It also includes a device ID, + * a handle to the MIDI Mapper, and a handle to the application's + * display window, so the callback can notify the window when input + * data is available. A single callback function is used to service + * all opened input devices. + */ + for (i=0; (i<(unsigned)nmidiin) && (idwDevice = i; + lpCallbackInstanceData[i]->lpBuf = lpInputBuffer; + + wRtn = midiInOpen((LPHMIDIIN)&hMidiIn[ndev], + midiinvec[i] - 1, + (DWORD)midiInputHandler, + (DWORD)lpCallbackInstanceData[ndev], + CALLBACK_FUNCTION); + if (wRtn) + { + FreeCallbackInstanceData(lpCallbackInstanceData[ndev]); + nt_midiinerror("midiInOpen: %s\n", wRtn); + } + else ndev++; + } + + /* Start MIDI input. + */ + for (i=0; i= nt_nexteventtime) +#endif + { + int msgtype = ((nt_nextevent.data & 0xf0) >> 4) - 8; + int commandbyte = nt_nextevent.data & 0xff; + int byte1 = (nt_nextevent.data >> 8) & 0xff; + int byte2 = (nt_nextevent.data >> 16) & 0xff; + int portno = nt_nextevent.dwDevice; + switch (msgtype) + { + case 0: + case 1: + case 2: + case 3: + case 6: + sys_midibytein(portno, commandbyte); + sys_midibytein(portno, byte1); + sys_midibytein(portno, byte2); + break; + case 4: + case 5: + sys_midibytein(portno, commandbyte); + sys_midibytein(portno, byte1); + break; + case 7: + sys_midibytein(portno, commandbyte); + break; + } + nt_isnextevent = 0; + } + } +} + +/* ------------------- public routines -------------------------- */ + +void sys_open_audio(int naudioindev, int *audioindev, + int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, + int nchoutdev, int *choutdev, int rate) /* IOhannes */ +{ + int inchans, outchans; + if (nchindev < 0) + inchans = (nchindev < 1 ? -1 : chindev[0]); + else + { + int i = nchindev; + int *l = chindev; + inchans = 0; + while (i--) + inchans += *l++; + } + if (nchoutdev<0) + outchans = (nchoutdev < 1 ? -1 : choutdev[0]); + else + { + int i = nchoutdev; + int *l = choutdev; + outchans = 0; + while (i--) + outchans += *l++; + } + if (inchans < 0) + inchans = DEFAULTCHANS; + if (outchans < 0) + outchans = DEFAULTCHANS; + if (inchans & 1) + { + post("input channels rounded up to even number"); + inchans += 1; + } + if (outchans & 1) + { + post("output channels rounded up to even number"); + outchans += 1; + } + if (inchans > NT_MAXCH) + inchans = NT_MAXCH; + if (outchans > NT_MAXCH) + outchans = NT_MAXCH; + if (sys_verbose) + post("channels in %d, out %d", inchans, outchans); + if (rate < 1) + rate = DEFAULTSRATE; + nt_setchsr(inchans, outchans, rate); + if (nt_whichapi == API_PORTAUDIO) + { + int blocksize = (nt_blocksize ? nt_blocksize : 256); + if (blocksize != (1 << ilog2(blocksize))) + post("warning: blocksize adjusted to power of 2: %d", + (blocksize = (1 << ilog2(blocksize)))); + pa_open_audio(inchans, outchans, rate, sys_soundin, sys_soundout, + blocksize, nt_advance_samples/blocksize, + (naudioindev < 1 ? -1 : audioindev[0]), + (naudiooutdev < 1 ? -1 : audiooutdev[0])); + } + else + { + nt_nwavein = inchans / 2; + nt_nwaveout = outchans / 2; + nt_whichdac = (naudiooutdev < 1 ? (nt_nwaveout > 1 ? 0 : -1) : audiooutdev[0] - 1); + nt_whichadc = (naudioindev < 1 ? (nt_nwavein > 1 ? 0 : -1) : audioindev[0] - 1); + if (naudiooutdev > 1 || naudioindev > 1) + post("separate audio device choice not supported; using sequential devices."); + if (nt_blocksize) + post("warning: blocksize not settable for MMIO, just ASIO"); + mmio_open_audio(); + } +} + +void sys_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec) +{ + if (nmidiout) + nt_open_midiout(nmidiout, midioutvec); + if (nmidiin) + { + post( + "midi input enabled; warning, don't close the DOS window directly!"); + nt_open_midiin(nmidiin, midiinvec); + } + else post("not using MIDI input (use 'pd -midiindev 1' to override)"); +} + +float sys_getsr(void) +{ + return (sys_dacsr); +} + +int sys_get_inchannels(void) +{ + return (2 * nt_nwavein); +} + +int sys_get_outchannels(void) +{ + return (2 * nt_nwaveout); +} + +void sys_audiobuf(int n) +{ + /* set the size, in msec, of the audio FIFO. It's incorrect to + calculate this on the basis of 44100 sample rate; really, the + work should have been done in nt_setchsr(). */ + int nbuf = n * (44100./(REALDACBLKSIZE * 1000.)); + if (nbuf >= MAXBUFFER) + { + fprintf(stderr, "pd: audio buffering maxed out to %d\n", + (int)(MAXBUFFER * ((REALDACBLKSIZE * 1000.)/44100.))); + nbuf = MAXBUFFER; + } + else if (nbuf < 4) nbuf = 4; + fprintf(stderr, "%d audio buffers\n", nbuf); + nt_naudiobuffer = nbuf; + if (nt_adcjitterbufsallowed > nbuf - 2) + nt_adcjitterbufsallowed = nbuf - 2; + if (nt_dacjitterbufsallowed > nbuf - 2) + nt_dacjitterbufsallowed = nbuf - 2; + sys_schedadvance = 1000 * n; +} + +void sys_getmeters(float *inmax, float *outmax) +{ + if (inmax) + { + nt_meters = 1; + *inmax = nt_inmax; + *outmax = nt_outmax; + } + else + nt_meters = 0; + nt_inmax = nt_outmax = 0; +} + +void sys_reportidle(void) +{ +} + +int sys_send_dacs(void) +{ + if (nt_whichapi == API_PORTAUDIO) + return (pa_send_dacs()); + else return (mmio_send_dacs()); +} + +void sys_close_audio( void) +{ + if (nt_whichapi == API_PORTAUDIO) + pa_close_audio(); + else mmio_close_audio(); +} + +void sys_close_midi( void) +{ + nt_close_midiin(); + nt_close_midiout(); +} + +void sys_setblocksize(int n) +{ + if (n < 1) + n = 1; + nt_blocksize = n; +} + +/* ----------- public routines which are only defined for MSW/NT ---------- */ + +/* select between MMIO and ASIO audio APIs */ +void nt_set_sound_api(int which) +{ + nt_whichapi = which; + if (sys_verbose) + post("nt_whichapi %d", nt_whichapi); +} + +/* list the audio and MIDI device names */ +void sys_listdevs(void) +{ + UINT wRtn, ndevices; + unsigned int i; + + /* for MIDI and audio in and out, get the number of devices. + Then get the capabilities of each device and print its description. */ + + ndevices = midiInGetNumDevs(); + for (i = 0; i < ndevices; i++) + { + MIDIINCAPS micap; + wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) &micap, + sizeof(micap)); + if (wRtn) nt_midiinerror("midiInGetDevCaps: %s\n", wRtn); + else fprintf(stderr, + "MIDI input device #%d: %s\n", i+1, micap.szPname); + } + + ndevices = midiOutGetNumDevs(); + for (i = 0; i < ndevices; i++) + { + MIDIOUTCAPS mocap; + wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap, + sizeof(mocap)); + if (wRtn) nt_midiouterror("midiOutGetDevCaps: %s\n", wRtn); + else fprintf(stderr, + "MIDI output device #%d: %s\n", i+1, mocap.szPname); + } + + if (nt_whichapi == API_PORTAUDIO) + { + pa_listdevs(); + return; + } + ndevices = waveInGetNumDevs(); + for (i = 0; i < ndevices; i++) + { + WAVEINCAPS wicap; + wRtn = waveInGetDevCaps(i, (LPWAVEINCAPS) &wicap, + sizeof(wicap)); + if (wRtn) nt_waveinerror("waveInGetDevCaps: %s\n", wRtn); + else fprintf(stderr, + "audio input device #%d: %s\n", i+1, wicap.szPname); + } + + ndevices = waveOutGetNumDevs(); + for (i = 0; i < ndevices; i++) + { + WAVEOUTCAPS wocap; + wRtn = waveOutGetDevCaps(i, (LPWAVEOUTCAPS) &wocap, + sizeof(wocap)); + if (wRtn) nt_waveouterror("waveOutGetDevCaps: %s\n", wRtn); + else fprintf(stderr, + "audio output device #%d: %s\n", i+1, wocap.szPname); + } +} + +void nt_soundindev(int which) +{ + nt_whichadc = which - 1; +} + +void nt_soundoutdev(int which) +{ + nt_whichdac = which - 1; +} + +void glob_audio(void *dummy, t_floatarg fadc, t_floatarg fdac) +{ + int adc = fadc, dac = fdac; + if (!dac && !adc) + post("%d channels in, %d channels out", + 2 * nt_nwavein, 2 * nt_nwaveout); + else + { + sys_close_audio(); + sys_open_audio(1, 0, 1, 0, /* dummy parameters */ + 1, &adc, 1, &dac, sys_dacsr); + } +} + diff --git a/pd/src/s_path.c b/pd/src/s_path.c new file mode 100644 index 00000000..a61956f1 --- /dev/null +++ b/pd/src/s_path.c @@ -0,0 +1,279 @@ +/* Copyright (c) 1999 Guenter Geiger 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 implements the loader for linux, which includes + * a little bit of path handling. + * + * Generalized by MSP to provide an open_via_path function + * and lists of files for all purposes. + */ + +/* #define DEBUG(x) x */ +#define DEBUG(x) +void readsf_banana( void); /* debugging */ + +#include +#ifdef UNIX +#include +#include +#endif +#ifdef NT +#include +#endif + +#include +#include "m_imp.h" +#include +#include + +static t_namelist *pd_path; + +/* Utility functions */ + +/* copy until delimiter and return position after delimiter in string */ +/* if it was the last substring, return NULL */ + +static const char* strtokcpy(char *to, const char *from, int delim) +{ + int size = 0; + + while (from[size] != (char)delim && from[size] != '\0') + size++; + + strncpy(to,from,size); + to[size] = '\0'; + if (from[size] == '\0') return NULL; + if (size) return from+size+1; + else return NULL; +} + +/* add a colon-separated list of names to a namelist */ + +#ifdef NT +#define SEPARATOR ';' +#else +#define SEPARATOR ':' +#endif + +static t_namelist *namelist_doappend(t_namelist *listwas, const char *s) +{ + t_namelist *nl = listwas, *rtn = listwas, *nl2; + nl2 = (t_namelist *)(getbytes(sizeof(*nl))); + nl2->nl_next = 0; + nl2->nl_string = (char *)getbytes(strlen(s) + 1); + strcpy(nl2->nl_string, s); + sys_unbashfilename(nl2->nl_string, nl2->nl_string); + if (!nl) + nl = rtn = nl2; + else + { + while (nl->nl_next) + nl = nl->nl_next; + nl->nl_next = nl2; + } + return (rtn); + +} + +t_namelist *namelist_append(t_namelist *listwas, const char *s) +{ + const char *npos; + char temp[MAXPDSTRING]; + t_namelist *nl = listwas, *rtn = listwas; + + npos = s; + do + { + npos = strtokcpy(temp, npos, SEPARATOR); + if (! *temp) continue; + nl = namelist_doappend(nl, temp); + } + while (npos); + return (nl); +} + +void namelist_free(t_namelist *listwas) +{ + t_namelist *nl, *nl2; + for (nl = listwas; nl; nl = nl2) + { + nl2 = nl->nl_next; + t_freebytes(nl->nl_string, strlen(nl->nl_string) + 1); + t_freebytes(nl, sizeof(*nl)); + } +} + +void sys_addpath(const char *p) +{ + pd_path = namelist_append(pd_path, p); +} + +#ifdef NT +#define NTOPENFLAG (bin ? _O_BINARY : _O_TEXT) +#else +#define NTOPENFLAG 0 +#endif + +/* search for a file in a specified directory, then along the globally +defined search path, using ext as filename extension. Exception: +if the 'name' starts with a slash or a letter, colon, and slash in NT, +there is no search and instead we just try to open the file literally. The +fd is returned, the directory ends up in the "dirresult" which must be at +least "size" bytes. "nameresult" is set to point to the filename, which +ends up in the same buffer as dirresult. */ + +int open_via_path(const char *dir, const char *name, const char* ext, + char *dirresult, char **nameresult, unsigned int size, int bin) +{ + t_namelist *nl, thislist; + int fd = -1; + char listbuf[MAXPDSTRING]; + + if (name[0] == '/' +#ifdef NT + || (name[1] == ':' && name[2] == '/') +#endif + ) + { + thislist.nl_next = 0; + thislist.nl_string = listbuf; + listbuf[0] = 0; + } + else + { + thislist.nl_string = listbuf; + thislist.nl_next = pd_path; + strncpy(listbuf, dir, MAXPDSTRING); + listbuf[MAXPDSTRING-1] = 0; + sys_unbashfilename(listbuf, listbuf); + } + for (nl = &thislist; nl; nl = nl->nl_next) + { + if (strlen(nl->nl_string) + strlen(name) + strlen(ext) + 4 > + size) + continue; + strcpy(dirresult, nl->nl_string); + if (*dirresult && dirresult[strlen(dirresult)-1] != '/') + strcat(dirresult, "/"); + strcat(dirresult, name); + strcat(dirresult, ext); + sys_bashfilename(dirresult, dirresult); + + DEBUG(post("looking for %s",dirresult)); + /* see if we can open the file for reading */ + if ((fd=open(dirresult,O_RDONLY | NTOPENFLAG)) >= 0) + { + /* in UNIX, further check that it's not a directory */ +#ifdef UNIX + struct stat statbuf; + int ok = ((fstat(fd, &statbuf) >= 0) && + !S_ISDIR(statbuf.st_mode)); + if (!ok) + { + if (sys_verbose) post("tried %s; stat failed or directory", + dirresult); + close (fd); + fd = -1; + } + else +#endif + { + char *slash; + if (sys_verbose) post("tried %s and succeeded", dirresult); + sys_unbashfilename(dirresult, dirresult); + slash = strrchr(dirresult, '/'); + if (slash) + { + *slash = 0; + *nameresult = slash + 1; + } + else *nameresult = dirresult; + + return (fd); + } + } + else + { + if (sys_verbose) post("tried %s and failed", dirresult); + } + } + *dirresult = 0; + *nameresult = dirresult; + return (-1); +} + +/* Startup file reading for linux */ + +#ifdef __linux__ + +#define STARTUPNAME ".pdrc" +#define NUMARGS 1000 + +int sys_argparse(int argc, char **argv); + +int sys_rcfile(void) +{ + FILE* file; + int i; + int k; + int rcargc; + char* rcargv[NUMARGS]; + char* buffer; + char fname[MAXPDSTRING], buf[1000]; + + /* parse a startup file */ + + *fname = '\0'; + + strncat(fname, getenv("HOME"), MAXPDSTRING-10); + strcat(fname, "/"); + + strcat(fname, STARTUPNAME); + + if (!(file = fopen(fname, "r"))) + return 1; + + post("reading startup file: %s", fname); + + rcargv[0] = "."; /* this no longer matters to sys_argparse() */ + + for (i = 1; i < NUMARGS-1; i++) + { + if (fscanf(file, "%999s", buf) < 0) + break; + buf[1000] = 0; + if (!(rcargv[i] = malloc(strlen(buf) + 1))) + return (1); + strcpy(rcargv[i], buf); + } + if (i >= NUMARGS-1) + fprintf(stderr, "startup file too long; extra args dropped\n"); + rcargv[i] = 0; + + rcargc = i; + + /* parse the options */ + + fclose(file); + if (sys_verbose) + { + if (rcargv) + { + post("startup args from RC file:"); + for (i = 1; i < rcargc; i++) + post("%s", rcargv[i]); + } + else post("no RC file arguments found"); + } + if (sys_argparse(rcargc, rcargv)) + { + post("error parsing RC arguments"); + return (1); + } + return (0); +} +#endif /* __linux__ */ + + diff --git a/pd/src/s_portaudio.c b/pd/src/s_portaudio.c new file mode 100644 index 00000000..73dd55ab --- /dev/null +++ b/pd/src/s_portaudio.c @@ -0,0 +1,197 @@ +/* Copyright (c) 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 calls Ross Bencina's and Phil Burk's Portaudio package. It's + the main way in for Mac OS and, with Michael Casey's help, also into + ASIO in Windows. */ + +/* dolist. + write a version of OpenAudioStream to take nchannels param + how do we specify latency? + listing devices? + choosing devices? +*/ + +#include "m_imp.h" +#include +#include +#include "pablio_pd.h" +#include "portaudio.h" +#ifndef NT +#include "portmidi.h" +#include "porttime.h" +#include "pminternal.h" +#endif + + /* public interface declared in m_imp.h */ + + /* implementation */ +static PABLIO_Stream *pa_stream; +static int pa_inchans, pa_outchans; +static float *pa_soundin, *pa_soundout; + +#define MAX_PA_CHANS 32 +#define MAX_SAMPLES_PER_FRAME MAX_PA_CHANS * DACBLKSIZE + +int pa_open_audio(int inchans, int outchans, int rate, t_sample *soundin, + t_sample *soundout, int framesperbuf, int nbuffers, + int indeviceno, int outdeviceno) +{ + PaError err; + /* post("in %d out %d rate %d device %d", inchans, outchans, rate, deviceno); */ + if (inchans != 0 && outchans != 0 && inchans != outchans) + error("portaudio: number of input and output channels must match"); + if (sys_verbose) + post("portaudio: opening for %d channels in, %d out", + inchans, outchans); + if (inchans > MAX_PA_CHANS) + { + post("input channels reduced to maximum %d", MAX_PA_CHANS); + inchans = MAX_PA_CHANS; + } + if (outchans > MAX_PA_CHANS) + { + post("output channels reduced to maximum %d", MAX_PA_CHANS); + outchans = MAX_PA_CHANS; + } + + if (inchans && outchans) + err = OpenAudioStream( &pa_stream, rate, paFloat32, + PABLIO_READ_WRITE, inchans, framesperbuf, nbuffers, indeviceno, outdeviceno); + else if (inchans) + err = OpenAudioStream( &pa_stream, rate, paFloat32, + PABLIO_READ, inchans, framesperbuf, nbuffers, indeviceno, outdeviceno); + else if (outchans) + err = OpenAudioStream( &pa_stream, rate, paFloat32, + PABLIO_WRITE, outchans, framesperbuf, nbuffers, indeviceno, outdeviceno); + else err = 0; + if ( err != paNoError ) + { + fprintf( stderr, "Error number %d occured opening portaudio stream\n", + err); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + Pa_Terminate(); + return (1); + } + else if (sys_verbose) + post("... opened OK."); + pa_inchans = inchans; + pa_outchans = outchans; + pa_soundin = soundin; + pa_soundout = soundout; + return (0); +} + +void pa_close_audio( void) +{ + if (pa_inchans || pa_outchans) + CloseAudioStream( pa_stream ); + pa_inchans = pa_outchans = 0; +} + +int pa_send_dacs(void) +{ + float samples[MAX_SAMPLES_PER_FRAME], *fp1, *fp2; + int i, j; + double timebefore; + + timebefore = sys_getrealtime(); + if (pa_inchans) + { + ReadAudioStream(pa_stream, samples, DACBLKSIZE); + for (j = 0, fp1 = pa_soundin; j < pa_inchans; j++, fp1 += DACBLKSIZE) + for (i = 0, fp2 = samples + j; i < DACBLKSIZE; i++, + fp2 += pa_inchans) + { + fp1[i] = *fp2; + } + } + if (pa_outchans) + { + for (j = 0, fp1 = pa_soundout; j < pa_outchans; j++, fp1 += DACBLKSIZE) + for (i = 0, fp2 = samples + j; i < DACBLKSIZE; i++, + fp2 += pa_outchans) + { + *fp2 = fp1[i]; + fp1[i] = 0; + } + WriteAudioStream(pa_stream, samples, DACBLKSIZE); + } + + if (sys_getrealtime() > timebefore + 0.002) + return (SENDDACS_SLEPT); + else return (SENDDACS_YES); +} + + +void pa_listdevs(void) /* lifted from pa_devs.c in portaudio */ +{ + int i,j; + int numDevices; + const PaDeviceInfo *pdi; + PaError err; + Pa_Initialize(); + numDevices = Pa_CountDevices(); + if( numDevices < 0 ) + { + fprintf(stderr, "ERROR: Pa_CountDevices returned 0x%x\n", numDevices ); + err = numDevices; + goto error; + } + fprintf(stderr, "Number of devices = %d\n", numDevices ); + for( i=0; iname ); + fprintf(stderr, "Max Inputs = %d", pdi->maxInputChannels ); + fprintf(stderr, ", Max Outputs = %d\n", pdi->maxOutputChannels ); + if( pdi->numSampleRates == -1 ) + { + fprintf(stderr, "Sample Rate Range = %f to %f\n", pdi->sampleRates[0], pdi->sampleRates[1] ); + } + else + { + fprintf(stderr, "Sample Rates ="); + for( j=0; jnumSampleRates; j++ ) + { + fprintf(stderr, " %8.2f,", pdi->sampleRates[j] ); + } + fprintf(stderr, "\n"); + } + fprintf(stderr, "Native Sample Formats = "); + if( pdi->nativeSampleFormats & paInt8 ) fprintf(stderr, "paInt8, "); + if( pdi->nativeSampleFormats & paUInt8 ) fprintf(stderr, "paUInt8, "); + if( pdi->nativeSampleFormats & paInt16 ) fprintf(stderr, "paInt16, "); + if( pdi->nativeSampleFormats & paInt32 ) fprintf(stderr, "paInt32, "); + if( pdi->nativeSampleFormats & paFloat32 ) fprintf(stderr, "paFloat32, "); + if( pdi->nativeSampleFormats & paInt24 ) fprintf(stderr, "paInt24, "); + if( pdi->nativeSampleFormats & paPackedInt24 ) fprintf(stderr, "paPackedInt24, "); + fprintf(stderr, "\n"); + } + + fprintf(stderr, "----------------------------------------------\n"); + goto midi; + +error: + fprintf( stderr, "An error occured while using the portaudio stream\n" ); + fprintf( stderr, "Error number: %d\n", err ); + fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) ); + + /* and MIDI */ +midi: ; +#ifndef NT + for (i = 0; i < Pm_CountDevices(); i++) + { + const PmDeviceInfo *info = Pm_GetDeviceInfo(i); + printf("%d: %s, %s", i, info->interf, info->name); + if (info->input) printf(" (input)"); + if (info->output) printf(" (output)"); + printf("\n"); + } +#endif +} diff --git a/pd/src/s_print.c b/pd/src/s_print.c new file mode 100644 index 00000000..33944286 --- /dev/null +++ b/pd/src/s_print.c @@ -0,0 +1,150 @@ +/* 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 +#include +#include +#include + +void post(char *fmt, ...) +{ + va_list ap; + t_int arg[8]; + int i; + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + putc('\n', stderr); +} + +void startpost(char *fmt, ...) +{ + va_list ap; + t_int arg[8]; + int i; + va_start(ap, fmt); + + for (i = 0 ; i < 8; i++) arg[i] = va_arg(ap, t_int); + va_end(ap); + fprintf(stderr, fmt, arg[0], arg[1], arg[2], arg[3], + arg[4], arg[5], arg[6], arg[7]); +} + +void poststring(char *s) +{ + fprintf(stderr, " %s", s); +} + +void postatom(int argc, t_atom *argv) +{ + int i; + for (i = 0; i < argc; i++) + { + char buf[80]; + atom_string(argv+i, buf, 80); + poststring(buf); + } +} + +void postfloat(float f) +{ + char buf[80]; + t_atom a; + SETFLOAT(&a, f); + postatom(1, &a); +} + +void endpost(void) +{ + fprintf(stderr, "\n"); +} + +void error(char *fmt, ...) +{ + va_list ap; + t_int arg[8]; + int i; + va_start(ap, fmt); + fprintf(stderr, "error: "); + vfprintf(stderr, fmt, ap); + va_end(ap); + putc('\n', stderr); +} + + /* here's the good way to log errors -- keep a pointer to the + offending or offended object around so the user can search for it + later. */ + +static void *error_object; +static char error_string[256]; +void canvas_finderror(void *object); + +void pd_error(void *object, char *fmt, ...) +{ + va_list ap; + t_int arg[8]; + int i; + static int saidit = 0; + va_start(ap, fmt); + vsprintf(error_string, fmt, ap); + va_end(ap); + fprintf(stderr, "error: %s\n", error_string); + error_object = object; + if (!saidit) + { + post("... you might be able to track this down from the Find menu."); + saidit = 1; + } +} + +void glob_finderror(t_pd *dummy) +{ + if (!error_object) + post("no findable error yet."); + else + { + post("last trackable error:"); + post("%s", error_string); + canvas_finderror(error_object); + } +} + +void bug(char *fmt, ...) +{ + va_list ap; + t_int arg[8]; + int i; + va_start(ap, fmt); + + for (i = 0 ; i < 8; i++) arg[i] = va_arg(ap, t_int); + va_end(ap); + fprintf(stderr, "Consistency check failed: "); + fprintf(stderr, fmt, arg[0], arg[1], arg[2], arg[3], + arg[4], arg[5], arg[6], arg[7]); + putc('\n', stderr); +} + + /* this isn't worked out yet. */ +static char *errobject; +static char *errstring; + +void sys_logerror(char *object, char *s) +{ + errobject = object; + errstring = s; +} + +void sys_unixerror(char *object) +{ + errobject = object; + errstring = strerror(errno); +} + +void sys_ouch(void) +{ + if (*errobject) error("%s: %s", errobject, errstring); + else error("%s", errstring); +} diff --git a/pd/src/s_sgi.c b/pd/src/s_sgi.c new file mode 100644 index 00000000..82a23dfb --- /dev/null +++ b/pd/src/s_sgi.c @@ -0,0 +1,433 @@ +/* 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_imp.h" +#include +#include +#include +#include +#ifdef HAVE_BSTRING_H +#include +#endif +#include +#include + +#include +#include +#include +int mdInit(void); /* prototype was messed up in midi.h */ +/* #include "sys/select.h" */ + +static ALport iport; +static ALport oport; +static ALconfig sgi_config; +#define DEFAULTCHANS 2 +#define SGI_MAXCH 8 +static int sys_inchannels, sys_outchannels; +static int sys_audiobufsamps; +int sys_schedadvance = 50000; /* scheduler advance in microseconds */ + /* (this is set ridiculously high until we can get the real-time + scheduling act together.) */ +int sys_hipriority = 0; +static int sgi_meters; /* true if we're metering */ +static float sgi_inmax; /* max input amplitude */ +static float sgi_outmax; /* max output amplitude */ + + + /* + set the special "flush zero" but (FS, bit 24) in the + Control Status Register of the FPU of R4k and beyond + so that the result of any underflowing operation will + be clamped to zero, and no exception of any kind will + be generated on the CPU. + + thanks to cpirazzi@cp.esd.sgi.com (Chris Pirazzi). + */ + +static void sgi_flush_all_underflows_to_zero(void) +{ + union fpc_csr f; + f.fc_word = get_fpc_csr(); + f.fc_struct.flush = 1; + set_fpc_csr(f.fc_word); +} + +static void sgi_setchsr(int inchans, int outchans, int sr) +{ + int inbytes = inchans * (DACBLKSIZE*sizeof(float)); + int outbytes = outchans * (DACBLKSIZE*sizeof(float)); + + sys_audiobufsamps = 64 * (int)(((float)sys_schedadvance)* sr / 64000000.); + sys_inchannels = inchans; + sys_outchannels = outchans; + sys_dacsr = sr; + + if (sys_soundin) + free(sys_soundin); + sys_soundin = (t_float *)malloc(inbytes); + memset(sys_soundin, 0, inbytes); + + if (sys_soundout) + free(sys_soundout); + sys_soundout = (t_float *)malloc(outbytes); + memset(sys_soundout, 0, outbytes); + + memset(sys_soundout, 0, sys_outchannels * (DACBLKSIZE*sizeof(float))); + memset(sys_soundin, 0, sys_inchannels * (DACBLKSIZE*sizeof(float))); +} + +static void sgi_open_audio(void) +{ + long pvbuf[4]; + long pvbuflen; + /* get current sample rate -- should use this to set logical SR */ + pvbuf[0] = AL_INPUT_RATE; + pvbuf[2] = AL_OUTPUT_RATE; + pvbuflen = 4; + + ALgetparams(AL_DEFAULT_DEVICE, pvbuf, pvbuflen); + + if (sys_inchannels && pvbuf[1] != sys_dacsr) + post("warning: input sample rate (%d) doesn't match mine (%f)\n", + pvbuf[1], sys_dacsr); + + if (sys_outchannels && pvbuf[3] != sys_dacsr) + post("warning: output sample rate (%d) doesn't match mine (%f)\n", + pvbuf[3], sys_dacsr); + + pvbuf[3] = pvbuf[1]; + ALsetparams(AL_DEFAULT_DEVICE, pvbuf, pvbuflen); + + sgi_config = ALnewconfig(); + + ALsetsampfmt(sgi_config, AL_SAMPFMT_FLOAT); + + if (sys_outchannels) + { + ALsetchannels(sgi_config, sys_outchannels); + ALsetqueuesize(sgi_config, sys_audiobufsamps * sys_outchannels); + oport = ALopenport("the ouput port", "w", sgi_config); + if (!oport) + fprintf(stderr,"Pd: failed to open audio write port\n"); + } + else oport = 0; + if (sys_inchannels) + { + ALsetchannels(sgi_config, sys_inchannels); + ALsetqueuesize(sgi_config, sys_audiobufsamps * sys_inchannels); + iport = ALopenport("the input port", "r", sgi_config); + if (!iport) + fprintf(stderr,"Pd: failed to open audio read port\n"); + } + else iport = 0; +} + +void sys_close_audio( void) +{ + if (iport) ALcloseport(iport); + if (oport) ALcloseport(oport); +} + +void sys_close_midi( void) +{ + /* ??? */ +} + +t_sample *sys_soundout; +t_sample *sys_soundin; +float sys_dacsr; + +int sys_send_dacs(void) +{ + float buf[SGI_MAXCH * DACBLKSIZE], *fp1, *fp2, *fp3, *fp4; + long outfill, infill; + int outchannels = sys_outchannels, inchannels = sys_inchannels; + int i, nwait = 0, channel; + int outblk = DACBLKSIZE * outchannels; + int inblk = DACBLKSIZE * inchannels; + outfill = ALgetfillable(oport); + if (sgi_meters) + { + int i, n; + float maxsamp; + for (i = 0, n = sys_inchannels * DACBLKSIZE, maxsamp = sgi_inmax; + i < n; i++) + { + float f = sys_soundin[i]; + if (f > maxsamp) maxsamp = f; + else if (-f > maxsamp) maxsamp = -f; + } + sgi_inmax = maxsamp; + for (i = 0, n = sys_outchannels * DACBLKSIZE, maxsamp = sgi_outmax; + i < n; i++) + { + float f = sys_soundout[i]; + if (f > maxsamp) maxsamp = f; + else if (-f > maxsamp) maxsamp = -f; + } + sgi_outmax = maxsamp; + } + + if (outfill <= outblk) + { + while ((infill = ALgetfilled(iport)) > 2*inblk) + { + if (sys_verbose) post("drop ADC buf"); + ALreadsamps(iport, buf, inblk); + return (0); + } + } + if (outchannels) + { + for (channel = 0, fp1 = buf, fp2 = sys_soundout; + channel < outchannels; channel++, fp1++, fp2 += DACBLKSIZE) + { + for (i = 0, fp3 = fp1, fp4 = fp2; i < DACBLKSIZE; + i++, fp3 += outchannels, fp4++) + *fp3 = *fp4, *fp4 = 0; + } + ALwritesamps(oport, buf, outchannels* DACBLKSIZE); + } + if (inchannels) + { + if (infill > inblk) + ALreadsamps(iport, buf, inchannels* DACBLKSIZE); + else + { + if (sys_verbose) post("extra ADC buf"); + memset(buf, 0, inblk*sizeof(float)); + } + for (channel = 0, fp1 = buf, fp2 = sys_soundin; + channel < inchannels; channel++, fp1++, fp2 += DACBLKSIZE) + { + for (i = 0, fp3 = fp1, fp4 = fp2; i < DACBLKSIZE; + i++, fp3 += inchannels, fp4++) + *fp4 = *fp3; + } + } + return (1); +} + +/* ------------------------- MIDI -------------------------- */ + +#define NPORT 2 + +static MDport sgi_inport[NPORT]; +static MDport sgi_outport[NPORT]; + +void sgi_open_midi(int midiin, int midiout) +{ + int i; + int sgi_nports = mdInit(); + if (sgi_nports < 0) sgi_nports = 0; + else if (sgi_nports > NPORT) sgi_nports = NPORT; + if (sys_verbose) + { + if (!sgi_nports) + { + post("no serial ports are configured for MIDI;"); + post("if you want to use MIDI, try exiting Pd, typing"); + post("'startmidi -d /dev/ttyd2' to a shell, and restarting Pd."); + } + else if (sgi_nports == 1) + post("Found one MIDI port on %s", mdGetName(0)); + else if (sgi_nports == 2) + post("Found MIDI ports on %s and %s", + mdGetName(0), mdGetName(1)); + } + if (midiin) + { + for (i = 0; i < sgi_nports; i++) + { + if (!(sgi_inport[i] = mdOpenInPort(mdGetName(i)))) + error("MIDI input port %d: open failed", i+1);; + } + } + if (midiout) + { + for (i = 0; i < sgi_nports; i++) + { + if (!(sgi_outport[i] = mdOpenOutPort(mdGetName(i)))) + error("MIDI output port %d: open failed", i+1);; + } + } + return; +} + +void sys_putmidimess(int portno, int a, int b, int c) +{ + MDevent mdv; + if (portno >= NPORT || portno < 0 || !sgi_outport[portno]) return; + mdv.msg[0] = a; + mdv.msg[1] = b; + mdv.msg[2] = c; + mdv.msg[3] = 0; + mdv.sysexmsg = 0; + mdv.stamp = 0; + mdv.msglen = 0; + if (mdSend(sgi_outport[portno], &mdv, 1) < 0) + error("MIDI output error\n"); + post("msg out %d %d %d", a, b, c); +} + +void sys_putmidibyte(int portno, int foo) +{ + error("MIDI raw byte output not available on SGI"); +} + +void inmidi_noteon(int portno, int channel, int pitch, int velo); +void inmidi_controlchange(int portno, int channel, int ctlnumber, int value); +void inmidi_programchange(int portno, int channel, int value); +void inmidi_pitchbend(int portno, int channel, int value); +void inmidi_aftertouch(int portno, int channel, int value); +void inmidi_polyaftertouch(int portno, int channel, int pitch, int value); + +void sys_poll_midi(void) +{ + int i; + MDport *mp; + for (i = 0, mp = sgi_inport; i < NPORT; i++, mp++) + { + int ret, status, b1, b2, nfds; + MDevent mdv; + fd_set inports; + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 0; + if (!*mp) continue; + FD_ZERO(&inports); + FD_SET(mdGetFd(*mp), &inports); + + if (select(mdGetFd(*mp)+1 , &inports, 0, 0, &timeout) < 0) + perror("midi select"); + if (FD_ISSET(mdGetFd(*mp),&inports)) + { + if (mdReceive(*mp, &mdv, 1) < 0) + error("failure receiving message\n"); + else if (mdv.msg[0] == MD_SYSEX) mdFree(mdv.sysexmsg); + + else + { + int status = mdv.msg[0]; + int channel = (status & 0xf) + 1; + int b1 = mdv.msg[1]; + int b2 = mdv.msg[2]; + switch(status & 0xf0) + { + case MD_NOTEOFF: + inmidi_noteon(i, channel, b1, 0); + break; + case MD_NOTEON: + inmidi_noteon(i, channel, b1, b2); + break; + case MD_POLYKEYPRESSURE: + inmidi_polyaftertouch(i, channel, b1, b2); + break; + case MD_CONTROLCHANGE: + inmidi_controlchange(i, channel, b1, b2); + break; + case MD_PITCHBENDCHANGE: + inmidi_pitchbend(i, channel, ((b2 << 7) + b1)); + break; + case MD_PROGRAMCHANGE: + inmidi_programchange(i, channel, b1); + break; + case MD_CHANNELPRESSURE: + inmidi_aftertouch(i, channel, b1); + break; + } + } + } + } +} + + /* public routines */ + +void sys_open_audio(int naudioindev, int *audioindev, int nchindev, int *chindev, + int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, + int rate) /* IOhannes */ +{ + int inchans=0; + int outchans=0; + if (nchindev<0)inchans == -1; + else { + int i=nchindev; + int *l=chindev; + while(i--)inchans+=*l++; + } + if (nchoutdev<0)outchans == -1; + else { + int i=nchoutdev; + int *l=choutdev; + while(i--)outchans+=*l++; + } + + if (inchans < 0) inchans = DEFAULTCHANS; + if (outchans < 0) outchans = DEFAULTCHANS; + if (inchans > SGI_MAXCH) inchans = SGI_MAXCH; + if (outchans > SGI_MAXCH) outchans = SGI_MAXCH; + + sgi_setchsr(inchans, outchans, rate); + sgi_flush_all_underflows_to_zero(); + sgi_open_audio(); +} + +void sys_open_midi(int nmidiin, int *midiinvec, + int nmidiout, int *midioutvec) +{ + sgi_open_midi(nmidiin!=0, nmidiout!=0); +} + +float sys_getsr(void) +{ + return (sys_dacsr); +} + +void sys_audiobuf(int n) +{ + /* set the size, in milliseconds, of the audio FIFO */ + if (n < 5) n = 5; + else if (n > 5000) n = 5000; + fprintf(stderr, "audio buffer set to %d milliseconds\n", n); + sys_schedadvance = n * 1000; +} + +void sys_getmeters(float *inmax, float *outmax) +{ + if (inmax) + { + sgi_meters = 1; + *inmax = sgi_inmax; + *outmax = sgi_outmax; + } + else + sgi_meters = 0; + sgi_inmax = sgi_outmax = 0; +} + +void sys_reportidle(void) +{ +} + +int sys_get_inchannels(void) +{ + return (sys_inchannels); +} + +int sys_get_outchannels(void) +{ + return (sys_outchannels); +} + +void sys_set_priority(int foo) +{ + fprintf(stderr, + "warning: priority boosting in IRIX not implemented yet\n"); +} + +void sys_setblocksize(int n) +{ + fprintf(stderr, + "warning: blocksize not settable in IRIX\n"); +} diff --git a/pd/src/s_unix.c b/pd/src/s_unix.c new file mode 100644 index 00000000..ee0ce160 --- /dev/null +++ b/pd/src/s_unix.c @@ -0,0 +1,445 @@ +/* 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. */ + +/* this file is misnamed. It seems to handle clock functions and MIDI I/O; +probably should be called "s_midi.c" */ + +#include "m_imp.h" +#ifdef UNIX +#include +#include +#ifdef HAVE_BSTRING_H +#include +#endif +#endif +#ifdef NT +#include +#include +#include +#include +#endif +#include +#include +#include + +#ifdef NT +static LARGE_INTEGER nt_inittime; +static double nt_freq = 0; + +static void sys_initntclock(void) +{ + LARGE_INTEGER f1; + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + if (!QueryPerformanceFrequency(&f1)) + { + fprintf(stderr, "pd: QueryPerformanceFrequency failed\n"); + f1.QuadPart = 1; + } + nt_freq = f1.QuadPart; + nt_inittime = now; +} + +#if 0 + /* this is a version you can call if you did the QueryPerformanceCounter + call yourself. Necessary for time tagging incoming MIDI at interrupt + level, for instance; but we're not doing that just now. */ + +double nt_tixtotime(LARGE_INTEGER *dumbass) +{ + if (nt_freq == 0) sys_initntclock(); + return (((double)(dumbass->QuadPart - nt_inittime.QuadPart)) / nt_freq); +} +#endif +#endif /* NT */ + +double sys_getrealtime(void) /* get "real time" in seconds */ +{ +#ifdef UNIX + static struct timeval then; + struct timeval now; + gettimeofday(&now, 0); + if (then.tv_sec == 0 && then.tv_usec == 0) then = now; + return ((now.tv_sec - then.tv_sec) + + (1./1000000.) * (now.tv_usec - then.tv_usec)); +#endif +#ifdef NT + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + if (nt_freq == 0) sys_initntclock(); + return (((double)(now.QuadPart - nt_inittime.QuadPart)) / nt_freq); +#endif +} + +typedef struct _midiqelem +{ + double q_time; + int q_portno; + unsigned char q_onebyte; + unsigned char q_byte1; + unsigned char q_byte2; + unsigned char q_byte3; +} t_midiqelem; + +#define MIDIQSIZE 1024 + +t_midiqelem midi_outqueue[MIDIQSIZE]; +int midi_outhead, midi_outtail; +t_midiqelem midi_inqueue[MIDIQSIZE]; +int midi_inhead, midi_intail; +static double sys_midiinittime; + + /* this is our current estimate for at what "system" real time the + current logical time's output should occur. */ +static double sys_dactimeminusrealtime; + /* same for input, should be schduler advance earlier. */ +static double sys_adctimeminusrealtime; + +static double sys_newdactimeminusrealtime = -1e20; +static double sys_newadctimeminusrealtime = -1e20; +static double sys_whenupdate; + +void sys_initmidiqueue( void) +{ + sys_midiinittime = clock_getlogicaltime(); + sys_dactimeminusrealtime = sys_adctimeminusrealtime = 0; +} + + /* this is called from the OS dependent code from time to time when we + think we know the delay (outbuftime) in seconds, at which the last-output + audio sample will go out the door. */ +void sys_setmiditimediff(double inbuftime, double outbuftime) +{ + double dactimeminusrealtime = + .001 * clock_gettimesince(sys_midiinittime) + - outbuftime - sys_getrealtime(); + double adctimeminusrealtime = + .001 * clock_gettimesince(sys_midiinittime) + + inbuftime - sys_getrealtime(); + if (dactimeminusrealtime > sys_newdactimeminusrealtime) + sys_newdactimeminusrealtime = dactimeminusrealtime; + if (adctimeminusrealtime > sys_newadctimeminusrealtime) + sys_newadctimeminusrealtime = adctimeminusrealtime; + if (sys_getrealtime() > sys_whenupdate) + { + sys_dactimeminusrealtime = sys_newdactimeminusrealtime; + sys_adctimeminusrealtime = sys_newadctimeminusrealtime; + sys_newdactimeminusrealtime = -1e20; + sys_newadctimeminusrealtime = -1e20; + sys_whenupdate = sys_getrealtime() + 1; + } +} + + /* return the logical time of the DAC sample we believe is currently + going out, based on how much "system time" has elapsed since the + last time sys_setmiditimediff got called. */ +static double sys_getmidioutrealtime( void) +{ + return (sys_getrealtime() + sys_dactimeminusrealtime); +} + +static double sys_getmidiinrealtime( void) +{ + return (sys_getrealtime() + sys_adctimeminusrealtime); +} + +static void sys_putnext( void) +{ + int portno = midi_outqueue[midi_outtail].q_portno; + if (midi_outqueue[midi_outtail].q_onebyte) + sys_putmidibyte(portno, midi_outqueue[midi_outtail].q_byte1); + else sys_putmidimess(portno, midi_outqueue[midi_outtail].q_byte1, + midi_outqueue[midi_outtail].q_byte2, + midi_outqueue[midi_outtail].q_byte3); + midi_outtail = (midi_outtail + 1 == MIDIQSIZE ? 0 : midi_outtail + 1); +} + +/* #define TEST_DEJITTER */ + +void sys_pollmidioutqueue( void) +{ +#ifdef TEST_DEJITTER + static int db = 0; +#endif + double midirealtime = sys_getmidioutrealtime(); +#ifdef TEST_DEJITTER + if (midi_outhead == midi_outtail) + db = 0; +#endif + while (midi_outhead != midi_outtail) + { +#ifdef TEST_DEJITTER + if (!db) + { + post("out: del %f, midiRT %f logicaltime %f, RT %f dacminusRT %f", + (midi_outqueue[midi_outtail].q_time - midirealtime), + midirealtime, .001 * clock_gettimesince(sys_midiinittime), + sys_getrealtime(), sys_dactimeminusrealtime); + db = 1; + } +#endif + if (midi_outqueue[midi_outtail].q_time <= midirealtime) + sys_putnext(); + else break; + } +} + +static void sys_queuemidimess(int portno, int onebyte, int a, int b, int c) +{ + t_midiqelem *midiqelem; + int newhead = midi_outhead +1; + if (newhead == MIDIQSIZE) + newhead = 0; + /* if FIFO is full flush an element to make room */ + if (newhead == midi_outtail) + sys_putnext(); + midi_outqueue[midi_outhead].q_portno = portno; + midi_outqueue[midi_outhead].q_onebyte = onebyte; + midi_outqueue[midi_outhead].q_byte1 = a; + midi_outqueue[midi_outhead].q_byte2 = b; + midi_outqueue[midi_outhead].q_byte3 = c; + midi_outqueue[midi_outhead].q_time = + .001 * clock_gettimesince(sys_midiinittime); + midi_outhead = newhead; + sys_pollmidioutqueue(); +} + +#define MIDI_NOTEON 144 +#define MIDI_POLYAFTERTOUCH 160 +#define MIDI_CONTROLCHANGE 176 +#define MIDI_PROGRAMCHANGE 192 +#define MIDI_AFTERTOUCH 208 +#define MIDI_PITCHBEND 224 + +void outmidi_noteon(int portno, int channel, int pitch, int velo) +{ + if (pitch < 0) pitch = 0; + else if (pitch > 127) pitch = 127; + if (velo < 0) velo = 0; + else if (velo > 127) velo = 127; + sys_queuemidimess(portno, 0, MIDI_NOTEON + (channel & 0xf), pitch, velo); +} + +void outmidi_controlchange(int portno, int channel, int ctl, int value) +{ + if (ctl < 0) ctl = 0; + else if (ctl > 127) ctl = 127; + if (value < 0) value = 0; + else if (value > 127) value = 127; + sys_queuemidimess(portno, 0, MIDI_CONTROLCHANGE + (channel & 0xf), + ctl, value); +} + +void outmidi_programchange(int portno, int channel, int value) +{ + if (value < 0) value = 0; + else if (value > 127) value = 127; + sys_queuemidimess(portno, 0, + MIDI_PROGRAMCHANGE + (channel & 0xf), value, 0); +} + +void outmidi_pitchbend(int portno, int channel, int value) +{ + if (value < 0) value = 0; + else if (value > 16383) value = 16383; + sys_queuemidimess(portno, 0, MIDI_PITCHBEND + (channel & 0xf), + (value & 127), ((value>>7) & 127)); +} + +void outmidi_aftertouch(int portno, int channel, int value) +{ + if (value < 0) value = 0; + else if (value > 127) value = 127; + sys_queuemidimess(portno, 0, MIDI_AFTERTOUCH + (channel & 0xf), value, 0); +} + +void outmidi_polyaftertouch(int portno, int channel, int pitch, int value) +{ + if (pitch < 0) pitch = 0; + else if (pitch > 127) pitch = 127; + if (value < 0) value = 0; + else if (value > 127) value = 127; + sys_queuemidimess(portno, 0, MIDI_POLYAFTERTOUCH + (channel & 0xf), + pitch, value); +} + +void outmidi_mclk(int portno) +{ + sys_queuemidimess(portno, 1, 0xf8, 0,0); +} + +/* ------------------------- MIDI input queue handling ------------------ */ +typedef struct midiparser +{ + int mp_status; + int mp_sysex; + int mp_gotbyte1; + int mp_byte1; +} t_midiparser; + +#define MIDINOTEOFF 0x80 +#define MIDINOTEON 0x90 +#define MIDIPOLYTOUCH 0xa0 +#define MIDICONTROLCHANGE 0xb0 +#define MIDIPROGRAMCHANGE 0xc0 +#define MIDICHANNELTOUCH 0xd0 +#define MIDIPITCHBEND 0xe0 + /* functions in x_midi.c */ +void inmidi_realtimein(int portno, int cmd); +void inmidi_byte(int portno, int byte); +void inmidi_sysex(int portno, int byte); +void inmidi_noteon(int portno, int channel, int pitch, int velo); +void inmidi_controlchange(int portno, int channel, int ctlnumber, int value); +void inmidi_programchange(int portno, int channel, int value); +void inmidi_pitchbend(int portno, int channel, int value); +void inmidi_aftertouch(int portno, int channel, int value); +void inmidi_polyaftertouch(int portno, int channel, int pitch, int value); + +static void sys_dispatchnextmidiin( void) +{ + static t_midiparser parser[MAXMIDIINDEV], *parserp; + int portno = midi_inqueue[midi_intail].q_portno, + byte = midi_inqueue[midi_intail].q_byte1; + if (!midi_inqueue[midi_intail].q_onebyte) + bug("sys_dispatchnextmidiin"); + if (portno < 0 || portno >= MAXMIDIINDEV) + bug("sys_dispatchnextmidiin 2"); + parserp = parser + portno; + outlet_setstacklim(); + + if (byte >= 0xf8) + inmidi_realtimein(portno, byte); + else + { + inmidi_byte(portno, byte); + if (byte < 0xf0) + { + if (byte & 0x80) + { + parserp->mp_status = byte; + parserp->mp_gotbyte1 = 0; + } + else + { + int cmd = (parserp->mp_status & 0xf0); + int chan = (parserp->mp_status & 0xf); + int byte1 = parserp->mp_byte1, gotbyte1 = parserp->mp_gotbyte1; + switch (cmd) + { + case MIDINOTEOFF: + if (gotbyte1) + inmidi_noteon(portno, chan, byte1, 0), + parserp->mp_gotbyte1 = 0; + else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1; + break; + case MIDINOTEON: + if (gotbyte1) + inmidi_noteon(portno, chan, byte1, byte), + parserp->mp_gotbyte1 = 0; + else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1; + break; + case MIDIPOLYTOUCH: + if (gotbyte1) + inmidi_polyaftertouch(portno, chan, byte1, byte), + parserp->mp_gotbyte1 = 0; + else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1; + break; + case MIDICONTROLCHANGE: + if (gotbyte1) + inmidi_controlchange(portno, chan, byte1, byte), + parserp->mp_gotbyte1 = 0; + else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1; + break; + case MIDIPROGRAMCHANGE: + inmidi_programchange(portno, chan, byte); + break; + case MIDICHANNELTOUCH: + inmidi_aftertouch(portno, chan, byte); + break; + case MIDIPITCHBEND: + if (gotbyte1) + inmidi_pitchbend(portno, chan, ((byte << 7) + byte1)), + parserp->mp_gotbyte1 = 0; + else parserp->mp_byte1 = byte, parserp->mp_gotbyte1 = 1; + break; + } + } + } + } + midi_intail = (midi_intail + 1 == MIDIQSIZE ? 0 : midi_intail + 1); +} + +void sys_pollmidiinqueue( void) +{ +#ifdef TEST_DEJITTER + static int db = 0; +#endif + double logicaltime = .001 * clock_gettimesince(sys_midiinittime); +#ifdef TEST_DEJITTER + if (midi_inhead == midi_intail) + db = 0; +#endif + while (midi_inhead != midi_intail) + { +#ifdef TEST_DEJITTER + if (!db) + { + post("in del %f, logicaltime %f, RT %f adcminusRT %f", + (midi_inqueue[midi_intail].q_time - logicaltime), + logicaltime, sys_getrealtime(), sys_adctimeminusrealtime); + db = 1; + } +#endif +#if 0 + if (midi_inqueue[midi_intail].q_time <= logicaltime - 0.007) + post("late %f", + 1000 * (logicaltime - midi_inqueue[midi_intail].q_time)); +#endif + if (midi_inqueue[midi_intail].q_time <= logicaltime) + { +#if 0 + post("diff %f", + 1000* (logicaltime - midi_inqueue[midi_intail].q_time)); +#endif + sys_dispatchnextmidiin(); + } + else break; + } +} + + /* this should be called from the system dependent MIDI code when a byte + comes in, as a result of our calling sys_poll_midi. We stick it on a + timetag queue and dispatch it at the appropriate logical time. */ + +void sys_midibytein(int portno, int byte) +{ + t_midiqelem *midiqelem; + int newhead = midi_inhead +1; + if (newhead == MIDIQSIZE) + newhead = 0; + /* if FIFO is full flush an element to make room */ + if (newhead == midi_intail) + post("flush"), sys_dispatchnextmidiin(); + midi_inqueue[midi_inhead].q_portno = portno; + midi_inqueue[midi_inhead].q_onebyte = 1; + midi_inqueue[midi_inhead].q_byte1 = byte; + midi_inqueue[midi_inhead].q_time = sys_getmidiinrealtime(); + midi_inhead = newhead; + sys_pollmidiinqueue(); +} + +void sys_pollmidiqueue( void) +{ +#if 0 + static double lasttime; + double newtime = sys_getrealtime(); + if (newtime - lasttime > 0.007) + post("delay %d", (int)(1000 * (newtime - lasttime))); + lasttime = newtime; +#endif + sys_poll_midi(); /* OS dependent poll for MIDI input */ + sys_pollmidioutqueue(); + sys_pollmidiinqueue(); +} + diff --git a/pd/src/s_watchdog.c b/pd/src/s_watchdog.c new file mode 100644 index 00000000..a2122824 --- /dev/null +++ b/pd/src/s_watchdog.c @@ -0,0 +1,47 @@ +/* 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. */ + +/* This file is compiled into the separate program, "pd-watchdog," which +tries to prevent Pd from locking up the processor if it's at realtime +priority. Linux only. Invoked from s_inter.c. */ + +#include +#include +#include +#include +#include + +int main(int argc, char **argv) +{ + int happy = 1; + while (1) + { + struct timeval timout; + fd_set readset; + if (happy) + { + timout.tv_sec = 5; + timout.tv_usec = 0; + } + else + { + timout.tv_sec = 2; + timout.tv_usec = 0; + } + FD_ZERO(&readset); + FD_SET(0, &readset); + select(1, &readset, 0, 0, &timout); + if (FD_ISSET(0, &readset)) + { + char buf[100]; + happy = 1; + if (read(0, &buf, 100) <= 0) + return (0); + else continue; + } + happy = 0; + kill(getppid(), SIGHUP); + fprintf(stderr, "watchdog: signaling pd...\n"); + } +} diff --git a/pd/src/t_main.c b/pd/src/t_main.c new file mode 100644 index 00000000..d4d48c63 --- /dev/null +++ b/pd/src/t_main.c @@ -0,0 +1,123 @@ +/* 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 should be compared with the corresponding thing in the TK +* distribution whenever updating to newer versions of TCL/TK. */ + +/* + * Copyright (c) 1993 The Regents of the University of California. + * Copyright (c) 1994 Sun Microsystems, Inc. + * + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ + + +#ifndef MACOSX /* linux and IRIX only; in MACOSX we don't link this in */ +#include "tk.h" +#include + +/* + * The following variable is a special hack that is needed in order for + * Sun shared libraries to be used for Tcl. + */ + +extern int matherr(void); +int *tclDummyMathPtr = (int *) matherr; + +/* + *---------------------------------------------------------------------- + * + * main -- + * + * This is the main program for the application. + * + * Results: + * None: Tk_Main never returns here, so this procedure never + * returns either. + * + * Side effects: + * Whatever the application does. + * + *---------------------------------------------------------------------- + */ + +void pdgui_startup(Tcl_Interp *interp); +void pdgui_setname(char *name); +void pdgui_setsock(int port); +void pdgui_sethost(char *name); + +int +main(int argc, char **argv) +{ + pdgui_setname(argv[0]); + if (argc >= 2) + { + pdgui_setsock(atoi(argv[1])); + argc--; argv++; + argv[0] = "Pd"; + } + if (argc >= 2) + { + pdgui_sethost(argv[1]); + argc--; argv++; + argv[0] = "Pd"; + } + Tk_Main(argc, argv, Tcl_AppInit); + return 0; /* Needed only to prevent compiler warning. */ +} + + +/* + *---------------------------------------------------------------------- + * + * Tcl_AppInit -- + * + * This procedure performs application-specific initialization. + * Most applications, especially those that incorporate additional + * packages, will have their own version of this procedure. + * + * Results: + * Returns a standard Tcl completion code, and leaves an error + * message in interp->result if an error occurs. + * + * Side effects: + * Depends on the startup script. + * + *---------------------------------------------------------------------- + */ + + +int +Tcl_AppInit(interp) + Tcl_Interp *interp; /* Interpreter for application. */ +{ + Tk_Window mainwindow; + + if (Tcl_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + if (Tk_Init(interp) == TCL_ERROR) { + return TCL_ERROR; + } + + /* setup specific to pd-gui: */ + + pdgui_startup(interp); + + /* + * Specify a user-specific startup file to invoke if the application + * is run interactively. Typically the startup file is "~/.apprc" + * where "app" is the name of the application. If this line is deleted + * then no user-specific startup file will be run under any conditions. + */ + +#if 0 + tcl_RcFileName = "~/.pdrc"; +#endif + + return TCL_OK; +} + +#endif /* MACOSX */ diff --git a/pd/src/t_tk.h b/pd/src/t_tk.h new file mode 100644 index 00000000..a6943679 --- /dev/null +++ b/pd/src/t_tk.h @@ -0,0 +1,10 @@ +/* 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. */ + +void pdgui_vmess(char *fmt, ...); +void pdgui_mess(char *s); + +void pdgui_evalfile(char *s); + +#define GUISTRING 1000 diff --git a/pd/src/t_tkcmd.c b/pd/src/t_tkcmd.c new file mode 100644 index 00000000..c2abd846 --- /dev/null +++ b/pd/src/t_tkcmd.c @@ -0,0 +1,367 @@ +/* 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. */ + +#ifdef UNIX /* in unix this only works first; in NT it only works last. */ +#include "tk.h" +#endif + +#include "t_tk.h" +#include +#include +#include +#include +#include +#ifdef UNIX +#include +#include +#include +#include +#ifdef HAVE_BSTRING_H +#include +#endif +#include +#include +#endif +#ifdef NT +#include +#include +#endif +#ifdef NT +#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 + +#ifdef NT +#include "tk.h" +#endif + +void tcl_mess(char *s); + +/***************** the socket setup code ********************/ + +static int portno = 5400; + + /* some installations of linux don't know about "localhost" so give + the loopback address; NT, on the other hand, can't understand the + hostname "127.0.0.1". */ +char hostname[100] = +#ifdef __linux__ + "127.0.0.1"; +#else + "localhost"; +#endif + +void pdgui_setsock(int port) +{ + portno = port; +} + +void pdgui_sethost(char *name) +{ + strncpy(hostname, name, 100); + hostname[99] = 0; +} + +static void pdgui_sockerror(char *s) +{ +#ifdef NT + int err = WSAGetLastError(); +#endif +#ifdef UNIX + int err = errno; +#endif + + fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err); + tcl_mess("exit\n"); + exit(1); +} + +static int sockfd; + +/* The "pd_suck" command, which polls the socket. + FIXME: if Pd sends something bigger than SOCKSIZE we're in trouble! + This has to be set bigger than any array update message for instance. +*/ +#define SOCKSIZE 20000 + +static char pd_tkbuf[SOCKSIZE+1]; +int pd_spillbytes = 0; + +static void pd_readsocket(ClientData cd, int mask) +{ + int ngot; + fd_set readset, writeset, exceptset; + struct timeval timout; + + timout.tv_sec = 0; + timout.tv_usec = 0; + FD_ZERO(&writeset); + FD_ZERO(&readset); + FD_ZERO(&exceptset); + FD_SET(sockfd, &readset); + FD_SET(sockfd, &exceptset); + + if (select(sockfd+1, &readset, &writeset, &exceptset, &timout) < 0) + perror("select"); + if (FD_ISSET(sockfd, &exceptset) || FD_ISSET(sockfd, &readset)) + { + int ret; + ret = recv(sockfd, pd_tkbuf + pd_spillbytes, + SOCKSIZE - pd_spillbytes, 0); + if (ret < 0) pdgui_sockerror("socket receive error"); + else if (ret == 0) + { + /* fprintf(stderr, "read %d\n", SOCKSIZE - pd_spillbytes); */ + fprintf(stderr, "pd_gui: pd process exited\n"); + tcl_mess("exit\n"); + } + else + { + char *lastcr = 0, *bp = pd_tkbuf, *ep = bp + (pd_spillbytes + ret); + int brace = 0; + char lastc = 0; + while (bp < ep) + { + char c = *bp; + if (c == '}' && brace) brace--; + else if (c == '{') brace++; + else if (!brace && c == '\n' && lastc != '\\') lastcr = bp; + lastc = c; + bp++; + } + if (lastcr) + { + int xtra = pd_tkbuf + pd_spillbytes + ret - (lastcr+1); + char bashwas = lastcr[1]; + lastcr[1] = 0; + tcl_mess(pd_tkbuf); + lastcr[1] = bashwas; + if (xtra) + { + /* fprintf(stderr, "x %d\n", xtra); */ + memmove(pd_tkbuf, lastcr+1, xtra); + } + pd_spillbytes = xtra; + } + else + { + pd_spillbytes += ret; + } + } + } +} + +#ifndef UNIX + /* if we aren't UNIX, we add a tcl command to poll the + socket for data. */ +static int pd_pollsocketCmd(ClientData cd, Tcl_Interp *interp, + int argc, char **argv) +{ + pd_readsocket(cd, 0); + return (TCL_OK); +} +#endif + +void pdgui_setupsocket(void) +{ + struct sockaddr_in server; + struct hostent *hp; + +#ifdef NT + short version = MAKEWORD(2, 0); + WSADATA nobby; + + if (WSAStartup(version, &nobby)) pdgui_sockerror("setup"); +#endif + + /* create a socket */ + sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) pdgui_sockerror("socket"); + + /* connect socket using hostname provided in command line */ + server.sin_family = AF_INET; + + hp = gethostbyname(hostname); + + if (hp == 0) + { + fprintf(stderr, + "localhost not found (inet protocol not installed?)\n"); + exit(1); + } + memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); + + /* assign client port number */ + server.sin_port = htons((unsigned short)portno); + + /* try to connect */ + if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) + pdgui_sockerror("connecting stream socket"); + +#ifdef UNIX + /* in unix we ask TK to call us back. In NT we have to poll. */ + Tk_CreateFileHandler(sockfd, TK_READABLE | TK_EXCEPTION, + pd_readsocket, 0); +#endif +} + +/**************************** commands ************************/ +static char *pdgui_path; + +/* The "pd" command, which cats its args together and throws the result +* at the Pd interpreter. +*/ +#define MAXWRITE 1024 + +static int pdCmd(ClientData cd, Tcl_Interp *interp, int argc, char **argv) +{ + if (argc == 2) + { + int n = strlen(argv[1]); + if (send(sockfd, argv[1], n, 0) < n) + { + perror("stdout"); + tcl_mess("exit\n"); + } + } + else + { + int i; + char buf[MAXWRITE]; + buf[0] = 0; + for (i = 1; i < argc; i++) + { + if (strlen(argv[i]) + strlen(buf) + 2 > MAXWRITE) + { + interp->result = "pd: arg list too long"; + return (TCL_ERROR); + } + if (i > 1) strcat(buf, " "); + strcat(buf, argv[i]); + } + if (send(sockfd, buf, strlen(buf), 0) < 0) + { + perror("stdout"); + tcl_mess("exit\n"); + } + } + return (TCL_OK); +} + +/*********** "c" level access to tk functions. ******************/ + +static Tcl_Interp *tk_myinterp; + +void tcl_mess(char *s) +{ + int result; + result = Tcl_Eval(tk_myinterp, s); + if (result != TCL_OK) + { + if (*tk_myinterp->result) printf("%s\n", tk_myinterp->result); + } +} + +/* LATER should do a bounds check -- but how do you get printf to do that? */ +void tcl_vmess(char *fmt, ...) +{ + int result, i; + char buf[MAXWRITE]; + va_list ap; + + va_start(ap, fmt); + + vsprintf(buf, fmt, ap); + result = Tcl_Eval(tk_myinterp, buf); + if (result != TCL_OK) + { + if (*tk_myinterp->result) printf("%s\n", tk_myinterp->result); + } + va_end(ap); +} + +#ifdef UNIX +void pdgui_doevalfile(Tcl_Interp *interp, char *s) +{ + char buf[GUISTRING]; + sprintf(buf, "set pd_guidir \"%s\"\n", pdgui_path); + tcl_mess(buf); + strcpy(buf, pdgui_path); + strcat(buf, "/bin/"); + strcat(buf, s); + if (Tcl_EvalFile(interp, buf) != TCL_OK) + { + char buf2[1000]; + sprintf(buf2, "puts [concat tcl: %s: can't open script]\n", + buf); + tcl_mess(buf2); + } +} + +void pdgui_evalfile(char *s) +{ + pdgui_doevalfile(tk_myinterp, s); +} +#endif + +void pdgui_startup(Tcl_Interp *interp) +{ + + /* save pointer to the main interpreter */ + tk_myinterp = interp; + + + /* add our own TK commands */ + Tcl_CreateCommand(interp, "pd", pdCmd, (ClientData)NULL, + (Tcl_CmdDeleteProc *)NULL); +#ifndef UNIX + Tcl_CreateCommand(interp, "pd_pollsocket", pd_pollsocketCmd, + (ClientData)NULL, (Tcl_CmdDeleteProc *)NULL); +#endif + pdgui_setupsocket(); + + /* read in the startup file */ +#if !defined(NT) && !defined(MACOSX) + pdgui_evalfile("pd.tk"); +#endif +} + +#ifdef UNIX +void pdgui_setname(char *s) +{ + char *t; + char *str; + int n; + if (t = strrchr(s, '/')) str = s, n = (t-s) + 1; + else str = "./", n = 2; + if (n > GUISTRING-100) n = GUISTRING-100; + pdgui_path = malloc(n+9); + + strncpy(pdgui_path, str, n); + while (strlen(pdgui_path) > 0 && pdgui_path[strlen(pdgui_path)-1] == '/') + pdgui_path[strlen(pdgui_path)-1] = 0; + if (t = strrchr(pdgui_path, '/')) + *t = 0; +} +#endif + +int Pdtcl_Init(Tcl_Interp *interp) +{ + char *myvalue = Tcl_GetVar(interp, "argv", 0); + int myportno; + if (myvalue && (myportno = atoi(myvalue)) > 1) + pdgui_setsock(myportno); + tk_myinterp = interp; + pdgui_startup(interp); + interp->result = "loaded pdtcl_init"; + + return (TCL_OK); +} + +int Pdtcl_SafeInit(Tcl_Interp *interp) { + fprintf(stderr, "Pdtcl_Safeinit 51\n"); + return (TCL_OK); +} + diff --git a/pd/src/u_main.tk b/pd/src/u_main.tk new file mode 100644 index 00000000..a19b0951 --- /dev/null +++ b/pd/src/u_main.tk @@ -0,0 +1,2570 @@ +set pd_nt 0 +# (The above is 0 for unix, 1 for microsoft, and 2 for Mac OSX. The first +# line is automatically munged by the relevant makefiles.) + +# 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. + +# changed by Thomas Musil 09.2001 +# between "pdtk_graph_dialog -- dialog window for graphs" +# and "pdtk_array_dialog -- dialog window for arrays" +# a new dialogbox was inserted, named: +# "pdtk_iemgui_dialog -- dialog window for iem guis" +# +# there are 2 new features: 1.) line-delete-protection in edit-menue +# +# 2.) there are all iem-guis in a seperated put-gui-menue +# +# all this changes are labeled with #######iemlib########## + +if {$pd_nt == 1} { + global pd_guidir + set pd_gui2 [string range $argv0 0 [expr [string last \\ $argv0 ] - 1]] + regsub -all \\\\ $pd_gui2 / pd_gui3 + set pd_guidir $pd_gui3/.. + load $pd_guidir/bin/pdtcl +} + +if {$pd_nt == 2} { + global pd_guidir + set pd_gui2 [string range $argv0 0 [expr [string last / $argv0 ] - 1]] + set pd_guidir $pd_gui2/.. + load $pd_guidir/bin/pdtcl +} + +# it's unfortunate but we seem to have to turn off global bindings +# for Text objects to get control-s and control-t to do what we want for +# "text" dialogs below. Also we have to get rid of tab's changing the focus. + +bind all "" +bind all "" +bind Text {} +bind Text {} +# puts stderr [bind all] + +################## set up main window ######################### +frame .mbar -relief raised -bd 2 +canvas .dummy -height 1c -width 1c +frame .controls +pack .mbar .controls .dummy -side top -fill x +menubutton .mbar.file -text File -menu .mbar.file.menu +menubutton .mbar.find -text Find -menu .mbar.find.menu +menubutton .mbar.windows -text Windows -menu .mbar.windows.menu +menubutton .mbar.audio -text Audio -menu .mbar.audio.menu +menubutton .mbar.help -text Help -menu .mbar.help.menu +pack .mbar.file .mbar.find .mbar.windows .mbar.audio -side left +pack .mbar.help -side right +menu .mbar.file.menu +menu .mbar.find.menu +menu .mbar.windows.menu -postcommand [concat pdtk_fixwindowmenu] +menu .mbar.audio.menu +menu .mbar.help.menu + +set ctrls_audio_on 0 +set ctrls_meter_on 0 +set ctrls_inlevel 0 +set ctrls_outlevel 0 + +frame .controls.switches +checkbutton .controls.switches.audiobutton -text {compute audio} \ + -variable ctrls_audio_on \ + -anchor w \ + -command {pd [concat pd dsp $ctrls_audio_on \;]} + +checkbutton .controls.switches.meterbutton -text {peak meters} \ + -variable ctrls_meter_on \ + -anchor w \ + -command {pd [concat pd meters $ctrls_meter_on \;]} + +pack .controls.switches.meterbutton .controls.switches.audiobutton -side left + +frame .controls.in +label .controls.in.label -text IN +entry .controls.in.level -textvariable ctrls_inlevel -width 3 +button .controls.in.clip -text {CLIP} -state disabled +pack .controls.in.label .controls.in.level .controls.in.clip -side top + +frame .controls.out +label .controls.out.label -text OUT +entry .controls.out.level -textvariable ctrls_outlevel -width 3 +button .controls.out.clip -text {CLIP} -state disabled +pack .controls.out.label .controls.out.level .controls.out.clip -side top + +button .controls.dio -text "DIO\nerrors" \ + -command {pd [concat pd audiostatus \;]} + +pack .controls.switches -side bottom +pack .controls.in .controls.out -side left +pack .controls.dio -side right + +bind . {pdtk_pd_ctrlkey %W %K 0} +bind . {pdtk_pd_ctrlkey %W %K 1} + + +############### set up global variables ################################ + +set untitled_number 1 +set untitled_directory [pwd] +set saveas_client doggy +set pd_opendir $untitled_directory +############iemlib################## +# need it to know, if new or open file +set iem_new_open_flag "open" +############iemlib################## + +################ utility functions ######################### + +proc pdtk_enquote {x} { + set foo [string map {"," "" ";" "" \" ""} $x] + set foo2 [string map {" " "\\ "} $foo] + concat $foo2 +} + +proc pdtk_debug {x} { + tk_messageBox -message $x -type ok +} + +proc pdtk_watchdog {} { + pd [concat pd ping \;] + after 2000 {pdtk_watchdog} +} + +proc pdtk_check {x message} { + set answer [tk_messageBox \-message $x \-type yesno \-icon question] + switch $answer { + yes {pd $message} } +# no {tk_messageBox \-message "cancelled" \-type ok} +} + +set menu_windowlist {} + +proc pdtk_fixwindowmenu {} { + global menu_windowlist + .mbar.windows.menu delete 0 end + foreach i $menu_windowlist { + .mbar.windows.menu add command -label [lindex $i 0] \ + -command [concat menu_domenuwindow [lindex $i 1]] + } +} + +############### the "New" menu command ######################## +proc menu_new {} { + global untitled_number + global untitled_directory +############iemlib################## + global iem_new_open_flag + + set iem_new_open_flag "new" +############iemlib################## + pd [concat pd filename Untitled-$untitled_number $untitled_directory \;] + pd { + #N canvas; + #X pop 1; + } + set untitled_number [expr $untitled_number + 1] +} + +################## the "Open" menu command ######################### + +proc menu_open {} { + global pd_opendir + global pd_nt +############iemlib################## + global iem_new_open_flag + + set iem_new_open_flag "open" +############iemlib################## + +# workaround -- initialdir doesn't work on MACOSX yet --- + if {$pd_nt == 2} { + cd $pd_opendir + set filename [tk_getOpenFile -defaultextension .pd \ + -filetypes { {{pd files} {.pd}} {{max files} {.pat}}} ] + } else { + set filename [tk_getOpenFile -defaultextension .pd \ + -filetypes { {{pd files} {.pd}} {{max files} {.pat}}} \ + -initialdir $pd_opendir] + } +# puts stderr $filename + if {$filename != ""} { + set directory [string range $filename 0 \ + [expr [string last / $filename ] - 1]] + set pd_opendir $directory + set basename [string range $filename \ + [expr [string last / $filename ] + 1] end] + +# pd_debug [concat file $filename base $basename dir $directory] + + pd [concat pd open [pdtk_enquote $basename] \ + [pdtk_enquote $directory]\;] + } +} + +################## the "Message" menu command ######################### +proc menu_send {} { + toplevel .sendpanel + entry .sendpanel.entry -textvariable send_textvariable + pack .sendpanel.entry -side bottom -fill both -ipadx 100 + .sendpanel.entry select from 0 + .sendpanel.entry select adjust end + bind .sendpanel.entry { + pd [concat $send_textvariable \;] + after 50 {destroy .sendpanel} + } + focus .sendpanel.entry +} + +################## the "Quit" menu command ######################### +proc menu_really_quit {} {pd {pd quit;}} + +proc menu_quit {} {pdtk_check {Really quit?} {pd quit;}} + +######### the "Pd" menu command, which puts the Pd window on top ######## +proc menu_pop_pd {} {raise .} + +######### the "audio" menu command ############### +proc menu_audio {flag} {pd [concat pd dsp $flag \;]} + +######### the "documentation" menu command ############### + +set doc_number 1 + +proc menu_opentext {filename} { + global doc_number + global pd_guidir + global pd_myversion + set name [format ".help%d" $doc_number] + toplevel $name + text $name.text -relief raised -bd 2 -font -*-courier-bold--normal--12-* \ + -yscrollcommand "$name.scroll set" -background white + scrollbar $name.scroll -command "$name.text yview" + pack $name.scroll -side right -fill y + pack $name.text -side left -fill both -expand 1 + + set f [open $filename] + while {![eof $f]} { + set bigstring [read $f 1000] + regsub -all PD_BASEDIR $bigstring $pd_guidir bigstring2 + regsub -all PD_VERSION $bigstring2 $pd_myversion bigstring3 + $name.text insert end $bigstring3 + } + close $f + set doc_number [expr $doc_number + 1] +} + +set help_directory $pd_guidir/doc + +proc menu_documentation {} { + global help_directory + global pd_nt +############iemlib################## + global iem_new_open_flag + + set iem_new_open_flag "open" +############iemlib################## + + if {$pd_nt == 2} { + cd $help_directory + set filename [tk_getOpenFile -defaultextension .pd \ + -filetypes { {{documentation} {.pd .txt .htm}} } ] + } else { + set filename [tk_getOpenFile -defaultextension .pd \ + -filetypes { {{documentation} {.pd .txt .htm}} } \ + -initialdir $help_directory] + } + + if {$filename != ""} { + if {[string first .txt $filename] >= 0} { + menu_opentext $filename + } elseif {[string first .htm $filename] >= 0} { + if {$pd_nt == 0} { +#I wish I could get this to run in the background; the "&" doesn't do it: + exec sh -c \ + [format "mozilla file:%s || netscape file:%s &\n" \ + $filename $filename] + } else { + tk_messageBox -message \ + {sorry -- can't open htm files yet; open this manually} \ + -type ok + } + } else { + set help_directory [string range $filename 0 \ + [expr [string last / $filename ] - 1]] + set basename [string range $filename \ + [expr [string last / $filename ] + 1] end] + pd [concat pd open [pdtk_enquote $basename] \ + [pdtk_enquote $help_directory] \;] + } + } +} + +proc menu_doc_open {subdir basename} { + global pd_guidir +############iemlib################## + global iem_new_open_flag + + set iem_new_open_flag "open" +############iemlib################## + + set dirname $pd_guidir/$subdir + + if {[string first .txt $basename] >= 0} { + menu_opentext $dirname/$basename + } else { + pd [concat pd open [pdtk_enquote $basename] \ + [pdtk_enquote $dirname] \;] + } +} + +#################### the "File" menu for the Pd window ############## +.mbar.file.menu add command -label New -command {menu_new} \ + -accelerator "Ctrl+n" +.mbar.file.menu add command -label Open -command {menu_open} \ + -accelerator "Ctrl+o" +.mbar.file.menu add command -label Message -command {menu_send} \ + -accelerator "Ctrl+m" +.mbar.file.menu add separator +.mbar.file.menu add command -label Quit -command {menu_quit} \ + -accelerator "Ctrl+q" + +#################### the "Find" menu for the Pd window ############## +.mbar.find.menu add command -label {last error?} -command {menu_finderror} + +#################### the "Audio" menu for the Pd window ############## +.mbar.audio.menu add command -label On -accelerator "Ctrl+/" \ + -command {menu_audio 1} +.mbar.audio.menu add command -label Off -accelerator "Ctrl+." \ + -command {menu_audio 0} + +#################### the "Help" menu for the Pd window ############## +.mbar.help.menu add command -label {About Pd} \ + -command {menu_doc_open doc/1.manual 1.introduction.txt} +.mbar.help.menu add command -label {Test Audio and MIDI} \ + -command {menu_doc_open doc/7.stuff/tools testtone.pd} +.mbar.help.menu add command -label {Load Meter} \ + -command {menu_doc_open doc/7.stuff/tools load-meter.pd} +.mbar.help.menu add command -label {Pure Documentation...} \ + -command {menu_documentation} + +########### functions for menu functions on document windows ######## + +proc menu_save {name} { + pdtk_canvas_checkgeometry $name + pd [concat $name menusave \;] +} + +proc menu_saveas {name} { + pdtk_canvas_checkgeometry $name + pd [concat $name menusaveas \;] +} + +proc menu_print {name} { + $name.c postscript -file x.ps +} + +proc menu_close {name} { + pd [concat $name menuclose \;] +} + +proc menu_cut {name} { + pd [concat $name cut \;] +} + +proc menu_copy {name} { + pd [concat $name copy \;] +} + +proc menu_paste {name} { + pd [concat $name paste \;] +} + +proc menu_duplicate {name} { + pd [concat $name duplicate \;] +} + +proc menu_selectall {name} { + pd [concat $name selectall \;] +} + +proc menu_texteditor {name} { + pd [concat $name texteditor \;] +} + +proc menu_font {name} { + pd [concat $name menufont \;] +} + +proc menu_tidyup {name} { + pd [concat $name tidy \;] +} + +proc menu_editmode {name} { + pd [concat $name editmode 0 \;] +} + +proc menu_object {name accel} { + pd [concat $name obj $accel \;] +} + +proc menu_message {name accel} { + pd [concat $name msg $accel \;] +} + +proc menu_floatatom {name accel} { + pd [concat $name floatatom $accel \;] +} + +proc menu_symbolatom {name accel} { + pd [concat $name symbolatom $accel \;] +} + +proc menu_comment {name accel} { + pd [concat $name text $accel \;] +} + +proc menu_graph {name} { + pd [concat $name graph \;] +} + +proc menu_array {name} { + pd [concat $name menuarray \;] +} + +############iemlib################## +proc menu_bng {name accel} { + pd [concat $name bng $accel \;] +} + +proc menu_toggle {name accel} { + pd [concat $name toggle $accel \;] +} + +proc menu_numbox {name accel} { + pd [concat $name numbox $accel \;] +} + +proc menu_vslider {name accel} { + pd [concat $name vslider $accel \;] +} + +proc menu_hslider {name accel} { + pd [concat $name hslider $accel \;] +} + +proc menu_hdial {name accel} { + pd [concat $name hdial $accel \;] +} + +proc menu_vdial {name accel} { + pd [concat $name vdial $accel \;] +} + +proc menu_vumeter {name accel} { + pd [concat $name vumeter $accel \;] +} + +proc menu_mycnv {name accel} { + pd [concat $name mycnv $accel \;] +} + +proc menu_protectmode {name} { + pd [concat $name protectmode 0 \;] +} + +############iemlib################## + +proc menu_windowparent {name} { + pd [concat $name findparent \;] +} + +proc menu_findagain {name} { + pd [concat $name findagain \;] +} + +proc menu_finderror {} { + pd [concat pd finderror \;] +} + +proc menu_domenuwindow {i} { + raise $i +} + +proc menu_fixwindowmenu {name} { + global menu_windowlist + $name.m.windows.m add command + $name.m.windows.m delete 4 end + foreach i $menu_windowlist { + $name.m.windows.m add command -label [lindex $i 0] \ + -command [concat menu_domenuwindow [lindex $i 1]] + } +} + +################## the "find" menu item ################### + +set find_canvas nobody +set find_string "" +set find_count 1 + +proc find_apply {name} { + global find_string + global find_canvas + regsub -all \; $find_string " _semi_ " find_string2 + regsub -all \, $find_string2 " _comma_ " find_string3 +# puts stderr [concat $find_canvas find $find_string3 \ +# \;] + pd [concat $find_canvas find $find_string3 \ + \;] + after 50 destroy $name +} + +proc find_cancel {name} { + after 50 destroy $name +} + +proc menu_findobject {canvas} { + global find_string + global find_canvas + global find_count + + set name [format ".find%d" $find_count] + set find_count [expr $find_count + 1] + + set find_canvas $canvas + + toplevel $name + + label $name.label -text {find...} + pack $name.label -side top + + entry $name.entry -textvariable find_string + pack $name.entry -side top + + frame $name.buttonframe + pack $name.buttonframe -side bottom -fill x -pady 2m + button $name.buttonframe.cancel -text {Cancel}\ + -command "find_cancel $name" + button $name.buttonframe.ok -text {OK}\ + -command "find_apply $name" + pack $name.buttonframe.cancel -side left -expand 1 + pack $name.buttonframe.ok -side left -expand 1 + + $name.entry select from 0 + $name.entry select adjust end + bind $name.entry [ concat find_apply $name] + focus $name.entry +} + + +############# pdtk_canvas_new -- create a new canvas ############### +proc pdtk_canvas_new {name width height geometry} { + global pd_opendir + global iem_new_open_flag + + toplevel $name + frame $name.m -relief raised -bd 2 +# puts stderr [concat geometry: $geometry] + wm geometry $name $geometry + canvas $name.c -width $width -height $height -background white \ + -yscrollcommand "$name.scrollvert set" \ + -xscrollcommand "$name.scrollhort set" \ + -scrollregion [concat 0 0 $width $height] + + scrollbar $name.scrollvert -command "$name.c yview" + scrollbar $name.scrollhort -command "$name.c xview" \ + -orient horizontal + + pack $name.m -side top -fill x + pack $name.scrollhort -side bottom -fill x + pack $name.scrollvert -side right -fill y + pack $name.c -side left -expand 1 -fill both + wm minsize $name 1 1 + wm geometry $name $geometry + +# the file menu + + menubutton $name.m.file -text File -menu $name.m.file.m + pack $name.m.file -side left + menu $name.m.file.m + + $name.m.file.m add command -label New -command {menu_new} \ + -accelerator "Ctrl+n" + + $name.m.file.m add command -label Open -command {menu_open} \ + -accelerator "Ctrl+o" + + $name.m.file.m add command -label Message -command {menu_send} \ + -accelerator "Ctrl+m" + + $name.m.file.m add separator + $name.m.file.m add command -label Save -command [concat menu_save $name] \ + -accelerator "Ctrl+s" + + $name.m.file.m add command -label Close \ + -command [concat menu_close $name] \ + -accelerator "Ctrl+w" + + $name.m.file.m add command -label "Save as..." \ + -command [concat menu_saveas $name] \ + -accelerator "Ctrl+S" + + $name.m.file.m add command -label Print -command [concat menu_print $name] \ + -accelerator "Ctrl+p" + + $name.m.file.m add separator + + $name.m.file.m add command -label Quit -command {menu_quit} \ + -accelerator "Ctrl+q" + +# the edit menu + menubutton $name.m.edit -text Edit -menu $name.m.edit.m + pack $name.m.edit -side left + menu $name.m.edit.m + + + $name.m.edit.m add command -label Cut -command [concat menu_cut $name] \ + -accelerator "Ctrl+x" + + $name.m.edit.m add command -label Copy -command [concat menu_copy $name] \ + -accelerator "Ctrl+c" + + $name.m.edit.m add command -label Paste \ + -command [concat menu_paste $name] \ + -accelerator "Ctrl+v" + + $name.m.edit.m add command -label Duplicate \ + -command [concat menu_duplicate $name] \ + -accelerator "Ctrl+d" + + $name.m.edit.m add command -label {Select all} \ + -command [concat menu_selectall $name] \ + -accelerator "Ctrl+a" + + $name.m.edit.m add command -label {Text Editor} \ + -command [concat menu_texteditor $name] \ + -accelerator "Ctrl+t" + + $name.m.edit.m add command -label Font \ + -command [concat menu_font $name] + + $name.m.edit.m add command -label {Tidy Up} \ + -command [concat menu_tidyup $name] + + $name.m.edit.m add separator + +############iemlib################## +# instead of "red = #BC3C60" we take "grey85", so there is no difference, +# if widget is selected or not. + + $name.m.edit.m add checkbutton -label "Edit mode" \ + -indicatoron true -selectcolor grey85 \ + -command [concat menu_editmode $name] \ + -accelerator "Ctrl+e" + + + + $name.m.edit.m add checkbutton -label "Protect" \ + -indicatoron true -selectcolor grey85 \ + -command [concat menu_protectmode $name] \ + -accelerator "Ctrl+r" + + if { $iem_new_open_flag == "open" } { + $name.m.edit.m entryconfigure "Edit mode" -indicatoron false } + $name.m.edit.m entryconfigure "Protect" -indicatoron false + +############iemlib################## + +# the put menu + menubutton $name.m.put -text Put -menu $name.m.put.m + pack $name.m.put -side left + menu $name.m.put.m + + $name.m.put.m add command -label Object \ + -command [concat menu_object $name 0] \ + -accelerator "Ctrl+1" + + $name.m.put.m add command -label Message \ + -command [concat menu_message $name 0] \ + -accelerator "Ctrl+2" + + $name.m.put.m add command -label Number \ + -command [concat menu_floatatom $name 0] \ + -accelerator "Ctrl+3" + + $name.m.put.m add command -label Symbol \ + -command [concat menu_symbolatom $name 0] \ + -accelerator "Ctrl+4" + + $name.m.put.m add command -label Comment \ + -command [concat menu_comment $name 0] \ + -accelerator "Ctrl+5" + +############iemlib################## + + $name.m.put.m add command -label Bang \ + -command [concat menu_bng $name 0] \ + -accelerator "Alt+b" + + $name.m.put.m add command -label Toggle \ + -command [concat menu_toggle $name 0] \ + -accelerator "Alt+t" + + $name.m.put.m add command -label Number2 \ + -command [concat menu_numbox $name 0] \ + -accelerator "Alt+n" + + $name.m.put.m add command -label Vslider \ + -command [concat menu_vslider $name 0] \ + -accelerator "Alt+v" + + $name.m.put.m add command -label Hslider \ + -command [concat menu_hslider $name 0] \ + -accelerator "Alt+h" + + $name.m.put.m add command -label Vdial \ + -command [concat menu_vdial $name 0] \ + -accelerator "Alt+d" + + $name.m.put.m add command -label Hdial \ + -command [concat menu_hdial $name 0] \ + -accelerator "Alt+i" + + $name.m.put.m add command -label VU \ + -command [concat menu_vumeter $name 0] \ + -accelerator "Alt+u" + + $name.m.put.m add command -label Canvas \ + -command [concat menu_mycnv $name 0] \ + -accelerator "Alt+c" + +############iemlib################## + + $name.m.put.m add command -label Graph \ + -command [concat menu_graph $name] + + $name.m.put.m add command -label Array \ + -command [concat menu_array $name] + + + +# the find menu + menubutton $name.m.find -text Find -menu $name.m.find.m + pack $name.m.find -side left + menu $name.m.find.m + $name.m.find.m add command -label {Find...} -accelerator "Ctrl+f" \ + -command [concat menu_findobject $name] + $name.m.find.m add command -label {Find Again} -accelerator "Ctrl+g" \ + -command [concat menu_findagain $name] + $name.m.find.m add command -label {Find last error} \ + -command [concat menu_finderror] + +# the window menu + menubutton $name.m.windows -text Windows -menu $name.m.windows.m + pack $name.m.windows -side left + menu $name.m.windows.m -postcommand [concat menu_fixwindowmenu $name] + $name.m.windows.m add command -label {parent window}\ + -command [concat menu_windowparent $name] + $name.m.windows.m add command -label {Pd window} -command menu_pop_pd + $name.m.windows.m add separator + +# the audio menu + menubutton $name.m.audio -text Audio -menu $name.m.audio.m + pack $name.m.audio -side left + menu $name.m.audio.m + $name.m.audio.m add command -label On -accelerator "Ctrl+/" \ + -command {menu_audio 1} + $name.m.audio.m add command -label Off -accelerator "Ctrl+." \ + -command {menu_audio 0} + +# the help menu + menubutton $name.m.help -text Help -menu $name.m.help.m + pack $name.m.help -side right + menu $name.m.help.m + $name.m.help.m add command -label {Getting Started} \ + -command {menu_doc_open doc/1.manual 1.introduction.txt} + $name.m.help.m add command -label {Test Audio and MIDI} \ + -command {menu_doc_open doc/7.stuff/tools testtone.pd} + $name.m.help.m add command -label {Load Meter} \ + -command {menu_doc_open doc/7.stuff/tools load-meter.pd} + $name.m.help.m add command -label {Pure Documentation} \ + -command {menu_documentation} + +# the popup menu + menu $name.popup -tearoff false + $name.popup add command -label {Properties} \ + -command [concat popup_action $name 0] + $name.popup add command -label {Open} \ + -command [concat popup_action $name 1] + $name.popup add command -label {Help} \ + -command [concat popup_action $name 2] + +# WM protocol + wm protocol $name WM_DELETE_WINDOW [concat menu_close $name] + +# bindings. +# this is idiotic -- how do you just sense what mod keys are down and +# pass them on? I can't find it anywhere. +# Here we encode shift as 1, control 2, alt 4, in agreement +# with definitions in g_canvas.c. The third button gets "8" but we don't +# bother with modifiers there. +# We don't handle multiple clicks yet. + + bind $name.c