diff options
author | IOhannes m zmölnig <zmoelnig@users.sourceforge.net> | 2008-02-08 13:00:32 +0000 |
---|---|---|
committer | IOhannes m zmölnig <zmoelnig@users.sourceforge.net> | 2008-02-08 13:00:32 +0000 |
commit | 4d84d14ac1aa13958eaa2971b03f7f929a519105 (patch) | |
tree | 6579d3f2cea5410a10c4baac8d0f372fb0dff372 /desiredata/src | |
parent | b334d38aefbd8e0e159d7af6c20d63c5d2b64859 (diff) |
reorganized
svn path=/trunk/; revision=9400
Diffstat (limited to 'desiredata/src')
122 files changed, 64411 insertions, 0 deletions
diff --git a/desiredata/src/ChangeLog b/desiredata/src/ChangeLog new file mode 100644 index 00000000..84cf1b43 --- /dev/null +++ b/desiredata/src/ChangeLog @@ -0,0 +1,233 @@ +$Id: ChangeLog,v 1.1.4.11.2.44 2007-09-06 16:12:01 chunlee Exp $ + +DesireData 2007.08.22 : + + * added more Bokml (Norwegian) translations from Gisle Frysland + * added Nihongo (Japanese) translations from Kentaro Fukuchi + * added Dansk (Danish) translations from Steffen Leve Poulsen + * added History class to unify command history for Listener/Runcommand/TextBox + * KeyboardDialog clean up, added font selector for console and virtual keyboard + * Appearance settings can be applied at run time + * new object/wire indexing system (diff-friendly) + * Added keyboard/mouse macro recording, playback, and copy (to clipboard) + * [select] has as many inlets as it has arguments + * Added [macro] so that a macro can be played back in a patch using messagebox + * Added [clipboard] to pull the content of system clipboard + * Fixed variable width font support and TextBox code clean up + * Added object id display toggle + * Added [display] object + * Added patch editing commands + * Added expand_port + * Added profiler (object speed measurements) (not compiled in by default) + * Can now use spaces and \{} in IEM labels and some other places. + * Added Locale diff tool: localeutils.tcl + +DesireData 2007.08.04 : + + * Unicode locales + * fixed type mismatch bug recently introduced in [unpack]... + * fixed lost console posts at startup + * turned most fprintf() into post() or error() + * added Chinese locale from Chun Lee + * added Polish locale from Michal Seta + * added object creation history + * added arrow keys and mouse clicks to KeyboardDialog + * added click drag and copy + * added background grid + * added snap to grid + * added new font selector (in client prefs) + +DesireData 2007.07.30 : + + * added classes [unpost], [tracecall], [parse], [unparse] + * non-constructed objects finally have a dashed box like they used to + * most of the rest of the C code switched to C++,PD_PLUSPLUS_FACE + * beginning to use C++ standard library components + * added event history view (help menu) + * added keyboard view + * fixed several bugs in copy/paste, undo/redo, subpatches, gop. + * added atom_ostream (similar to atom_string) + * lifted many string length restrictions + * fixed the hexmunge generator (for classnames with special chars) + * pd_error() is deprecated + * added verror(), added open_via_path2(), canvas_open2(), outlet_atom(), ... + * [route] and [select] support mixed floats and symbols + * [unpack] supports type "e" meaning any atom ("e" stands for "element") + * added variable mouse cursor sensitivity + * various fixes on keyboard navigation + +DesireData 2007.06.27 (which should have been 2007.01.12) : + + * merged new loader from Miller's pd 0.40-2 + * merged (but not tested) the rest of the [declare] code from pd 0.40-2 + * added gensym2 (support for NUL in symbols) + * most of the code now uses C++,PD_PLUSPLUS_FACE,class_new2,etc + * auto show/hide scrollbars + * menu bar can be disabled + * new Find widget (FireFox style) + * added "subpatcherize" (turn a selection into a subpatch) + * IEMGUI can now by controled with keyboard + * more general keyboard control + * merged t_alist and t_binbuf together and aliased them to t_list + * delay uploading until #X restore or #X pop + * don't upload all abstractions instances to client (much faster) + * introduced zombie objects to deal with dead objects + * Command evaluator per canvas window + * Added locale for Euskara (Basque) by Ibon Rodriguez Garcia + * PureUnity is now part of the DesireData project (but is designed to + run also on Miller's 0.40). + * added -port option in desire.tk so that server and client may + be started separately. + * PureUnity has type suffixes for some class families; for each $1 in + f,~,# (float,signal,grid) there is [inlet.$1] [outlet.$1] [taa.$1] + [op2.$1] [rand.$1] [norm.$1] [swap.$1] [packunpack3.$1] + * Other new PureUnity classes: [^] [commutator] [associator] + [invertor] [distributor] [tree] [protocols-tree] + +DesireData 0.40.pre5 (2006.12.19) (-r desiredata; ./configure && make) : + + * merged changes from Miller's pd 0.40-2 (80% of it) + * new canvas method "reply_with" of canvases replaces the implicit + reply-matching of pre4. (even less bug-prone) + * server-side wires and scalars appear to client just like other objects. + * floatatom,symbolatom,[nbx] use normal Tk text edition just like + ObjectBox,MessageBox,Comment have done for a while + * obsolete t_object fields removed: te_type te_width + * global object table (crash protection for bindless .x targets) + * variable width font usable in ObjectBoxes and MessageBoxes and Comments. + * [hsl] [vsl] support jump-on-click again + * lots of bugfixes + * -console and -lang moved to Client Preferences dialog + * added some more translations by Patrice Colet + * removed Find menu in main window + * added Find, Find Next, Find Last Error (canvas windows only) + * choose between horizontal and vertical in Properties of slider or radio. + +DesireData 0.39.A.pre4 (2006.12.07) (-r desiredata; ./configure && make) : + + * major speedup of the GUI (sometimes 3-4 times faster) + * lots of bugfixes + * logging of the socket into the terminal is now disabled by default + * introducing PD_PLUSPLUS_FACE, a new way to use <m_pd.h> and <desire.h> + * new branch "desiredata" instead of "devel_0_39". + * got rid of #ifdef DESIRE + * reply-matching in client-server protocol (less bug-prone) + * reversing the connection to what it was supposed to be: + the client connects to the server, not the other way around. + * the server uses [netreceive] to receive the connection from the GUI + * removed support for .pdsettings, .plist, microsoft registry. + * cross-platform libpd + * new titlebar icon + * removed t_guiconnect + * removed [scope] + +DesireData 0.39.A.pre3 (2006.11.27) (-r devel_0_39; ./configure && make) : + * franais updated by Patrice Colet + * italiano updated by Federico Ferri + * tons of bugfixes + * better pdrc editor (renamed to server prefs) + * removed media menu (split to: help menu, file menu, server prefs) + * removed Gdb box, added crash report dialog + * renamed objective.tcl to poe.tcl (because the name was already taken) + * replaced scons by autoconf and make (starting from Miller's 0.39's files) + * removed detection of Tcl (we don't need to use libtcl) + * removed the setuid option because no-one needs it; also fixed the + setuid security vulnerability in case someone does chmod u+s anyway + * Portaudio 18 is no longer supported. + * simplified configure.in (detector and makefile generator) + * APIs not compiled in show up in "pd -help", with a special mention + "(support not compiled in)"; those options don't give you a "unknown + option" when trying them, it says "option -foo not compiled in this pd". + * switched desire.c to C++, as another way to reduce redundancy in code. + * can be compiled without audio support. + * can be compiled without MIDI support. + * can --disable-portaudio on OSX + * added multiple wire connection support + * fixed copy/paste on canvas + * keyboard navigation pointer makeover + * added automatic object insertion support + +DesireData 0.39.A.pre2 (2006.11.12) (-r devel_0_39; scons desire=1) : + * espaol updated by Mario Mora + * subpatches + * GOPs + * abstraction instances + * multi-line objectboxes, messageboxes and comments + * keyboard-based navigation + * made desire.c C++ compatible (for future use) + * lots of things not written here + +DesireData 0.39.A.pre1 (-r devel_0_39; scons desire=1) : + * merged into the devel branch; enable with scons desire=1, which + disables lots of g_*.c files (and s_print.c) and enables desire.c; + use the std devel gui using desire=0. + * added an object-oriented programming system in desire.tk (do not + confuse with a dataflow system). added proc unknown, which allows + subject-verb-complement method-calling in tcl (aka objective.tcl) + * run the client to start the server and not the other way around: do wish desire.tk + * the client can make the server start via GDB + * added Pd box (like Ctrl+M but with history) + * added Gdb box + * menu translations in 8 languages + * classbrowser now show short descriptions in 3 languages + * objectbox tooltip now replaced by mini-classbrowser + * client conf editor + * other stuff I forget to write about + * looks for .ddrc + * pdrc and ddrc config becomes server and client configuration editor + * graphics rendering completely removed from the server + * toolbar and status bar can be enabled/disabled + * added Patcher->View->Reload: client reloads the patch from the server + * localization support (currently 8 languages: english, franais, deutsch, + catal, espaol, portugus, bokml, italiano.) + * lots of things not written here + +ImpureData 0.37.B : + * moving rendering to the TCL side + * moving event-handling to the TCL side too + * new file u_object.tk + * added pd_scanargs(), pd_upload(), sys_mgui(). + * added color schemes (modifiable in u_main.tk) + * switched to a jmaxish look + * merged g_vdial.c into g_hdial.c + * merged g_vslider.c into g_hslider.c + * added Patcher->View->Redraw + * added proc populate_menu, proc property_dialog + * added ~/.pd.tk loading + * inlet tooltips have new look + * rewrote all of the property dialogs + * added object class name completion (the <Tab> key) + * mouse scrollwheel works in patchers + * plus/minus button on tcl listener + * changed default font and borderwidth + * if conf not found in ~ ($HOME), + looks in Pd's install directory (eg. /usr/local/lib/pd) + * looks for .impdrc before .pdrc + * pdrc editor + * -help lists unavailable options with note "not compiled in" + * sys_vgui() message size limit removed + * new peak meters (thanks Carmen) + * dropper object outputs symbols of filenames (requires tkdnd) + * joe sarlo's VST-plugin i/o scheduler available on windows + * error() merged into pd_error() + and using strerror() to get meaningful error messages for failed I/O + * completely breaking compatibility with Pd's GUI externals + (for a good reason) + +ImpureData 0.37.A.2 (-r impd_0_37_A_2) : + * merged GG's reverting of "quote hack" + +ImpureData 0.37.A (-r impd_0_37_A) : + * forked from devel_0_37, 2004.02.21 + * added console for post() + * .pdrc: -console <number_of_lines> + * added button bar (that does like Ctrl+E & Put menu) + * .pdrc: -look <directory_of_icons> + (remember you can't use ~ nor $HOME in .pdrc) + * includes a selectable windowid (for those who know how to use it) + * class list dialog + * scans for loaded classes, abstractions/externs dirs + * help button fetches help file without needing to instantiate first + * filter box helps finding classes quickly + * displays some info on the class, like a list of defined methods and such. + * statusbar shows cursor position (enable with -statusbar) diff --git a/desiredata/src/TODO b/desiredata/src/TODO new file mode 100644 index 00000000..0e866840 --- /dev/null +++ b/desiredata/src/TODO @@ -0,0 +1,454 @@ +DesireData's TODO list, $Id: TODO,v 1.1.2.28.2.63 2007-09-09 17:45:45 matju Exp $ + +LEGEND: + [c] client + [s] server + [b] both + [x] done + +------------------8<--------cut-here--------8<------------------ + +[s] encapsulate wires list +[s] observable boxes list +[s] observable wires list +[s] gop filter +[s] triad + +------------------8<--------cut-here--------8<------------------ + +[s] class_setreinit(t_pd *, t_symbol *s, int argc, t_atom *argv) +[s] fix canvas_merge, canvas_remove_nth, canvas_sort +[s] recreate abstr instances after abstr save +[s] [bng] messages get duplicated upon entering a subpatch??? +[s] rewrite [any] +[s] counter-test.pd shows [nbx] jamming the update queue if too many updates at once...? +[s] queue priorities cause double call to changed() to change the expected order of updates (dangling pointer in client) +[c] Ctrl+e doesn't change Edit menu entry; likewise for visual_diff +[c] Save Changes? Save Changes? Save Changes? Save Changes? Save Changes? Save Changes? Save Changes? Save Changes? Save Changes? +[s] canvas_resortinlets confuses wire upload +[s] should do canvas_resortinlets after moving objects +[c] split View into several classes +[s] extraneous space at end of binbuf_gettext +[s] make t_hash thread-safe (use external iterator) +[s] take advantage of the support for zero-size arrays in gcc. +[s] writesf_free has deadlock. (assigned to Sylvain) +[b] look into race conditions and locking +[c] figure out what to do about the existence of [$self look] vs ordinary attributes (pointer_sense= and such) +[s] [error] +[b] localise error messages +[b] colorised console with hyperlinked error messages +[c] atomic undo +[c] undo subpatch +[c] numbox: is_log +[c] change the order of the fields in Properties if it makes things more logical than the order of fields of savefn +[c] [hradio] : chg -> is_log +[c] [vu] props : scale should appear instead of is_log +[s] figure out how to keep [pd] subscribed even when closed. +[s] new parser for nested lists and extended symbols +[s] update s_audio.c to support any number of devices (not just max 4) +[s] look for new bugs involving %*s added around 2007.06.28 +[c] new way to do View get_canvas +[b] too much duplication of inlets vs outlets +[s] too much duplication of adc vs dac (see s_audio.c) +[c] implement multiple cascaded languages (use listbox+up+down) +[c] def Menuable raise {} {wm withdraw $w; wm deiconify $w +[s] what to do with post() in case of -listdev, etc.? +[s] turn old [dropper] code into a feature of SymbolBox +[s] inheritance: [super] "instantiates" other abstraction with same $0; [self] allows sending messages to self; [declare super ...] makes the tree. + +[b] GOP problems are back due to recent changes in canvas_map and canvas_vis. + now that abstractions don't get loaded into the client anymore, GOP can't always be drawn anymore too. + i mean, because the content of GOP is not uploaded to the client, so the client can't draw anything about it.. + unless def View outside_of_the_box are moved to the server side and the server only uploads what needs to be drawn + for gop + + another side effect of not uploading the content of [pd]/abs that i just found out is that, when deleting such object, + 1. the client won't be able to perform things like $wire deconstruct in def Canvas del, + as the object don't exists in the client side + 2. even if client don't call things like that, the server would still send -> delete message to the client, + which causes the same error because the object don't exists... + +[s] it might make sense to always upload subpatches but upload instances only when needed +[s] server don't send delete message back after client sending "object_delete" (http://pastebin.ca/318343) +[s] serial got sent too early when creating [pd] with push & #N canvas (http://pastebin.ca/318318) +[s] GOP problems are back due to recent changes in canvas_map and canvas_vis +[s] [route] should be reconfigurable and accept pointer +[s] [select] should accept pointer +[s] [moses] should be multi-arg (and be aliased to [range] ?) +[s] get rid of stderr in server +[s] prevent hidden subpatches/abstraction-instances from being loaded in the client all of the time. +[s] -lib in .pdrc crashes server: http://pastebin.ca/286300 +[s] server don't pick up changes via NumBox reload, ie the width: + <- .x8068058 reload 2 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 256; + -> change x8068058 x8067c50 {#X obj 335 166 nbx 8 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 256;} +[b] fix gop + [c] reimplement View get_canvas so that it does not rely on [focus] + [s] gop contains can be drawn if not uploaded to client +[ ] fix deleteing/closing Canvas + [ ] server sends -> x806a3b8 delete twice + [ ] fix deletion order +[ ] graphical array rendering optimization +[ ] fix double delete of [pd]. +[s] added "version 2" syntax parser (optional), which has nested lists, + extended quoting, extended float syntax + (some ex-symbols now parsed as floats: +1 +1.0 +1e8 0x10 and so on.) +[ ] note that Marius Schebella is interested in benchmarks +[ ] atomically with multiple level +[ ] tidy_up/snap to grid +[ ] cleaner parsing of [expr]... remove int type because it causes e.g. [expr 8.0 / 10.0] = 0 but [expr 8.0/10.0] = 0.8 +[ ] how does [declare -stdpath] work? this is a mystery. can't get it to work in pd 0.40. + +[ ] PureUnity + [ ] benchmark + [ ] signals + [ ] grids + [ ] transitive, antisymmetric, predicate + [ ] contracts : *-rule.pd + [ ] tests for the frameworks' own components? + * not finished: + [glue-test] + [comparators-test] [arith-test] + [operator1-rule] [operator1-test] + [operator2-rule] [operator2-test] + +[s] those are the externs that have to be recompiled if I want to trap calls to return values of getfn. + ./externals/olafmatt/clone/clone.c:544: mess1(&cv->gl_pd, gensym("dsp"), sp); + ./externals/grill/dynext/src/main.cpp:952: mess1((t_pd *)canvas,const_cast<t_symbol *>(sym_dsp),NULL); + +<shift8> matju: another big bug for me - v/h sliders in existing patches bounce + repeatedly back to bottom when some kind of event occurs. you have to move the + mouse very gently back and forth until the slider stays at the location you want, + then without moving the mouse, release the mouse button :( + +[s] allow helpfiles to load without having to "make install". +[s] switch iemguis to using only t_atoms +[s] MIDI loopback pseudo-device (inside pd) +[s] audio loopback pseudo-device (inside pd) +[s] use vector doubling in binbuf_add,binbuf_addv... +[c] opening an already-open subpatch should just raise that subpatch +[c] bug: you can move parts of gop subpatches, from the parent patch! +[c] pixel offsets are not correct in [hsl] [vsl]: + distinguish between $@w or $@h and actual size. there should be 4 extra pixels. +[c] hide inlets/outlets of IEMGUI when they have receive-symbols/send-symbols +[c] rightclick help doesn't work on [cnv]: tries to find help for "cnv.pd" ??? +[s] creating a graph causes crash. +[b] fix NumBox's width and height (or remove those settings) +[x] Implement (or fix) Find, Find Again in class Client + [x] with ability to search substrings + [ ] with ability to search across canvases + [ ] all canvases + [x] all subcanvases + [x] only the current one + [ ] with regexps + [ ] with replace +[ ] in hsl-help.pd, [hsl] shouldn't appear as a Radio. +[ ] canvas dialog: + [ ] add option for old-style GOP + [ ] make hidetext work + [ ] add units/pixel +[c] array button doesn't work (menuarray) +[c] implement timeout in def Manager call +[s] finish merging 0.40 (wasn't it finished?) +[s] merge 0.41 (no?) +[s] proper symbol quoting +[s] refcounted symbols +[c] patching-in-tongues follow-up +[c] def def +[c] take care of needless "save changes?" +[c] make List objects, to manage children, visible_children, wires, selection, selection_wires. +[c] skip unneeded motion events inside client (do we need this?) +[b] rename pd to pd-server, desire.tk to pd-client, add new program "pd" which would launch both (will we really do this?) +[b] #V test with bg color +[c] kill global tooltip variable ? +[c] iemprops: min,max labels broken +[b] fix the [key] and [keyup] and [mouse] (?) classes +[s] fix all issues with backslashes and braces. +[-] write an installer in Tcl/Tk. +[s] canvas_object_insert() +[c] simplify def Dialog add +[c] devlist isn't supposed to be like a choice, rather like several choices. +[c] implement def Canvas tidy +[c] set tk::mac::useCGDrawing 1 +[s] differential upload +[c] differential redraw +[b] improved dirty_lists including proper array support +[b] bang counter (instead of sending every bang message to client) +[b] implement garray +[c] implement a mark-and-sweep in order to find leaks... (?) +[b] rewrite [image], [display], [knob], [grid], [popup], ... +[ ] this seems fishy but might have good ideas -> http://www.rebelscience.org/Cosas/Reliability.htm +[c] make a statistical profiler for Tcl, if possible +[c] try to use profiler.tcl (tcllib) in a sensible way +[s] try C-oriented tools: + [ ] oprofile (with GUI) + [ ] memprof + [ ] sysprof + [ ] kprof + [ ] gprof + [ ] prospect + +[s] iirc, bang~ registers a timer callback. the problem is that the timer callbacks are only executed every dac block, + which is 64 samples. so running bang~ in subpatch with less than 64 samples, bang~ sets the same timer several times, + but it's only executed once. + +[b] think about this if you are reconsidering properties panels, I strongly encourage all y'all to make them Pd patches. + This is how Max/MSP does it and I think it would work very well for Pd as well. Sounds like this is a good opportunity + to make the switch. -- hcs + +<chun> 1. a keyboard operated cursor, a bit like Active, but one can use it like the mouse pointer +<chun> 2. snap to grid feature, or similiar +<chun> 3. prefix keybindings, like M-x in emacs + +Iohannes said about redirecting stdout/stderr: + pd -verbose -stderr 2>&1 | while read line; do echo "${line};" | pdsend 6666 localhost udp; done + +[b] [struct], scalar, DS-array, [plot], [drawpolygon], [drawnumber], ... +[c] desire.tk can be *really* loaded without Tk +[c] ReadLine or NCurses interface +[c] make commandline options reloadable at runtime +[x] canvas scrollbars auto-disappear when not needed. +[b] implement dirty flag +[s] use gensym2() in builtin classes and stuff. +[s] fix 64-bit arrays so that carmen gets a use for DD. +[s] carmen also needs strings (no symbol leakage) +[c] right-click on labels for translations. +[b] http://www.w3.org/DesignIssues/Diff +[c] solve printing problems with GDB. use a pty (pseudo-teletype) ---> http://wiki.tcl.tk/3916 +[c] try TkZinc +[c] try Tcl/Gtk (Gnocl) with emulation layer +[?] http://www.comparisonics.com/gallery.html +[c] obiwannabe writes: + it would be good to have the choice as a comandline arg of the first one launched with a way + to accept patches to open in the same instance from, say a web browser. Like also when you are + in a file manager and browsing some .pd files you really want them to open in the same running instance. +[s] must work with ALL gridflow samples +[b] objectbox argument completion +[b] messagebox completion +[c] tooltips on arguments/inlets/outlets +[c] option to make non-gui objects appear on a GOP (?) +[b] multilingual labels in objects +[b] multilingual comments +[b] what would be needed to be able to use gdb --pid=... ? +[ ] set tk::mac::CGAntialiasLimit 2 +[ ] try Doxygen's callgraphs +[ ] try splint (http://www.splint.org/) +[ ] try uno +[ ] try CCCC +[ ] try OSX Shark +[ ] try http://www.drugphish.ch/~jonny/cca.html +[-] do we move the trac to artengine or not? +[c] remember that it's possible to use break in a bind-handler, to completely override system's behaviour. +[c] try: itcl itk iwidgets (itk implements megawidgets) +[c] try tkgate, a hardware sim program +[ ] try libentity +[c] try vtk-tcl +[s] make sure $0 actually works (see canvas_realizedollar) +[c] test rcv_able, snd_able +[?] iem: snd/rcv problem(s) ? (what was that?) +[c] [vu] have fcol in props ! +[c] [vu] has snd in props ! +[b] Duplicate wires? +[c] Can connect object to an object that is inside a GOP (!!!!) +[c] weird offset stuff when there are negative canvas coords sometimes. +[s] Bug: bad quoting in sys_mgui() +[b] Bug: spaces in name of vslider cause corruption of properties (devel_0_37) +[b] classlist: add method signatures +[c] bang flash delays should be reimplemented +[c] pdrc_options radio don't load/save +[c] patch window may open off-screen (all branches) +[c] patch window may open too big (all branches, osx) +[b] properties on objectboxes (generic dialogs tapping into method signatures) (?) + [s] hooks for outsourcing the preceding stuff to a plugin (eg: GridFlow, PyExt) +[b] VT100 colours in console +[b] [graph] is too slow (gui) for real big arrays +[b] freeform comments (no atom parsing) +[b] preserve whitespace in textboxes? +[b] inlet inspector to show what are the message types expected by an inlet + that could read like "int: set left operand; bang: do it" +[c] custom buttonbars (including premade objects with args like a [t b f] and such) + [c] with configurable hotkeys +[b] colored wires +[b] insert_object makes error with multiple selection. +[c] popup_properties on multiple selection. +[b] segmented patchcords: + [c] a hotkey to click on the cord, and add a new segment + [c] a hotkey to drag the "points" (where two lines meet) + [c] a modifier key to delete a segment (actually the others should be that way too) + [c] you should be able to right click on a regular wire, and press "segment" or do a hotkey with it + and it automatically turns it into an straight-elbow multisegmented wire +[b] [t a] could be a very small GUI object (called "null object") +[b] all the [t] could be GUI objects +[b] GUI objects for [inlet] and [outlet] and [pd] ([page]) +[-] make a sort of pd-extended, call it DesireData Express or (gasp) Extraordinaire. + +[s] symbol vs strings: Ruby is right: the Symbol vs String distinction is annoying and possibly obsolete. + according to me, symbols exist mostly because LISP had them before they had strings, and because most + Strings implementations aren't powerful enough to be as fast (or almost as fast) as Symbols. + (well, for compatibility reasons, just like in Ruby, we can't remove symbol support completely, but + at least we can reduce the difference between strings and symbols to a minimum.) + +[b] server-side IEMGUI could be turned into Tcl-based externs OR EVEN become abstractions. + it's possible to make a DesireData GUI for any Pd class, including abstractions. + to turn IEMGUI into an abstraction, what's missing is the savefn/saveargs/scanargs business. + +[s] I would like to know how much it is feasible to compress the t_atom + structure so that even with 64-bit pointers the t_atom still stays 8 bytes + instead of 16. I think it's possible, but not necessarily in a + backwards-compatible way, and not necessarily in a portable way. also maybe it's not that useful. + +[c] splashscreen: we could make it different than other programs by inserting the splashscreen + inside the main window or we could make it a separate window but no timer, just an [OK] button, + so actually, this would be exactly the same as the "About" dialog. + +[s] turn [makefilename] into something that doesn't suck. (alias it to [sprintf] or [format]) + +[s] merge martin peach's tcp externs into the core +[b] data inspector: when this tool is enabled, it prints on the console any data coming through whatever cable you + currently have selected. if you select multiple wires, it reports whats going through multiple wires. +[b] you need a way to see cpu usage on individual objects or on patchers or on groups of selected objects +[c] objectbox history: see whether ddrc should have a history count entry; + think about saving history; matju thought that it could be turned into a dynamic button bar that you can drag from. +[?] send to front, send to back +[c] make windows not get auto-resized to the width of the toolbar, so that people can have tiny windows. +[c] <Dossy> fconfigure -encoding binary ... +[s] implement the stuff that is in iostreams.txt + +[c] Luke Iannini suggests some OSX bindings: + Command-` to switch between different windows within the application. + Command-, to bring up preferences (though this one is more difficult since there are multiple preference windows...) + Command-m to minimize the window (this currently brings up the "send message" dialogue box) + +[s] Claude: + Sending a message to vline~ creates a t_vseg, which are stored in a + sorted linear linked list, which means the time taken to add each new + line segment would be O(n), where n is the number of existing line segments. + +[-] look at some object sync protocols that we can think of: NFS for folders, palm sync for calendars, rsync for file contents + +Marius Schebella: +I have a small keyboard shortcut wish: change between "entering mode" and "selected mode" with boxes. +when I create a new object/message... then it would be nice to have a +shortcut that switches from the mode, where the cursor is in the box to the +mode, where the object is selected. I think the tab-key could be used for +that. As it is now, I type something in, then I have to grab the mouse, then +klick, then select, then I can adjust it (which I also do with the keyboard, +because it is more precise). +I know the toggeling will not be possible when more objects are selected, +but maybe someone has an idea for that. + +[c] command for unpatcherizing a subpatch or abstraction (useful for making variants) + or for turning an abstraction into a subpatch. + +[s] I fixed it now, but I don't know if this is not a bug in pd 0.40: + +"The problem is, that canvas-local search path really tread each path as local to the canvas-path ( see line 1561 in g_canvas.c). +So if you add e.g. /usr/local/lib/pd/extra/iemmatrix, it will search for this path, but local to the canvas path - so if I started +Pd from /home/me it will search in /home/me//usr/local/lib/pd/extra/iemmatrix ! Is this a feature or a bug of Pd ?" -- Holzi + +[s] "I don't quite understand how this explains why wrap~ of -1 returns 1." -- steffen + +[s] there's a [wrap~] but no [wrap]. there's a [>] but no [>~] (without externals). -- matju + +dmotd about converting patches to postscript: +"the internal pd postscript printer grabs the viewable canvas size, +this would need to change to encompass the virtual limits of the patch. +tcl/tk's 'canvas postscript' command takes the -width -height flags, +so making it the virtual bounds is trivial. this works for snapshotting +canvas bounds: sys_vgui("set cnv_bbox [.x%x.c bbox all] \n .x%x.c +postscript -file /tmp/canvas.ps -width [lindex $cnv_bbox 2] -height +[lindex $cnv_bbox 3] \n ", canvas, canvas); + +------------------8<--------cut-here--------8<------------------ + +Patching-in-tongues Project + +[ ] make entries counter and matcher. + + entries +en: english [ ] +es: espaol [ ] Mario Mora & Ramiro Cosentino +de: deutsch [ ] M Neupert, G Holzmann, T Grill +nb: bokml [ ] Gisle Frysland +it: italiano [ ] Davide Morelli + Federico Ferri +pt: portugus [ ] Nuno Godinho +fr: franais [ ] Patrice Colet +ca: catal [ ] Nria Verges +pl: polski [ ] Michal Seta +eu: euskara [ ] Ibon Rodriguez Garcia (Enrike Hurtado) +cn: chinese [ ] Chun Lee +jp: nihongo [ ] Kentaro Fukuchi +tu: trke [ ] ... Koray Tahiroglu +sv: svenska [ ] ... Daniel Skoglund (NOT FOUND) +br: brasiliano [ ] ... Gabriel Menotti +dk: dansk [ ] ... Steffen Leve Poulsen + +------------------8<--------cut-here--------8<------------------ +Dec 18 2006 + +1. there's no way to limit the size of the output buffer. If the other +side of the connection doesn't respond, the sending buffer just +inflates quickly. I've seen it happen that a bug in the sender causes +it to try to send so fast that it ate memory like an infinite recursion +or a forkbomb. + +2. That operation is not realtime-safe (but still it's much closer to +being so than just blocking...) + +3. It's only usable by the GUI socket and never by [netsend]. + +4. While that buffer together with t_guiqueue allow GUI updates to be +delayed for as long as necessary, it doesn't solve the problem that it can +send information that is already outdated and redundant. This can be +important in preventing problem #1 for a very heavy GUI. DesireData has +had this problem essentially dealt with since a long time, but it lacks +some fine tuning to get more robust. + +------------------8<--------cut-here--------8<------------------ + +<arjen> matju, you may be interested in "Jim" - that does have closures and on the Wiki you can find several attempts and + experiments regarding closures: http://wiki.tcl.tk/closures + +<matju> is there something like listbox but which works as a popup menu ? +<tclguy> tk_optionMenu is an old-school version +<tclguy> or get a combobox from BWidget or tile + +1. wish8.5 desire.tk city.pd &> err +2. ctrl+e, ctrl+a, shift+right*3 (or just once if profiling) +3. times (tclx) + +<matju> is there a wrapper for libagg for tcl? AGG of antigrain.com +<ijchain> <kbk> don't know of one, but SWIG, Critcl, or ffidl might plug the gap + +[initbang] & [closebang]: https://sourceforge.net/tracker/?func=detail&atid=478072&aid=1544041&group_id=55736 + +[s] Ed Kelly <morph_2016@yahoo.co.uk> reports that ALSA 24-bit output does not work? + +to dave griffiths: I think that this is three feature requests: +1. performing replacement of wire-object-wire by a single wire, as a +single operation in the GUI +2. make that operation atomic in terms of DSP recompilation. +3. break down DSP recompilation in pieces so that it is more "incremental" +for large patches. +In the case of object insertion, (2) and (3) are not implemented either, +but step (1) already has most of the desired effect; for the message +system, step (1) is all that is needed. DSP is more work and I know less +about DSP. It'll take me a while to get there; but (2) doesn't seem so +hard. + +http://www.tomsawyer.com/gallery/index.php + +[b] matju to atwood: + If I had automatic positioning in DesireData, it would be as an option: e.g. objects could default to "floating around" + (that is, automatic positioning), but be pinned down into specific positions. In that case, an exception has to be done + for [inlet] and [outlet] objects mostly. (the DS subsystem should be skipped over as well...) I could also have a use + for some semi-automatic repositionings: for example, there could be a keyboard shortcut to reposition a non-"floating" + object wherever it would go if it were floating, and if the user doesn't want it s/he can Ctrl+z it. + +http://www.graphdrawing.org/ + +<mamalala> you select a bunch of objects and group them together .... then you can select all the objects in that group +later, if needed, by its name like, you start to build some mixer patch .. and then you add a chain for an external fx, +so you can put all associated objects in the "ext.fx" group ... whenever it gets so messed up that you forgot what is +what, you can just select the groupt you want, and see/move/whatever the involved objects the group name could also act +as a template for the name of a subpatch, if one decides to finally put them into one .... + diff --git a/desiredata/src/bgerror.tcl b/desiredata/src/bgerror.tcl new file mode 100644 index 00000000..bff0dd2c --- /dev/null +++ b/desiredata/src/bgerror.tcl @@ -0,0 +1,254 @@ +# bgerror.tcl -- +# +# Implementation of the bgerror procedure. It posts a dialog box with +# the error message and gives the user a chance to see a more detailed +# stack trace, and possible do something more interesting with that +# trace (like save it to a log). This is adapted from work done by +# Donal K. Fellows. +# +# Copyright (c) 1998-2000 by Ajuba Solutions. +# All rights reserved. +# +# RCS: @(#) $Id: bgerror.tcl,v 1.1.2.2.2.2 2007-08-12 02:38:45 matju Exp $ +# $Id: bgerror.tcl,v 1.1.2.2.2.2 2007-08-12 02:38:45 matju Exp $ + +package provide bgerror 8.4 + +namespace eval ::tk::dialog::error { + namespace import -force ::tk::msgcat::* + namespace export bgerror + option add *ErrorDialog.function.text [mc "Save To Log"] \ + widgetDefault + option add *ErrorDialog.function.command [namespace code SaveToLog] +} + +proc ::tk::dialog::error::Return {} { + variable button + .bgerrorDialog.ok configure -state active -relief sunken + update idletasks + after 100 + set button 0 +} + +proc ::tk::dialog::error::Details {} {if {[catch {Details2}]} {::error_dump}} +proc ::tk::dialog::error::Details2 {} { + set w .bgerrorDialog + set caption [option get $w.function text {}] + set command [option get $w.function command {}] + if { ($caption eq "") || ($command eq "") } { + grid forget $w.function + } + lappend command [.bgerrorDialog.top.info.text get 1.0 end-1c] + $w.function configure -text $caption -command $command + grid $w.top.info - -sticky nsew -padx 3m -pady 3m +} + +proc ::tk::dialog::error::SaveToLog {text} { + if { $::tcl_platform(platform) eq "windows" } { + set allFiles *.* + } else { + set allFiles * + } + set types [list \ + [list [mc "Log Files"] .log] \ + [list [mc "Text Files"] .txt] \ + [list [mc "All Files"] $allFiles]] + set filename [tk_getSaveFile -title [mc "Select Log File"] \ + -filetypes $types -defaultextension .log -parent .bgerrorDialog] + if {![string length $filename]} { + return + } + set f [open $filename w] + puts -nonewline $f $text + close $f +} + +proc ::tk::dialog::error::Destroy {w} { + if {$w eq ".bgerrorDialog"} { + variable button + set button -1 + } +} + +# ::tk::dialog::error::bgerror -- +# This is the default version of bgerror. +# It tries to execute tkerror, if that fails it posts a dialog box containing +# the error message and gives the user a chance to ask to see a stack +# trace. +# Arguments: +# err - The error message. + +proc ::tk::dialog::error::bgerror {err} {if {[catch { + global errorInfo tcl_platform + variable button + +# matju: use objective.tcl's +# set info $errorInfo + set info [::error_text] + + set ret [catch {::tkerror $err} msg]; + if {$ret != 1} {return -code $ret $msg} + + # Ok the application's tkerror either failed or was not found + # we use the default dialog then : + if {($tcl_platform(platform) eq "macintosh") + || ([tk windowingsystem] eq "aqua")} { + set ok [mc Ok] + set messageFont system + set textRelief flat + set textHilight 0 + } else { + set ok [mc OK] + set messageFont {Helvetica -14 bold} + set textRelief sunken + set textHilight 1 + } + + # Truncate the message if it is too wide (longer than 30 characacters) or + # too tall (more than 4 newlines). Truncation occurs at the first point at + # which one of those conditions is met. + set displayedErr "" + set lines 0 + foreach line [split $err \n] { + if {[string length $line]>30} { + append displayedErr "[string range $line 0 29]..." + break + } + if {$lines>4} { + append displayedErr "..." + break + } else { + append displayedErr "${line}\n" + } + incr lines + } + + set w .bgerrorDialog + set title [mc "Application Error"] + set text [mc {Error: %1$s} $err] + set buttons [list ok $ok dismiss [mc "Skip Messages"] \ + function [mc "Details >>"]] + + # 1. Create the top-level window and divide it into top + # and bottom parts. + + catch {destroy .bgerrorDialog} + toplevel .bgerrorDialog -class ErrorDialog + wm withdraw .bgerrorDialog + wm title .bgerrorDialog $title + wm iconname .bgerrorDialog ErrorDialog + wm protocol .bgerrorDialog WM_DELETE_WINDOW { } + + if {($tcl_platform(platform) eq "macintosh") + || ([tk windowingsystem] eq "aqua")} { + ::tk::unsupported::MacWindowStyle style .bgerrorDialog zoomDocProc + } + + frame .bgerrorDialog.bot + frame .bgerrorDialog.top + if {[tk windowingsystem] eq "x11"} { + .bgerrorDialog.bot configure -relief raised -bd 1 + .bgerrorDialog.top configure -relief raised -bd 1 + } + pack .bgerrorDialog.bot -side bottom -fill both + pack .bgerrorDialog.top -side top -fill both -expand 1 + + set W [frame $w.top.info] + text $W.text -bd 2 -yscrollcommand [list $W.scroll set] -setgrid true \ + -width 80 -height 10 -state normal -relief $textRelief -highlightthickness $textHilight -wrap char + + scrollbar $W.scroll -relief sunken -command [list $W.text yview] + pack $W.scroll -side right -fill y + pack $W.text -side left -expand yes -fill both +# $W.text insert 0.0 "$err\n$info" + $W.text insert 0.0 $info + $W.text mark set insert 0.0 + bind $W.text <ButtonPress-1> { focus %W } + $W.text configure -state disabled + + # 2. Fill the top part with bitmap and message. + # Max-width of message is the width of the screen... + set wrapwidth [winfo screenwidth .bgerrorDialog] + # ...minus the width of the icon, padding and a fudge factor for + # the window manager decorations and aesthetics. + set wrapwidth [expr {$wrapwidth-60-[winfo pixels .bgerrorDialog 9m]}] + label .bgerrorDialog.msg -justify left -text $text -font $messageFont \ + -wraplength $wrapwidth + if {($tcl_platform(platform) eq "macintosh") + || ([tk windowingsystem] eq "aqua")} { + # On the Macintosh, use the stop bitmap + label .bgerrorDialog.bitmap -bitmap stop + } else { + # On other platforms, make the error icon + canvas .bgerrorDialog.bitmap -width 32 -height 32 -highlightthickness 0 -bd 0 + .bgerrorDialog.bitmap create oval 0 0 31 31 -fill red -outline black + .bgerrorDialog.bitmap create line 9 9 23 23 -fill white -width 4 + .bgerrorDialog.bitmap create line 9 23 23 9 -fill white -width 4 + } + grid .bgerrorDialog.bitmap .bgerrorDialog.msg \ + -in .bgerrorDialog.top \ + -row 0 \ + -padx 3m \ + -pady 3m + grid configure .bgerrorDialog.msg -sticky nsw -padx {0 3m} + grid rowconfigure .bgerrorDialog.top 1 -weight 1 + grid columnconfigure .bgerrorDialog.top 1 -weight 1 + + # 3. Create a row of buttons at the bottom of the dialog. + set i 0 + foreach {name caption} $buttons { + button .bgerrorDialog.$name -text $caption -default normal -command [namespace code [list set button $i]] + grid .bgerrorDialog.$name -in .bgerrorDialog.bot -column $i -row 0 -sticky ew -padx 10 + grid columnconfigure .bgerrorDialog.bot $i -weight 1 + # We boost the size of some Mac buttons for l&f + if {($tcl_platform(platform) eq "macintosh") + || ([tk windowingsystem] eq "aqua")} { + if {($name eq "ok") || ($name eq "dismiss")} { + grid columnconfigure .bgerrorDialog.bot $i -minsize 79 + } + } + incr i + } + # The "OK" button is the default for this dialog. + .bgerrorDialog.ok configure -default active + + bind .bgerrorDialog <Return> [namespace code Return] + bind .bgerrorDialog <Destroy> [namespace code [list Destroy %W]] + .bgerrorDialog.function configure -command [namespace code Details] + + # 6. Update all the geometry information so we know how big it wants + # to be, then center the window in the display and deiconify it. + ::tk::PlaceWindow .bgerrorDialog + + # 7. Ensure that we are topmost. + raise .bgerrorDialog + if {$tcl_platform(platform) eq "windows"} { + # Place it topmost if we aren't at the top of the stacking + # order to ensure that it's seen + if {[lindex [wm stackorder .] end] ne ".bgerrorDialog"} { + wm attributes .bgerrorDialog -topmost 1 + } + } + + # 8. Set a grab and claim the focus too. + ::tk::SetFocusGrab .bgerrorDialog .bgerrorDialog.ok + + # 9. Wait for the user to respond, then restore the focus and + # return the index of the selected button. Restore the focus + # before deleting the window, since otherwise the window manager + # may take the focus away so we can't redirect it. Finally, + # restore any grab that was in effect. + vwait [namespace which -variable button] + set copy $button; # Save a copy... + ::tk::RestoreFocusGrab .bgerrorDialog .bgerrorDialog.ok destroy + if {$copy == 1} {return -code break} +}]} { + ::error_dump +}} + +namespace eval :: { + # Fool the indexer + proc bgerror err {} + rename bgerror {} + namespace import ::tk::dialog::error::bgerror +} diff --git a/desiredata/src/builtins.c b/desiredata/src/builtins.c new file mode 100644 index 00000000..891e1f60 --- /dev/null +++ b/desiredata/src/builtins.c @@ -0,0 +1,3060 @@ +/* Copyright (c) 2007 Mathieu Bouchard + 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_PLUSPLUS_FACE +#include "desire.h" +#include "s_stuff.h" +#include <stdlib.h> +#include <stdarg.h> +#include <math.h> +#include <stdio.h> +#include <string.h> +#include <sstream> +#ifdef UNISTD +#include <sys/types.h> +#include <sys/time.h> +#include <sys/times.h> +#include <sys/param.h> +#include <unistd.h> +#endif +#ifdef MSW +#include <wtypes.h> +#include <time.h> +#endif +#ifdef MSW +#include <winsock.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <stdio.h> +#define SOCKET_ERROR -1 +#endif +#ifdef MSW +#include <io.h> +#endif +#if defined (__APPLE__) || defined (__FreeBSD__) +#define HZ CLK_TCK +#endif +#ifndef HAVE_ALLOCA +#ifdef _WIN32 +#define HAVE_ALLOCA 0 /* LATER this should be set by configure script! */ +#else +#define HAVE_ALLOCA 1 /* LATER this should be set by configure script! */ +#endif +#endif + +#define a_float a_w.w_float +#define a_symbol a_w.w_symbol +#define a_pointer a_w.w_gpointer + +#define LIST_NGETBYTE 100 /* bigger that this we use alloc, not alloca */ + +/* #include <string.h> */ +#ifdef MSW +#include <malloc.h> +#else +#include <alloca.h> +#endif + +#define LOGTEN 2.302585092994 +#undef min +#undef max + +//conflict with min,max +//using namespace std; + +float mtof(float f) {return f>-1500 ? 8.17579891564 * exp(.0577622650 * min(f,1499.f)) : 0;} +float ftom(float f) {return f>0 ? 17.3123405046 * log(.12231220585 * f) : -1500;} +float powtodb(float f) {return f>0 ? max(100 + 10./LOGTEN * log(f),0.) : 0;} +float rmstodb(float f) {return f>0 ? max(100 + 20./LOGTEN * log(f),0.) : 0;} +float dbtopow(float f) {return f>0 ? exp((LOGTEN * 0.1 ) * (min(f,870.f)-100.)) : 0;} +float dbtorms(float f) {return f>0 ? exp((LOGTEN * 0.05) * (min(f,485.f)-100.)) : 0;} + +/* ------------- corresponding objects ----------------------- */ + +#define FUNC1(C,EXPR) \ +static t_class *C##_class; \ +static void *C##_new() { \ + t_object *x = (t_object *)pd_new(C##_class); \ + outlet_new(x, &s_float); return x;} \ +static void C##_float(t_object *x, t_float a) {outlet_float(x->outlet, EXPR);} +#define FUNC1DECL(C,SYM) \ + C##_class = class_new2(SYM,C##_new,0,sizeof(t_object),0,""); \ + class_addfloat(C##_class, (t_method)C##_float); \ + class_sethelpsymbol(C##_class,s); + +FUNC1(mtof, mtof(a)) +FUNC1(ftom, ftom(a)) +FUNC1(powtodb,powtodb(a)) +FUNC1(rmstodb,rmstodb(a)) +FUNC1(dbtorms,dbtorms(a)) +FUNC1(dbtopow,dbtopow(a)) + +/* -------------------------- openpanel ------------------------------ */ +static t_class *openpanel_class; +struct t_openpanel : t_object { + t_symbol *s; + t_symbol *path; +}; +static void *openpanel_new(t_symbol *s) { + t_openpanel *x = (t_openpanel *)pd_new(openpanel_class); + x->s = symprintf("d%lx",(t_int)x); + x->path = s; + pd_bind(x,x->s); + outlet_new(x,&s_symbol); + return x; +} +static void openpanel_bang(t_openpanel *x) { + sys_vgui("pdtk_openpanel {%s} {%s}\n", x->s->name, (x->path&&x->path->name)?x->path->name:"\"\""); +} +static void openpanel_symbol(t_openpanel *x, t_symbol *s) { + sys_vgui("pdtk_openpanel {%s} {%s}\n", x->s->name, (s && s->name) ? s->name : "\"\""); +} +static void openpanel_callback(t_openpanel *x, t_symbol *s) {outlet_symbol(x->outlet, s);} +static void openpanel_path(t_openpanel *x, t_symbol *s) {x->path=s;} +static void openpanel_free(t_openpanel *x) {pd_unbind(x, x->s);} +static void openpanel_setup() { + t_class *c = openpanel_class = class_new2("openpanel",openpanel_new,openpanel_free,sizeof(t_openpanel),0,"S"); + class_addbang(c, openpanel_bang); + class_addmethod2(c, openpanel_path, "path","s"); + class_addmethod2(c, openpanel_callback,"callback","s"); + class_addsymbol(c, openpanel_symbol); +} + +/* -------------------------- savepanel ------------------------------ */ +static t_class *savepanel_class; +struct t_savepanel : t_object { + t_symbol *s; + t_symbol *path; +}; +static void *savepanel_new(t_symbol*s) { + t_savepanel *x = (t_savepanel *)pd_new(savepanel_class); + x->s = symprintf("d%lx",(t_int)x); + x->path=s; + pd_bind(x, x->s); + outlet_new(x, &s_symbol); + return x; +} +static void savepanel_bang(t_savepanel *x) { + sys_vgui("pdtk_savepanel {%s} {%s}\n", x->s->name, (x->path&&x->path->name)?x->path->name:"\"\""); +} +static void savepanel_symbol(t_savepanel *x, t_symbol *s) { + sys_vgui("pdtk_savepanel {%s} {%s}\n", x->s->name, (s && s->name) ? s->name : "\"\""); +} +static void savepanel_callback(t_savepanel *x, t_symbol *s) {outlet_symbol(x->outlet, s);} +static void savepanel_free(t_savepanel *x) {pd_unbind(x, x->s);} +static void savepanel_setup() { + t_class *c = savepanel_class = class_new2("savepanel",savepanel_new,savepanel_free,sizeof(t_savepanel),0,"S"); + class_addbang(c, savepanel_bang); + class_addmethod2(c, openpanel_path, "path","s"); + class_addmethod2(c, savepanel_callback, "callback","s"); + class_addsymbol(c, savepanel_symbol); +} + +/* ---------------------- key and its relatives ------------------ */ +static t_symbol *key_sym, *keyup_sym, *keyname_sym; +static t_class *key_class, *keyup_class, *keyname_class; +struct t_key : t_object {}; +static void *key_new() { + t_key *x = (t_key *)pd_new(key_class); + outlet_new(x, &s_float); + pd_bind(x, key_sym); + return x; +} +static void key_float(t_key *x, t_floatarg f) {outlet_float(x->outlet, f);} + +struct t_keyup : t_object {}; +static void *keyup_new() { + t_keyup *x = (t_keyup *)pd_new(keyup_class); + outlet_new(x, &s_float); + pd_bind(x, keyup_sym); + return x; +} +static void keyup_float(t_keyup *x, t_floatarg f) {outlet_float(x->outlet, f);} + +struct t_keyname : t_object {}; +static void *keyname_new() { + t_keyname *x = (t_keyname *)pd_new(keyname_class); + outlet_new(x, &s_float); + outlet_new(x, &s_symbol); + pd_bind(x, keyname_sym); + return x; +} +static void keyname_list(t_keyname *x, t_symbol *s, int ac, t_atom *av) { + outlet_symbol(x->out(1), atom_getsymbolarg(1, ac, av)); + outlet_float( x->out(0), atom_getfloatarg( 0, ac, av)); +} +static void key_free( t_key *x) {pd_unbind(x, key_sym);} +static void keyup_free( t_keyup *x) {pd_unbind(x, keyup_sym);} +static void keyname_free(t_keyname *x) {pd_unbind(x, keyname_sym);} + +static void key_setup() { + key_class = class_new2("key", key_new, key_free, sizeof(t_key), CLASS_NOINLET,""); + keyup_class = class_new2("keyup", keyup_new, keyup_free, sizeof(t_keyup), CLASS_NOINLET,""); + keyname_class = class_new2("keyname",keyname_new,keyname_free,sizeof(t_keyname),CLASS_NOINLET,""); + class_addfloat(key_class, key_float); + class_addfloat(keyup_class, keyup_float); + class_addlist( keyname_class, keyname_list); + class_sethelpsymbol(keyup_class, gensym("key")); + class_sethelpsymbol(keyname_class, gensym("key")); + key_sym = gensym("#key"); + keyup_sym = gensym("#keyup"); + keyname_sym = gensym("#keyname"); +} + +static t_class *netsend_class; +struct t_netsend : t_object { + int fd; + int protocol; +}; +static void *netsend_new(t_floatarg udpflag) { + t_netsend *x = (t_netsend *)pd_new(netsend_class); + outlet_new(x, &s_float); + x->fd = -1; + x->protocol = (udpflag != 0 ? SOCK_DGRAM : SOCK_STREAM); + return x; +} +static void netsend_connect(t_netsend *x, t_symbol *hostname, t_floatarg fportno) { + struct sockaddr_in server; + int portno = (int)fportno; + if (x->fd >= 0) {error("netsend_connect: already connected"); return;} + /* create a socket */ + int sockfd = socket(AF_INET, x->protocol, 0); + if (sockfd < 0) {sys_sockerror("socket"); return;} + /* connect socket using hostname provided in command line */ + server.sin_family = AF_INET; + struct hostent *hp = gethostbyname(hostname->name); + if (!hp) {error("bad host?"); return;} +#if 0 + int intarg = 0; + if (setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &intarg, sizeof(intarg)) < 0) + error("setsockopt (SO_RCVBUF) failed"); +#endif + /* for stream (TCP) sockets, specify "nodelay" */ + if (x->protocol == SOCK_STREAM) { + int intarg = 1; + if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &intarg, sizeof(intarg)) < 0) + error("setsockopt (TCP_NODELAY) failed"); + } + memcpy((char *)&server.sin_addr, (char *)hp->h_addr, hp->h_length); + /* assign client port number */ + server.sin_port = htons((u_short)portno); + post("connecting to port %d", portno); + /* try to connect. LATER make a separate thread to do this because it might block */ + if (connect(sockfd, (struct sockaddr *) &server, sizeof (server)) < 0) { + sys_sockerror("connecting stream socket"); + sys_closesocket(sockfd); + return; + } + x->fd = sockfd; + outlet_float(x->outlet, 1); +} + +static void netsend_disconnect(t_netsend *x) { + if (x->fd < 0) return; + sys_closesocket(x->fd); + x->fd = -1; + outlet_float(x->outlet, 0); +} +static void netsend_send(t_netsend *x, t_symbol *s, int argc, t_atom *argv) { + if (x->fd < 0) {error("netsend: not connected"); return;} + t_binbuf *b = binbuf_new(); + t_atom at; + binbuf_add(b, argc, argv); + SETSEMI(&at); + binbuf_add(b, 1, &at); + char *buf; + int length, sent=0; + binbuf_gettext(b, &buf, &length); + char *bp = buf; + while (sent < length) { + static double lastwarntime; + static double pleasewarn; + double timebefore = sys_getrealtime(); + int res = send(x->fd, buf, length-sent, 0); + double timeafter = sys_getrealtime(); + int late = (timeafter - timebefore > 0.005); + if (late || pleasewarn) { + if (timeafter > lastwarntime + 2) { + post("netsend blocked %d msec", (int)(1000 * ((timeafter - timebefore) + pleasewarn))); + pleasewarn = 0; + lastwarntime = timeafter; + } else if (late) pleasewarn += timeafter - timebefore; + } + if (res <= 0) { + sys_sockerror("netsend"); + netsend_disconnect(x); + break; + } else { + sent += res; + bp += res; + } + } + free(buf); + binbuf_free(b); +} +static void netsend_free(t_netsend *x) {netsend_disconnect(x);} +static void netsend_setup() { + netsend_class = class_new2("netsend",netsend_new,netsend_free,sizeof(t_netsend),0,"F"); + class_addmethod2(netsend_class, netsend_connect, "connect","sf"); + class_addmethod2(netsend_class, netsend_disconnect, "disconnect",""); + class_addmethod2(netsend_class, netsend_send, "send","*"); +} + +static t_class *netreceive_class; +struct t_netreceive : t_object { + t_outlet *msgout; + t_outlet *connectout; + int connectsocket; + int nconnections; + int udp; +/* only used for sending (bidirectional socket to desire.tk) */ + t_socketreceiver *sr; +}; +static void netreceive_notify(t_netreceive *x) { + outlet_float(x->connectout, --x->nconnections); +} +static void netreceive_doit(t_netreceive *x, t_binbuf *b) { + int natom = binbuf_getnatom(b); + t_atom *at = binbuf_getvec(b); + for (int msg = 0; msg < natom;) { + int emsg; + for (emsg = msg; emsg < natom && at[emsg].a_type != A_COMMA && at[emsg].a_type != A_SEMI; emsg++) {} + if (emsg > msg) { + for (int i = msg; i < emsg; i++) if (at[i].a_type == A_DOLLAR || at[i].a_type == A_DOLLSYM) { + error("netreceive: got dollar sign in message"); + goto nodice; + } + if (at[msg].a_type == A_FLOAT) { + if (emsg > msg + 1) outlet_list(x->msgout, 0, emsg-msg, at + msg); + else outlet_float(x->msgout, at[msg].a_float); + } else if (at[msg].a_type == A_SYMBOL) + outlet_anything(x->msgout, at[msg].a_symbol, emsg-msg-1, at + msg + 1); + } + nodice: + msg = emsg + 1; + } +} +static void netreceive_connectpoll(t_netreceive *x) { + int fd = accept(x->connectsocket, 0, 0); + if (fd < 0) post("netreceive: accept failed"); + else { + t_socketreceiver *y = socketreceiver_new((t_pd *)x, fd, + (t_socketnotifier)netreceive_notify, x->msgout?(t_socketreceivefn)netreceive_doit:0, 0); + sys_addpollfn(fd, (t_fdpollfn)socketreceiver_read, y); + outlet_float(x->connectout, ++x->nconnections); + y->next = x->sr; + x->sr = y; + } +} +extern "C" t_text *netreceive_new(t_symbol *compatflag, t_floatarg fportno, t_floatarg udpflag) { + struct sockaddr_in server; + int udp = !!udpflag; + int old = !strcmp(compatflag->name , "old"); + int intarg; + /* create a socket */ + int sockfd = socket(AF_INET, (udp ? SOCK_DGRAM : SOCK_STREAM), 0); + if (sockfd < 0) {sys_sockerror("socket"); return 0;} + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; +#if 1 + /* ask OS to allow another Pd to reopen this port after we close it. */ + intarg = 1; + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &intarg, sizeof(intarg)) < 0) + post("setsockopt (SO_REUSEADDR) failed"); +#endif +#if 0 + intarg = 0; + if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &intarg, sizeof(intarg)) < 0) + post("setsockopt (SO_RCVBUF) failed"); +#endif + /* Stream (TCP) sockets are set NODELAY */ + if (!udp) { + intarg = 1; + if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &intarg, sizeof(intarg)) < 0) + post("setsockopt (TCP_NODELAY) failed"); + } + /* assign server port number */ + server.sin_port = htons((u_short)fportno); + /* name the socket */ + if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) { + sys_sockerror("bind"); + sys_closesocket(sockfd); + return 0; + } + t_netreceive *x = (t_netreceive *)pd_new(netreceive_class); + if (old) { + /* old style, nonsecure version */ + x->msgout = 0; + } else x->msgout = outlet_new(x, &s_anything); + if (udp) { /* datagram protocol */ + t_socketreceiver *y = socketreceiver_new((t_pd *)x, sockfd, (t_socketnotifier)netreceive_notify, + x->msgout ? (t_socketreceivefn)netreceive_doit : 0, 1); + sys_addpollfn(sockfd, (t_fdpollfn)socketreceiver_read, y); + x->connectout = 0; + } else { /* streaming protocol */ + if (listen(sockfd, 5) < 0) { + sys_sockerror("listen"); + sys_closesocket(sockfd); + sockfd = -1; + } else { + sys_addpollfn(sockfd, (t_fdpollfn)netreceive_connectpoll, x); + x->connectout = outlet_new(x, &s_float); + } + } + x->connectsocket = sockfd; + x->nconnections = 0; + x->udp = udp; + x->sr = 0; + return x; +} +static void netreceive_free(t_netreceive *x) { + /* LATER make me clean up open connections */ + if (x->connectsocket >= 0) { + sys_rmpollfn(x->connectsocket); + sys_closesocket(x->connectsocket); + } +} +static void netreceive_setup() { + netreceive_class = class_new2("netreceive",netreceive_new,netreceive_free,sizeof(t_netreceive),CLASS_NOINLET,"FFS"); +} +extern "C" t_socketreceiver *netreceive_newest_receiver(t_netreceive *x) {return x->sr;} + +struct t_qlist : t_object { + t_binbuf *binbuf; + int onset; /* playback position */ + t_clock *clock; + float tempo; + double whenclockset; + float clockdelay; + t_symbol *dir; + t_canvas *canvas; + int reentered; +}; +static void qlist_tick(t_qlist *x); +static t_class *qlist_class; +static void *qlist_new() { + t_qlist *x = (t_qlist *)pd_new(qlist_class); + x->binbuf = binbuf_new(); + x->clock = clock_new(x, (t_method)qlist_tick); + outlet_new(x, &s_list); + outlet_new(x, &s_bang); + x->onset = 0x7fffffff; + x->tempo = 1; + x->whenclockset = 0; + x->clockdelay = 0; + x->canvas = canvas_getcurrent(); + x->reentered = 0; + return x; +} +static void qlist_rewind(t_qlist *x) { + x->onset = 0; + if (x->clock) clock_unset(x->clock); + x->whenclockset = 0; + x->reentered = 1; +} +static void qlist_donext(t_qlist *x, int drop, int automatic) { + t_pd *target = 0; + while (1) { + int argc = binbuf_getnatom(x->binbuf), count, onset = x->onset, onset2, wasreentered; + t_atom *argv = binbuf_getvec(x->binbuf); + t_atom *ap = argv + onset, *ap2; + if (onset >= argc) goto end; + while (ap->a_type == A_SEMI || ap->a_type == A_COMMA) { + if (ap->a_type == A_SEMI) target = 0; + onset++, ap++; + if (onset >= argc) goto end; + } + if (!target && ap->a_type == A_FLOAT) { + ap2 = ap + 1; + onset2 = onset + 1; + while (onset2 < argc && ap2->a_type == A_FLOAT) onset2++, ap2++; + x->onset = onset2; + if (automatic) { + clock_delay(x->clock, x->clockdelay = ap->a_float * x->tempo); + x->whenclockset = clock_getsystime(); + } else outlet_list(x->outlet, 0, onset2-onset, ap); + return; + } + ap2 = ap + 1; + onset2 = onset + 1; + while (onset2 < argc && (ap2->a_type == A_FLOAT || ap2->a_type == A_SYMBOL)) onset2++, ap2++; + x->onset = onset2; + count = onset2 - onset; + if (!target) { + if (ap->a_type != A_SYMBOL) continue; + target = ap->a_symbol->thing; + if (!target) {error("qlist: %s: no such object", ap->a_symbol->name); continue;} + ap++; + onset++; + count--; + if (!count) {x->onset = onset2; continue;} + } + wasreentered = x->reentered; + x->reentered = 0; + if (!drop) { + if (ap->a_type == A_FLOAT) typedmess(target, &s_list, count, ap); + else if (ap->a_type == A_SYMBOL) typedmess(target, ap->a_symbol, count-1, ap+1); + } + if (x->reentered) return; + x->reentered = wasreentered; + } /* while (1); never falls through */ + +end: + x->onset = 0x7fffffff; + outlet_bang(x->out(1)); + x->whenclockset = 0; +} +static void qlist_next(t_qlist *x, t_floatarg drop) {qlist_donext(x, drop != 0, 0);} +static void qlist_bang(t_qlist *x) {qlist_rewind(x); qlist_donext(x, 0, 1);} +static void qlist_tick(t_qlist *x) {x->whenclockset=0; qlist_donext(x, 0, 1);} +static void qlist_add(t_qlist *x, t_symbol *s, int ac, t_atom *av) { + t_atom a; + SETSEMI(&a); + binbuf_add(x->binbuf, ac, av); + binbuf_add(x->binbuf, 1, &a); +} +static void qlist_add2(t_qlist *x, t_symbol *s, int ac, t_atom *av) { + binbuf_add(x->binbuf, ac, av); +} +static void qlist_clear(t_qlist *x) { + qlist_rewind(x); + binbuf_clear(x->binbuf); +} +static void qlist_set(t_qlist *x, t_symbol *s, int ac, t_atom *av) { + qlist_clear(x); + qlist_add(x, s, ac, av); +} +static void qlist_read(t_qlist *x, t_symbol *filename, t_symbol *format) { + int cr = 0; + if (!strcmp(format->name, "cr")) cr = 1; + else if (*format->name) error("qlist_read: unknown flag: %s", format->name); + if (binbuf_read_via_canvas(x->binbuf, filename->name, x->canvas, cr)) + error("%s: read failed", filename->name); + x->onset = 0x7fffffff; + x->reentered = 1; +} +static void qlist_write(t_qlist *x, t_symbol *filename, t_symbol *format) { + int cr = 0; + char *buf = canvas_makefilename(x->canvas,filename->name,0,0); + if (!strcmp(format->name, "cr")) cr = 1; + else if (*format->name) error("qlist_read: unknown flag: %s", format->name); + if (binbuf_write(x->binbuf, buf, "", cr)) error("%s: write failed", filename->name); + free(buf); +} +static void qlist_print(t_qlist *x) { + post("--------- textfile or qlist contents: -----------"); + binbuf_print(x->binbuf); +} +static void qlist_tempo(t_qlist *x, t_float f) { + float newtempo; + if (f < 1e-20) f = 1e-20; + else if (f > 1e20) f = 1e20; + newtempo = 1./f; + if (x->whenclockset != 0) { + float elapsed = clock_gettimesince(x->whenclockset); + float left = x->clockdelay - elapsed; + if (left < 0) left = 0; + left *= newtempo / x->tempo; + clock_delay(x->clock, left); + } + x->tempo = newtempo; +} +static void qlist_free(t_qlist *x) { + binbuf_free(x->binbuf); + if (x->clock) clock_free(x->clock); +} + +static t_class *textfile_class; +typedef t_qlist t_textfile; +static void *textfile_new() { + t_textfile *x = (t_textfile *)pd_new(textfile_class); + x->binbuf = binbuf_new(); + outlet_new(x, &s_list); + outlet_new(x, &s_bang); + x->onset = 0x7fffffff; + x->reentered = 0; + x->tempo = 1; + x->whenclockset = 0; + x->clockdelay = 0; + x->clock = NULL; + x->canvas = canvas_getcurrent(); + return x; +} +static void textfile_bang(t_textfile *x) { + int argc = binbuf_getnatom(x->binbuf), onset = x->onset, onset2; + t_atom *argv = binbuf_getvec(x->binbuf); + t_atom *ap = argv + onset, *ap2; + while (onset < argc && (ap->a_type == A_SEMI || ap->a_type == A_COMMA)) onset++, ap++; + onset2 = onset; + ap2 = ap; + while (onset2 < argc && (ap2->a_type != A_SEMI && ap2->a_type != A_COMMA)) onset2++, ap2++; + if (onset2 > onset) { + x->onset = onset2; + if (ap->a_type == A_SYMBOL) + outlet_anything(x->outlet, ap->a_symbol, onset2-onset-1, ap+1); + else outlet_list(x->outlet, 0, onset2-onset, ap); + } else { + x->onset = 0x7fffffff; + outlet_bang(x->out(1)); + } +} + +static void textfile_rewind(t_qlist *x) {x->onset = 0;} +static void textfile_free(t_textfile *x) {binbuf_free(x->binbuf);} + +extern t_pd *newest; + +/* the "list" object family. + + list append - append a list to another + list prepend - prepend a list to another + list split - first n elements to first outlet, rest to second outlet + list trim - trim off "list" selector + list length - output number of items in list + +Need to think more about: + list foreach - spit out elements of a list one by one (also in reverse?) + list array - get items from a named array as a list + list reverse - permute elements of a list back to front + list pack - synonym for 'pack' + list unpack - synonym for 'unpack' + list cat - build a list by accumulating elements + +Probably don't need: + list first - output first n elements. + list last - output last n elements + list nth - nth item in list, counting from zero +*/ + +/* -------------- utility functions: storage, copying -------------- */ + +#if HAVE_ALLOCA +#define ATOMS_ALLOCA(x, n) ((x) = (t_atom *)((n) < LIST_NGETBYTE ? \ + alloca((n) * sizeof(t_atom)) : getbytes((n) * sizeof(t_atom)))) +#define ATOMS_FREEA(x, n) ( \ + ((n) < LIST_NGETBYTE || (free((x)), 0))) +#else +#define ATOMS_ALLOCA(x, n) ((x) = (t_atom *)getbytes((n) * sizeof(t_atom))) +#define ATOMS_FREEA(x, n) (free((x))) +#endif + +static void atoms_copy(int argc, t_atom *from, t_atom *to) { + for (int i = 0; i < argc; i++) to[i] = from[i]; +} + +/* ------------- fake class to divert inlets to ----------------- */ +static void alist_list(t_binbuf *x, t_symbol *s, int argc, t_atom *argv) { + binbuf_clear(x); + x->v = (t_atom *)getbytes(argc * sizeof(*x->v)); + if (!x->v) {x->n = 0; error("list_alloc: out of memory"); return;} + x->n = argc; + for (int i = 0; i < argc; i++) x->v[i] = argv[i]; +} +static void alist_anything(t_binbuf *x, t_symbol *s, int argc, t_atom *argv) { + binbuf_clear(x); + x->v = (t_atom *)getbytes((argc+1) * sizeof(*x->v)); + if (!x->v) {x->n = 0; error("list_alloc: out of memory"); return;} + x->n = argc+1; + SETSYMBOL(&x->v[0], s); + for (int i = 0; i < argc; i++) x->v[i+1] = argv[i]; +} +static void alist_toatoms(t_binbuf *x, t_atom *to) {for (size_t i=0; i<x->n; i++) to[i] = x->v[i];} + +t_class *list_append_class; struct t_list_append : t_object {t_binbuf *alist;}; +t_class *list_prepend_class; struct t_list_prepend : t_object {t_binbuf *alist;}; +t_class *list_split_class; struct t_list_split : t_object {t_float f;}; +t_class *list_trim_class; struct t_list_trim : t_object {}; +t_class *list_length_class; struct t_list_length : t_object {}; + +static t_pd *list_append_new(t_symbol *s, int argc, t_atom *argv) { + t_list_append *x = (t_list_append *)pd_new(list_append_class); + x->alist = binbuf_new(); alist_list(x->alist, 0, argc, argv); outlet_new(x, &s_list); inlet_new(x,x->alist, 0, 0); + return x; +} +static t_pd *list_prepend_new(t_symbol *s, int argc, t_atom *argv) { + t_list_prepend *x = (t_list_prepend *)pd_new(list_prepend_class); + x->alist = binbuf_new(); alist_list(x->alist, 0, argc, argv); outlet_new(x, &s_list); inlet_new(x,x->alist,0,0); + return x; +} +static void list_append_free (t_list_append *x) {binbuf_free(x->alist);} +static void list_prepend_free(t_list_prepend *x) {binbuf_free(x->alist);} + +static void list_append_list(t_list_append *x, t_symbol *s, int argc, t_atom *argv) { + t_atom *outv; int outc = x->alist->n + argc; ATOMS_ALLOCA(outv, outc); + atoms_copy(argc, argv, outv); + alist_toatoms(x->alist, outv+argc); + outlet_list(x->outlet, &s_list, outc, outv); ATOMS_FREEA(outv, outc); +} +static void list_append_anything(t_list_append *x, t_symbol *s, int argc, t_atom *argv) { + t_atom *outv; int outc = x->alist->n+argc+1; ATOMS_ALLOCA(outv, outc); + SETSYMBOL(outv, s); + atoms_copy(argc, argv, outv + 1); + alist_toatoms(x->alist, outv + 1 + argc); + outlet_list(x->outlet, &s_list, outc, outv); ATOMS_FREEA(outv, outc); +} +static void list_prepend_list(t_list_prepend *x, t_symbol *s, int argc, t_atom *argv) { + t_atom *outv; int outc = x->alist->n + argc; ATOMS_ALLOCA(outv, outc); + alist_toatoms(x->alist, outv); + atoms_copy(argc, argv, outv + x->alist->n); + outlet_list(x->outlet, &s_list, outc, outv); ATOMS_FREEA(outv, outc); +} +static void list_prepend_anything(t_list_prepend *x, t_symbol *s, int argc, t_atom *argv) { + t_atom *outv; int outc = x->alist->n+argc+1; ATOMS_ALLOCA(outv, outc); + alist_toatoms(x->alist, outv); + SETSYMBOL(outv + x->alist->n, s); + atoms_copy(argc, argv, outv + x->alist->n + 1); + outlet_list(x->outlet, &s_list, outc, outv); ATOMS_FREEA(outv, outc); +} +static t_pd *list_split_new(t_floatarg f) { + t_list_split *x = (t_list_split *)pd_new(list_split_class); + outlet_new(x, &s_list); + outlet_new(x, &s_list); + outlet_new(x, &s_list); + floatinlet_new(x, &x->f); + x->f = f; + return x; +} +static void list_split_list(t_list_split *x, t_symbol *s, int argc, t_atom *argv) { + int n = (int)x->f; + if (n < 0) n = 0; + if (argc >= n) { + outlet_list(x->out(1), &s_list, argc-n, argv+n); + outlet_list(x->out(0), &s_list, n, argv); + } else outlet_list(x->out(2), &s_list, argc, argv); +} +static void list_split_anything(t_list_split *x, t_symbol *s, int argc, t_atom *argv) { + t_atom *outv; + ATOMS_ALLOCA(outv, argc+1); + SETSYMBOL(outv, s); + atoms_copy(argc, argv, outv + 1); + list_split_list(x, &s_list, argc+1, outv); + ATOMS_FREEA(outv, argc+1); +} + +static t_pd *list_trim_new() { + t_list_trim *x = (t_list_trim *)pd_new(list_trim_class); + outlet_new(x, &s_list); + return x; +} +static void list_trim_list(t_list_trim *x, t_symbol *s, int argc, t_atom *argv) { + if (argc < 1 || argv[0].a_type != A_SYMBOL) outlet_list(x->outlet, &s_list, argc, argv); + else outlet_anything(x->outlet, argv[0].a_symbol, argc-1, argv+1); +} +static void list_trim_anything(t_list_trim *x, t_symbol *s, int argc, t_atom *argv) { + outlet_anything(x->outlet, s, argc, argv); +} + +static t_pd *list_length_new() { + t_list_length *x = (t_list_length *)pd_new(list_length_class); + outlet_new(x, &s_float); + return x; +} +static void list_length_list( t_list_length *x, t_symbol *s, int argc, t_atom *argv) {outlet_float(x->outlet, (float)argc);} +static void list_length_anything(t_list_length *x, t_symbol *s, int argc, t_atom *argv) {outlet_float(x->outlet, (float)argc+1);} + +static void *list_new(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) { + t_pd *newest = 0; /* hide global var */ + if (!argc || argv[0].a_type != A_SYMBOL) newest = list_append_new(s, argc, argv); + else { + t_symbol *s2 = argv[0].a_symbol; + if (s2 == gensym("append")) newest = list_append_new(s, argc-1, argv+1); + else if (s2 == gensym("prepend")) newest = list_prepend_new(s, argc-1, argv+1); + else if (s2 == gensym("split")) newest = list_split_new(atom_getfloatarg(1, argc, argv)); + else if (s2 == gensym("trim")) newest = list_trim_new(); + else if (s2 == gensym("length")) newest = list_length_new(); + else error("list %s: unknown function", s2->name); + } + /* workaround for bug in kernel.c */ + if (newest) pd_set_newest(newest); + return newest; +} + +#define LISTOP(name,argspec,freer) \ + list_##name##_class = class_new2("list " #name,list_##name##_new,freer,sizeof(t_list_##name),0,argspec); \ + class_addlist(list_##name##_class, list_##name##_list); \ + class_addanything(list_##name##_class, list_##name##_anything); \ + class_sethelpsymbol(list_##name##_class, &s_list); + +static void list_setup () { + LISTOP(append,"*",list_append_free) + LISTOP(prepend,"*",list_prepend_free) + LISTOP(split,"F",0) + LISTOP(trim,"",0) + LISTOP(length,"",0) + class_addcreator2("list",list_new,"*"); +} + +/* miller's "homebrew" linear-congruential algorithm */ +static t_class *random_class; +struct t_random : t_object { + t_float f; + unsigned int state; +}; +static int makeseed() { + static unsigned int random_nextseed = 1489853723; + random_nextseed = random_nextseed * 435898247 + 938284287; + return random_nextseed & 0x7fffffff; +} +static void *random_new(t_floatarg f) { + t_random *x = (t_random *)pd_new(random_class); + x->f = f; + x->state = makeseed(); + floatinlet_new(x,&x->f); + outlet_new(x,&s_float); + return x; +} +static void random_bang(t_random *x) { + int n = (int)x->f, nval; + int range = max(n,1); + unsigned int randval = x->state; + x->state = randval = randval * 472940017 + 832416023; + nval = (int)((double)range * (double)randval * (1./4294967296.)); + if (nval >= range) nval = (int)(range-1); + outlet_float(x->outlet, nval); +} +static void random_seed(t_random *x, float f, float glob) {x->state = (int)f;} +static void random_setup() { + random_class = class_new2("random",random_new,0,sizeof(t_random),0,"F"); + class_addbang(random_class, random_bang); + class_addmethod2(random_class, random_seed,"seed","f"); +} + +static t_class *loadbang_class; +struct t_loadbang : t_object {}; +static void *loadbang_new() { + t_loadbang *x = (t_loadbang *)pd_new(loadbang_class); + outlet_new(x,&s_bang); + return x; +} +static void loadbang_loadbang(t_loadbang *x) { + if (!sys_noloadbang) outlet_bang(x->outlet); +} +static void loadbang_setup() { + loadbang_class = class_new2("loadbang",loadbang_new,0,sizeof(t_loadbang),CLASS_NOINLET,""); + class_addmethod2(loadbang_class, loadbang_loadbang, "loadbang",""); +} + +static t_class *namecanvas_class; +struct t_namecanvas : t_object { + t_symbol *sym; + t_pd *owner; +}; +static void *namecanvas_new(t_symbol *s) { + t_namecanvas *x = (t_namecanvas *)pd_new(namecanvas_class); + x->owner = (t_pd *)canvas_getcurrent(); + x->sym = s; + if (*s->name) pd_bind(x->owner, s); + return x; +} +static void namecanvas_free(t_namecanvas *x) { + if (*x->sym->name) pd_unbind(x->owner, x->sym); +} +static void namecanvas_setup() { + namecanvas_class = class_new2("namecanvas",namecanvas_new,namecanvas_free,sizeof(t_namecanvas),CLASS_NOINLET,"S"); +} + +static t_class *cputime_class; +struct t_cputime : t_object { +#ifdef UNISTD + struct tms setcputime; +#endif +#ifdef MSW + LARGE_INTEGER kerneltime; + LARGE_INTEGER usertime; + bool warned; +#endif +}; +static t_class *realtime_class; struct t_realtime : t_object {double setrealtime;}; +static t_class *timer_class; struct t_timer : t_object {double settime;}; + + +static void cputime_bang(t_cputime *x) { +#ifdef UNISTD + times(&x->setcputime); +#endif +#ifdef MSW + FILETIME ignorethis, ignorethat; + BOOL retval = GetProcessTimes(GetCurrentProcess(), &ignorethis, &ignorethat, + (FILETIME *)&x->kerneltime, (FILETIME *)&x->usertime); + if (!retval) { + if (!x->warned) {error("cputime is apparently not supported on your platform"); return;} + x->warned = 1; + x->kerneltime.QuadPart = 0; + x->usertime.QuadPart = 0; + } +#endif +} +static void cputime_bang2(t_cputime *x) { +#ifdef UNISTD + struct tms newcputime; + times(&newcputime); + float elapsedcpu = 1000 * (newcputime.tms_utime + newcputime.tms_stime + - x->setcputime.tms_utime - x->setcputime.tms_stime) / HZ; +#endif +#ifdef MSW + FILETIME ignorethis, ignorethat; + LARGE_INTEGER usertime, kerneltime; + BOOL retval = GetProcessTimes(GetCurrentProcess(), &ignorethis, &ignorethat, (FILETIME *)&kerneltime, (FILETIME *)&usertime); + float elapsedcpu = retval ? 0.0001 * + ((kerneltime.QuadPart - x->kerneltime.QuadPart) + + (usertime.QuadPart - x->usertime.QuadPart)) : 0; +#endif + outlet_float(x->outlet, elapsedcpu); +} + +static void realtime_bang(t_realtime *x) {x->setrealtime = sys_getrealtime();} +static void timer_bang(t_timer *x ) {x->settime = clock_getsystime();} +static void realtime_bang2(t_realtime *x) {outlet_float(x->outlet, (sys_getrealtime() - x->setrealtime) * 1000.);} +static void timer_bang2(t_timer *x ) {outlet_float(x->outlet, clock_gettimesince(x->settime));} + +static void *cputime_new() { + t_cputime *x = (t_cputime *)pd_new(cputime_class); + outlet_new(x,gensym("float")); + inlet_new(x,x,gensym("bang"),gensym("bang2")); +#ifdef MSW + x->warned = 0; +#endif + cputime_bang(x); + return x; +} +static void *realtime_new() { + t_realtime *x = (t_realtime *)pd_new(realtime_class); + outlet_new(x,gensym("float")); + inlet_new(x,x,gensym("bang"),gensym("bang2")); + realtime_bang(x); + return x; +} +static void *timer_new(t_floatarg f) { + t_timer *x = (t_timer *)pd_new(timer_class); + timer_bang(x); + outlet_new(x, gensym("float")); + inlet_new(x, x, gensym("bang"), gensym("bang2")); + return x; +} +static void timer_setup() { + realtime_class = class_new2("realtime",realtime_new,0,sizeof(t_realtime),0,""); + cputime_class = class_new2("cputime", cputime_new, 0,sizeof(t_cputime), 0,""); + timer_class = class_new2("timer", timer_new, 0,sizeof(t_timer), 0,"F"); + class_addbang(realtime_class, realtime_bang); + class_addbang(cputime_class, cputime_bang); + class_addbang(timer_class, timer_bang); + class_addmethod2(realtime_class,realtime_bang2,"bang2",""); + class_addmethod2(cputime_class, cputime_bang2, "bang2",""); + class_addmethod2(timer_class, timer_bang2, "bang2",""); +} + +static t_class *print_class; +struct t_print : t_object { + t_symbol *sym; +}; +static void *print_new(t_symbol *s) { + t_print *x = (t_print *)pd_new(print_class); + if (*s->name) x->sym = s; + else x->sym = gensym("print"); + return x; +} +static void print_bang(t_print *x) {post("%s: bang", x->sym->name);} +static void print_pointer(t_print *x, t_gpointer *gp) {post("%s: (gpointer)", x->sym->name);} +static void print_float(t_print *x, t_float f) {post("%s: %g", x->sym->name, f);} +static void print_list(t_print *x, t_symbol *s, int argc, t_atom *argv) { + if (argc && argv->a_type != A_SYMBOL) startpost("%s:", x->sym->name); + else startpost("%s: %s", x->sym->name, (argc>1 ? s_list : argc==1 ? s_symbol : s_bang).name); + postatom(argc, argv); + endpost(); +} +static void print_anything(t_print *x, t_symbol *s, int argc, t_atom *argv) { + startpost("%s: %s", x->sym->name, s->name); + postatom(argc, argv); + endpost(); +} +static void print_setup() { + t_class *c = print_class = class_new2("print",print_new,0,sizeof(t_print),0,"S"); + class_addbang(c, print_bang); + class_addfloat(c, print_float); + class_addpointer(c, print_pointer); + class_addlist(c, print_list); + class_addanything(c, print_anything); +} + +/*---- Macro ----*/ +static t_class *macro_class; +struct t_macro : t_object { + t_symbol *sym; + t_outlet *bangout; +}; + +static void *macro_new(t_symbol *s) { + t_macro *x = (t_macro *)pd_new(macro_class); + if (*s->name) x->sym = s; + else x->sym = gensym("macro"); + x-> bangout = outlet_new(x, &s_bang); + return x; +} + +static void macro_bang(t_macro *x) {outlet_bang(x->bangout);} + +static void macro_send(t_macro *x, t_symbol *s, int argc, t_atom *argv) { + std::ostringstream t; + t << s->name; + for (int i=0; i<argc; i++) {t << " " << &argv[i];} + sys_mgui(x->dix->canvas, "macro_event_append", "Sp", t.str().data(), x); +} +static void macro_setup() { + t_class *c = macro_class = class_new2("macro",macro_new,0,sizeof(t_macro),0,"S"); + class_addanything(c, macro_send); + class_addmethod2(c, macro_bang, "mbang",""); +} + +/*---- Clipboard ----*/ +static t_class *clipboard_class; +struct t_clipboard : t_object { + t_binbuf *alist; + t_symbol *sym; + t_outlet *dump; +}; + +static void *clipboard_new(t_symbol *s) { + t_clipboard *x = (t_clipboard *)pd_new(clipboard_class); + if (*s->name) x->sym = s; + else x->sym = gensym("clipboard"); + x->alist = binbuf_new(); + x->dump = outlet_new(x,&s_list); + return x; +} + +static void clipboard_bang(t_clipboard *x) {sys_mgui(x->dix->canvas, "get_clipboard", "p", x);} + +static void clipboard_reply (t_clipboard *x, t_symbol *s, int argc, t_atom *argv) { + outlet_list(x->dump, &s_list, argc, argv); +} + +static void clipboard_setup() { + t_class *c = clipboard_class = class_new2("clipboard",clipboard_new,0,sizeof(t_clipboard),0,"S"); + class_addbang(c, clipboard_bang); + class_addmethod2(c, clipboard_reply,"clipboard_set","*"); +} + +/*---- Display ----*/ +static t_class *display_class; +struct t_display : t_object { + t_float height; +}; + +static void *display_new(t_floatarg f) { + t_display *x = (t_display *)pd_new(display_class); + x->height = f; + if (!x->height) x->height = 1; + return x; +} + +static void display_height (t_display *x) {sys_mgui(x, "height=", "i", (int)x->height);} + +static void display_send(t_display *x, t_symbol *s, int argc, t_atom *argv) { + std::ostringstream t; + t << s->name; + for (int i=0; i<argc; i++) {t << " " << &argv[i];} + sys_mgui(x, "dis", "S", t.str().data()); +} + +static void display_setup() { + t_class *c = display_class = class_new2("display",display_new,0,sizeof(t_display),0,"F"); + class_addanything(c, display_send); + class_addmethod2(c, display_height, "height",""); +} + +/*---- Any ----*/ +static t_class *any_class; +struct t_any : t_object {t_binbuf *alist;}; + +static void *any_new(t_symbol *s,int argc, t_atom *argv) { + t_any *x = (t_any *)pd_new(any_class); + x->alist = binbuf_new(); + if (argc) { + if (argv[0].a_type == A_FLOAT) {alist_list(x->alist, 0, argc, argv);} + if (argv[0].a_type == A_SYMBOL) {alist_anything(x->alist, argv[0].a_symbol, argc-1, argv+1);} + } + outlet_new(x, &s_anything); + inlet_new(x,x->alist, 0, 0); + return x; +} + +static void any_anything(t_any *x, t_symbol *s, int argc, t_atom *argv) { + t_atom *outv; int outc = x->alist->n+argc+1; ATOMS_ALLOCA(outv, outc); + if (argv[0].a_type == A_FLOAT && s->name == "list" || s->name == "float") { + alist_list(x->alist, 0, argc, argv); outlet_anything(x->outlet, &s_list, argc, argv);return; + } + if (argv[0].a_type == A_SYMBOL || s->name != "list" || s->name != "float") { + alist_anything(x->alist, s, argc, argv); outlet_anything(x->outlet, s, argc, argv); + } +} + +static void any_bang(t_any *x) { + t_atom *outv; int outc = x->alist->n; + ATOMS_ALLOCA(outv, outc); + alist_toatoms(x->alist, outv); + if (!binbuf_getnatom(x->alist)) {outlet_bang(x->outlet);return;} + if (outv[0].a_type == A_FLOAT) {outlet_anything(x->outlet, &s_list, outc, outv);} + if (outv[0].a_type == A_SYMBOL) {outlet_anything(x->outlet, outv[0].a_symbol, outc-1, outv+1);} + ATOMS_FREEA(outv, outc); +} + +static void any_setup() { + post("DesireData iemlib2 [any] clone"); + t_class *c = any_class = class_new2("any",any_new,0,sizeof(t_any),0,"*"); + class_addanything(c, any_anything); + class_addbang(c, any_bang); +} + +/* MSW and OSX don't appear to have single-precision ANSI math */ +#if defined(MSW) || defined(__APPLE__) +#define sinf sin +#define cosf cos +#define atanf atan +#define atan2f atan2 +#define sqrtf sqrt +#define logf log +#define expf exp +#define fabsf fabs +#define powf pow +#endif + +struct t_binop : t_object { + t_float f1; + t_float f2; +}; +static void *binop_new(t_class *floatclass, t_floatarg f) { + t_binop *x = (t_binop *)pd_new(floatclass); + outlet_new(x, &s_float); + floatinlet_new(x, &x->f2); + x->f1 = 0; + x->f2 = f; + return x; +} + +#define BINOP(NAME,EXPR) \ +static t_class *NAME##_class; \ +static void *NAME##_new(t_floatarg f) {return binop_new(NAME##_class, f);} \ +static void NAME##_bang(t_binop *x) {float a=x->f1,b=x->f2; outlet_float(x->outlet,(EXPR));} \ +static void NAME##_float(t_binop *x, t_float f) {x->f1=f; NAME##_bang(x);} + +BINOP(binop_plus,a+b) +BINOP(binop_minus,a-b) +BINOP(binop_times,a*b) +BINOP(binop_div,a/b) +BINOP(binop_pow, a>0?powf(a,b):0) +BINOP(binop_max, a>b?a:b) +BINOP(binop_min, a<b?a:b) +BINOP(binop_ee,a==b) +BINOP(binop_ne,a!=b) +BINOP(binop_gt,a>b) +BINOP(binop_lt,a<b) +BINOP(binop_ge,a>=b) +BINOP(binop_le,a<=b) +BINOP(binop_ba,(int)a&(int)b) +BINOP(binop_la,a&&b) +BINOP(binop_bo,(int)a|(int)b) +BINOP(binop_lo,a||b) +BINOP(binop_ls,(int)a<<(int)b) +BINOP(binop_rs,(int)a>>(int)b) +BINOP(binop_pc,(int)a % (b?(int)b:1)) +static int mymod(int a, int b) { + int n2 = (int)b; + if (n2 < 0) n2 = -n2; else if (!n2) n2 = 1; + int r = (int)a % n2; + return r<0 ? r+n2 : r; +} +BINOP(binop_mymod, (float)mymod((int)a,(int)b)) +static int mydiv(int a, int b) { + if (b < 0) b=-b; else if (!b) b=1; + if (a < 0) a -= b-1; + return a/b; +} +BINOP(binop_mydiv, (float)mydiv((int)a,(int)b)) + +FUNC1(sin,sinf(a)) +FUNC1(cos,cosf(a)) +FUNC1(tan,tanf(a)) +FUNC1(atan,atanf(a)) + +static t_class *atan2_class; +struct t_atan2 : t_object { + float f; +}; +static void *atan2_new() { + t_atan2 *x = (t_atan2 *)pd_new(atan2_class); + static int warned; + if (!warned) post("warning: atan2 inlets switched from Pd 0.37 to 0.38"), warned=1; + floatinlet_new(x, &x->f); + x->f = 0; + outlet_new(x, &s_float); + return x; +} +static void atan2_float(t_atan2 *x, t_float f) { + float r = (f == 0 && x->f == 0 ? 0 : atan2f(f, x->f)); + outlet_float(x->outlet, r); +} + +FUNC1(sqrt,sqrtf(a)) +FUNC1(log, a>0 ? logf(a) : -1000) +FUNC1(exp,expf(min(a,87.3365f))) +FUNC1(abs,fabsf(a)) + +static t_class *clip_class; +struct t_clip : t_object { + float f1; + float f2; + float f3; +}; +static void *clip_new(t_floatarg f2, t_floatarg f3) { + t_clip *x = (t_clip *)pd_new(clip_class); + floatinlet_new(x, &x->f2); x->f2 = f2; + floatinlet_new(x, &x->f3); x->f3 = f3; + outlet_new(x, &s_float); + return x; +} +static void clip_bang( t_clip *x) { outlet_float(x->outlet, clip(x->f1,x->f2,x->f3));} +static void clip_float(t_clip *x, t_float f) {x->f1 = f; outlet_float(x->outlet, clip(x->f1,x->f2,x->f3));} + +void arithmetic_setup() { + t_symbol *s = gensym("operators"); +#define BINOPDECL(NAME,SYM) \ + NAME##_class = class_new2(SYM,NAME##_new,0,sizeof(t_binop),0,"F"); \ + class_addbang(NAME##_class, NAME##_bang); \ + class_addfloat(NAME##_class, (t_method)NAME##_float); \ + class_sethelpsymbol(NAME##_class,s); + + BINOPDECL(binop_plus,"+") + BINOPDECL(binop_minus,"-") + BINOPDECL(binop_times,"*") + BINOPDECL(binop_div,"/") + BINOPDECL(binop_pow,"pow") + BINOPDECL(binop_max,"max") + BINOPDECL(binop_min,"min") + + s = gensym("otherbinops"); + + BINOPDECL(binop_ee,"==") + BINOPDECL(binop_ne,"!=") + BINOPDECL(binop_gt,">") + BINOPDECL(binop_lt,"<") + BINOPDECL(binop_ge,">=") + BINOPDECL(binop_le,"<=") + + BINOPDECL(binop_ba,"&") + BINOPDECL(binop_la,"&&") + BINOPDECL(binop_bo,"|") + BINOPDECL(binop_lo,"||") + BINOPDECL(binop_ls,"<<") + BINOPDECL(binop_rs,">>") + + BINOPDECL(binop_pc,"%") + BINOPDECL(binop_mymod,"mod") + BINOPDECL(binop_mydiv,"div") + + s = gensym("math"); + +#define FUNCDECL(NAME,SYM) \ + NAME##_class = class_new2(SYM,NAME##_new,0,sizeof(t_object),0,""); \ + class_addfloat(NAME##_class, (t_method)NAME##_float); \ + class_sethelpsymbol(NAME##_class,s); + + FUNCDECL(sin,"sin"); + FUNCDECL(cos,"cos"); + FUNCDECL(tan,"tan"); + FUNCDECL(atan,"atan"); + FUNCDECL(atan2,"atan2"); /* actually a binop */ + FUNCDECL(sqrt,"sqrt"); + FUNCDECL(log,"log"); + FUNCDECL(exp,"exp"); + FUNCDECL(abs,"abs"); + + clip_class = class_new2("clip",clip_new,0,sizeof(t_clip),0,"FF"); + class_addfloat(clip_class, clip_float); + class_addbang(clip_class, clip_bang); +} + +static t_class *pdint_class; struct t_pdint : t_object {t_float f;}; +static t_class *pdfloat_class; struct t_pdfloat : t_object {t_float f;}; +static t_class *pdsymbol_class; struct t_pdsymbol : t_object {t_symbol *s;}; +static t_class *bang_class; struct t_bang : t_object {}; + +static void pdint_bang(t_pdint *x) {outlet_float(x->outlet, (t_float)(int)x->f);} +static void pdint_float(t_pdint *x, t_float f) {x->f = f; pdint_bang(x);} + +static void pdfloat_bang(t_pdfloat *x) {outlet_float(x->outlet, x->f);} +static void pdfloat_float(t_pdfloat *x, t_float f) {x->f=f; pdfloat_bang(x);} + +static void pdsymbol_bang(t_pdsymbol *x) {outlet_symbol(x->outlet, x->s);} +static void pdsymbol_symbol( t_pdsymbol *x, t_symbol *s ) {x->s=s; pdsymbol_bang(x);} +static void pdsymbol_anything(t_pdsymbol *x, t_symbol *s, int ac, t_atom *av) {x->s=s; pdsymbol_bang(x);} + +/* For "list" message don't just output "list"; if empty, we want to bang the symbol and + if it starts with a symbol, we output that. Otherwise it's not clear what we should do + so we just go for the "anything" method. LATER figure out if there are other places + where empty lists aren't equivalent to "bang"??? Should Pd's message passer always check + and call the more specific method, or should it be the object's responsibility? Dunno... */ +#if 0 +static void pdsymbol_list(t_pdsymbol *x, t_symbol *s, int ac, t_atom *av) { + if (!ac) pdsymbol_bang(x); + else if (av->a_type == A_SYMBOL) pdsymbol_symbol(x, av->a_symbol); + else pdsymbol_anything(x, s, ac, av); +} +#endif + +static void *pdint_new(t_floatarg f) { + t_pdint *x = (t_pdint *)pd_new(pdint_class); + x->f = f; outlet_new(x, &s_float); floatinlet_new(x, &x->f); + return x; +} +static void *pdfloat_new(t_pd *dummy, t_float f) { + t_pdfloat *x = (t_pdfloat *)pd_new(pdfloat_class); + x->f = f; outlet_new(x, &s_float); floatinlet_new(x, &x->f); + pd_set_newest((t_pd *)x); + return x; +} +static void *pdfloat_new2(t_floatarg f) {return pdfloat_new(0, f);} +static void *pdsymbol_new(t_pd *dummy, t_symbol *s) { + t_pdsymbol *x = (t_pdsymbol *)pd_new(pdsymbol_class); + x->s = s; outlet_new(x, &s_symbol);symbolinlet_new(x, &x->s); + pd_set_newest((t_pd *)x); + return x; +} +static void *bang_new(t_pd *dummy) { + t_bang *x = (t_bang *)pd_new(bang_class); + outlet_new(x, &s_bang); + pd_set_newest((t_pd *)x); + return x; +} +static void *bang_new2(t_bang f) {return bang_new(0);} +static void bang_bang(t_bang *x) {outlet_bang(x->outlet);} + +void misc_setup() { + pdint_class = class_new2("int",pdint_new,0,sizeof(t_pdint),0,"F"); + class_addcreator2("i",pdint_new,"F"); + class_addbang(pdint_class, pdint_bang); + class_addfloat(pdint_class, pdint_float); + pdfloat_class = class_new2("float",pdfloat_new,0,sizeof(t_pdfloat),0,"F"); + class_addcreator2("f",pdfloat_new2,"F"); + class_addbang(pdfloat_class, pdfloat_bang); + class_addfloat(pdfloat_class, pdfloat_float); + pdsymbol_class = class_new2("symbol",pdsymbol_new,0,sizeof(t_pdsymbol),0,"S"); + class_addbang(pdsymbol_class, pdsymbol_bang); + class_addsymbol(pdsymbol_class, pdsymbol_symbol); + class_addanything(pdsymbol_class, pdsymbol_anything); + t_class *c = bang_class = class_new2("bang",bang_new,0,sizeof(t_bang),0,""); + class_addcreator2("b",bang_new2,""); + class_addbang(c, bang_bang); + class_addfloat(c, bang_bang); + class_addsymbol(c, bang_bang); + class_addlist(c, bang_bang); + class_addanything(c, bang_bang); +} + +/* -------------------- send & receive ------------------------------ */ + +static t_class * send_class; struct t_send : t_object {t_symbol *sym;}; +static t_class *receive_class; struct t_receive : t_object {t_symbol *sym;}; + +static void send_bang( t_send *x) { if (x->sym->thing) pd_bang(x->sym->thing);} +static void send_float( t_send *x, t_float f) { if (x->sym->thing) pd_float(x->sym->thing,f);} +static void send_symbol( t_send *x, t_symbol *s) { if (x->sym->thing) pd_symbol(x->sym->thing,s);} +static void send_pointer( t_send *x, t_gpointer *gp) { if (x->sym->thing) pd_pointer(x->sym->thing,gp);} +static void send_list( t_send *x, t_symbol *s, int argc, t_atom *argv) {if (x->sym->thing) pd_list(x->sym->thing,s,argc,argv);} +static void send_anything(t_send *x, t_symbol *s, int argc, t_atom *argv) {if (x->sym->thing) typedmess(x->sym->thing,s,argc,argv);} + +static void receive_bang( t_receive *x) { outlet_bang(x->outlet);} +static void receive_float( t_receive *x, t_float f) { outlet_float(x->outlet, f);} +static void receive_symbol( t_receive *x, t_symbol *s) { outlet_symbol(x->outlet, s);} +static void receive_pointer( t_receive *x, t_gpointer *gp) { outlet_pointer(x->outlet, gp);} +static void receive_list( t_receive *x, t_symbol *s, int argc, t_atom *argv) { outlet_list(x->outlet, s, argc, argv);} +static void receive_anything(t_receive *x, t_symbol *s, int argc, t_atom *argv) {outlet_anything(x->outlet, s, argc, argv);} +static void *receive_new(t_symbol *s) { + t_receive *x = (t_receive *)pd_new(receive_class); + x->sym = s; + pd_bind(x, s); + outlet_new(x, 0); + return x; +} +static void receive_free(t_receive *x) {pd_unbind(x, x->sym);} + +static void *send_new(t_symbol *s) { + t_send *x = (t_send *)pd_new(send_class); + if (!*s->name) symbolinlet_new(x, &x->sym); + x->sym = s; + return x; +} +static void sendreceive_setup() { + t_class *c; + c = send_class = class_new2("send",send_new,0,sizeof(t_send),0,"S"); + class_addcreator2("s",send_new,"S"); + class_addbang(c, send_bang); + class_addfloat(c, send_float); + class_addsymbol(c, send_symbol); + class_addpointer(c, send_pointer); + class_addlist(c, send_list); + class_addanything(c, send_anything); + c = receive_class = class_new2("receive",receive_new,receive_free,sizeof(t_receive),CLASS_NOINLET,"S"); + class_addcreator2("r",receive_new,"S"); + class_addbang(c, receive_bang); + class_addfloat(c, receive_float); + class_addsymbol(c, receive_symbol); + class_addpointer(c, receive_pointer); + class_addlist(c, receive_list); + class_addanything(c, receive_anything); +} + +/* -------------------------- select ------------------------------ */ + +static t_class *select_class; +struct t_selectelement { + t_atom a; + t_outlet *out; +}; +struct t_select : t_object { + t_int nelement; + t_selectelement *vec; + t_outlet *rejectout; +}; +#define select_each(e,x) for (t_selectelement *e = x->vec;e;e=0) for (int nelement = x->nelement; nelement--; e++) + +static void select_float(t_select *x, t_float f) { + select_each(e,x) if (e->a.a_type==A_FLOAT && e->a.a_float==f) {outlet_bang(e->out); return;} + outlet_float(x->rejectout, f); +} +static void select_symbol(t_select *x, t_symbol *s) { + select_each(e,x) if (e->a.a_type==A_SYMBOL && e->a.a_symbol==s) {outlet_bang(e->out); return;} + outlet_symbol(x->rejectout, s); +} +static void select_free(t_select *x) {free(x->vec);} +static void *select_new(t_symbol *s, int argc, t_atom *argv) { + t_atom a; + if (argc == 0) { + argc = 1; + SETFLOAT(&a, 0); + argv = new t_atom[1]; + } + t_select *x = (t_select *)pd_new(select_class); + x->nelement = argc; + x->vec = (t_selectelement *)getbytes(argc * sizeof(*x->vec)); + t_selectelement *e = x->vec; + for (int n = 0; n < argc; n++, e++) { + e->out = outlet_new(x, &s_bang); + e->a = argv[n]; + if (e->a.a_type == A_FLOAT) + floatinlet_new(x, &x->vec[n].a.a_float); + else symbolinlet_new(x, &x->vec[n].a.a_symbol); + } + x->rejectout = outlet_new(x, &s_float); + return x; +} +void select_setup() { + select_class = class_new2("select",0,select_free,sizeof(t_select),0,""); + class_addfloat(select_class, select_float); + class_addsymbol(select_class, select_symbol); + class_addcreator2("select",select_new,"*"); + class_addcreator2("sel", select_new,"*"); +} + +/* -------------------------- route ------------------------------ */ + +static t_class *route_class; +struct t_routeelement { + t_atom a; + t_outlet *out; +}; +struct t_route : t_object { + t_int n; + t_routeelement *vec; + t_outlet *rejectout; +}; +static void route_anything(t_route *x, t_symbol *sel, int argc, t_atom *argv) { + t_routeelement *e = x->vec; + post("1: sel=%s",sel->name); + for (int n = x->n; n--; e++) if (e->a.a_type == A_SYMBOL) if (e->a.a_symbol == sel) { + if (argc > 0 && argv[0].a_type == A_SYMBOL) outlet_anything(e->out, argv[0].a_symbol, argc-1, argv+1); + else { /* tb {: avoid 1 element lists */ + if (argc > 1) outlet_list(e->out, 0, argc, argv); + else if (argc == 0) outlet_bang(e->out); + else outlet_atom(e->out,&argv[0]); + } /* tb } */ + return; + } + outlet_anything(x->rejectout, sel, argc, argv); +} + +#define route_eachr(E,L) for (t_routeelement *E = L->vec;E;E=0) for (int ROUTEN = x->n; ROUTEN--; E++) +static void route_list(t_route *x, t_symbol *sel, int argc, t_atom *argv) { + if (argc && argv->a_type == A_FLOAT) { + float f = atom_getfloat(argv); + route_eachr(e,x) if (e->a.a_type == A_FLOAT && e->a.a_float == f) { + if (argc > 1 && argv[1].a_type == A_SYMBOL) + outlet_anything(e->out, argv[1].a_symbol, argc-2, argv+2); + else { + argc--; argv++; + if (argc > 1) outlet_list(e->out, 0, argc, argv); + else if (argc == 0) outlet_bang(e->out); + else outlet_atom(e->out,&argv[0]); + } + return; + } else if (e->a.a_type == A_SYMBOL && e->a.a_symbol == &s_float) { + outlet_float(e->out, argv[0].a_float); + return; + } + } else { /* symbol arguments */ + if (argc > 1) { /* 2 or more args: treat as "list" */ + route_eachr(e,x) if (e->a.a_type == A_SYMBOL && e->a.a_symbol == &s_list) { + if (argv[0].a_type==A_SYMBOL) outlet_anything(e->out, argv[0].a_symbol, argc-1, argv+1); + else outlet_list(e->out, 0, argc, argv); + return; + } + } else if (argc==0) {route_eachr(e,x) {if (e->a.a_symbol==&s_bang ) {outlet_bang(e->out ); return;}} + } else if (argv[0].a_type==A_FLOAT) {route_eachr(e,x) {if (e->a.a_symbol==&s_float ) {outlet_atom(e->out,&argv[0]); return;}} + } else if (argv[0].a_type==A_SYMBOL) {route_eachr(e,x) {if (e->a.a_symbol==&s_symbol ) {outlet_atom(e->out,&argv[0]); return;}} + } else if (argv[0].a_type==A_POINTER){route_eachr(e,x) {if (e->a.a_symbol==&s_pointer) {outlet_atom(e->out,&argv[0]); return;}} + } + } + if (!argc) outlet_bang(x->rejectout); else outlet_list(x->rejectout, 0, argc, argv); +} + +static void route_free(t_route *x) {free(x->vec);} +static void *route_new(t_symbol *s, int argc, t_atom *argv) { + t_route *x = (t_route *)pd_new(route_class); + if (argc == 0) { + t_atom a; + argc = 1; + SETFLOAT(&a, 0); + argv = &a; + } + x->n = argc; + x->vec = (t_routeelement *)getbytes(argc * sizeof(*x->vec)); + t_routeelement *e = x->vec; + for (int n = 0; n < argc; n++, e++) { + e->out = outlet_new(x, &s_list); + e->a = argv[n]; + } + x->rejectout = outlet_new(x, &s_list); + return x; +} +void route_setup() { + route_class = class_new2("route",route_new,route_free,sizeof(t_route),0,"*"); + class_addlist(route_class, route_list); + class_addanything(route_class, route_anything); +} + +static t_class *pack_class; +struct t_pack : t_object { + t_int n; /* number of args */ + t_atom *vec; /* input values */ + t_atom *outvec; /* space for output values */ +}; +static void *pack_new(t_symbol *s, int argc, t_atom *argv) { + t_pack *x = (t_pack *)pd_new(pack_class); + t_atom defarg[2], *ap, *vec, *vp; + if (!argc) { + argv = defarg; + argc = 2; + SETFLOAT(&defarg[0], 0); + SETFLOAT(&defarg[1], 0); + } + x->n = argc; + vec = x->vec = (t_atom *)getbytes(argc * sizeof(*x->vec)); + x->outvec = (t_atom *)getbytes(argc * sizeof(*x->outvec)); + vp = x->vec; + ap = argv; + for (int i = 0; i < argc; i++, ap++, vp++) { + if (ap->a_type == A_FLOAT) { + *vp = *ap; + if (i) floatinlet_new(x, &vp->a_float); + } else if (ap->a_type == A_SYMBOL) { + char c = *ap->a_symbol->name; + if (c == 's') { SETSYMBOL(vp, &s_symbol); if (i) symbolinlet_new(x, &vp->a_symbol);} + else if (c == 'p') { /* SETPOINTER(vp, gpointer_new()) if (i) pointerinlet_new(x, gp); */} + else if (c == 'f') { SETFLOAT(vp, 0); if (i) floatinlet_new(x, &vp->a_float);} + else error("pack: %s: bad type '%c'", ap->a_symbol->name, c); + } + } + outlet_new(x, &s_list); + return x; +} +static void pack_bang(t_pack *x) { + int reentered = 0, size = x->n * sizeof (t_atom); + t_atom *outvec; + /* reentrancy protection. The first time through use the pre-allocated outvec; if we're reentered we have to allocate new memory. */ + if (!x->outvec) { + outvec = (t_atom *)t_getbytes(size); + reentered = 1; + } else { + outvec = x->outvec; + x->outvec = 0; + } + memcpy(outvec, x->vec, size); + outlet_list(x->outlet, &s_list, x->n, outvec); + if (reentered) free(outvec); else x->outvec = outvec; +} + +static void pack_pointer(t_pack *x, t_gpointer *gp) { + if (x->vec->a_type == A_POINTER) { + //gpointer_unset(x->gpointer); + //*x->gpointer = *gp; + //if (gp->o) gp->o->refcount++; + pack_bang(x); + } else error("pack_pointer: wrong type"); +} +static void pack_float(t_pack *x, t_float f) { + if (x->vec->a_type == A_FLOAT ) {x->vec->a_float = f; pack_bang(x);} else error("pack_float: wrong type"); +} +static void pack_symbol(t_pack *x, t_symbol *s) { + if (x->vec->a_type == A_SYMBOL) {x->vec->a_symbol = s; pack_bang(x);} else error("pack_symbol: wrong type"); +} +static void pack_list(t_pack *x, t_symbol *s, int ac, t_atom *av) {obj_list(x, 0, ac, av);} +static void pack_anything(t_pack *x, t_symbol *s, int ac, t_atom *av) { + t_atom *av2 = (t_atom *)getbytes((ac + 1) * sizeof(t_atom)); + for (int i = 0; i < ac; i++) av2[i + 1] = av[i]; + SETSYMBOL(av2, s); + obj_list(x, 0, ac+1, av2); + free(av2); +} +static void pack_free(t_pack *x) { + free(x->vec); + free(x->outvec); +} +static void pack_setup() { + t_class *c = pack_class = class_new2("pack",pack_new,pack_free,sizeof(t_pack),0,"*"); + class_addbang(c, pack_bang); + class_addpointer(c, pack_pointer); + class_addfloat(c, pack_float); + class_addsymbol(c, pack_symbol); + class_addlist(c, pack_list); + class_addanything(c, pack_anything); +} + +static t_class *unpack_class; +struct t_unpack : t_object { + t_int n; + t_outlet **vec; + char *vat; +}; + +static t_atomtype atomtype_from_letter(char c) { + switch (c) { + case 'e': return A_ATOM; + case 'f': return A_FLOAT; + case 's': return A_SYMBOL; + case 'p': return A_POINTER; + default: return A_CANT; + } +} + +static void *unpack_new(t_symbol *s, int argc, t_atom *argv) { + t_unpack *x = (t_unpack *)pd_new(unpack_class); + t_atom defarg[2]; + if (!argc) { + argv = defarg; + argc = 2; + SETFLOAT(&defarg[0], 0); + SETFLOAT(&defarg[1], 0); + } + x->n = argc; + x->vec = (t_outlet **)getbytes(argc * sizeof(*x->vec)); + x->vat = (char *)getbytes(argc); + t_atom *ap=argv; + for (int i=0; i<argc; ap++, i++) { + x->vec[i] = outlet_new(x,0); + switch (argv[i].a_type) { + case A_FLOAT: x->vat[i]=A_FLOAT; break; + case A_SYMBOL: { + const char *s = atom_getstring(&argv[i]); + if (strlen(s)<1 || (x->vat[i]=atomtype_from_letter(s[0]))==A_CANT) {error("%s: bad type", s); x->vat[i]=A_FLOAT;} + break; + } + default: error("bad type"); + } + } + return x; +} +static void unpack_list(t_unpack *x, t_symbol *s, int argc, t_atom *argv) { + if (argc > x->n) argc = x->n; + //for (int i=argc-1; i>=0; i--) outlet_atom(x->vec[i],&argv[i]); + for (int i=argc-1; i>=0; i--) { + if (x->vat[i]==A_ATOM || x->vat[i]==argv[i].a_type) outlet_atom(x->vec[i],&argv[i]); + else error("type mismatch"); + } +} +static void unpack_anything(t_unpack *x, t_symbol *s, int ac, t_atom *av) { + t_atom *av2 = (t_atom *)getbytes((ac+1) * sizeof(t_atom)); + for (int i=0; i<ac; i++) av2[i+1] = av[i]; + SETSYMBOL(av2, s); + unpack_list(x, 0, ac+1, av2); + free(av2); +} +static void unpack_free(t_unpack *x) {free(x->vec); free(x->vat);} +static void unpack_setup() { + unpack_class = class_new2("unpack",unpack_new,unpack_free,sizeof(t_unpack),0,"*"); + class_addlist(unpack_class, unpack_list); + class_addanything(unpack_class, unpack_anything); +} + +static t_class *trigger_class; +struct t_triggerout { + int type; + t_outlet *outlet; +}; +typedef struct t_trigger : t_object { + t_int n; + t_triggerout *vec; +}; +static void *trigger_new(t_symbol *s, int argc, t_atom *argv) { + t_trigger *x = (t_trigger *)pd_new(trigger_class); + t_atom defarg[2]; + if (!argc) { + argv = defarg; + argc = 2; + SETSYMBOL(&defarg[0], &s_bang); + SETSYMBOL(&defarg[1], &s_bang); + } + x->n = argc; + x->vec = (t_triggerout *)getbytes(argc * sizeof(*x->vec)); + t_triggerout *u = x->vec; + t_atom *ap = argv; + for (int i=0; i<argc; u++, ap++, i++) { + t_atomtype thistype = ap->a_type; + char c; + if (thistype == A_SYMBOL) c = ap->a_symbol->name[0]; + else if (thistype == A_FLOAT) c = 'f'; + else c = 0; + if (c == 'p') u->outlet = outlet_new(x, &s_pointer); + else if (c == 'f') u->outlet = outlet_new(x, &s_float); + else if (c == 'b') u->outlet = outlet_new(x, &s_bang); + else if (c == 'l') u->outlet = outlet_new(x, &s_list); + else if (c == 's') u->outlet = outlet_new(x, &s_symbol); + else if (c == 'a') u->outlet = outlet_new(x, &s_symbol); + else { + error("trigger: %s: bad type", ap->a_symbol->name); + c='f'; u->outlet = outlet_new(x, &s_float); + } + u->type = c; + } + return x; +} +static void trigger_list(t_trigger *x, t_symbol *s, int argc, t_atom *argv) { + t_triggerout *u = x->vec+x->n; + for (int i = x->n; u--, i--;) { + if (u->type == 'f') outlet_float(u->outlet, argc ? atom_getfloat(argv) : 0); + else if (u->type == 'b') outlet_bang(u->outlet); + else if (u->type == 's') outlet_symbol(u->outlet, argc ? atom_getsymbol(argv) : &s_symbol); + else if (u->type == 'p') { + if (!argc || argv->a_type != 'p') error("unpack: bad pointer"); + else outlet_pointer(u->outlet, argv->a_pointer); + } else outlet_list(u->outlet, &s_list, argc, argv); + } +} +static void trigger_anything(t_trigger *x, t_symbol *s, int argc, t_atom *argv) { + t_triggerout *u = x->vec+x->n; + for (int i = x->n; u--, i--;) { + if (u->type == 'b') outlet_bang(u->outlet); + else if (u->type == 'a') outlet_anything(u->outlet, s, argc, argv); + else error("trigger: can only convert 's' to 'b' or 'a'", s->name); + } +} +static void trigger_bang(t_trigger *x) {trigger_list(x, 0, 0, 0);} +static void trigger_pointer(t_trigger *x, t_gpointer *gp) {t_atom at; SETPOINTER(&at, gp); trigger_list(x, 0, 1, &at);} +static void trigger_float(t_trigger *x, t_float f) { t_atom at; SETFLOAT(&at, f); trigger_list(x, 0, 1, &at);} +static void trigger_symbol(t_trigger *x, t_symbol *s) { t_atom at; SETSYMBOL(&at, s); trigger_list(x, 0, 1, &at);} +static void trigger_free(t_trigger *x) {free(x->vec);} +static void trigger_setup() { + t_class *c = trigger_class = class_new2("trigger",trigger_new,trigger_free,sizeof(t_trigger),0,"*"); + class_addcreator2("t",trigger_new,"*"); + class_addlist(c, trigger_list); + class_addbang(c, trigger_bang); + class_addpointer(c, trigger_pointer); + class_addfloat(c, trigger_float); + class_addsymbol(c, trigger_symbol); + class_addanything(c, trigger_anything); +} + +static t_class *spigot_class; +struct t_spigot : t_object {float state;}; +static void *spigot_new(t_floatarg f) { + t_spigot *x = (t_spigot *)pd_new(spigot_class); + floatinlet_new(x, &x->state); + outlet_new(x, 0); + x->state = f; + return x; +} +static void spigot_bang( t_spigot *x) { if (x->state) outlet_bang(x->outlet);} +static void spigot_pointer( t_spigot *x, t_gpointer *gp) { if (x->state) outlet_pointer(x->outlet, gp);} +static void spigot_float( t_spigot *x, t_float f) { if (x->state) outlet_float(x->outlet, f);} +static void spigot_symbol( t_spigot *x, t_symbol *s) { if (x->state) outlet_symbol(x->outlet, s);} +static void spigot_list( t_spigot *x, t_symbol *s, int argc, t_atom *argv) {if (x->state) outlet_list(x->outlet, s, argc, argv);} +static void spigot_anything(t_spigot *x, t_symbol *s, int argc, t_atom *argv) {if (x->state) outlet_anything(x->outlet, s, argc, argv);} +static void spigot_setup() { + t_class *c = spigot_class = class_new2("spigot",spigot_new,0,sizeof(t_spigot),0,"F"); + class_addbang(c, spigot_bang); + class_addpointer(c, spigot_pointer); + class_addfloat(c, spigot_float); + class_addsymbol(c, spigot_symbol); + class_addlist(c, spigot_list); + class_addanything(c, spigot_anything); +} + +static t_class *moses_class; +struct t_moses : t_object {float y;}; +static void *moses_new(t_floatarg f) { + t_moses *x = (t_moses *)pd_new(moses_class); + floatinlet_new(x, &x->y); + outlet_new(x, &s_float); + outlet_new(x, &s_float); + x->y = f; + return x; +} +static void moses_float(t_moses *x, t_float f) {outlet_float(x->out(f>=x->y), f);} +static void moses_setup() { + moses_class = class_new2("moses",moses_new,0,sizeof(t_moses),0,"F"); + class_addfloat(moses_class, moses_float); +} +static t_class *until_class; +struct t_until : t_object { + int run; + int count; +}; +static void *until_new() { + t_until *x = (t_until *)pd_new(until_class); + inlet_new(x, x, gensym("bang"), gensym("bang2")); + outlet_new(x, &s_bang); + x->run = 0; + return x; +} +static void until_bang(t_until *x) { x->run=1; x->count=-1; while (x->run && x->count) {x->count--; outlet_bang(x->outlet);}} +static void until_float(t_until *x, t_float f) {x->run=1; x->count=(int)f; while (x->run && x->count) {x->count--; outlet_bang(x->outlet);}} +static void until_bang2(t_until *x) {x->run = 0;} +static void until_setup() { + until_class = class_new2("until",until_new,0,sizeof(t_until),0,""); + class_addbang(until_class, until_bang); + class_addfloat(until_class, until_float); + class_addmethod2(until_class, until_bang2,"bang2",""); +} + +static t_class *makefilename_class; +struct t_makefilename : t_object {t_symbol *format;}; +static void *makefilename_new(t_symbol *s) { + t_makefilename *x = (t_makefilename *)pd_new(makefilename_class); + if (!s->name) s = gensym("file.%d"); + outlet_new(x, &s_symbol); + x->format = s; + return x; +} + +/* doesn't do any typechecking or even counting the % signs properly */ +static void makefilename_float(t_makefilename *x, t_floatarg f) {outlet_symbol(x->outlet,symprintf(x->format->name,(int)f));} +static void makefilename_symbol(t_makefilename *x, t_symbol *s) {outlet_symbol(x->outlet,symprintf(x->format->name,s->name));} +static void makefilename_set(t_makefilename *x, t_symbol *s) {x->format = s;} + +static void makefilename_setup() { + t_class *c = makefilename_class = class_new2("makefilename",makefilename_new,0,sizeof(t_makefilename),0,"S"); + class_addfloat(c, makefilename_float); + class_addsymbol(c, makefilename_symbol); + class_addmethod2(c, makefilename_set, "set","s"); +} + +static t_class *swap_class; +struct t_swap : t_object { + t_float f1; + t_float f2; +}; +static void *swap_new(t_floatarg f) { + t_swap *x = (t_swap *)pd_new(swap_class); + x->f2 = f; + x->f1 = 0; + outlet_new(x, &s_float); + outlet_new(x, &s_float); + floatinlet_new(x, &x->f2); + return x; +} +static void swap_bang(t_swap *x) { + outlet_float(x->out(1), x->f1); + outlet_float(x->out(0), x->f2); +} +static void swap_float(t_swap *x, t_float f) { + x->f1 = f; + swap_bang(x); +} +void swap_setup() { + swap_class = class_new2("swap",swap_new,0,sizeof(t_swap),0,"F"); + class_addcreator2("fswap",swap_new,"F"); + class_addbang(swap_class, swap_bang); + class_addfloat(swap_class, swap_float); +} + +static t_class *change_class; +struct t_change : t_object {t_float f;}; +static void *change_new(t_floatarg f) { + t_change *x = (t_change *)pd_new(change_class); + x->f = f; + outlet_new(x, &s_float); + return x; +} +static void change_bang(t_change *x) {outlet_float(x->outlet, x->f);} +static void change_float(t_change *x, t_float f) { + if (f != x->f) { + x->f = f; + outlet_float(x->outlet, x->f); + } +} +static void change_set(t_change *x, t_float f) {x->f = f;} +void change_setup() { + change_class = class_new2("change",change_new,0,sizeof(t_change),0,"F"); + class_addbang(change_class, change_bang); + class_addfloat(change_class, change_float); + class_addmethod2(change_class, change_set, "set","F"); +} + +static t_class *value_class, *vcommon_class; +struct t_vcommon : t_pd { + int c_refcount; + t_float f; +}; +typedef struct t_value : t_object { + t_symbol *sym; + t_float *floatstar; +}; + +/* get a pointer to a named floating-point variable. The variable + belongs to a "vcommon" object, which is created if necessary. */ +t_float *value_get(t_symbol *s) { + t_vcommon *c = (t_vcommon *)pd_findbyclass(s, vcommon_class); + if (!c) { + c = (t_vcommon *)pd_new(vcommon_class); + c->f = 0; + c->c_refcount = 0; + pd_bind(c,s); + } + c->c_refcount++; + return &c->f; +} +/* release a variable. This only frees the "vcommon" resource when the last interested party releases it. */ +void value_release(t_symbol *s) { + t_vcommon *c = (t_vcommon *)pd_findbyclass(s, vcommon_class); + if (c) { + if (!--c->c_refcount) { + pd_unbind(c,s); + pd_free(c); + } + } else bug("value_release"); +} +int value_getfloat(t_symbol *s, t_float *f) {t_vcommon *c=(t_vcommon *)pd_findbyclass(s,vcommon_class); if (!c) return 1; *f=c->f;return 0;} +int value_setfloat(t_symbol *s, t_float f) {t_vcommon *c=(t_vcommon *)pd_findbyclass(s,vcommon_class); if (!c) return 1; c->f=f; return 0;} +static void *value_new(t_symbol *s) { + t_value *x = (t_value *)pd_new(value_class); + x->sym = s; + x->floatstar = value_get(s); + outlet_new(x, &s_float); + return x; +} +static void value_bang(t_value *x) {outlet_float(x->outlet, *x->floatstar);} +static void value_float(t_value *x, t_float f) {*x->floatstar = f;} +static void value_ff(t_value *x) {value_release(x->sym);} +static void value_setup() { + value_class = class_new2("value",value_new,value_ff,sizeof(t_value),0,"S"); + class_addcreator2("v",value_new,"S"); + class_addbang(value_class, value_bang); + class_addfloat(value_class, value_float); + vcommon_class = class_new2("value",0,0,sizeof(t_vcommon),CLASS_PD,""); +} + +/* MIDI. */ + +void outmidi_noteon(int portno, int channel, int pitch, int velo); +void outmidi_controlchange(int portno, int channel, int ctlno, int value); +void outmidi_programchange(int portno, int channel, int value); +void outmidi_pitchbend(int portno, int channel, int value); +void outmidi_aftertouch(int portno, int channel, int value); +void outmidi_polyaftertouch(int portno, int channel, int pitch, int value); +void outmidi_mclk(int portno); + +static t_symbol *midiin_sym, *sysexin_sym, *notein_sym, *ctlin_sym; +static t_class *midiin_class, *sysexin_class, *notein_class, *ctlin_class; +struct t_midiin : t_object {}; +struct t_notein : t_object {t_float ch;}; +struct t_ctlin : t_object {t_float ch; t_float ctlno;}; + +static void midiin_list(t_midiin *x, t_symbol *s, int ac, t_atom *av) { + outlet_float(x->out(1), atom_getfloatarg(1, ac, av) + 1); + outlet_float(x->out(0), atom_getfloatarg(0, ac, av)); +} +void inmidi_byte(int portno, int byte) { + if ( midiin_sym->thing) {t_atom at[2]; SETFLOAT(at, byte); SETFLOAT(at+1, portno+1); pd_list( midiin_sym->thing, 0, 2, at);} +} +void inmidi_sysex(int portno, int byte) { + if (sysexin_sym->thing) {t_atom at[2]; SETFLOAT(at, byte); SETFLOAT(at+1, portno+1); pd_list(sysexin_sym->thing, 0, 2, at);} +} + +static void notein_list(t_notein *x, t_symbol *s, int argc, t_atom *argv) { + float pitch = atom_getfloatarg(0, argc, argv); + float velo = atom_getfloatarg(1, argc, argv); + float channel = atom_getfloatarg(2, argc, argv); + if (x->ch) {if (channel != x->ch) return;} else outlet_float(x->out(2), channel); + outlet_float(x->out(1), velo); + outlet_float(x->out(0), pitch); +} + +void inmidi_noteon(int portno, int channel, int pitch, int velo) { + if (notein_sym->thing) { + t_atom at[3]; + SETFLOAT(at, pitch); + SETFLOAT(at+1, velo); + SETFLOAT(at+2, (channel + (portno << 4) + 1)); + pd_list(notein_sym->thing, &s_list, 3, at); + } +} + +static void *midiin_new() { + t_midiin *x = (t_midiin *)pd_new(midiin_class); + outlet_new(x, &s_float); + outlet_new(x, &s_float); + pd_bind(x, midiin_sym); + return x; +} +static void *sysexin_new() { + t_midiin *x = (t_midiin *)pd_new(sysexin_class); + outlet_new(x, &s_float); + outlet_new(x, &s_float); + pd_bind(x, sysexin_sym); + return x; +} +static void *notein_new(t_floatarg f) { + t_notein *x = (t_notein *)pd_new(notein_class); + x->ch = f; + outlet_new(x, &s_float); + outlet_new(x, &s_float); + if (f == 0) outlet_new(x, &s_float); + pd_bind(x, notein_sym); + return x; +} +static void *ctlin_new(t_symbol *s, int argc, t_atom *argv) { + t_ctlin *x = (t_ctlin *)pd_new(ctlin_class); + int ctlno = (int)(argc ? atom_getfloatarg(0, argc, argv) : -1); + int channel = (int)atom_getfloatarg(1, argc, argv); + x->ch = channel; + x->ctlno = ctlno; + outlet_new(x, &s_float); + if (!channel) { + if (x->ctlno < 0) outlet_new(x, &s_float); + outlet_new(x, &s_float); + } + pd_bind(x, ctlin_sym); + return x; +} +static void ctlin_list(t_ctlin *x, t_symbol *s, int argc, t_atom *argv) { + t_float ctlnumber = atom_getfloatarg(0, argc, argv); + t_float value = atom_getfloatarg(1, argc, argv); + t_float channel = atom_getfloatarg(2, argc, argv); + if (x->ctlno >= 0 && x->ctlno != ctlnumber) return; + if (x->ch > 0 && x->ch != channel) return; + if (x->ch == 0) outlet_float(x->out(2), channel); + if (x->ctlno < 0) outlet_float(x->out(1), ctlnumber); + outlet_float(x->out(0), value); +} + +static void midiin_free(t_midiin *x) {pd_unbind(x, midiin_sym);} +static void sysexin_free(t_midiin *x) {pd_unbind(x, sysexin_sym);} +static void notein_free(t_notein *x) {pd_unbind(x, notein_sym);} +static void ctlin_free(t_ctlin *x) {pd_unbind(x, ctlin_sym);} + +void inmidi_controlchange(int portno, int channel, int ctlnumber, int value) { + if (ctlin_sym->thing) { + t_atom at[3]; + SETFLOAT(at, ctlnumber); + SETFLOAT(at+1, value); + SETFLOAT(at+2, (channel + (portno << 4) + 1)); + pd_list(ctlin_sym->thing, &s_list, 3, at); + } +} + +struct t_midi2 : t_object {t_float ch;}; +static void *midi2_new(t_class *cl, t_floatarg ch) { + t_midi2 *x = (t_midi2 *)pd_new(cl); + x->ch = ch; + outlet_new(x, &s_float); + if (!ch) outlet_new(x, &s_float); + return x; +} +static void midi2_list(t_midi2 *x, t_symbol *s, int argc, t_atom *argv) { + float value = atom_getfloatarg(0, argc, argv); + float channel = atom_getfloatarg(1, argc, argv); + if (x->ch) {if (channel != x->ch) return;} else outlet_float(x->out(1), channel); + outlet_float(x->out(0), value); +} +static t_symbol *pgmin_sym, *bendin_sym, *touchin_sym; +static t_class *pgmin_class, *bendin_class, *touchin_class; +struct t_pgmin : t_midi2 {}; +struct t_bendin : t_midi2 {}; +struct t_touchin : t_midi2 {}; +static void *pgmin_new(t_floatarg f) { + t_pgmin *x = (t_pgmin *) midi2_new(pgmin_class ,f); pd_bind(x, pgmin_sym); return x;} +static void *bendin_new(t_floatarg f) { + t_bendin *x = (t_bendin *) midi2_new(bendin_class ,f); pd_bind(x, bendin_sym); return x;} +static void *touchin_new(t_floatarg f) { + t_touchin *x = (t_touchin *)midi2_new(touchin_class,f); pd_bind(x, touchin_sym); return x;} +static void pgmin_free( t_pgmin *x) {pd_unbind(x, pgmin_sym);} +static void bendin_free( t_bendin *x) {pd_unbind(x, bendin_sym);} +static void touchin_free(t_touchin *x) {pd_unbind(x, touchin_sym);} +void inmidi_programchange(int portno, int channel, int value) { + if (pgmin_sym->thing) { + t_atom at[2]; SETFLOAT(at,value+1); SETFLOAT(at+1, channel+(portno<<4)+1); pd_list( pgmin_sym->thing, &s_list, 2, at); + } +} +void inmidi_pitchbend(int portno, int channel, int value) { + if (bendin_sym->thing) { + t_atom at[2]; SETFLOAT(at,value); SETFLOAT(at+1, channel+(portno<<4)+1); pd_list( bendin_sym->thing, &s_list, 2, at); + } +} +void inmidi_aftertouch(int portno, int channel, int value) { + if (touchin_sym->thing) { + t_atom at[2]; SETFLOAT(at,value); SETFLOAT(at+1, channel+(portno<<4)+1); pd_list(touchin_sym->thing, &s_list, 2, at); + } +} + +/* ----------------------- polytouchin ------------------------- */ +static t_symbol *polytouchin_sym; +static t_class *polytouchin_class; +struct t_polytouchin : t_object { + t_float ch; +}; +static void *polytouchin_new(t_floatarg f) { + t_polytouchin *x = (t_polytouchin *)pd_new(polytouchin_class); + x->ch = f; + outlet_new(x, &s_float); + outlet_new(x, &s_float); + if (f == 0) outlet_new(x, &s_float); + pd_bind(x, polytouchin_sym); + return x; +} +static void polytouchin_list(t_polytouchin *x, t_symbol *s, int argc, t_atom *argv) { + t_float channel = atom_getfloatarg(2, argc, argv); + if (x->ch) {if (channel != x->ch) return;} else outlet_float(x->out(2), channel); + outlet_float(x->out(1), atom_getfloatarg(0, argc, argv)); /*pitch*/ + outlet_float(x->out(0), atom_getfloatarg(1, argc, argv)); /*value*/ +} +static void polytouchin_free(t_polytouchin *x) {pd_unbind(x, polytouchin_sym);} +static void polytouchin_setup() { + polytouchin_class = class_new2("polytouchin",polytouchin_new,polytouchin_free, + sizeof(t_polytouchin), CLASS_NOINLET,"F"); + class_addlist(polytouchin_class, polytouchin_list); + class_sethelpsymbol(polytouchin_class, gensym("midi")); + polytouchin_sym = gensym("#polytouchin"); +} +void inmidi_polyaftertouch(int portno, int channel, int pitch, int value) { + if (polytouchin_sym->thing) { + t_atom at[3]; + SETFLOAT(at, pitch); + SETFLOAT(at+1, value); + SETFLOAT(at+2, (channel + (portno << 4) + 1)); + pd_list(polytouchin_sym->thing, &s_list, 3, at); + } +} + +/*----------------------- midiclkin--(midi F8 message )---------------------*/ +static t_symbol *midiclkin_sym; +static t_class *midiclkin_class; +struct t_midiclkin : t_object {}; +static void *midiclkin_new(t_floatarg f) { + t_midiclkin *x = (t_midiclkin *)pd_new(midiclkin_class); + outlet_new(x, &s_float); + outlet_new(x, &s_float); + pd_bind(x, midiclkin_sym); + return x; +} +static void midiclkin_list(t_midiclkin *x, t_symbol *s, int argc, t_atom *argv) { + outlet_float(x->out(1), atom_getfloatarg(1, argc, argv)); /*count*/ + outlet_float(x->out(0), atom_getfloatarg(0, argc, argv)); /*value*/ +} +static void midiclkin_free(t_midiclkin *x) {pd_unbind(x, midiclkin_sym);} +static void midiclkin_setup() { + midiclkin_class = class_new2("midiclkin",midiclkin_new,midiclkin_free,sizeof(t_midiclkin),CLASS_NOINLET,"F"); + class_addlist(midiclkin_class, midiclkin_list); + class_sethelpsymbol(midiclkin_class, gensym("midi")); + midiclkin_sym = gensym("#midiclkin"); +} +void inmidi_clk(double timing) { + static float prev = 0; + static float count = 0; + if (midiclkin_sym->thing) { + t_atom at[2]; + float diff = timing - prev; + count++; + /* 24 count per quarter note */ + if (count == 3) {SETFLOAT(at, 1); count = 0;} else SETFLOAT(at, 0); + SETFLOAT(at+1, diff); + pd_list(midiclkin_sym->thing, &s_list, 2, at); + prev = timing; + } +} + +/*----------midirealtimein (midi FA,FB,FC,FF message)-----------------*/ +static t_symbol *midirealtimein_sym; +static t_class *midirealtimein_class; +struct t_midirealtimein : t_object {}; +static void *midirealtimein_new() { + t_midirealtimein *x = (t_midirealtimein *)pd_new(midirealtimein_class); + outlet_new(x, &s_float); + outlet_new(x, &s_float); + pd_bind(x, midirealtimein_sym); + return x; +} +static void midirealtimein_list(t_midirealtimein *x, t_symbol *s, int argc, t_atom *argv) { + outlet_float(x->out(1), atom_getfloatarg(0, argc, argv)); /*portno*/ + outlet_float(x->out(0), atom_getfloatarg(1, argc, argv)); /*byte*/ +} +static void midirealtimein_free(t_midirealtimein *x) {pd_unbind(x, midirealtimein_sym);} +static void midirealtimein_setup() { + midirealtimein_class = class_new2("midirealtimein",midirealtimein_new,midirealtimein_free, + sizeof(t_midirealtimein),CLASS_NOINLET,"F"); + class_addlist(midirealtimein_class, midirealtimein_list); + class_sethelpsymbol(midirealtimein_class, gensym("midi")); + midirealtimein_sym = gensym("#midirealtimein"); +} +void inmidi_realtimein(int portno, int SysMsg) { + if (midirealtimein_sym->thing) { + t_atom at[2]; + SETFLOAT(at, portno); + SETFLOAT(at+1, SysMsg); + pd_list(midirealtimein_sym->thing, &s_list, 2, at); + } +} + +void outmidi_byte(int portno, int byte); +static t_class *midiout_class; struct t_midiout : t_object {t_float portno;}; +static t_class *noteout_class; struct t_noteout : t_object {t_float v; t_float ch;}; +static t_class *ctlout_class; struct t_ctlout : t_object {t_float v; t_float ch;}; + +static void *noteout_new(t_floatarg channel) { + t_noteout *x = (t_noteout *)pd_new(noteout_class); + x->v = 0; + x->ch = channel<1?channel:1; + floatinlet_new(x, &x->v); + floatinlet_new(x, &x->ch); + return x; +} +static void *ctlout_new(t_floatarg v, t_floatarg channel) { + t_ctlout *x = (t_ctlout *)pd_new(ctlout_class); + x->v = v; + x->ch = channel<1?channel:1; + floatinlet_new(x, &x->v); + floatinlet_new(x, &x->ch); + return x; +} +static void *midiout_new(t_floatarg portno) { + t_midiout *x = (t_midiout *)pd_new(midiout_class); + if (portno <= 0) portno = 1; + x->portno = portno; + floatinlet_new(x, &x->portno); +#ifdef __irix__ + post("midiout: unimplemented in IRIX"); +#endif + return x; +} + +static void midiout_float(t_midiout *x, t_floatarg f){ + outmidi_byte((int)x->portno-1, (int)f); +} +static void noteout_float(t_noteout *x, t_float f) { + int binchan = max(0,(int)x->ch-1); + outmidi_noteon((binchan >> 4), (binchan & 15), (int)f, (int)x->v); +} +static void ctlout_float(t_ctlout *x, t_float f) { + int binchan = (int)x->ch - 1; + if (binchan < 0) binchan = 0; + outmidi_controlchange((binchan >> 4), (binchan & 15), (int)x->v, (int)f); +} + +static t_class *pgmout_class, *bendout_class, *touchout_class; +struct t_mido2 : t_object {t_float ch;}; +struct t_pgmout : t_mido2 {}; +struct t_bendout : t_mido2 {}; +struct t_touchout : t_mido2 {}; +static void *mido2_new(t_class *cl, t_floatarg channel) { + t_pgmout *x = (t_pgmout *)pd_new(cl); + x->ch = channel<1?channel:1; floatinlet_new(x, &x->ch); return x; +} +static void *pgmout_new( t_floatarg ch) {return mido2_new(pgmout_class,ch);} +static void *bendout_new( t_floatarg ch) {return mido2_new(bendout_class,ch);} +static void *touchout_new(t_floatarg ch) {return mido2_new(touchout_class,ch);} +static void pgmout_float(t_pgmout *x, t_floatarg f) { + int binchan = max(0,(int)x->ch-1); + outmidi_programchange(binchan>>4, binchan&15, min(127,max(0,(int)f-1))); +} +static void bendout_float(t_bendout *x, t_float f) { + int binchan = max(0,(int)x->ch-1); + outmidi_pitchbend(binchan>>4, binchan&15, (int)f+8192); +} +static void touchout_float(t_touchout *x, t_float f) { + int binchan = max(0,(int)x->ch-1); + outmidi_aftertouch(binchan>>4, binchan&15, (int)f); +} + +static t_class *polytouchout_class; +struct t_polytouchout : t_object { + t_float ch; + t_float pitch; +}; +static void *polytouchout_new(t_floatarg channel) { + t_polytouchout *x = (t_polytouchout *)pd_new(polytouchout_class); + x->ch = channel<1?channel:1; + x->pitch = 0; + floatinlet_new(x, &x->pitch); + floatinlet_new(x, &x->ch); + return x; +} +static void polytouchout_float(t_polytouchout *x, t_float n) { + int binchan = max(0,(int)x->ch-1); + outmidi_polyaftertouch((binchan >> 4), (binchan & 15), (int)x->pitch, (int)n); +} +static void polytouchout_setup() { + polytouchout_class = class_new2("polytouchout",polytouchout_new,0,sizeof(t_polytouchout),0,"F"); + class_addfloat(polytouchout_class, polytouchout_float); + class_sethelpsymbol(polytouchout_class, gensym("midi")); +} + +static t_class *makenote_class; +struct t_hang { + t_clock *clock; + t_hang *next; + t_float pitch; + struct t_makenote *owner; +}; +struct t_makenote : t_object { + t_float velo; + t_float dur; + t_hang *hang; +}; +static void *makenote_new(t_floatarg velo, t_floatarg dur) { + t_makenote *x = (t_makenote *)pd_new(makenote_class); + x->velo = velo; + x->dur = dur; + floatinlet_new(x, &x->velo); + floatinlet_new(x, &x->dur); + outlet_new(x, &s_float); + outlet_new(x, &s_float); + x->hang = 0; + return x; +} +static void makenote_tick(t_hang *hang) { + t_makenote *x = hang->owner; + t_hang *h2, *h3; + outlet_float(x->out(1), 0); + outlet_float(x->out(0), hang->pitch); + if (x->hang == hang) x->hang = hang->next; + else for (h2 = x->hang; (h3 = h2->next); h2 = h3) { + if (h3 == hang) { + h2->next = h3->next; + break; + } + } + clock_free(hang->clock); + free(hang); +} +static void makenote_float(t_makenote *x, t_float f) { + if (!x->velo) return; + outlet_float(x->out(1), x->velo); + outlet_float(x->out(0), f); + t_hang *hang = (t_hang *)getbytes(sizeof *hang); + hang->next = x->hang; + x->hang = hang; + hang->pitch = f; + hang->owner = x; + hang->clock = clock_new(hang, (t_method)makenote_tick); + clock_delay(hang->clock, (x->dur >= 0 ? x->dur : 0)); +} +static void makenote_stop(t_makenote *x) { + t_hang *hang; + while ((hang = x->hang)) { + outlet_float(x->out(1), 0); + outlet_float(x->out(0), hang->pitch); + x->hang = hang->next; + clock_free(hang->clock); + free(hang); + } +} +static void makenote_clear(t_makenote *x) { + t_hang *hang; + while ((hang = x->hang)) { + x->hang = hang->next; + clock_free(hang->clock); + free(hang); + } +} +static void makenote_setup() { + makenote_class = class_new2("makenote",makenote_new,makenote_clear,sizeof(t_makenote),0,"FF"); + class_addfloat(makenote_class, makenote_float); + class_addmethod2(makenote_class, makenote_stop, "stop",""); + class_addmethod2(makenote_class, makenote_clear,"clear",""); +} + +static t_class *stripnote_class; +struct t_stripnote : t_object { + t_float velo; +}; +static void *stripnote_new() { + t_stripnote *x = (t_stripnote *)pd_new(stripnote_class); + floatinlet_new(x, &x->velo); + outlet_new(x, &s_float); + outlet_new(x, &s_float); + return x; +} +static void stripnote_float(t_stripnote *x, t_float f) { + if (!x->velo) return; + outlet_float(x->out(1), x->velo); + outlet_float(x->out(0), f); +} +static void stripnote_setup() { + stripnote_class = class_new2("stripnote",stripnote_new,0,sizeof(t_stripnote),0,""); + class_addfloat(stripnote_class, stripnote_float); +} + +static t_class *poly_class; +struct t_voice { + float pitch; + int used; + unsigned long serial; +}; +struct t_poly : t_object { + int n; + t_voice *vec; + float vel; + unsigned long serial; + int steal; +}; +static void *poly_new(float fnvoice, float fsteal) { + int i, n = (int)fnvoice; + t_poly *x = (t_poly *)pd_new(poly_class); + t_voice *v; + if (n < 1) n = 1; + x->n = n; + x->vec = (t_voice *)getbytes(n * sizeof(*x->vec)); + for (v = x->vec, i = n; i--; v++) v->pitch = v->used = v->serial = 0; + x->vel = 0; + x->steal = (fsteal != 0); + floatinlet_new(x, &x->vel); + outlet_new(x, &s_float); + outlet_new(x, &s_float); + outlet_new(x, &s_float); + x->serial = 0; + return x; +} +static void poly_float(t_poly *x, t_float f) { + int i; + t_voice *v; + t_voice *firston, *firstoff; + unsigned int serialon, serialoff, onindex = 0, offindex = 0; + if (x->vel > 0) { + /* note on. Look for a vacant voice */ + for (v=x->vec, i=0, firston=firstoff=0, serialon=serialoff=0xffffffff; i<x->n; v++, i++) { + if (v->used && v->serial < serialon) + firston = v, serialon = v->serial, onindex = i; + else if (!v->used && v->serial < serialoff) + firstoff = v, serialoff = v->serial, offindex = i; + } + if (firstoff) { + outlet_float(x->out(2), x->vel); + outlet_float(x->out(1), firstoff->pitch = f); + outlet_float(x->out(0), offindex+1); + firstoff->used = 1; + firstoff->serial = x->serial++; + } + /* if none, steal one */ + else if (firston && x->steal) { + outlet_float(x->out(2), 0); + outlet_float(x->out(1), firston->pitch); + outlet_float(x->out(0), onindex+1); + outlet_float(x->out(2), x->vel); + outlet_float(x->out(1), firston->pitch = f); + outlet_float(x->out(0), onindex+1); + firston->serial = x->serial++; + } + } else { /* note off. Turn off oldest match */ + for (v = x->vec, i = 0, firston = 0, serialon = 0xffffffff; i < x->n; v++, i++) + if (v->used && v->pitch == f && v->serial < serialon) + firston = v, serialon = v->serial, onindex = i; + if (firston) { + firston->used = 0; + firston->serial = x->serial++; + outlet_float(x->out(2), 0); + outlet_float(x->out(1), firston->pitch); + outlet_float(x->out(0), onindex+1); + } + } +} +static void poly_stop(t_poly *x) { + t_voice *v = x->vec; + for (int i = 0; i < x->n; i++, v++) if (v->used) { + outlet_float(x->out(2), 0L); + outlet_float(x->out(1), v->pitch); + outlet_float(x->out(0), i+1); + v->used = 0; + v->serial = x->serial++; + } +} +static void poly_clear(t_poly *x) { + t_voice *v = x->vec; + for (int i = x->n; i--; v++) v->used = v->serial = 0; +} +static void poly_free(t_poly *x) {free(x->vec);} +static void poly_setup() { + poly_class = class_new2("poly",poly_new,poly_free,sizeof(t_poly),0,"FF"); + class_addfloat(poly_class, poly_float); + class_addmethod2(poly_class, poly_stop, "stop",""); + class_addmethod2(poly_class, poly_clear, "clear",""); +} + +static t_class *bag_class; +struct t_bagelem { + struct t_bagelem *next; + t_float value; +}; +struct t_bag : t_object { + t_float velo; + t_bagelem *first; +}; +static void *bag_new() { + t_bag *x = (t_bag *)pd_new(bag_class); + x->velo = 0; + floatinlet_new(x, &x->velo); + outlet_new(x, &s_float); + x->first = 0; + return x; +} +static void bag_float(t_bag *x, t_float f) { + t_bagelem *bagelem, *e2, *e3; + if (x->velo != 0) { + bagelem = (t_bagelem *)getbytes(sizeof *bagelem); + bagelem->next = 0; + bagelem->value = f; + if (!x->first) x->first = bagelem; + else { /* LATER replace with a faster algorithm */ + for (e2 = x->first; (e3 = e2->next); e2 = e3) {} + e2->next = bagelem; + } + } else { + if (!x->first) return; + if (x->first->value == f) { + bagelem = x->first; + x->first = x->first->next; + free(bagelem); + return; + } + for (e2 = x->first; (e3 = e2->next); e2 = e3) if (e3->value == f) { + e2->next = e3->next; + free(e3); + return; + } + } +} +static void bag_flush(t_bag *x) { + t_bagelem *bagelem; + while ((bagelem = x->first)) { + outlet_float(x->outlet, bagelem->value); + x->first = bagelem->next; + free(bagelem); + } +} +static void bag_clear(t_bag *x) { + t_bagelem *bagelem; + while ((bagelem = x->first)) { + x->first = bagelem->next; + free(bagelem); + } +} +static void bag_setup() { + bag_class = class_new2("bag",bag_new,bag_clear,sizeof(t_bag),0,""); + class_addfloat(bag_class, bag_float); + class_addmethod2(bag_class,bag_flush,"flush",""); + class_addmethod2(bag_class,bag_clear,"clear",""); +} +void midi_setup() { + midiin_class = class_new2( "midiin", midiin_new, midiin_free,sizeof(t_midiin),CLASS_NOINLET,"F"); + sysexin_class = class_new2("sysexin",sysexin_new,sysexin_free,sizeof(t_midiin),CLASS_NOINLET,"F"); + notein_class = class_new2( "notein", notein_new, notein_free,sizeof(t_notein),CLASS_NOINLET,"F"); + ctlin_class = class_new2( "ctlin", ctlin_new, ctlin_free,sizeof( t_ctlin),CLASS_NOINLET,"*"); + class_addlist( midiin_class, midiin_list); midiin_sym = gensym( "#midiin"); class_sethelpsymbol( midiin_class, gensym("midi")); + class_addlist(sysexin_class, midiin_list); sysexin_sym = gensym("#sysexin"); class_sethelpsymbol(sysexin_class, gensym("midi")); + class_addlist( notein_class, notein_list); notein_sym = gensym( "#notein"); class_sethelpsymbol( notein_class, gensym("midi")); + class_addlist( ctlin_class, ctlin_list); ctlin_sym = gensym( "#ctlin"); class_sethelpsymbol( ctlin_class, gensym("midi")); + + pgmin_class = class_new2("pgmin", pgmin_new, pgmin_free, sizeof(t_pgmin), CLASS_NOINLET,"F"); + bendin_class = class_new2("bendin", bendin_new, bendin_free, sizeof(t_bendin), CLASS_NOINLET,"F"); + touchin_class = class_new2("touchin",touchin_new,touchin_free,sizeof(t_touchin),CLASS_NOINLET,"F"); + class_addlist( pgmin_class,midi2_list); + class_addlist( bendin_class,midi2_list); + class_addlist(touchin_class,midi2_list); + class_sethelpsymbol( pgmin_class, gensym("midi")); + class_sethelpsymbol( bendin_class, gensym("midi")); + class_sethelpsymbol(touchin_class, gensym("midi")); + pgmin_sym = gensym("#pgmin"); + bendin_sym = gensym("#bendin"); + touchin_sym = gensym("#touchin"); + + polytouchin_setup(); midirealtimein_setup(); midiclkin_setup(); + midiout_class = class_new2("midiout", midiout_new, 0, sizeof(t_midiout), 0,"FF"); + ctlout_class = class_new2("ctlout", ctlout_new, 0, sizeof(t_ctlout), 0,"FF"); + noteout_class = class_new2("noteout", noteout_new, 0, sizeof(t_noteout), 0,"F"); + pgmout_class = class_new2("pgmout", pgmout_new, 0, sizeof(t_pgmout), 0,"F"); + bendout_class = class_new2("bendout", bendout_new, 0, sizeof(t_bendout), 0,"F"); + touchout_class = class_new2("touchout", touchout_new, 0, sizeof(t_touchout), 0,"F"); + class_addfloat( midiout_class, midiout_float); class_sethelpsymbol( midiout_class, gensym("midi")); + class_addfloat( ctlout_class, ctlout_float); class_sethelpsymbol( ctlout_class, gensym("midi")); + class_addfloat( noteout_class, noteout_float); class_sethelpsymbol( noteout_class, gensym("midi")); + class_addfloat( pgmout_class, pgmout_float); class_sethelpsymbol( pgmout_class, gensym("midi")); + class_addfloat( bendout_class, bendout_float); class_sethelpsymbol( bendout_class, gensym("midi")); + class_addfloat(touchout_class, touchout_float); class_sethelpsymbol(touchout_class, gensym("midi")); + polytouchout_setup(); makenote_setup(); stripnote_setup(); poly_setup(); bag_setup(); +} + +static t_class *delay_class; +struct t_delay : t_object { + t_clock *clock; + double deltime; +}; +static void delay_bang(t_delay *x) {clock_delay(x->clock, x->deltime);} +static void delay_stop(t_delay *x) {clock_unset(x->clock);} +static void delay_ft1(t_delay *x, t_floatarg g) {x->deltime = max(0.f,g);} +static void delay_float(t_delay *x, t_float f) {delay_ft1(x, f); delay_bang(x);} +static void delay_tick(t_delay *x) {outlet_bang(x->outlet);} +static void delay_free(t_delay *x) {clock_free(x->clock);} +static void *delay_new(t_floatarg f) { + t_delay *x = (t_delay *)pd_new(delay_class); + delay_ft1(x, f); + x->clock = clock_new(x, (t_method)delay_tick); + outlet_new(x, gensym("bang")); + inlet_new(x, x, gensym("float"), gensym("ft1")); + return x; +} +static void delay_setup() { + t_class *c = delay_class = class_new2("delay",delay_new,delay_free,sizeof(t_delay),0,"F"); + class_addcreator2("del",delay_new,"F"); + class_addbang(c, delay_bang); + class_addmethod2(c,delay_stop,"stop",""); + class_addmethod2(c,delay_ft1,"ft1","f"); + class_addfloat(c, delay_float); +} + +static t_class *metro_class; +struct t_metro : t_object { + t_clock *clock; + double deltime; + int hit; +}; +static void metro_tick(t_metro *x) { + x->hit = 0; + outlet_bang(x->outlet); + if (!x->hit) clock_delay(x->clock, x->deltime); +} +static void metro_float(t_metro *x, t_float f) { + if (f) metro_tick(x); else clock_unset(x->clock); + x->hit = 1; +} +static void metro_bang(t_metro *x) {metro_float(x, 1);} +static void metro_stop(t_metro *x) {metro_float(x, 0);} +static void metro_ft1(t_metro *x, t_floatarg g) {x->deltime = max(1.f,g);} +static void metro_free(t_metro *x) {clock_free(x->clock);} +static void *metro_new(t_floatarg f) { + t_metro *x = (t_metro *)pd_new(metro_class); + metro_ft1(x, f); + x->hit = 0; + x->clock = clock_new(x, (t_method)metro_tick); + outlet_new(x, gensym("bang")); + inlet_new(x, x, gensym("float"), gensym("ft1")); + return x; +} +static void metro_setup() { + t_class *c = metro_class = class_new2("metro",metro_new,metro_free,sizeof(t_metro),0,"F"); + class_addbang(c, metro_bang); + class_addmethod2(c,metro_stop,"stop",""); + class_addmethod2(c,metro_ft1,"ft1","f"); + class_addfloat(c, metro_float); +} + +static t_class *line_class; +struct t_line : t_object { + t_clock *clock; + double targettime; + t_float targetval; + double prevtime; + t_float setval; + int gotinlet; + t_float grain; + double oneovertimediff; + double in1val; +}; +static void line_tick(t_line *x) { + double timenow = clock_getsystime(); + double msectogo = - clock_gettimesince(x->targettime); + if (msectogo < 1E-9) { + outlet_float(x->outlet, x->targetval); + } else { + outlet_float(x->outlet, x->setval + x->oneovertimediff * (timenow-x->prevtime) * (x->targetval-x->setval)); + clock_delay(x->clock, (x->grain > msectogo ? msectogo : x->grain)); + } +} +static void line_float(t_line *x, t_float f) { + double timenow = clock_getsystime(); + if (x->gotinlet && x->in1val > 0) { + if (timenow > x->targettime) x->setval = x->targetval; + else x->setval = x->setval + x->oneovertimediff * (timenow-x->prevtime) * (x->targetval-x->setval); + x->prevtime = timenow; + x->targettime = clock_getsystimeafter(x->in1val); + x->targetval = f; + line_tick(x); + x->gotinlet = 0; + x->oneovertimediff = 1./ (x->targettime - timenow); + clock_delay(x->clock, (x->grain > x->in1val ? x->in1val : x->grain)); + } else { + clock_unset(x->clock); + x->targetval = x->setval = f; + outlet_float(x->outlet, f); + } + x->gotinlet = 0; +} +static void line_ft1(t_line *x, t_floatarg g) { + x->in1val = g; + x->gotinlet = 1; +} +static void line_stop(t_line *x) { + x->targetval = x->setval; + clock_unset(x->clock); +} +static void line_set(t_line *x, t_floatarg f) { + clock_unset(x->clock); + x->targetval = x->setval = f; +} +static void line_set_granularity(t_line *x, t_floatarg grain) { + if (grain <= 0) grain = 20; + x->grain = grain; +} +static void line_free(t_line *x) { + clock_free(x->clock); +} +static void *line_new(t_floatarg f, t_floatarg grain) { + t_line *x = (t_line *)pd_new(line_class); + x->targetval = x->setval = f; + x->gotinlet = 0; + x->oneovertimediff = 1; + x->clock = clock_new(x, (t_method)line_tick); + x->targettime = x->prevtime = clock_getsystime(); + line_set_granularity(x, grain); + outlet_new(x, gensym("float")); + inlet_new(x, x, gensym("float"), gensym("ft1")); + return x; +} +static void line_setup() { + t_class *c = line_class = class_new2("line",line_new,line_free,sizeof(t_line),0,"FF"); + class_addmethod2(c,line_ft1,"ft1","f"); + class_addmethod2(c,line_stop,"stop",""); + class_addmethod2(c,line_set,"set","f"); + class_addmethod2(c,line_set_granularity,"granularity","f"); + class_addfloat(c,line_float); +} + +static t_class *pipe_class; +struct t_hang2 { + t_clock *clock; + t_hang2 *next; + struct t_pipe *owner; + union word vec[0]; /* not the actual number of elements */ +}; +struct t_pipe : t_object { + int n; + float deltime; + t_atom *vec; + t_hang2 *hang; +}; +static void *pipe_new(t_symbol *s, int argc, t_atom *argv) { + t_pipe *x = (t_pipe *)pd_new(pipe_class); + t_atom defarg, *ap; + t_atom *vec, *vp; + float deltime=0; + if (argc) { + if (argv[argc-1].a_type != A_FLOAT) { + std::ostringstream os; + atom_ostream(&argv[argc-1], os); + post("pipe: %s: bad time delay value", os.str().data()); + } else deltime = argv[argc-1].a_float; + argc--; + } + if (!argc) { + argv = &defarg; + argc = 1; + SETFLOAT(&defarg, 0); + } + x->n = argc; + vec = x->vec = (t_atom *)getbytes(argc * sizeof(*x->vec)); + vp = vec; + ap = argv; + for (int i=0; i<argc; i++, ap++, vp++) { + if (ap->a_type == A_FLOAT) { + *vp = *ap; + outlet_new(x, &s_float); + if (i) floatinlet_new(x, &vp->a_float); + } else if (ap->a_type == A_SYMBOL) { + char c = *ap->a_symbol->name; + if (c=='s') {SETSYMBOL(vp, &s_symbol); outlet_new(x, &s_symbol); if (i) symbolinlet_new(x, &vp->a_symbol);} + else if (c=='p') {vp->a_type = A_POINTER; outlet_new(x, &s_pointer); /*if (i) pointerinlet_new(x, gp);*/} + else if (c=='f') {SETFLOAT(vp,0); outlet_new(x, &s_float); if (i) floatinlet_new(x, &vp->a_float);} + else error("pack: %s: bad type", ap->a_symbol->name); + } + } + floatinlet_new(x, &x->deltime); + x->hang = 0; + x->deltime = deltime; + return x; +} +static void hang_free(t_hang2 *h) { + clock_free(h->clock); + free(h); +} +static void hang_tick(t_hang2 *h) { + t_pipe *x = h->owner; + t_hang2 *h2, *h3; + if (x->hang == h) x->hang = h->next; + else for (h2 = x->hang; (h3 = h2->next); h2 = h3) { + if (h3 == h) { + h2->next = h3->next; + break; + } + } + t_atom *p = x->vec + x->n-1; + t_word *w = h->vec + x->n-1; + for (int i = x->n; i--; p--, w--) { + switch (p->a_type) { + case A_FLOAT: outlet_float( x->out(i), w->w_float ); break; + case A_SYMBOL: outlet_symbol( x->out(i), w->w_symbol ); break; + case A_POINTER:outlet_pointer(x->out(i), w->w_gpointer); break; + default:{} + } + } + hang_free(h); +} +static void pipe_list(t_pipe *x, t_symbol *s, int ac, t_atom *av) { + t_hang2 *h = (t_hang2 *)getbytes(sizeof(*h) + (x->n - 1) * sizeof(*h->vec)); + int n = x->n; + if (ac > n) ac = n; + t_atom *p = x->vec; + t_atom *ap = av; + for (int i=0; i<ac; i++, p++, ap++) *p = *ap; + t_word *w = h->vec; + p = x->vec; + for (int i=0; i< n; i++, p++, w++) *w = p->a_w; + h->next = x->hang; + x->hang = h; + h->owner = x; + h->clock = clock_new(h, (t_method)hang_tick); + clock_delay(h->clock, (x->deltime >= 0 ? x->deltime : 0)); +} +static void pipe_flush(t_pipe *x) { + while (x->hang) hang_tick(x->hang); +} +static void pipe_clear(t_pipe *x) { + t_hang2 *hang; + while ((hang = x->hang)) { + x->hang = hang->next; + hang_free(hang); + } +} +static void pipe_setup() { + pipe_class = class_new2("pipe",pipe_new,pipe_clear,sizeof(t_pipe),0,"*"); + class_addlist(pipe_class, pipe_list); + class_addmethod2(pipe_class,pipe_flush,"flush",""); + class_addmethod2(pipe_class,pipe_clear,"clear",""); +} + +/* ---------------------------------------------------------------- */ +/* new desiredata classes are below this point. */ + +static t_class *unpost_class; +struct t_unpost : t_object { + t_outlet *o0,*o1; +}; +struct t_unpost_frame { + t_unpost *self; + std::ostringstream buf; +}; +static t_unpost_frame *current_unpost; + +void *unpost_new (t_symbol *s) { + t_unpost *x = (t_unpost *)pd_new(unpost_class); + x->o0 = outlet_new(x,&s_symbol); + x->o1 = outlet_new(x,&s_symbol); + return x; +} +extern t_printhook sys_printhook; +void unpost_printhook (const char *s) { + std::ostringstream &b = current_unpost->buf; + b << s; + char *p; + const char *d=b.str().data(),*dd=d; + for (;;) { + p = strchr(d,'\n'); + if (!p) break; + outlet_symbol(current_unpost->self->o1,gensym2(d,p-d)); + d=p+1; + } + if (d!=dd) { + char *q = strdup(d); /* well i could use memmove, but i'm not supposed to use strcpy because of overlap */ + current_unpost->buf.clear(); + current_unpost->buf << q; + free(q); + } +} +void unpost_anything (t_unpost *x, t_symbol *s, int argc, t_atom *argv) { + t_printhook backup1 = sys_printhook; + t_unpost_frame *backup2 = current_unpost; + sys_printhook = unpost_printhook; + current_unpost = new t_unpost_frame; + current_unpost->self = x; + outlet_anything(x->o0,s,argc,argv); + sys_printhook = backup1; + current_unpost = backup2; +} + +struct t_unparse : t_object {}; +static t_class *unparse_class; +void *unparse_new (t_symbol *s) { + t_unparse *x = (t_unparse *)pd_new(unparse_class); + outlet_new(x,&s_symbol); + return x; +} +void unparse_list (t_unparse *x, t_symbol *s, int argc, t_atom *argv) { + std::ostringstream o; + for (int i=0; i<argc; i++) { + o << ' '; + atom_ostream(argv+i,o); + } + outlet_symbol(x->outlet,gensym(o.str().data()+1)); +} + +struct t_parse : t_object {}; +static t_class *parse_class; +void *parse_new (t_symbol *s) { + t_parse *x = (t_parse *)pd_new(parse_class); + outlet_new(x,&s_list); + return x; +} +void parse_symbol (t_unpost *x, t_symbol *s) { + t_binbuf *b = binbuf_new(); + binbuf_text(b,s->name,s->n); + outlet_anything(x->outlet,&s_list,b->n,b->v); + binbuf_free(b); +} + +struct t_tracecall : t_object {}; +static t_class *tracecall_class; +void *tracecall_new (t_symbol *s) { + t_tracecall *x = (t_tracecall *)pd_new(tracecall_class); + outlet_new(x,&s_list); + return x; +} +void tracecall_anything (t_tracecall *x, t_symbol *dummy, int dum, t_atom *my) { + t_atom a[2]; + for (int i=pd_stackn-1; i>=0; i--) { + SETSYMBOL( &a[0],pd_stack[i].self->_class->name); + SETSYMBOL( &a[1],pd_stack[i].s); + //SETPOINTER(&a[2],pd_stack[i].self); + outlet_list(x->outlet,&s_list,2,a); + } +} + +static void matju_setup() { + unpost_class = class_new2("unpost",unpost_new,0,sizeof(t_unpost),0,""); + class_addanything(unpost_class, unpost_anything); + unparse_class = class_new2("unparse",unparse_new,0,sizeof(t_unparse),0,""); + class_addlist(unparse_class, unparse_list); + parse_class = class_new2("parse",parse_new,0,sizeof(t_parse),0,""); + class_addsymbol(parse_class, parse_symbol); + tracecall_class = class_new2("tracecall",tracecall_new,0,sizeof(t_tracecall),0,""); + class_addanything(tracecall_class,tracecall_anything); +} + +/* end of new desiredata classes */ +/* ---------------------------------------------------------------- */ + +void builtins_setup() { + t_symbol *s = gensym("acoustics.pd"); + FUNC1DECL(mtof, "mtof"); + FUNC1DECL(ftom, "ftom"); + FUNC1DECL(powtodb,"powtodb"); + FUNC1DECL(rmstodb,"rmstodb"); + FUNC1DECL(dbtopow,"dbtopow"); + FUNC1DECL(dbtorms,"dbtorms"); + random_setup(); + loadbang_setup(); + namecanvas_setup(); + print_setup(); + macro_setup(); + display_setup(); + any_setup(); + clipboard_setup(); + delay_setup(); + metro_setup(); + line_setup(); + timer_setup(); + pipe_setup(); + misc_setup(); + sendreceive_setup(); + select_setup(); + route_setup(); + pack_setup(); + unpack_setup(); + trigger_setup(); + spigot_setup(); + moses_setup(); + until_setup(); + makefilename_setup(); + swap_setup(); + change_setup(); + value_setup(); + + t_class *c; + qlist_class = c = class_new2("qlist",qlist_new,qlist_free,sizeof(t_qlist),0,""); + class_addmethod2(c,qlist_rewind, "rewind",""); + class_addmethod2(c,qlist_next, "next","F"); + class_addmethod2(c,qlist_set, "set","*"); + class_addmethod2(c,qlist_clear, "clear",""); + class_addmethod2(c,qlist_add, "add","*"); + class_addmethod2(c,qlist_add2, "add2","*"); + class_addmethod2(c,qlist_add, "append","*"); + class_addmethod2(c,qlist_read, "read","sS"); + class_addmethod2(c,qlist_write, "write","sS"); + class_addmethod2(c,qlist_print, "print","S"); + class_addmethod2(c,qlist_tempo, "tempo","f"); + class_addbang(c,qlist_bang); + + textfile_class = c = class_new2("textfile",textfile_new,textfile_free,sizeof(t_textfile),0,""); + class_addmethod2(c,textfile_rewind, "rewind",""); + class_addmethod2(c,qlist_set, "set","*"); + class_addmethod2(c,qlist_clear, "clear",""); + class_addmethod2(c,qlist_add, "add","*"); + class_addmethod2(c,qlist_add2, "add2","*"); + class_addmethod2(c,qlist_add, "append","*"); + class_addmethod2(c,qlist_read, "read","sS"); + class_addmethod2(c,qlist_write, "write","sS"); + class_addmethod2(c,qlist_print, "print","S"); + class_addbang(c,textfile_bang); + netsend_setup(); + netreceive_setup(); + openpanel_setup(); + savepanel_setup(); + key_setup(); + +extern t_class *binbuf_class; + class_addlist(binbuf_class, alist_list); + class_addanything(binbuf_class, alist_anything); + list_setup(); + arithmetic_setup(); + midi_setup(); + matju_setup(); +} diff --git a/desiredata/src/builtins_dsp.c b/desiredata/src/builtins_dsp.c new file mode 100644 index 00000000..cf2d460e --- /dev/null +++ b/desiredata/src/builtins_dsp.c @@ -0,0 +1,3844 @@ +/* Copyright (c) 2007 Mathieu Bouchard + 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. +*/ + +//#define HAVE_LIBFFTW3F + +#define PD_PLUSPLUS_FACE +#include "desire.h" +#include "m_simd.h" +#include "s_stuff.h" +#include <math.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +extern int ugen_getsortno (); +#define DEFDELVS 64 /* LATER get this from canvas at DSP time */ +#ifdef HAVE_LIBFFTW3F +#include <fftw3.h> +#endif +#define DEFSENDVS 64 /* LATER get send to get this from canvas */ +#define LOGTEN 2.302585092994 +#define UNITBIT32 1572864. /* 3*2^19; bit 32 has place value 1 */ +#define int32 int +#ifdef BIGENDIAN +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#else +#define HIOFFSET 1 +#define LOWOFFSET 0 +#endif + +#undef CLASS_MAINSIGNALIN +/* because C++ bitches about null pointers, we'll use 8 instead (really): */ +#define CLASS_MAINSIGNALIN(c, type, field) class_domainsignalin(c, (char *)(&((type *)8)->field) - (char *)8) + +#define clock_new(a,b) clock_new(a,(t_method)b) + +#undef min +#undef max + +/* ----------------------------- plus ----------------------------- */ + +struct t_dop : t_object { + float a; + t_float b; /* inlet value, if necessary */ +}; + +#define DSPDECL(NAME) static t_class *NAME##_class, *scalar##NAME##_class; typedef t_dop t_##NAME, t_scalar##NAME; +DSPDECL(plus) +DSPDECL(minus) +DSPDECL(times) +DSPDECL(over) +DSPDECL(max) +DSPDECL(min) +DSPDECL(lt) +DSPDECL(gt) +DSPDECL(le) +DSPDECL(ge) +DSPDECL(eq) +DSPDECL(ne) + +#define DSPNEW(NAME,SYM) \ +static void *NAME##_new(t_symbol *s, int argc, t_atom *argv) { \ + if (argc > 1) error("extra arguments ignored"); \ + t_dop *x = (t_dop *)pd_new(argc ? scalar##NAME##_class : NAME##_class); \ + if (argc) { \ + floatinlet_new(x, &x->b); \ + x->b = atom_getfloatarg(0, argc, argv); \ + } else inlet_new(x, x, &s_signal, &s_signal); \ + outlet_new(x, &s_signal); \ + x->a = 0; \ + return x;} \ +static void NAME##_setup() { \ + t_class *c = NAME##_class = class_new2(SYM,NAME##_new,0,sizeof(t_dop),0,"*"); \ + class_addmethod2(c, NAME##_dsp, "dsp",""); \ + CLASS_MAINSIGNALIN(c, t_dop, a); \ + class_sethelpsymbol(NAME##_class, gensym("sigbinops")); \ + c = scalar##NAME##_class = class_new2(SYM,0,0,sizeof(t_dop),0,""); \ + CLASS_MAINSIGNALIN(c, t_dop, a); \ + class_addmethod2(c, scalar##NAME##_dsp, "dsp",""); \ + class_sethelpsymbol(scalar##NAME##_class, gensym("sigbinops"));} + +/* use when simd functions are present */ +#define DSPDSP(NAME) \ +static void NAME##_dsp(t_minus *x, t_signal **sp) { \ + const int n = sp[0]->n; \ + if(n&7) dsp_add(NAME##_perform, 4, sp[0]->v, sp[1]->v, sp[2]->v, n); \ + else if(SIMD_CHECK3(n,sp[0]->v,sp[1]->v,sp[2]->v)) \ + dsp_add(NAME##_perf_simd, 4, sp[0]->v, sp[1]->v, sp[2]->v, n); \ + else dsp_add(NAME##_perf8, 4, sp[0]->v, sp[1]->v, sp[2]->v, n);} \ +static void scalar##NAME##_dsp(t_scalarminus *x, t_signal **sp) { \ + const int n = sp[0]->n;\ + if(n&7) dsp_add(scalar##NAME##_perform, 4, sp[0]->v, &x->b,sp[1]->v, n);\ + else if(SIMD_CHECK2(n,sp[0]->v,sp[1]->v)) \ + dsp_add(scalar##NAME##_perf_simd, 4, sp[0]->v, &x->b, sp[1]->v, n);\ + else dsp_add(scalar##NAME##_perf8, 4, sp[0]->v, &x->b, sp[1]->v, n);} + +/* use when simd functions are missing */ +#define DSPDSP2(NAME) \ +static void NAME##_dsp(t_minus *x, t_signal **sp) { \ + const int n = sp[0]->n; \ + if(n&7) dsp_add(NAME##_perform, 4, sp[0]->v, sp[1]->v, sp[2]->v, n); \ + else dsp_add(NAME##_perf8, 4, sp[0]->v, sp[1]->v, sp[2]->v, n);} \ +static void scalar##NAME##_dsp(t_scalarminus *x, t_signal **sp) { \ + const int n = sp[0]->n;\ + if(n&7) dsp_add(scalar##NAME##_perform, 4, sp[0]->v, &x->b,sp[1]->v, n);\ + else dsp_add(scalar##NAME##_perf8, 4, sp[0]->v, &x->b, sp[1]->v, n);} + +#define PERFORM(NAME,EXPR) \ +t_int *NAME##_perform(t_int *w) { \ + t_float *in1 = (t_float *)w[1], *in2 = (t_float *)w[2], *out = (t_float *)w[3]; \ + int n = (int)w[4]; \ + while (n--) {t_float a=*in1++, b=*in2++; *out++ = (EXPR);} \ + return w+5;} \ +t_int *NAME##_perf8(t_int *w) { \ + t_float *in1 = (t_float *)w[1], *in2 = (t_float *)w[2], *out = (t_float *)w[3]; \ + int n = (int)w[4]; \ + for (; n; n -= 8, in1 += 8, in2 += 8, out += 8) { \ + {t_float a=in1[0], b=in2[0]; out[0] = (EXPR);} \ + {t_float a=in1[1], b=in2[1]; out[1] = (EXPR);} \ + {t_float a=in1[2], b=in2[2]; out[2] = (EXPR);} \ + {t_float a=in1[3], b=in2[3]; out[3] = (EXPR);} \ + {t_float a=in1[4], b=in2[4]; out[4] = (EXPR);} \ + {t_float a=in1[5], b=in2[5]; out[5] = (EXPR);} \ + {t_float a=in1[6], b=in2[6]; out[6] = (EXPR);} \ + {t_float a=in1[7], b=in2[7]; out[7] = (EXPR);}} \ + return w+5;} \ +t_int *scalar##NAME##_perform(t_int *w) { \ + t_float *in = (t_float *)w[1]; t_float b = *(t_float *)w[2]; t_float *out = (t_float *)w[3]; \ + int n = (int)w[4]; \ + while (n--) {t_float a=*in++; *out++ = (EXPR);} \ + return w+5;} \ +t_int *scalar##NAME##_perf8(t_int *w) { \ + t_float *in = (t_float *)w[1]; t_float b = *(t_float *)w[2]; t_float *out = (t_float *)w[3]; \ + int n = (int)w[4]; \ + for (; n; n -= 8, in += 8, out += 8) { \ + {t_float a=in[0]; out[0] = (EXPR);} \ + {t_float a=in[1]; out[1] = (EXPR);} \ + {t_float a=in[2]; out[2] = (EXPR);} \ + {t_float a=in[3]; out[3] = (EXPR);} \ + {t_float a=in[4]; out[4] = (EXPR);} \ + {t_float a=in[5]; out[5] = (EXPR);} \ + {t_float a=in[6]; out[6] = (EXPR);} \ + {t_float a=in[7]; out[7] = (EXPR);}} \ + 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 if(SIMD_CHECK3(n,in1,in2,out)) dsp_add(plus_perf_simd, 4, in1, in2, out, n); + else dsp_add(plus_perf8, 4, in1, in2, out, n); +} + +/* T.Grill - squaring: optimized * for equal input signals */ +t_int *sqr_perf8(t_int *w) { + t_float *in = (t_float *)w[1]; + t_float *out = (t_float *)w[2]; + int n = (int)w[3]; + 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 * f0; out[1] = f1 * f1; out[2] = f2 * f2; out[3] = f3 * f3; + out[4] = f4 * f4; out[5] = f5 * f5; out[6] = f6 * f6; out[7] = f7 * f7; + } + return w+4; +} + +/* T.Grill - added optimization for equal input signals */ +static void times_dsp(t_times *x, t_signal **sp) { + const int n = sp[0]->n; + if (n&7) dsp_add(times_perform, 4, sp[0]->v, sp[1]->v, sp[2]->v, n); + else if(sp[0]->v == sp[1]->v) { + if(SIMD_CHECK2(n,sp[0]->v,sp[2]->v)) + dsp_add(sqr_perf_simd, 3, sp[0]->v, sp[2]->v, n); + else dsp_add(sqr_perf8, 3, sp[0]->v, sp[2]->v, n); + } else { + if(SIMD_CHECK3(n,sp[0]->v,sp[1]->v,sp[2]->v)) + dsp_add(times_perf_simd, 4, sp[0]->v, sp[1]->v, sp[2]->v, n); + else dsp_add(times_perf8, 4, sp[0]->v, sp[1]->v, sp[2]->v, n); + } +} +static void scalartimes_dsp(t_scalartimes *x, t_signal **sp) { + const int n = sp[0]->n; + if (n&7) dsp_add(scalartimes_perform, 4, sp[0]->v, &x->b,sp[1]->v, n); + else if(SIMD_CHECK2(n,sp[0]->v,sp[1]->v)) + dsp_add(scalartimes_perf_simd, 4, sp[0]->v, &x->b, sp[1]->v, n); + else dsp_add(scalartimes_perf8, 4, sp[0]->v, &x->b, sp[1]->v, n); +} + +PERFORM(plus ,a+b) DSPDSP(plus) DSPNEW(plus ,"+~") +PERFORM(minus,a-b) DSPDSP(minus) DSPNEW(minus,"-~") +PERFORM(times,a*b) /*DSPDSP(times)*/ DSPNEW(times,"*~") +/*PERFORM(over,a/b)*/ DSPDSP(over) DSPNEW(over ,"/~") +PERFORM(min ,a<b?a:b) DSPDSP(min) DSPNEW(min ,"min~") +PERFORM(max ,a>b?a:b) DSPDSP(max) DSPNEW(max ,"max~") +PERFORM(lt ,a<b) DSPDSP2(lt) DSPNEW(lt ,"<~") +PERFORM(gt ,a>b) DSPDSP2(gt) DSPNEW(gt ,">~") +PERFORM(le ,a<=b) DSPDSP2(le) DSPNEW(le ,"<=~") +PERFORM(ge ,a>=b) DSPDSP2(ge) DSPNEW(ge ,">=~") +PERFORM(eq ,a==b) DSPDSP2(eq) DSPNEW(eq ,"==~") +PERFORM(ne ,a!=b) DSPDSP2(ne) DSPNEW(ne ,"!=~") + +t_int *over_perform(t_int *w) { + t_float *in1 = (t_float *)w[1], *in2 = (t_float *)w[2], *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.Grill - added check for zero */ +t_int *scalarover_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]; + if(f) f = 1./f; + 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) { + out[0] = in[0] * g; out[1] = in[1] * g; out[2] = in[2] * g; out[3] = in[3] * g; + out[4] = in[4] * g; out[5] = in[5] * g; out[6] = in[6] * g; out[7] = in[7] * g; + } + return w+5; +} + +/* ------------------------- tabwrite~ -------------------------- */ +static t_class *tabwrite_tilde_class; +struct t_tabwrite_tilde : t_object { + int phase; + int nsampsintab; + float *vec; + t_symbol *arrayname; + float a; +}; +static void *tabwrite_tilde_new(t_symbol *s) { + t_tabwrite_tilde *x = (t_tabwrite_tilde *)pd_new(tabwrite_tilde_class); + x->phase = 0x7fffffff; + x->arrayname = s; + x->a = 0; + return x; +} +static void tabwrite_tilde_redraw(t_tabwrite_tilde *x) { + t_garray *a = (t_garray *)pd_findbyclass(x->arrayname, garray_class); + if (!a) bug("tabwrite_tilde_redraw"); + else garray_redraw(a); +} +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->phase, endphase = x->nsampsintab; + if (!x->vec) goto bad; + if (endphase > phase) { + int nxfer = endphase - phase; + float *fp = x->vec + phase; + if (nxfer > n) nxfer = n; + phase += nxfer; + testcopyvec(fp, in, nxfer); + if (phase >= endphase) { + tabwrite_tilde_redraw(x); + phase = 0x7fffffff; + } + x->phase = phase; + } else x->phase = 0x7fffffff; +bad: + return w+4; +} +static t_int *tabwrite_tilde_perf_simd(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->phase, endphase = x->nsampsintab; + if (!x->vec) goto bad; + if (endphase > phase) { + int nxfer = endphase - phase; + float *fp = x->vec + phase; + if (nxfer > n) nxfer = n; + phase += nxfer; + if (SIMD_CHKCNT(nxfer)) testcopyvec_simd(fp, in, nxfer); + else testcopyvec(fp, in, nxfer); + if (phase >= endphase) { + tabwrite_tilde_redraw(x); + phase = 0x7fffffff; + } + x->phase = phase; + } else x->phase = 0x7fffffff; +bad: + return w+4; +} +void tabwrite_tilde_set(t_tabwrite_tilde *x, t_symbol *s) { + x->arrayname = s; + t_garray *a = (t_garray *)pd_findbyclass(x->arrayname, garray_class); + if (!a) { + if (*s->name) error("tabwrite~: %s: no such array", x->arrayname->name); + x->vec = 0; + } else if (!garray_getfloatarray(a, &x->nsampsintab, &x->vec)) { + error("%s: bad template for tabwrite~", x->arrayname->name); + x->vec = 0; + } else garray_usedindsp(a); +} +static void tabwrite_tilde_dsp(t_tabwrite_tilde *x, t_signal **sp) { + tabwrite_tilde_set(x, x->arrayname); + if (SIMD_CHECK1(sp[0]->n, sp[0]->v)) + dsp_add(tabwrite_tilde_perf_simd, 3, x, sp[0]->v, sp[0]->n); + else dsp_add(tabwrite_tilde_perform, 3, x, sp[0]->v, sp[0]->n); +} +static void tabwrite_tilde_bang(t_tabwrite_tilde *x) {x->phase = 0;} +static void tabwrite_tilde_start(t_tabwrite_tilde *x, t_floatarg f) {x->phase = (int)max((int)f,0);} +static void tabwrite_tilde_stop(t_tabwrite_tilde *x) { + if (x->phase != 0x7fffffff) { + tabwrite_tilde_redraw(x); + x->phase = 0x7fffffff; + } +} + +/* ------------ tabplay~ - non-transposing sample playback --------------- */ +static t_class *tabplay_tilde_class; +struct t_tabplay_tilde : t_object { + int phase; + int nsampsintab; + int limit; + float *vec; + t_symbol *arrayname; + t_clock *clock; +}; +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->clock = clock_new(x, tabplay_tilde_tick); + x->phase = 0x7fffffff; + x->limit = 0; + x->arrayname = s; + outlet_new(x, &s_signal); + outlet_new(x, &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->phase, endphase = (x->nsampsintab < x->limit ? x->nsampsintab : x->limit), nxfer, n3; + if (!x->vec || phase >= endphase) {while (n--) *out++ = 0; goto bye;} + nxfer = min(endphase-phase,n); + fp = x->vec + phase; + n3 = n - nxfer; + phase += nxfer; + while (nxfer--) *out++ = *fp++; + if (phase >= endphase) { + clock_delay(x->clock, 0); + x->phase = 0x7fffffff; + while (n3--) *out++ = 0; + } else x->phase = phase; + return w+4; +bye: + return w+4; +} +void tabplay_tilde_set(t_tabplay_tilde *x, t_symbol *s) { + t_garray *a = (t_garray *)pd_findbyclass(x->arrayname, garray_class); + x->arrayname = s; + if (!a) { + if (*s->name) error("tabplay~: %s: no such array", x->arrayname->name); + x->vec = 0; + } else if (!garray_getfloatarray(a, &x->nsampsintab, &x->vec)) { + error("%s: bad template for tabplay~", x->arrayname->name); + x->vec = 0; + } else garray_usedindsp(a); +} +static void tabplay_tilde_dsp(t_tabplay_tilde *x, t_signal **sp) { + tabplay_tilde_set(x, x->arrayname); + dsp_add(tabplay_tilde_perform, 3, x, sp[0]->v, sp[0]->n); +} +static void tabplay_tilde_list(t_tabplay_tilde *x, t_symbol *s, int argc, t_atom *argv) { + long start = atom_getintarg(0, argc, argv); + long length = atom_getintarg(1, argc, argv); + if (start < 0) start = 0; + if (length <= 0) x->limit = 0x7fffffff; else x->limit = start + length; + x->phase = start; +} +static void tabplay_tilde_stop(t_tabplay_tilde *x) {x->phase = 0x7fffffff;} +static void tabplay_tilde_tick(t_tabplay_tilde *x) {outlet_bang(x->out(1));} +static void tabplay_tilde_free(t_tabplay_tilde *x) {clock_free(x->clock);} + +/******************** tabread~ ***********************/ +static t_class *tabread_tilde_class; +struct t_tabread_tilde : t_object { + int npoints; + float *vec; + t_symbol *arrayname; + float a; +}; +static void *tabread_tilde_new(t_symbol *s) { + t_tabread_tilde *x = (t_tabread_tilde *)pd_new(tabread_tilde_class); + x->arrayname = s; + x->vec = 0; + outlet_new(x, &s_signal); + x->a = 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]; + float *buf = x->vec; + int maxindex = x->npoints - 1; + if (!buf) {while (n--) *out++ = 0; goto bad;} + for (int i = 0; i < n; i++) { + int index = (int)*in++; + if (index < 0) index = 0; else if (index > maxindex) index = maxindex; + *out++ = buf[index]; + } +bad:return w+5; +} +void tabread_tilde_set(t_tabread_tilde *x, t_symbol *s) { + x->arrayname = s; + t_garray *a = (t_garray *)pd_findbyclass(x->arrayname, garray_class); + if (!a) { + if (*s->name) error("tabread~: %s: no such array", x->arrayname->name); + x->vec = 0; + } else if (!garray_getfloatarray(a, &x->npoints, &x->vec)) { + error("%s: bad template for tabread~", x->arrayname->name); + x->vec = 0; + } else garray_usedindsp(a); +} +static void tabread_tilde_dsp(t_tabread_tilde *x, t_signal **sp) { + tabread_tilde_set(x, x->arrayname); + dsp_add(tabread_tilde_perform, 4, x, sp[0]->v, sp[1]->v, sp[0]->n); +} +static void tabread_tilde_free(t_tabread_tilde *x) {} + +/******************** tabread4~ ***********************/ +static t_class *tabread4_tilde_class; +struct t_tabread4_tilde : t_object { + int npoints; + float *vec; + t_symbol *arrayname; + float a; +}; +static void *tabread4_tilde_new(t_symbol *s) { + t_tabread4_tilde *x = (t_tabread4_tilde *)pd_new(tabread4_tilde_class); + x->arrayname = s; + x->vec = 0; + outlet_new(x, &s_signal); + x->a = 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->vec, *fp; + maxindex = 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 (int i=0; i<n; i++) { + float findex = *in++; + int index = (int)findex; + float frac; + if (index < 1) index = 1, frac = 0; + else if (index > maxindex) index = maxindex, frac = 1; + else frac = findex - index; + fp = buf + index; + float 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); */ + float cminusb = c-b; + *out++ = b + frac * (cminusb - 0.1666667f * (1.-frac) * ((d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b))); + } + return w+5; + zero: + while (n--) *out++ = 0; + return w+5; +} +void tabread4_tilde_set(t_tabread4_tilde *x, t_symbol *s) { + x->arrayname = s; + t_garray *a = (t_garray *)pd_findbyclass(x->arrayname, garray_class); + if (!a) { + if (*s->name) error("tabread4~: %s: no such array", x->arrayname->name); + x->vec = 0; + } else if (!garray_getfloatarray(a, &x->npoints, &x->vec)) { + error("%s: bad template for tabread4~", x->arrayname->name); + x->vec = 0; + } else garray_usedindsp(a); +} +static void tabread4_tilde_dsp(t_tabread4_tilde *x, t_signal **sp) { + tabread4_tilde_set(x, x->arrayname); + dsp_add(tabread4_tilde_perform, 4, x, sp[0]->v, sp[1]->v, sp[0]->n); +} +static void tabread4_tilde_free(t_tabread4_tilde *x) {} + +/******************** tabosc4~ ***********************/ + +#define UNITBIT32 1572864. /* 3*2^19; bit 32 has place value 1 */ + +#ifdef BIGENDIAN +#define HIOFFSET 0 /* word offset to find MSB */ +#define LOWOFFSET 1 /* word offset to find LSB */ +#else +#define HIOFFSET 1 +#define LOWOFFSET 0 +#endif +#include <sys/types.h> +//#define int32 int32_t +#define int32 int + +union tabfudge { + double d; + int32 i[2]; +}; +static t_class *tabosc4_tilde_class; +struct t_tabosc4_tilde : t_object { + float fnpoints; + float finvnpoints; + float *vec; + t_symbol *arrayname; + float a; + double phase; + float conv; +}; +static void *tabosc4_tilde_new(t_symbol *s) { + t_tabosc4_tilde *x = (t_tabosc4_tilde *)pd_new(tabosc4_tilde_class); + x->arrayname = s; + x->vec = 0; + x->fnpoints = 512.; + x->finvnpoints = (1./512.); + outlet_new(x, &s_signal); + inlet_new(x, x, &s_float, gensym("ft1")); + x->a = 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]; + union tabfudge tf; + float fnpoints = x->fnpoints; + int mask = (int)(fnpoints-1); + float conv = fnpoints * x->conv; + float *tab = x->vec, *addr; + double dphase = fnpoints * x->phase + UNITBIT32; + if (!tab) {while (n--) *out++ = 0; return w+5;} + tf.d = UNITBIT32; + int normhipart = tf.i[HIOFFSET]; +#if 1 + while (n--) { + tf.d = dphase; + dphase += *in++ * conv; + addr = tab + (tf.i[HIOFFSET] & mask); + tf.i[HIOFFSET] = normhipart; + float frac = tf.d - UNITBIT32; + float a = addr[0]; + float b = addr[1]; + float c = addr[2]; + float d = addr[3]; + float cminusb = c-b; + *out++ = b + frac * (cminusb - 0.1666667f * (1.-frac) * ((d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b))); + } +#endif + tf.d = UNITBIT32 * fnpoints; + normhipart = tf.i[HIOFFSET]; + tf.d = dphase + (UNITBIT32 * fnpoints - UNITBIT32); + tf.i[HIOFFSET] = normhipart; + x->phase = (tf.d - UNITBIT32 * fnpoints) * x->finvnpoints; + return w+5; +} +void tabosc4_tilde_set(t_tabosc4_tilde *x, t_symbol *s) { + t_garray *a = (t_garray *)pd_findbyclass(x->arrayname, garray_class); + int npoints, pointsinarray; + x->arrayname = s; + if (!a) { + if (*s->name) error("tabosc4~: %s: no such array", x->arrayname->name); + x->vec = 0; + } else if (!garray_getfloatarray(a, &pointsinarray, &x->vec)) { + error("%s: bad template for tabosc4~", x->arrayname->name); + x->vec = 0; + } else if ((npoints = pointsinarray - 3) != (1 << ilog2(pointsinarray - 3))) { + error("%s: number of points (%d) not a power of 2 plus three", x->arrayname->name, pointsinarray); + x->vec = 0; + garray_usedindsp(a); + } else { + x->fnpoints = npoints; + x->finvnpoints = 1./npoints; + garray_usedindsp(a); + } +} +static void tabosc4_tilde_ft1(t_tabosc4_tilde *x, t_float f) { + x->phase = f; +} +static void tabosc4_tilde_dsp(t_tabosc4_tilde *x, t_signal **sp) { + x->conv = 1. / sp[0]->sr; + tabosc4_tilde_set(x, x->arrayname); + dsp_add(tabosc4_tilde_perform, 4, x, sp[0]->v, sp[1]->v, sp[0]->n); +} +static void tabosc4_tilde_setup() { +} + +static void tab_tilde_setup() { + t_class *c; + c = tabwrite_tilde_class = class_new2("tabwrite~",tabwrite_tilde_new,0,sizeof(t_tabwrite_tilde),0,"S"); + CLASS_MAINSIGNALIN(c, t_tabwrite_tilde, a); + class_addmethod2(c, tabwrite_tilde_dsp, "dsp",""); + class_addmethod2(c, tabwrite_tilde_set, "set","s"); + class_addmethod2(c, tabwrite_tilde_stop,"stop",""); + class_addmethod2(c, tabwrite_tilde_start,"start","F"); + class_addbang(c, tabwrite_tilde_bang); + c = tabplay_tilde_class = class_new2("tabplay~",tabplay_tilde_new,tabplay_tilde_free,sizeof(t_tabplay_tilde),0,"S"); + class_addmethod2(c, tabplay_tilde_dsp, "dsp",""); + class_addmethod2(c, tabplay_tilde_stop, "stop",""); + class_addmethod2(c, tabplay_tilde_set, "set","S"); + class_addlist(c, tabplay_tilde_list); + c = tabread_tilde_class = class_new2("tabread~",tabread_tilde_new,tabread_tilde_free, sizeof(t_tabread_tilde),0,"S"); + CLASS_MAINSIGNALIN(c, t_tabread_tilde, a); + class_addmethod2(c, tabread_tilde_dsp, "dsp",""); + class_addmethod2(c, tabread_tilde_set, "set","s"); + c = tabread4_tilde_class = class_new2("tabread4~",tabread4_tilde_new,tabread4_tilde_free,sizeof(t_tabread4_tilde),0,"S"); + CLASS_MAINSIGNALIN(c, t_tabread4_tilde, a); + class_addmethod2(c, tabread4_tilde_dsp, "dsp",""); + class_addmethod2(c, tabread4_tilde_set, "set","s"); + c = tabosc4_tilde_class = class_new2("tabosc4~",tabosc4_tilde_new,0,sizeof(t_tabosc4_tilde),0,"S"); + CLASS_MAINSIGNALIN(c, t_tabosc4_tilde, a); + class_addmethod2(c, tabosc4_tilde_dsp, "dsp",""); + class_addmethod2(c, tabosc4_tilde_set, "set","s"); + class_addmethod2(c, tabosc4_tilde_ft1, "ft1","f"); +} + +/* ------------------------ tabsend~ ------------------------- */ +static t_class *tabsend_class; +struct t_tabsend : t_object { + float *vec; + int graphperiod; + int graphcount; + t_symbol *arrayname; + float a; +}; +static void *tabsend_new(t_symbol *s) { + t_tabsend *x = (t_tabsend *)pd_new(tabsend_class); + x->graphcount = 0; + x->arrayname = s; + x->a = 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->vec; + int i = x->graphcount; + if (!x->vec) goto bad; + testcopyvec(dest,in,n); + if (!i--) { + t_garray *a = (t_garray *)pd_findbyclass(x->arrayname, garray_class); + if (!a) bug("tabsend_dsp"); + else garray_redraw(a); + i = x->graphperiod; + } + x->graphcount = i; +bad: + return w+4; +} +static t_int *tabsend_perf_simd(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->vec; + int i = x->graphcount; + if (!x->vec) goto bad; + testcopyvec_simd(dest,in,n); + if (!i--) { + t_garray *a = (t_garray *)pd_findbyclass(x->arrayname, garray_class); + if (!a) bug("tabsend_dsp"); + else garray_redraw(a); + i = x->graphperiod; + } + x->graphcount = i; +bad: + return w+4; +} +static void tabsend_dsp(t_tabsend *x, t_signal **sp) { + int vecsize; + t_garray *a = (t_garray *)pd_findbyclass(x->arrayname, garray_class); + if (!a) { + if (*x->arrayname->name) {error("tabsend~: %s: no such array", x->arrayname->name); return;} + } else if (!garray_getfloatarray(a, &vecsize, &x->vec)) { + error("%s: bad template for tabsend~", x->arrayname->name); return; + } else { + int n = sp[0]->n; + int ticksper = (int)(sp[0]->sr/n); + if (ticksper < 1) ticksper = 1; + x->graphperiod = ticksper; + if (x->graphcount > ticksper) x->graphcount = ticksper; + if (n < vecsize) vecsize = n; + garray_usedindsp(a); + if(SIMD_CHECK1(vecsize,sp[0]->v)) + dsp_add(tabsend_perf_simd, 3, x, sp[0]->v, vecsize); + else dsp_add(tabsend_perform, 3, x, sp[0]->v, vecsize); + } +} + +static void tabsend_setup() { + tabsend_class = class_new2("tabsend~",tabsend_new,0,sizeof(t_tabsend),0,"S"); + CLASS_MAINSIGNALIN(tabsend_class, t_tabsend, a); + class_addmethod2(tabsend_class, tabsend_dsp, "dsp",""); +} + +/* ------------------------ tabreceive~ ------------------------- */ +static t_class *tabreceive_class; +struct t_tabreceive : t_object { + float *vec; + int vecsize; + t_symbol *arrayname; +}; +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->vec; + if (from) { + int vecsize = x->vecsize; while (vecsize--) *out++ = *from++; + vecsize = n - x->vecsize; while (vecsize--) *out++ = 0; + } else while (n--) *out++ = 0; + return w+4; +} +static t_int *tabreceive_perf8(t_int *w) { + t_tabreceive *x = (t_tabreceive *)w[1]; + t_float *from = x->vec; + if (from) copyvec_8((t_float *)w[2],from,w[3]); + else zerovec_8((t_float *)w[2], w[3]); + return w+4; +} +static t_int *tabreceive_perfsimd(t_int *w) { + t_tabreceive *x = (t_tabreceive *)w[1]; + t_float *from = x->vec; + if(from) copyvec_simd((t_float *)w[2],from,w[3]); + else zerovec_simd((t_float *)w[2], w[3]); + return w+4; +} +static void tabreceive_dsp(t_tabreceive *x, t_signal **sp) { + t_garray *a; + if (!(a = (t_garray *)pd_findbyclass(x->arrayname, garray_class))) { + if (*x->arrayname->name) error("tabsend~: %s: no such array", x->arrayname->name); + } else if (!garray_getfloatarray(a, &x->vecsize, &x->vec)) { + error("%s: bad template for tabreceive~", x->arrayname->name); + } else { + if (x->vecsize > sp[0]->n) x->vecsize = sp[0]->n; + garray_usedindsp(a); + /* the array is aligned in any case */ + if(sp[0]->n&7) dsp_add(tabreceive_perform, 3, x, sp[0]->v, sp[0]->n); + else if(SIMD_CHECK1(sp[0]->n,sp[0]->v)) + dsp_add(tabreceive_perfsimd, 3, x, sp[0]->v, sp[0]->n); + else dsp_add(tabreceive_perf8, 3, x, sp[0]->v, sp[0]->n); + } +} +static void *tabreceive_new(t_symbol *s) { + t_tabreceive *x = (t_tabreceive *)pd_new(tabreceive_class); + x->arrayname = s; + outlet_new(x, &s_signal); + return x; +} +static void tabreceive_setup() { + tabreceive_class = class_new2("tabreceive~",tabreceive_new,0,sizeof(t_tabreceive),0,"S"); + class_addmethod2(tabreceive_class, tabreceive_dsp, "dsp",""); +} + +/* ---------- tabread: control, non-interpolating ------------------------ */ +static t_class *tabread_class; +struct t_tabread : t_object { + t_symbol *arrayname; +}; +static void tabread_float(t_tabread *x, t_float f) { + int npoints; + t_float *vec; + t_garray *a = (t_garray *)pd_findbyclass(x->arrayname, garray_class); + if (!a) {error("%s: no such array", x->arrayname->name); return;} + if (!garray_getfloatarray(a, &npoints, &vec)) {error("%s: bad template for tabread", x->arrayname->name); return;} + int n = clip(int(f),0,npoints-1); + outlet_float(x->outlet, (npoints ? vec[n] : 0)); +} +static void tabread_set(t_tabread *x, t_symbol *s) { + x->arrayname = s; +} +static void *tabread_new(t_symbol *s) { + t_tabread *x = (t_tabread *)pd_new(tabread_class); + x->arrayname = s; + outlet_new(x, &s_float); + return x; +} +static void tabread_setup() { + tabread_class = class_new2("tabread",tabread_new,0,sizeof(t_tabread),0,"S"); + class_addfloat(tabread_class, tabread_float); + class_addmethod2(tabread_class, tabread_set, "set","s"); +} + +/* ---------- tabread4: control, 4-point interpolation --------------- */ +static t_class *tabread4_class; +struct t_tabread4 : t_object { + t_symbol *arrayname; +}; +static void tabread4_float(t_tabread4 *x, t_float f) { + t_garray *ar = (t_garray *)pd_findbyclass(x->arrayname, garray_class); + int npoints; + t_float *vec; + if (!ar) {error("%s: no such array", x->arrayname->name); return;} + if (!garray_getfloatarray(ar, &npoints, &vec)) {error("%s: bad template for tabread4", x->arrayname->name); return;} + if (npoints < 4) {outlet_float(x->outlet, 0); return;} + if (f <= 1) {outlet_float(x->outlet, vec[1]); return;} + if (f >= npoints-2) {outlet_float(x->outlet, vec[npoints-2]); return;} + int n = min(int(f),npoints-3); + float *fp = vec + n; + float frac = f - n; + float a=fp[-1], b=fp[0], c=fp[1], d=fp[2]; + float cminusb = c-b; + outlet_float(x->outlet, b + frac * (cminusb - 0.1666667f * (1.-frac) * ((d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b)))); +} +static void tabread4_set(t_tabread4 *x, t_symbol *s) {x->arrayname = s;} +static void *tabread4_new(t_symbol *s) { + t_tabread4 *x = (t_tabread4 *)pd_new(tabread4_class); + x->arrayname = s; + outlet_new(x, &s_float); + return x; +} +static void tabread4_setup() { + tabread4_class = class_new2("tabread4",tabread4_new,0,sizeof(t_tabread4),0,"S"); + class_addfloat(tabread4_class, tabread4_float); + class_addmethod2(tabread4_class, tabread4_set, "set","s"); +} + +/* ------------------ tabwrite: control ------------------------ */ +static t_class *tabwrite_class; +struct t_tabwrite : t_object { + t_symbol *arrayname; + float ft1; +}; +static void tabwrite_float(t_tabwrite *x, t_float f) { + int vecsize; + t_garray *a = (t_garray *)pd_findbyclass(x->arrayname, garray_class); + t_float *vec; + if (!a) {error("%s: no such array", x->arrayname->name); return;} + if (!garray_getfloatarray(a, &vecsize, &vec)) {error("%s: bad template for tabwrite", x->arrayname->name); return;} + int n = (int)x->ft1; + if (n < 0) n = 0; else if (n > vecsize-1) n = vecsize-1; + vec[n] = f; + garray_redraw(a); +} +static void tabwrite_set(t_tabwrite *x, t_symbol *s) {x->arrayname = s;} +static void *tabwrite_new(t_symbol *s) { + t_tabwrite *x = (t_tabwrite *)pd_new(tabwrite_class); + x->ft1 = 0; + x->arrayname = s; + floatinlet_new(x, &x->ft1); + return x; +} +void tabwrite_setup() { + tabwrite_class = class_new2("tabwrite",tabwrite_new,0,sizeof(t_tabwrite),0,"S"); + class_addfloat(tabwrite_class, tabwrite_float); + class_addmethod2(tabwrite_class, tabwrite_set, "set","s"); +} + +/* -------------------------- sig~ ------------------------------ */ +static t_class *sig_tilde_class; +struct t_sig : t_object { + float a; +}; +t_int *sig_tilde_perform(t_int *w) { + t_float f = *(t_float *)w[1]; + t_float *out = (t_float *)w[2]; + int n = (int)w[3]; + while (n--) *out++ = f; + return w+4; +} +t_int *sig_tilde_perf8(t_int *w) { + t_float f = *(t_float *)w[1]; + t_float *out = (t_float *)w[2]; + int n = (int)w[3]; + for (; n; n -= 8, out += 8) { + out[0] = f; out[1] = f; + out[2] = f; out[3] = f; + out[4] = f; out[5] = f; + out[6] = f; out[7] = f; + } + return w+4; +} +void dsp_add_scalarcopy(t_sample *in, t_sample *out, int n) { + if (n&7) + dsp_add(sig_tilde_perform, 3, in, out, n); + else if(SIMD_CHECK1(n,out)) + dsp_add(sig_tilde_perf_simd, 3, in, out, n); + else dsp_add(sig_tilde_perf8, 3, in, out, n); +} +static void sig_tilde_float(t_sig *x, t_float f) { + x->a = f; +} +static void sig_tilde_dsp(t_sig *x, t_signal **sp) { +/* dsp_add(sig_tilde_perform, 3, &x->a, sp[0]->v, sp[0]->n); */ + /* T.Grill - use chance of unrolling */ + dsp_add_scalarcopy(&x->a, sp[0]->v, sp[0]->n); +} +static void *sig_tilde_new(t_floatarg f) { + t_sig *x = (t_sig *)pd_new(sig_tilde_class); + x->a = f; + outlet_new(x, &s_signal); + return x; +} +static void sig_tilde_setup() { + sig_tilde_class = class_new2("sig~",sig_tilde_new,0,sizeof(t_sig),0,"F"); + class_addfloat(sig_tilde_class, sig_tilde_float); + class_addmethod2(sig_tilde_class, sig_tilde_dsp,"dsp",""); +} + +/* -------------------------- line~ ------------------------------ */ +static t_class *line_tilde_class; +struct t_line : t_object { + float target; + float value; + float biginc; + float inc; + float oneovern; + float dspticktomsec; + float inletvalue; + float inletwas; + int ticksleft; + int retarget; + float* slopes; /* tb: for simd-optimized line */ + float slopestep; /* tb: 4*x->inc */ +}; +static t_int *line_tilde_perform(t_int *w) { + t_line *x = (t_line *)w[1]; + t_float *out = (t_float *)w[2]; + int n = (int)w[3]; + float f = x->value; + if (PD_BIGORSMALL(f)) x->value = f = 0; + if (x->retarget) { + int nticks = (int)(x->inletwas * x->dspticktomsec); + if (!nticks) nticks = 1; + x->ticksleft = nticks; + x->biginc = (x->target - x->value)/(float)nticks; + x->inc = x->oneovern * x->biginc; + x->retarget = 0; + } + if (x->ticksleft) { + float f = x->value; + float slope = x->inc; /* tb: make sure, x->inc is loaded to a register */ + while (n--) *out++ = f, f += slope; + x->value += x->biginc; + x->ticksleft--; + } else { + float g = x->value = x->target; + while (n--) + *out++ = g; + } + return w+4; +} +/* tb: vectorized / simd version { */ +static void line_tilde_slope(t_float* out, t_int n, t_float* value, t_float* slopes, t_float* slopestep) { + t_float slope = slopes[1]; + t_float f = *value; + n>>=3; + while (n--) { + *out++ = f;f += slope; + *out++ = f;f += slope; + *out++ = f;f += slope; + *out++ = f;f += slope; + *out++ = f;f += slope; + *out++ = f;f += slope; + *out++ = f;f += slope; + *out++ = f;f += slope; + } +} +static t_int *line_tilde_perf8(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->value; + if (PD_BIGORSMALL(f)) x->value = f = 0; + if (x->retarget) { + int nticks = (int)(x->inletwas * x->dspticktomsec); + if (!nticks) nticks = 1; + x->ticksleft = nticks; + x->biginc = (x->target - x->value)/(float)nticks; + x->inc = x->oneovern * x->biginc; + x->retarget = 0; + /* tb: rethink!!! this is ugly */ + for (int i = 0; i != 4; ++i) x->slopes[i] = i*x->inc; + x->slopestep = 4 * x->inc; + } + if (x->ticksleft) { + line_tilde_slope(out, n, &x->value, x->slopes, &x->slopestep); + x->value += x->biginc; + x->ticksleft--; + } else { + float f = x->value = x->target; + setvec_8(out,f,n); + } + return w+4; +} +static t_int *line_tilde_perfsimd(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->value; + if (PD_BIGORSMALL(f)) x->value = f = 0; + if (x->retarget) { + int nticks = (int)(x->inletwas * x->dspticktomsec); + if (!nticks) nticks = 1; + x->ticksleft = nticks; + x->biginc = (x->target - x->value)/(float)nticks; + x->inc = x->oneovern * x->biginc; + x->retarget = 0; + for (int i = 0; i != 4; ++i) x->slopes[i] = i*x->inc; + x->slopestep = 4 * x->inc; + } + if (x->ticksleft) { + line_tilde_slope_simd(out, n, &x->value, x->slopes, &x->slopestep); + x->value += x->biginc; + x->ticksleft--; + } else { + float f = x->value = x->target; + setvec_simd(out,f,n); + } + return w+4; +} +/* tb } */ +static void line_tilde_float(t_line *x, t_float f) { + if (x->inletvalue <= 0) { + x->target = x->value = f; + x->ticksleft = x->retarget = 0; + } else { + x->target = f; + x->retarget = 1; + x->inletwas = x->inletvalue; + x->inletvalue = 0; + } +} +static void line_tilde_stop(t_line *x) { + x->target = x->value; + x->ticksleft = x->retarget = 0; +} +static void line_tilde_dsp(t_line *x, t_signal **sp) { + if(sp[0]->n&7) + dsp_add(line_tilde_perform, 3, x, sp[0]->v, sp[0]->n); + else if (SIMD_CHECK1(sp[0]->n,sp[0]->v)) + dsp_add(line_tilde_perfsimd, 3, x, sp[0]->v, sp[0]->n); + else dsp_add(line_tilde_perf8, 3, x, sp[0]->v, sp[0]->n); + x->oneovern = 1./sp[0]->n; + x->dspticktomsec = sp[0]->sr / (1000 * sp[0]->n); +} +/* tb: modified for simd-optimized line~ */ +static void *line_tilde_new() { + t_line *x = (t_line *)pd_new(line_tilde_class); + outlet_new(x, &s_signal); + floatinlet_new(x, &x->inletvalue); + x->ticksleft = x->retarget = 0; + x->value = x->target = x->inletvalue = x->inletwas = 0; + x->slopes = (t_float *)getalignedbytes(4*sizeof(t_float)); + return x; +} + +static void line_tilde_free(t_line * x) { + freealignedbytes(x->slopes, 4*sizeof(t_float)); +} +static void line_tilde_setup() { + line_tilde_class = class_new2("line~",line_tilde_new,line_tilde_free,sizeof(t_line),0,""); + class_addfloat(line_tilde_class, line_tilde_float); + class_addmethod2(line_tilde_class, line_tilde_dsp, "dsp",""); + class_addmethod2(line_tilde_class, line_tilde_stop,"stop",""); +} + +/* -------------------------- vline~ ------------------------------ */ +static t_class *vline_tilde_class; +struct t_vseg { + double s_targettime; + double s_starttime; + float s_target; + t_vseg *s_next; +}; +struct t_vline : t_object { + double value; + double inc; + double referencetime; + double samppermsec; + double msecpersamp; + double targettime; + float target; + float inlet1; + float inlet2; + t_vseg *list; +}; +static t_int *vline_tilde_perform(t_int *w) { + t_vline *x = (t_vline *)w[1]; + t_float *out = (t_float *)w[2]; + int n = (int)w[3], i; + double f = x->value; + double inc = x->inc; + double msecpersamp = x->msecpersamp; + double timenow = clock_gettimesince(x->referencetime) - n * msecpersamp; + t_vseg *s = x->list; + for (i = 0; i < n; i++) { + double timenext = timenow + msecpersamp; + checknext: + if (s) { + /* has starttime elapsed? If so update value and increment */ + if (s->s_starttime < timenext) { + if (x->targettime <= timenext) + f = x->target, inc = 0; + /* if zero-length segment bash output value */ + if (s->s_targettime <= s->s_starttime) { + f = s->s_target; + inc = 0; + } else { + double incpermsec = (s->s_target - f)/ + (s->s_targettime - s->s_starttime); + f = f + incpermsec * (timenext - s->s_starttime); + inc = incpermsec * msecpersamp; + } + x->inc = inc; + x->target = s->s_target; + x->targettime = s->s_targettime; + x->list = s->s_next; + free(s); + s = x->list; + goto checknext; + } + } + if (x->targettime <= timenext) + f = x->target, inc = x->inc = 0, x->targettime = 1e20; + *out++ = f; + f = f + inc; + timenow = timenext; + } + x->value = f; + return w+4; +} +static void vline_tilde_stop(t_vline *x) { + t_vseg *s1, *s2; + for (s1 = x->list; s1; s1 = s2) + s2 = s1->s_next, free(s1); + x->list = 0; + x->inc = 0; + x->inlet1 = x->inlet2 = 0; + x->target = x->value; + x->targettime = 1e20; +} +static void vline_tilde_float(t_vline *x, t_float f) { + double timenow = clock_gettimesince(x->referencetime); + float inlet1 = (x->inlet1 < 0 ? 0 : x->inlet1); + float inlet2 = x->inlet2; + double starttime = timenow + inlet2; + t_vseg *s1, *s2, *deletefrom = 0, *snew; + if (PD_BIGORSMALL(f)) f = 0; + /* negative delay input means stop and jump immediately to new value */ + if (inlet2 < 0) { + x->value = f; + vline_tilde_stop(x); + return; + } + snew = (t_vseg *)t_getbytes(sizeof(*snew)); + /* check if we supplant the first item in the list. We supplant + an item by having an earlier starttime, or an equal starttime unless + the equal one was instantaneous and the new one isn't (in which case + we'll do a jump-and-slide starting at that time.) */ + if (!x->list || x->list->s_starttime > starttime || + (x->list->s_starttime == starttime && + (x->list->s_targettime > x->list->s_starttime || inlet1 <= 0))) { + deletefrom = x->list; + x->list = snew; + } else { + for (s1 = x->list; (s2 = s1->s_next); s1 = s2) { + if (s2->s_starttime > starttime || + (s2->s_starttime == starttime && + (s2->s_targettime > s2->s_starttime || inlet1 <= 0))) { + deletefrom = s2; + s1->s_next = snew; + goto didit; + } + } + s1->s_next = snew; + deletefrom = 0; + didit: ; + } + while (deletefrom) { + s1 = deletefrom->s_next; + free(deletefrom); + deletefrom = s1; + } + snew->s_next = 0; + snew->s_target = f; + snew->s_starttime = starttime; + snew->s_targettime = starttime + inlet1; + x->inlet1 = x->inlet2 = 0; +} +static void vline_tilde_dsp(t_vline *x, t_signal **sp) { + dsp_add(vline_tilde_perform, 3, x, sp[0]->v, sp[0]->n); + x->samppermsec = ((double)(sp[0]->sr)) / 1000; + x->msecpersamp = ((double)1000) / sp[0]->sr; +} +static void *vline_tilde_new() { + t_vline *x = (t_vline *)pd_new(vline_tilde_class); + outlet_new(x, &s_signal); + floatinlet_new(x, &x->inlet1); + floatinlet_new(x, &x->inlet2); + x->inlet1 = x->inlet2 = 0; + x->value = x->inc = 0; + x->referencetime = clock_getlogicaltime(); + x->list = 0; + x->samppermsec = 0; + x->targettime = 1e20; + return x; +} +static void vline_tilde_setup() { + vline_tilde_class = class_new2("vline~",vline_tilde_new,vline_tilde_stop,sizeof(t_vline),0,""); + class_addfloat(vline_tilde_class, vline_tilde_float); + class_addmethod2(vline_tilde_class, vline_tilde_dsp, "dsp",""); + class_addmethod2(vline_tilde_class, vline_tilde_stop,"stop",""); +} + +/* -------------------------- snapshot~ ------------------------------ */ +static t_class *snapshot_tilde_class; +struct t_snapshot : t_object { + t_sample value; + float a; +}; +static void *snapshot_tilde_new() { + t_snapshot *x = (t_snapshot *)pd_new(snapshot_tilde_class); + x->value = 0; + outlet_new(x, &s_float); + x->a = 0; + return x; +} +static t_int *snapshot_tilde_perform(t_int *w) { + t_float *in = (t_float *)w[1]; + t_float *out = (t_float *)w[2]; + *out = *in; + return w+3; +} +static void snapshot_tilde_dsp(t_snapshot *x, t_signal **sp) { + dsp_add(snapshot_tilde_perform, 2, sp[0]->v + (sp[0]->n-1), &x->value); +} +static void snapshot_tilde_bang(t_snapshot *x) { + outlet_float(x->outlet, x->value); +} +static void snapshot_tilde_set(t_snapshot *x, t_floatarg f) { + x->value = f; +} +static void snapshot_tilde_setup() { + snapshot_tilde_class = class_new2("snapshot~",snapshot_tilde_new,0,sizeof(t_snapshot),0,""); + CLASS_MAINSIGNALIN(snapshot_tilde_class, t_snapshot, a); + class_addmethod2(snapshot_tilde_class, snapshot_tilde_dsp, "dsp",""); + class_addmethod2(snapshot_tilde_class, snapshot_tilde_set, "set","s"); + class_addbang(snapshot_tilde_class, snapshot_tilde_bang); +} + +/* -------------------------- vsnapshot~ ------------------------------ */ +static t_class *vsnapshot_tilde_class; +struct t_vsnapshot : t_object { + int n; + int gotone; + t_sample *vec; + float a; + float sampspermsec; + double time; +}; +static void *vsnapshot_tilde_new() { + t_vsnapshot *x = (t_vsnapshot *)pd_new(vsnapshot_tilde_class); + outlet_new(x, &s_float); + x->a = 0; + x->n = 0; + x->vec = 0; + x->gotone = 0; + return x; +} +static t_int *vsnapshot_tilde_perform(t_int *w) { + t_float *in = (t_float *)w[1]; + t_vsnapshot *x = (t_vsnapshot *)w[2]; + t_float *out = x->vec; + int n = x->n, i; + for (i = 0; i < n; i++) out[i] = in[i]; + x->time = clock_getlogicaltime(); + x->gotone = 1; + return w+3; +} +static void vsnapshot_tilde_dsp(t_vsnapshot *x, t_signal **sp) { + int n = sp[0]->n; + if (n != x->n) { + if (x->vec) free(x->vec); + x->vec = (t_sample *)getbytes(n * sizeof(t_sample)); + x->gotone = 0; + x->n = n; + } + x->sampspermsec = sp[0]->sr / 1000; + dsp_add(vsnapshot_tilde_perform, 2, sp[0]->v, x); +} +static void vsnapshot_tilde_bang(t_vsnapshot *x) { + float val; + if (x->gotone) { + int indx = clip((int)(clock_gettimesince(x->time) * x->sampspermsec),0,x->n-1); + val = x->vec[indx]; + } else val = 0; + outlet_float(x->outlet, val); +} +static void vsnapshot_tilde_ff(t_vsnapshot *x) { + if (x->vec) free(x->vec); +} +static void vsnapshot_tilde_setup() { + vsnapshot_tilde_class = + class_new2("vsnapshot~",vsnapshot_tilde_new, vsnapshot_tilde_ff,sizeof(t_vsnapshot), 0,""); + CLASS_MAINSIGNALIN(vsnapshot_tilde_class, t_vsnapshot, a); + class_addmethod2(vsnapshot_tilde_class, vsnapshot_tilde_dsp, "dsp",""); + class_addbang(vsnapshot_tilde_class, vsnapshot_tilde_bang); +} + +/* ---------------- env~ - simple envelope follower. ----------------- */ +#define MAXOVERLAP 10 +#define MAXVSTAKEN 64 +struct t_sigenv : t_object { + t_clock *clock; /* a "clock" object */ + float *buf; /* a Hanning window */ + int phase; /* number of points since last output */ + int period; /* requested period of output */ + int realperiod; /* period rounded up to vecsize multiple */ + int npoints; /* analysis window size in samples */ + float result; /* result to output */ + float sumbuf[MAXOVERLAP]; /* summing buffer */ + float a; +}; +t_class *env_tilde_class; +static void env_tilde_tick(t_sigenv *x); +static void *env_tilde_new(t_floatarg fnpoints, t_floatarg fperiod) { + int npoints = (int)fnpoints; + int period = (int)fperiod; + float *buf; + if (npoints < 1) npoints = 1024; + if (period < 1) period = npoints/2; + if (period < npoints / MAXOVERLAP + 1) + period = npoints / MAXOVERLAP + 1; + if (!(buf = (float *)getalignedbytes(sizeof(float) * (npoints + MAXVSTAKEN)))) { + error("env: couldn't allocate buffer"); + return 0; + } + t_sigenv *x = (t_sigenv *)pd_new(env_tilde_class); + x->buf = buf; + x->npoints = npoints; + x->phase = 0; + x->period = period; + int i; + for (i = 0; i < MAXOVERLAP; i++) 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->clock = clock_new(x, env_tilde_tick); + x->outlet = outlet_new(x, &s_float); + x->a = 0; + return x; +} +static t_int *env_tilde_perform(t_int *w) { + t_sigenv *x = (t_sigenv *)w[1]; + t_float *in = (t_float *)w[2]; + int n = (int)w[3]; + float *sump = x->sumbuf; + in += n; + for (int count = x->phase; count < x->npoints; count += x->realperiod, sump++) { + float *hp = x->buf + count; + float *fp = in; + float sum = *sump; + for (int i = 0; i < n; i++) { + fp--; + sum += *hp++ * (*fp * *fp); + } + *sump = sum; + } + sump[0] = 0; + x->phase -= n; + if (x->phase < 0) { + x->result = x->sumbuf[0]; + sump = x->sumbuf; + for (int count = x->realperiod; count < x->npoints; count += x->realperiod, sump++) sump[0] = sump[1]; + sump[0] = 0; + x->phase = x->realperiod - n; + clock_delay(x->clock, 0L); + } + return w+4; +} +/* tb: loop unrolling and simd */ +static float env_tilde_accum_8(t_float* in, t_float* hp, t_int n) { + float ret = 0; + n>>=3; + for (int i = 0; i !=n; ++i) { + in--; ret += *hp++ * (*in * *in); + in--; ret += *hp++ * (*in * *in); + in--; ret += *hp++ * (*in * *in); + in--; ret += *hp++ * (*in * *in); + in--; ret += *hp++ * (*in * *in); + in--; ret += *hp++ * (*in * *in); + in--; ret += *hp++ * (*in * *in); + in--; ret += *hp++ * (*in * *in); + } + return ret; +} +static t_int *env_tilde_perf8(t_int *w) { + t_sigenv *x = (t_sigenv *)w[1]; + t_float *in = (t_float *)w[2]; + int n = (int)w[3]; + float *sump = x->sumbuf; + in += n; + for (int count = x->phase; count < x->npoints; count += x->realperiod, sump++) { + *sump += env_tilde_accum_8(in, x->buf + count, n); + } + sump[0] = 0; + x->phase -= n; + if (x->phase < 0) { + x->result = x->sumbuf[0]; + float *sump = x->sumbuf; + for (int count = x->realperiod; count < x->npoints; count += x->realperiod, sump++) + sump[0] = sump[1]; + sump[0] = 0; + x->phase = x->realperiod - n; + clock_delay(x->clock, 0L); + } + return w+4; +} +static t_int *env_tilde_perf_simd(t_int *w) { + t_sigenv *x = (t_sigenv *)w[1]; + t_float *in = (t_float *)w[2]; + int n = (int)w[3]; + float *sump = x->sumbuf; + in += n; + for (int count = x->phase; count < x->npoints; count += x->realperiod, sump++) { + *sump += env_tilde_accum_simd(in, x->buf + count, n); + } + sump[0] = 0; + x->phase -= n; + if (x->phase < 0) { + x->result = x->sumbuf[0]; + float *sump = x->sumbuf; + for (int count = x->realperiod; count < x->npoints; count += x->realperiod, sump++) sump[0] = sump[1]; + sump[0] = 0; + x->phase = x->realperiod - n; + clock_delay(x->clock, 0L); + } + return w+4; +} +static void env_tilde_dsp(t_sigenv *x, t_signal **sp) { + int mod = x->period % sp[0]->n; + if (mod) x->realperiod = x->period + sp[0]->n - mod; else x->realperiod = x->period; + if (sp[0]->n & 7) + dsp_add(env_tilde_perform, 3, x, sp[0]->v, sp[0]->n); + else if (SIMD_CHECK1(sp[0]->n, sp[0]->v)) + dsp_add(env_tilde_perf_simd, 3, x, sp[0]->v, sp[0]->n); + else dsp_add(env_tilde_perf8, 3, x, sp[0]->v, sp[0]->n); + if (sp[0]->n > MAXVSTAKEN) bug("env_tilde_dsp"); +} +static void env_tilde_tick(t_sigenv *x) { + outlet_float(x->outlet, powtodb(x->result)); +} +static void env_tilde_ff(t_sigenv *x) { + clock_free(x->clock); + freealignedbytes(x->buf, (x->npoints + MAXVSTAKEN) * sizeof(float)); +} +void env_tilde_setup() { + env_tilde_class = class_new2("env~",env_tilde_new,env_tilde_ff,sizeof(t_sigenv),0,"FF"); + CLASS_MAINSIGNALIN(env_tilde_class, t_sigenv, a); + class_addmethod2(env_tilde_class, env_tilde_dsp, "dsp",""); +} + +/* --------------------- threshold~ ----------------------------- */ +static t_class *threshold_tilde_class; +struct t_threshold_tilde : t_object { + t_clock *clock; /* wakeup for message output */ + float a; /* scalar inlet */ + int state; /* 1 = high, 0 = low */ + float hithresh; /* value of high threshold */ + float lothresh; /* value of low threshold */ + float deadwait; /* msec remaining in dead period */ + float msecpertick; /* msec per DSP tick */ + float hideadtime; /* hi dead time in msec */ + float lodeadtime; /* lo dead time in msec */ +}; +static void threshold_tilde_tick(t_threshold_tilde *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->hithresh = hithresh; x->hideadtime = hideadtime; + x->lothresh = lothresh; x->lodeadtime = 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->state = 0; /* low state */ + x->deadwait = 0; /* no dead time */ + x->clock = clock_new(x, threshold_tilde_tick); + outlet_new(x, &s_bang); + outlet_new(x, &s_bang); + inlet_new(x, x, &s_float, gensym("ft1")); + x->msecpertick = 0.; + x->a = 0; + threshold_tilde_set(x, hithresh, hideadtime, lothresh, lodeadtime); + return x; +} +/* 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->state = (f != 0); + x->deadwait = 0; +} +static void threshold_tilde_tick(t_threshold_tilde *x) { + outlet_bang(x->out(x->state?0:1)); +} +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->deadwait > 0) + x->deadwait -= x->msecpertick; + else if (x->state) { + /* we're high; look for low sample */ + for (; n--; in1++) { + if (*in1 < x->lothresh) { + clock_delay(x->clock, 0L); + x->state = 0; + x->deadwait = x->lodeadtime; + goto done; + } + } + } else { + /* we're low; look for high sample */ + for (; n--; in1++) { + if (*in1 >= x->hithresh) { + clock_delay(x->clock, 0L); + x->state = 1; + x->deadwait = x->hideadtime; + goto done; + } + } + } +done: + return w+4; +} +void threshold_tilde_dsp(t_threshold_tilde *x, t_signal **sp) { + x->msecpertick = 1000. * sp[0]->n / sp[0]->sr; + dsp_add(threshold_tilde_perform, 3, sp[0]->v, x, sp[0]->n); +} +static void threshold_tilde_ff(t_threshold_tilde *x) {clock_free(x->clock);} +static void threshold_tilde_setup() { + t_class *c = threshold_tilde_class = + class_new2("threshold~",threshold_tilde_new,threshold_tilde_ff,sizeof(t_threshold_tilde), 0, "FFFF"); + CLASS_MAINSIGNALIN(c, t_threshold_tilde, a); + class_addmethod2(c, threshold_tilde_set, "set","ffff"); + class_addmethod2(c, threshold_tilde_ft1, "ft1","f"); + class_addmethod2(c, threshold_tilde_dsp, "dsp",""); +} + +/* ----------------------------- dac~ --------------------------- */ +static t_class *dac_class; +struct t_dac : t_object { + t_int n; + t_int *vec; + float a; +}; +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]; + if (!argc) { + argv = defarg; + argc = 2; + SETFLOAT(&defarg[0], 1); + SETFLOAT(&defarg[1], 2); + } + x->n = argc; + x->vec = (t_int *)getbytes(argc * sizeof(*x->vec)); + for (int i = 0; i < argc; i++) x->vec[i] = atom_getintarg(i, argc, argv); + for (int i = 1; i < argc; i++) inlet_new(x, x, &s_signal, &s_signal); + x->a = 0; + return x; +} +static void dac_dsp(t_dac *x, t_signal **sp) { + t_int i, *ip; + t_signal **sp2; + for (i = x->n, ip = x->vec, sp2 = sp; i--; ip++, sp2++) { + int ch = *ip - 1; + if ((*sp2)->n != sys_dacblocksize) error("dac~: bad vector size"); + else if (ch >= 0 && ch < sys_get_outchannels()) + if(SIMD_CHECK3(sys_dacblocksize,sys_soundout + sys_dacblocksize*ch, (*sp2)->v,sys_soundout + sys_dacblocksize*ch)) + dsp_add(plus_perf_simd, 4, sys_soundout + sys_dacblocksize*ch, + (*sp2)->v, sys_soundout + sys_dacblocksize*ch, sys_dacblocksize); + else + dsp_add(plus_perform, 4, sys_soundout + sys_dacblocksize*ch, (*sp2)->v, sys_soundout + sys_dacblocksize*ch, + sys_dacblocksize); + } +} +static void dac_free(t_dac *x) {free(x->vec);} +static void dac_setup() { + dac_class = class_new2("dac~",dac_new,dac_free,sizeof(t_dac),0,"*"); + CLASS_MAINSIGNALIN(dac_class, t_dac, a); + class_addmethod2(dac_class, dac_dsp, "dsp","!"); + class_sethelpsymbol(dac_class, gensym("adc~_dac~")); +} + +/* ----------------------------- adc~ --------------------------- */ +static t_class *adc_class; +struct t_adc : t_object { + t_int n; + t_int *vec; +}; +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]; + if (!argc) { + argv = defarg; + argc = 2; + SETFLOAT(&defarg[0], 1); + SETFLOAT(&defarg[1], 2); + } + x->n = argc; + x->vec = (t_int *)getbytes(argc * sizeof(*x->vec)); + for (int i = 0; i < argc; i++) x->vec[i] = atom_getintarg(i, argc, argv); + for (int i = 0; i < argc; i++) outlet_new(x, &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) { + out[0] = in1[0]; + out[1] = in1[1]; + out[2] = in1[2]; + out[3] = in1[3]; + out[4] = in1[4]; + out[5] = in1[5]; + out[6] = in1[6]; + out[7] = in1[7]; + } + 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 if (SIMD_CHECK2(n,in,out)) + dsp_add(copy_perf_simd, 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->n, ip = x->vec, sp2 = sp; i--; ip++, sp2++) { + int ch = *ip - 1; + if ((*sp2)->n != sys_dacblocksize) + error("adc~: bad vector size"); + else if (ch >= 0 && ch < sys_get_inchannels()) + dsp_add_copy(sys_soundin + sys_dacblocksize*ch, (*sp2)->v, sys_dacblocksize); + else dsp_add_zero((*sp2)->v, sys_dacblocksize); + } +} +static void adc_free(t_adc *x) {free(x->vec);} +static void adc_setup() { + adc_class = class_new2("adc~",adc_new,adc_free,sizeof(t_adc),0,"*"); + class_addmethod2(adc_class, adc_dsp, "dsp","!"); + class_sethelpsymbol(adc_class, gensym("adc~_dac~")); +} + +/* ----------------------------- delwrite~ ----------------------------- */ +static t_class *sigdelwrite_class; +struct t_delwritectl { + int c_n; + float *c_vec; + int c_phase; +}; +struct t_sigdelwrite : t_object { + t_symbol *sym; + t_delwritectl cspace; + int sortno; /* DSP sort number at which this was last put on chain */ + int rsortno; /* DSP sort # for first delread or write in chain */ + int vecsize; /* vector size for delread~ to use */ + float a; +}; +#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->rsortno != ugen_getsortno()) { + x->vecsize = vecsize; + x->rsortno = ugen_getsortno(); + } + /* LATER this should really check sample rate and blocking, once that is + supported. Probably we don't actually care about vecsize. + For now just suppress this check. */ +#if 0 + else if (vecsize != x->vecsize) + error("delread/delwrite/vd vector size mismatch"); +#endif +} +static void *sigdelwrite_new(t_symbol *s, t_floatarg msec) { + t_sigdelwrite *x = (t_sigdelwrite *)pd_new(sigdelwrite_class); + if (!*s->name) s = gensym("delwrite~"); + pd_bind(x, s); + x->sym = s; + int nsamps = (int)(msec * sys_getsr() * (float)(0.001f)); + if (nsamps < 1) nsamps = 1; + nsamps += ((- nsamps) & (SAMPBLK - 1)); + nsamps += DEFDELVS; +#ifdef SIMD_BYTEALIGN + nsamps += (SIMD_BYTEALIGN - nsamps) % SIMD_BYTEALIGN; /* tb: for simd */ +#endif + x->cspace.c_n = nsamps; + x->cspace.c_vec = (float *)getalignedbytes((nsamps + XTRASAMPS) * sizeof(float)); + x->cspace.c_phase = XTRASAMPS; + x->sortno = 0; + x->vecsize = 0; + x->a = 0; + return x; +} +static t_int *sigdelwrite_perform(t_int *w) { + t_float *in = (t_float *)w[1]; + t_delwritectl *c = (t_delwritectl *)w[2]; + int n = (int)w[3]; + int phase = c->c_phase, nsamps = c->c_n; + float *vp = c->c_vec, *bp = vp + phase, *ep = vp + (c->c_n + XTRASAMPS); + phase += n; + while (n--) { + float f = *in++; + if (PD_BIGORSMALL(f)) + f = 0; + *bp++ = f; + if (bp == ep) { + vp[0] = ep[-4]; + vp[1] = ep[-3]; + vp[2] = ep[-2]; + vp[3] = ep[-1]; + bp = vp + XTRASAMPS; + phase -= nsamps; + } + } + c->c_phase = phase; + return w+4; +} +static t_int *sigdelwrite_perf8(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; + if (phase > nsamps) + while (n--) { + float f = *in++; + if (PD_BIGORSMALL(f)) f = 0; + *bp++ = f; + if (bp == ep) { + vp[0] = ep[-4]; + vp[1] = ep[-3]; + vp[2] = ep[-2]; + vp[3] = ep[-1]; + bp = vp + XTRASAMPS; + phase -= nsamps; + } + } + else testcopyvec_8(bp, in, n); + c->c_phase = phase; + return w+4; +} +static t_int *sigdelwrite_perfsimd(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; + if (phase > nsamps ) + while (n--) { + float f = *in++; + if (PD_BIGORSMALL(f)) f = 0; + *bp++ = f; + if (bp == ep) { + vp[0] = ep[-4]; + vp[1] = ep[-3]; + vp[2] = ep[-2]; + vp[3] = ep[-1]; + bp = vp + XTRASAMPS; + phase -= nsamps; + } + } + else testcopyvec_simd(bp, in, n); + c->c_phase = phase; + return w+4; +} +static void sigdelwrite_dsp(t_sigdelwrite *x, t_signal **sp) { + if (sp[0]->n & 7) + dsp_add(sigdelwrite_perform, 3, sp[0]->v, &x->cspace, sp[0]->n); + else if (SIMD_CHECK1(sp[0]->n, sp[0]->v)) + dsp_add(sigdelwrite_perfsimd, 3, sp[0]->v, &x->cspace, sp[0]->n); + else dsp_add(sigdelwrite_perf8, 3, sp[0]->v, &x->cspace, sp[0]->n); + x->sortno = ugen_getsortno(); + sigdelwrite_checkvecsize(x, sp[0]->n); +} +static void sigdelwrite_free(t_sigdelwrite *x) { + pd_unbind(x, x->sym); + freealignedbytes(x->cspace.c_vec, (x->cspace.c_n + XTRASAMPS) * sizeof(float)); +} +static void sigdelwrite_setup() { + sigdelwrite_class = class_new2("delwrite~",sigdelwrite_new,sigdelwrite_free,sizeof(t_sigdelwrite),0,"SF"); + CLASS_MAINSIGNALIN(sigdelwrite_class, t_sigdelwrite, a); + class_addmethod2(sigdelwrite_class, sigdelwrite_dsp,"dsp",""); +} + +/* ----------------------------- delread~ ----------------------------- */ +static t_class *sigdelread_class; +struct t_sigdelread : t_object { + t_symbol *sym; + t_float deltime; /* delay in msec */ + int delsamps; /* delay in samples */ + t_float sr; /* samples per msec */ + t_float n; /* vector size */ + int zerodel; /* 0 or vecsize depending on read/write order */ + void (*copy_fp)(t_float*, const t_float*, int); /* tb: copy function */ +}; +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->sym = s; + x->sr = 1; + x->n = 1; + x->zerodel = 0; + sigdelread_float(x, f); + outlet_new(x, &s_signal); + return x; +} +static void sigdelread_float(t_sigdelread *x, t_float f) { + t_sigdelwrite *delwriter = (t_sigdelwrite *)pd_findbyclass(x->sym, sigdelwrite_class); + x->deltime = f; + if (delwriter) { + x->delsamps = (int)(0.5 + x->sr * x->deltime + x->n - x->zerodel); + if (x->delsamps < x->n) x->delsamps = (int)x->n; + else if (x->delsamps > delwriter->cspace.c_n - DEFDELVS) + x->delsamps = (int)(delwriter->cspace.c_n - DEFDELVS); + } + if (SIMD_CHKCNT(x->delsamps)) x->copy_fp = copyvec_simd; + else x->copy_fp = copyvec_simd_unalignedsrc; +} +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 t_int *sigdelread_perf8(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; + if (phase + n > nsamps) + while (n--) { + *out++ = *bp++; + if (bp == ep) bp -= nsamps; + } + else copyvec_8(out, bp, n); + return w+5; +} +static t_int *sigdelread_perfsimd(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]; + t_sigdelread *x = (t_sigdelread *)w[5]; + 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; + if (phase + n > nsamps) + while (n--) { + *out++ = *bp++; + if (bp == ep) bp -= nsamps; + } + else x->copy_fp(out, bp, n); + return w+6; +} +static void sigdelread_dsp(t_sigdelread *x, t_signal **sp) { + t_sigdelwrite *delwriter = + (t_sigdelwrite *)pd_findbyclass(x->sym, sigdelwrite_class); + x->sr = sp[0]->sr * 0.001; + x->n = sp[0]->n; + if (delwriter) { + sigdelwrite_checkvecsize(delwriter, sp[0]->n); + x->zerodel = (delwriter->sortno == ugen_getsortno() ? + 0 : delwriter->vecsize); + sigdelread_float(x, x->deltime); + if (sp[0]->n & 7) + dsp_add(sigdelread_perform, 4, sp[0]->v, &delwriter->cspace, &x->delsamps, sp[0]->n); + else if (SIMD_CHECK1(sp[0]->n, sp[0]->v)) + dsp_add(sigdelread_perfsimd, 5, sp[0]->v, &delwriter->cspace, &x->delsamps, sp[0]->n, x); + else dsp_add(sigdelread_perf8, 4, sp[0]->v, &delwriter->cspace, &x->delsamps, sp[0]->n); + } else if (*x->sym->name) error("delread~: %s: no such delwrite~",x->sym->name); +} +static void sigdelread_setup() { + sigdelread_class = class_new2("delread~",sigdelread_new,0,sizeof(t_sigdelread),0,"SF"); + class_addmethod2(sigdelread_class, sigdelread_dsp,"dsp",""); + class_addfloat(sigdelread_class, sigdelread_float); +} + +/* ----------------------------- vd~ ----------------------------- */ +static t_class *sigvd_class; +struct t_sigvd : t_object { + t_symbol *sym; + t_float sr; /* samples per msec */ + int zerodel; /* 0 or vecsize depending on read/write order */ + float a; +}; +static void *sigvd_new(t_symbol *s) { + t_sigvd *x = (t_sigvd *)pd_new(sigvd_class); + if (!*s->name) s = gensym("vd~"); + x->sym = s; + x->sr = 1; + x->zerodel = 0; + outlet_new(x, &s_signal); + x->a = 0; + return x; +} +static t_int *sigvd_perform(t_int *w) { + t_float *in = (t_float *)w[1]; + t_float *out = (t_float *)w[2]; + t_delwritectl *ctl = (t_delwritectl *)w[3]; + t_sigvd *x = (t_sigvd *)w[4]; + int n = (int)w[5]; + + int nsamps = ctl->c_n; + float limit = nsamps - n - 1; + float fn = n-1; + float *vp = ctl->c_vec, *bp, *wp = vp + ctl->c_phase; + float zerodel = x->zerodel; + while (n--) { + float delsamps = 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 = (int)delsamps; + frac = delsamps - (float)idelsamps; + bp = wp - idelsamps; + if (bp < vp + 4) bp += nsamps; + d = bp[-3]; + c = bp[-2]; + b = bp[-1]; + a = bp[0]; + cminusb = c-b; + *out++ = b + frac * ( + cminusb - 0.1666667f * (1.-frac) * ( + (d - a - 3.0f * cminusb) * frac + (d + 2.0f*a - 3.0f*b) + ) + ); + } + return w+6; +} +static void sigvd_dsp(t_sigvd *x, t_signal **sp) { + t_sigdelwrite *delwriter = (t_sigdelwrite *)pd_findbyclass(x->sym, sigdelwrite_class); + x->sr = sp[0]->sr * 0.001; + if (delwriter) { + sigdelwrite_checkvecsize(delwriter, sp[0]->n); + x->zerodel = (delwriter->sortno == ugen_getsortno() ? 0 : delwriter->vecsize); + dsp_add(sigvd_perform, 5, sp[0]->v, sp[1]->v, &delwriter->cspace, x, sp[0]->n); + } else error("vd~: %s: no such delwrite~",x->sym->name); +} +static void sigvd_setup() { + sigvd_class = class_new2("vd~",sigvd_new,0,sizeof(t_sigvd),0,"S"); + class_addmethod2(sigvd_class, sigvd_dsp, "dsp",""); + CLASS_MAINSIGNALIN(sigvd_class, t_sigvd, a); +} + +#ifndef HAVE_LIBFFTW3F +/* ------------------------ fft~ and ifft~ -------------------------------- */ +/* ----------------------- rfft~ -------------------------------- */ +/* ----------------------- rifft~ -------------------------------- */ +static t_class *sigfft_class; struct t_sigfft : t_object {float a;}; +static t_class *sigifft_class; struct t_sigifft : t_object {float a;}; +static t_class *sigrfft_class; struct t_sigrfft : t_object {float a;}; +static t_class *sigrifft_class; struct t_sigrifft : t_object {float a;}; + +static void *sigfft_new() { + t_sigfft *x = (t_sigfft *)pd_new(sigfft_class); + outlet_new(x, &s_signal); + outlet_new(x, &s_signal); + inlet_new(x, x, &s_signal, &s_signal); x->a=0; return x;} +static void *sigifft_new() { + t_sigifft *x = (t_sigifft *)pd_new(sigifft_class); + outlet_new(x, &s_signal); + outlet_new(x, &s_signal); + inlet_new(x, x, &s_signal, &s_signal); x->a=0; return x;} +static void *sigrfft_new() { + t_sigrfft *x = (t_sigrfft *)pd_new(sigrfft_class); + outlet_new(x, &s_signal); + outlet_new(x, &s_signal); + x->a = 0; return x;} +static void *sigrifft_new() { + t_sigrifft *x = (t_sigrifft *)pd_new(sigrifft_class); + inlet_new(x, x, &s_signal, &s_signal); + outlet_new(x, &s_signal); + x->a = 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], *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], *in2=(t_float *)w[2]; int n=w[3]; mayer_ifft(n,in1,in2); 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 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 sigfft_dspx(t_sigfft *x, t_signal **sp, t_int *(*f)(t_int *w)) { + int n = sp[0]->n; + float *in1 = sp[0]->v; + float *in2 = sp[1]->v; + float *out1 = sp[2]->v; + float *out2 = sp[3]->v; + 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]->v, sp[3]->v, 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 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 void sigrfft_dsp(t_sigrfft *x, t_signal **sp) { + int n = sp[0]->n, n2 = (n>>1); + float *in1 = sp[0]->v; + float *out1 = sp[1]->v; + float *out2 = sp[2]->v; + if (n < 4) { + error("fft: minimum 4 points"); + return; + } + /* this probably never happens */ + if (in1 == out2) { + 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 sigrifft_dsp(t_sigrifft *x, t_signal **sp) { + int n = sp[0]->n, n2 = (n>>1); + float *in1 = sp[0]->v; + float *in2 = sp[1]->v; + float *out1 = sp[2]->v; + 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 sigfft_setup() { + sigfft_class = class_new2("fft~", sigfft_new, 0,sizeof(t_sigfft), 0,""); + sigifft_class = class_new2("ifft~", sigifft_new, 0,sizeof(t_sigfft), 0,""); + sigrfft_class = class_new2("rfft~", sigrfft_new, 0,sizeof(t_sigrfft), 0,""); + sigrifft_class = class_new2("rifft~",sigrifft_new,0,sizeof(t_sigrifft),0,""); + CLASS_MAINSIGNALIN(sigfft_class, t_sigfft, a); + CLASS_MAINSIGNALIN(sigifft_class, t_sigfft, a); + CLASS_MAINSIGNALIN(sigrfft_class, t_sigrfft, a); + CLASS_MAINSIGNALIN(sigrifft_class,t_sigrifft,a); + class_addmethod2(sigfft_class, sigfft_dsp, "dsp",""); + class_addmethod2(sigifft_class, sigifft_dsp, "dsp",""); + class_addmethod2(sigrfft_class, sigrfft_dsp, "dsp",""); + class_addmethod2(sigrifft_class,sigrifft_dsp,"dsp",""); + class_sethelpsymbol(sigifft_class, gensym("fft~")); + class_sethelpsymbol(sigrfft_class, gensym("fft~")); + class_sethelpsymbol(sigrifft_class,gensym("fft~")); +} + +#else +/* Support for fftw3 by Tim Blechmann */ +/* ------------------------ fft~ and ifft~ -------------------------------- */ +/* ----------------------- rfft~ --------------------------------- */ +/* ----------------------- rifft~ -------------------------------- */ + +static t_class *sigfftw_class; struct t_sigfftw : t_object {float a; fftwf_plan plan; fftwf_iodim dim;}; +static t_class *sigifftw_class; struct t_sigifftw : t_object {float a; fftwf_plan plan; fftwf_iodim dim;}; +static t_class *sigrfftw_class; struct t_sigrfftw : t_object {float a; fftwf_plan plan; fftwf_iodim dim;}; +static t_class *sigrifftw_class;struct t_sigrifftw : t_object {float a; fftwf_plan plan; fftwf_iodim dim;}; + +static void *sigfftw_new() { + t_sigfftw *x = (t_sigfftw *)pd_new(sigfftw_class); outlet_new(x, &s_signal); outlet_new(x, &s_signal); + inlet_new(x, x, &s_signal, &s_signal); x->a=0; return x;} +static void *sigifftw_new() { + t_sigifftw *x = (t_sigifftw *)pd_new(sigfftw_class); outlet_new(x, &s_signal); outlet_new(x, &s_signal); + inlet_new(x, x, &s_signal, &s_signal); x->a=0; return x;} +static void *sigrfftw_new() { + t_sigrfftw *x = (t_sigrfftw *)pd_new(sigrfftw_class); + outlet_new(x, &s_signal); outlet_new(x, &s_signal); x->a=0; return x;} +static void *sigrifftw_new() { + t_sigrifftw *x = (t_sigrifftw *)pd_new(sigrifftw_class); + inlet_new(x, x, &s_signal, &s_signal); + outlet_new(x, &s_signal); x->a=0; return x;} + +static void sigfftw_free( t_sigfftw *x) {fftwf_destroy_plan(x->plan);} +static void sigifftw_free( t_sigifftw *x) {fftwf_destroy_plan(x->plan);} +static void sigrfftw_free( t_sigrfftw *x) {fftwf_destroy_plan(x->plan);} +static void sigrifftw_free(t_sigrifftw *x) {fftwf_destroy_plan(x->plan);} + +/* for compatibility reasons with the mayer fft, we'll have to invert some samples. this is ugly, but someone might rely on that. */ +static void sigrfftw_invert(t_sample * s, t_int n) { + while (n!=0) {--n; s[n]=-s[n];} +} +static t_int *sigfftw_perform(t_int *w) { + fftwf_execute(*(fftwf_plan *)w[1]); + return w+2; +} +static t_int *sigrfftw_perform(t_int *w) { + fftwf_execute(*(fftwf_plan*)w[1]); + sigrfftw_invert((t_sample*)w[2],(t_int)w[3]); + return w+4; +} +static t_int *sigrifftw_perform(t_int *w) { + sigrfftw_invert((t_sample *)w[2],w[3]); + fftwf_execute(*(fftwf_plan*)w[1]); + return w+4; +} + +static void sigfftw_dsp(t_sigfftw *x, t_signal **sp) { + int n = sp[0]->n; + float *in1 = sp[0]->v; + float *in2 = sp[1]->v; + float *out1 = sp[2]->v; + float *out2 = sp[3]->v; + x->dim.n=n; + x->dim.is=1; + x->dim.os=1; + x->plan = fftwf_plan_guru_split_dft(1, &(x->dim), 0, NULL, in1, in2, out1, out2, FFTW_ESTIMATE); + dsp_add(sigfftw_perform, 1, &x->plan); +} +static void sigifftw_dsp(t_sigfftw *x, t_signal **sp) { + int n = sp[0]->n; + float *in1 = sp[0]->v; + float *in2 = sp[1]->v; + float *out1 = sp[2]->v; + float *out2 = sp[3]->v; + x->dim.n=n; + x->dim.is=1; + x->dim.os=1; + x->plan = fftwf_plan_guru_split_dft(1, &(x->dim), 0, NULL, in2, in1, out2, out1, FFTW_ESTIMATE); + dsp_add(sigfftw_perform, 1, &x->plan); +} + +static void sigrfftw_dsp(t_sigrfftw *x, t_signal **sp) { + int n = sp[0]->n, n2 = (n>>1); + float *in = sp[0]->v; + float *out1 = sp[1]->v; + float *out2 = sp[2]->v; + if (n < 4) {error("fft: minimum 4 points"); return;} + x->dim.n=n; + x->dim.is=1; + x->dim.os=1; + x->plan = fftwf_plan_guru_split_dft_r2c(1, &(x->dim), 0, NULL, in, out1, out2, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); + dsp_add(sigrfftw_perform,3,&x->plan,out2+1,n2-1); + dsp_add_zero(out1 + n2, n2); + dsp_add_zero(out2 + n2, n2); +} + +static void sigrifftw_dsp(t_sigrifftw *x, t_signal **sp) { + int n = sp[0]->n, n2 = (n>>1); + float *in1 = sp[0]->v; + float *in2 = sp[1]->v; + float *out = sp[2]->v; + if (n < 4) {error("fft: minimum 4 points"); return;} + x->dim.n=n; + x->dim.is=1; + x->dim.os=1; + x->plan = fftwf_plan_guru_split_dft_c2r(1, &(x->dim), 0, NULL, in1, in2, out, FFTW_ESTIMATE | FFTW_PRESERVE_INPUT); + dsp_add_zero(in1+ n/2, n/2); + dsp_add(sigrifftw_perform,3,&x->plan,in2,n2); +} +static void sigfftw_setup() { + sigfftw_class = class_new2( "fft~",sigfftw_new, sigfftw_free, sizeof(t_sigfftw), 0,""); + sigifftw_class = class_new2( "ifft~",sigifftw_new, sigifftw_free, sizeof(t_sigfftw), 0,""); + sigrfftw_class = class_new2( "rfft~",sigrfftw_new, sigrfftw_free, sizeof(t_sigrfftw), 0,""); + sigrifftw_class = class_new2("rifft~",sigrifftw_new,sigrifftw_free,sizeof(t_sigrifftw),0,""); + CLASS_MAINSIGNALIN(sigfftw_class, t_sigfftw, a); + CLASS_MAINSIGNALIN(sigifftw_class, t_sigfftw, a); + CLASS_MAINSIGNALIN(sigrfftw_class, t_sigrfftw, a); + CLASS_MAINSIGNALIN(sigrifftw_class,t_sigrifftw,a); + class_addmethod2(sigfftw_class, sigfftw_dsp, "dsp",""); + class_addmethod2(sigifftw_class, sigifftw_dsp, "dsp",""); + class_addmethod2(sigrfftw_class, sigrfftw_dsp, "dsp",""); + class_addmethod2(sigrifftw_class, sigrifftw_dsp,"dsp",""); + class_sethelpsymbol(sigifftw_class, gensym("fft~")); + class_sethelpsymbol(sigrfftw_class, gensym("fft~")); + class_sethelpsymbol(sigrifftw_class,gensym("fft~")); +} +#endif /* HAVE_LIBFFTW3F */ +/* end of FFTW support */ + +/* ----------------------- framp~ -------------------------------- */ +static t_class *sigframp_class; +struct t_sigframp : t_object { + float a; +}; +static void *sigframp_new() { + t_sigframp *x = (t_sigframp *)pd_new(sigframp_class); + inlet_new(x, x, &s_signal, &s_signal); + outlet_new(x, &s_signal); + outlet_new(x, &s_signal); + x->a = 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]->n, n2 = (n>>1); + if (n < 4) { + error("framp: minimum 4 points"); + return; + } + dsp_add(sigframp_perform, 5, sp[0]->v, sp[1]->v, + sp[2]->v, sp[3]->v, n2); + dsp_add(sigsqrt_perform, 3, sp[3]->v, sp[3]->v, n2); +} +static void sigframp_setup() { + sigframp_class = class_new2("framp~",sigframp_new,0,sizeof(t_sigframp),0,""); + CLASS_MAINSIGNALIN(sigframp_class, t_sigframp, a); + class_addmethod2(sigframp_class, sigframp_dsp, "dsp",""); +} + +/* ---------------- hip~ - 1-pole 1-zero hipass filter. ----------------- */ +/* ---------------- lop~ - 1-pole lopass filter. ----------------- */ +t_class *sighip_class; struct t_hipctl {float x; float coef;}; +t_class *siglop_class; struct t_lopctl {float x; float coef;}; +struct t_sighip : t_object {float sr; float hz; t_hipctl cspace; t_hipctl *ctl; float a;}; +struct t_siglop : t_object {float sr; float hz; t_lopctl cspace; t_lopctl *ctl; float a;}; + +static void sighip_ft1(t_sighip *x, t_floatarg f) {x->hz = max(f,0.f); x->ctl->coef = clip(1-f*(2*3.14159)/x->sr,0.,1.);} +static void siglop_ft1(t_siglop *x, t_floatarg f) {x->hz = max(f,0.f); x->ctl->coef = clip( f*(2*3.14159)/x->sr,0.,1.);} + +static void *sighip_new(t_floatarg f) { + t_sighip *x = (t_sighip *)pd_new(sighip_class); + inlet_new(x, x, &s_float, gensym("ft1")); outlet_new(x, &s_signal); + x->sr = 44100; x->ctl = &x->cspace; x->cspace.x = 0; sighip_ft1(x, f); x->a = 0; return x;} +static void *siglop_new(t_floatarg f) { + t_siglop *x = (t_siglop *)pd_new(siglop_class); + inlet_new(x, x, &s_float, gensym("ft1")); outlet_new(x, &s_signal); + x->sr = 44100; x->ctl = &x->cspace; x->cspace.x = 0; siglop_ft1(x, f); x->a = 0; return x;} + +static void sighip_clear(t_sighip *x, t_floatarg q) {x->cspace.x = 0;} +static void siglop_clear(t_siglop *x, t_floatarg q) {x->cspace.x = 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]; + float last = c->x; + float coef = c->coef; + if (coef < 1) { + for (int i = 0; i < n; i++) { + float noo = *in++ + coef * last; + *out++ = noo - last; + last = noo; + } + if (PD_BIGORSMALL(last)) last = 0; + c->x = last; + } else { + for (int i = 0; i < n; i++) *out++ = *in++; + c->x = 0; + } + return w+5; +} +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]; + float last = c->x; + float coef = c->coef; + float feedback = 1 - coef; + for (int i = 0; i < n; i++) last = *out++ = coef * *in++ + feedback * last; + if (PD_BIGORSMALL(last)) last = 0; + c->x = last; + return w+5; +} +static void sighip_dsp(t_sighip *x, t_signal **sp) { + x->sr = sp[0]->sr; sighip_ft1(x, x->hz); + dsp_add(sighip_perform, 4, sp[0]->v, sp[1]->v, x->ctl, sp[0]->n);} +static void siglop_dsp(t_siglop *x, t_signal **sp) { + x->sr = sp[0]->sr; siglop_ft1(x, x->hz); + dsp_add(siglop_perform, 4, sp[0]->v, sp[1]->v, x->ctl, sp[0]->n);} + +void sighip_setup() { + sighip_class = class_new2("hip~",sighip_new,0,sizeof(t_sighip),0,"F"); + CLASS_MAINSIGNALIN(sighip_class, t_sighip, a); + class_addmethod2(sighip_class, sighip_dsp, "dsp",""); + class_addmethod2(sighip_class, sighip_ft1, "ft1","f"); + class_addmethod2(sighip_class, sighip_clear,"clear",""); +} +void siglop_setup() { + siglop_class = class_new2("lop~",siglop_new,0,sizeof(t_siglop),0,"F"); + CLASS_MAINSIGNALIN(siglop_class, t_siglop, a); + class_addmethod2(siglop_class, siglop_dsp, "dsp",""); + class_addmethod2(siglop_class, siglop_ft1, "ft1","f"); + class_addmethod2(siglop_class, siglop_clear, "clear",""); +} + +/* ---------------- bp~ - 2-pole bandpass filter. ----------------- */ +struct t_bpctl { + float x1; + float x2; + float coef1; + float coef2; + float gain; +}; +struct t_sigbp : t_object { + float sr; + float freq; + float q; + t_bpctl cspace; + t_bpctl *ctl; + float a; +}; +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, &s_float, gensym("ft1")); + inlet_new(x, x, &s_float, gensym("ft2")); + outlet_new(x, &s_signal); + x->sr = 44100; + x->ctl = &x->cspace; + x->cspace.x1 = 0; + x->cspace.x2 = 0; + sigbp_docoef(x, f, q); + x->a = 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->freq = f; + x->q = q; + omega = f * (2.0f * 3.14159f) / 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->ctl->coef1 = 2.0f * sigbp_qcos(omega) * r; + x->ctl->coef2 = - r * r; + x->ctl->gain = 2 * oneminusr * (oneminusr + r * omega); + /* post("r %f, omega %f, coef1 %f, coef2 %f", r, omega, x->ctl->coef1, x->ctl->coef2); */ +} +static void sigbp_ft1(t_sigbp *x, t_floatarg f) {sigbp_docoef(x, f, x->q);} +static void sigbp_ft2(t_sigbp *x, t_floatarg q) {sigbp_docoef(x, x->freq, q);} +static void sigbp_clear(t_sigbp *x, t_floatarg q) {x->ctl->x1 = x->ctl->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]; + float last = c->x1; + float prev = c->x2; + float coef1 = c->coef1; + float coef2 = c->coef2; + float gain = c->gain; + for (int i = 0; i < n; i++) { + float output = *in++ + coef1 * last + coef2 * prev; + *out++ = gain * output; + prev = last; + last = output; + } + if (PD_BIGORSMALL(last)) last = 0; + if (PD_BIGORSMALL(prev)) prev = 0; + c->x1 = last; + c->x2 = prev; + return w+5; +} +static void sigbp_dsp(t_sigbp *x, t_signal **sp) { + x->sr = sp[0]->sr; + sigbp_docoef(x, x->freq, x->q); + dsp_add(sigbp_perform, 4, sp[0]->v, sp[1]->v, x->ctl, sp[0]->n); + +} +void sigbp_setup() { + sigbp_class = class_new2("bp~",sigbp_new,0,sizeof(t_sigbp),0,"FF"); + CLASS_MAINSIGNALIN(sigbp_class, t_sigbp, a); + class_addmethod2(sigbp_class, sigbp_dsp, "dsp",""); + class_addmethod2(sigbp_class, sigbp_ft1, "ft1","f"); + class_addmethod2(sigbp_class, sigbp_ft2, "ft2","f"); + class_addmethod2(sigbp_class, sigbp_clear, "clear",""); +} + +/* ---------------- biquad~ - raw biquad filter ----------------- */ +struct t_biquadctl { + float x1; + float x2; + float fb1; + float fb2; + float ff1; + float ff2; + float ff3; +}; +struct t_sigbiquad : t_object { + float a; + t_biquadctl cspace; + t_biquadctl *ctl; +}; +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, &s_signal); + x->ctl = &x->cspace; + x->cspace.x1 = x->cspace.x2 = 0; + sigbiquad_list(x, s, argc, argv); + x->a = 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]; + float last = c->x1; + float prev = c->x2; + float fb1 = c->fb1; + float fb2 = c->fb2; + float ff1 = c->ff1; + float ff2 = c->ff2; + float ff3 = c->ff3; + for (int i = 0; i < n; i++) { + float output = *in++ + fb1 * last + fb2 * prev; + if (PD_BIGORSMALL(output)) output = 0; + *out++ = ff1 * output + ff2 * last + ff3 * prev; + prev = last; + last = output; + } + c->x1 = last; + c->x2 = prev; + return w+5; +} +/* tb: some loop unrolling & do some relaxed denormal bashing */ +/* (denormal bashing = non-Pentium4 penalised for Pentium4's failings) */ +static t_int *sigbiquad_perf8(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]>>3; + float last = c->x1; + float prev = c->x2; + float fb1 = c->fb1; + float fb2 = c->fb2; + float ff1 = c->ff1; + float ff2 = c->ff2; + float ff3 = c->ff3; + for (int i = 0; i < n; i++) { + float output = *in++ + fb1*last + fb2*prev; + if (PD_BIGORSMALL(output)) output = 0; + *out++ = ff1 * output + ff2*last + ff3*prev; prev=last; last=output; output = *in++ + fb1*last + fb2*prev; + if (PD_BIGORSMALL(output)) output = 0; + *out++ = ff1 * output + ff2*last + ff3*prev; prev=last; last=output; output = *in++ + fb1*last + fb2*prev; + *out++ = ff1 * output + ff2*last + ff3*prev; prev=last; last=output; output = *in++ + fb1*last + fb2*prev; + *out++ = ff1 * output + ff2*last + ff3*prev; prev=last; last=output; output = *in++ + fb1*last + fb2*prev; + output += 1e-10; + output -= 1e-10; + *out++ = ff1*output + ff2*last + ff3*prev; prev=last; last=output; output = *in++ + fb1*last + fb2*prev; + *out++ = ff1*output + ff2*last + ff3*prev; prev=last; last=output; output = *in++ + fb1*last + fb2*prev; + *out++ = ff1*output + ff2*last + ff3*prev; prev=last; last=output; output = *in++ + fb1*last + fb2*prev; + *out++ = ff1*output + ff2*last + ff3*prev; prev=last; last=output; + } + c->x1 = last; + 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->ctl; + /* imaginary roots -- resonant filter */ + if (discriminant < 0) { + /* 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->fb1 = fb1; + c->fb2 = fb2; + c->ff1 = ff1; + c->ff2 = ff2; + c->ff3 = ff3; +} +static void sigbiquad_set(t_sigbiquad *x, t_symbol *s, int argc, t_atom *argv) { + t_biquadctl *c = x->ctl; + c->x1 = atom_getfloatarg(0, argc, argv); + c->x2 = atom_getfloatarg(1, argc, argv); +} +static void sigbiquad_dsp(t_sigbiquad *x, t_signal **sp) { + const int n = sp[0]->n; + if (n&7) dsp_add(sigbiquad_perform, 4, sp[0]->v, sp[1]->v, x->ctl, sp[0]->n); + else dsp_add(sigbiquad_perf8, 4, sp[0]->v, sp[1]->v, x->ctl, sp[0]->n); +} +void sigbiquad_setup() { + sigbiquad_class = class_new2("biquad~",sigbiquad_new,0,sizeof(t_sigbiquad),0,"*"); + CLASS_MAINSIGNALIN(sigbiquad_class, t_sigbiquad, a); + class_addmethod2(sigbiquad_class, sigbiquad_dsp, "dsp",""); + class_addlist(sigbiquad_class, sigbiquad_list); + class_addmethod2(sigbiquad_class, sigbiquad_set, "set","*"); + class_addmethod2(sigbiquad_class, sigbiquad_set, "clear","*"); +} + +/* ---------------- samphold~ - sample and hold ----------------- */ +struct t_sigsamphold : t_object { + float a; + float lastin; + float lastout; +}; +t_class *sigsamphold_class; +static void *sigsamphold_new() { + t_sigsamphold *x = (t_sigsamphold *)pd_new(sigsamphold_class); + inlet_new(x, x, &s_signal, &s_signal); + outlet_new(x, &s_signal); + x->lastin = 0; + x->lastout = 0; + x->a = 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]; + float lastin = x->lastin; + float lastout = x->lastout; + for (int i = 0; i < n; i++, *in1++) { + float next = *in2++; + if (next < lastin) lastout = *in1; + *out++ = lastout; + lastin = next; + } + x->lastin = lastin; + x->lastout = lastout; + return w+6; +} +static void sigsamphold_dsp(t_sigsamphold *x, t_signal **sp) { + dsp_add(sigsamphold_perform, 5, sp[0]->v, sp[1]->v, sp[2]->v, x, sp[0]->n); +} +static void sigsamphold_reset(t_sigsamphold *x, t_symbol *s, int argc, t_atom *argv) { + x->lastin = ((argc > 0 && (argv[0].a_type == A_FLOAT)) ? argv[0].a_w.w_float : 1e20); +} +static void sigsamphold_set(t_sigsamphold *x, t_float f) { + x->lastout = f; +} +void sigsamphold_setup() { + sigsamphold_class = class_new2("samphold~",sigsamphold_new,0,sizeof(t_sigsamphold),0,""); + CLASS_MAINSIGNALIN(sigsamphold_class, t_sigsamphold, a); + class_addmethod2(sigsamphold_class, sigsamphold_set, "set","F"); + class_addmethod2(sigsamphold_class, sigsamphold_reset, "reset","*"); + class_addmethod2(sigsamphold_class, sigsamphold_dsp, "dsp",""); +} + +/* ---------------- rpole~ - real one-pole filter (raw) ----------------- */ +/* ---------------- rzero~ - real one-zero filter (raw) ----------------- */ +/* --- rzero_rev~ - real, reverse one-zero filter (raw) ----------------- */ +t_class *sigrpole_class; struct t_sigrpole : t_object {float a; float last;}; +t_class *sigrzero_class; struct t_sigrzero : t_object {float a; float last;}; +t_class *sigrzrev_class; struct t_sigrzrev : t_object {float a; float last;}; + +static void *sigrpole_new(t_float f) { + t_sigrpole *x = (t_sigrpole *)pd_new(sigrpole_class); + pd_float((t_pd *)inlet_new(x, x, &s_signal, &s_signal), f); outlet_new(x, &s_signal); x->last=0; return x;} +static void *sigrzero_new(t_float f) { + t_sigrzero *x = (t_sigrzero *)pd_new(sigrzero_class); + pd_float((t_pd *)inlet_new(x, x, &s_signal, &s_signal), f); outlet_new(x, &s_signal); x->last=0; return x;} +static void *sigrzrev_new(t_float f) { + t_sigrzrev *x = (t_sigrzrev *)pd_new(sigrzrev_class); + pd_float((t_pd *)inlet_new(x, x, &s_signal, &s_signal), f); outlet_new(x, &s_signal); x->last=0; return x;} + +static t_int *sigrpole_perform(t_int *w) { + float *in1 = (float *)w[1]; float *in2 = (float *)w[2]; float *out = (float *)w[3]; + t_sigrpole *x = (t_sigrpole *)w[4]; int n = (t_int)w[5]; float last = x->last; + for (int i=0; i<n; i++) { + float next = *in1++, coef = *in2++; + *out++ = last = coef*last + next; + } + if (PD_BIGORSMALL(last)) last = 0; + x->last = last; + return w+6; +} +static t_int *sigrzero_perform(t_int *w) { + float *in1 = (float *)w[1]; float *in2 = (float *)w[2]; float *out = (float *)w[3]; + t_sigrzero *x = (t_sigrzero *)w[4]; int n = (t_int)w[5]; float last = x->last; + for (int i = 0; i < n; i++) { + float next = *in1++, coef = *in2++; + *out++ = next - coef*last; + last = next; + } + x->last = last; + return w+6; +} +static t_int *sigrzrev_perform(t_int *w) { + float *in1 = (float *)w[1]; float *in2 = (float *)w[2]; float *out = (float *)w[3]; + t_sigrzrev *x = (t_sigrzrev *)w[4]; int n = (t_int)w[5]; float last = x->last; + for (int i = 0; i < n; i++) { + float next = *in1++, coef = *in2++; + *out++ = last - coef*next; + last = next; + } + x->last = last; + return w+6; +} + +static void sigrpole_dsp(t_sigrpole *x, t_signal **sp) {dsp_add(sigrpole_perform, 5, sp[0]->v, sp[1]->v, sp[2]->v, x, sp[0]->n);} +static void sigrzero_dsp(t_sigrzero *x, t_signal **sp) {dsp_add(sigrzero_perform, 5, sp[0]->v, sp[1]->v, sp[2]->v, x, sp[0]->n);} +static void sigrzrev_dsp(t_sigrzrev *x, t_signal **sp) {dsp_add(sigrzrev_perform, 5, sp[0]->v, sp[1]->v, sp[2]->v, x, sp[0]->n);} +static void sigrpole_clear(t_sigrpole *x) {x->last = 0;} +static void sigrzero_clear(t_sigrzero *x) {x->last = 0;} +static void sigrzrev_clear(t_sigrzrev *x) {x->last = 0;} +static void sigrpole_set(t_sigrpole *x, t_float f) {x->last = f;} +static void sigrzero_set(t_sigrzero *x, t_float f) {x->last = f;} +static void sigrzrev_set(t_sigrzrev *x, t_float f) {x->last = f;} +void sigr_setup() { + sigrpole_class = class_new2("rpole~", sigrpole_new,0,sizeof(t_sigrpole),0,"F"); + sigrzero_class = class_new2("rzero~", sigrzero_new,0,sizeof(t_sigrzero),0,"F"); + sigrzrev_class = class_new2("rzero_rev~",sigrzrev_new,0,sizeof(t_sigrzrev),0,"F"); + CLASS_MAINSIGNALIN(sigrpole_class, t_sigrpole, a); + CLASS_MAINSIGNALIN(sigrzero_class, t_sigrzero, a); + CLASS_MAINSIGNALIN(sigrzrev_class, t_sigrzrev, a); + class_addmethod2(sigrpole_class, sigrpole_set, "set","F"); + class_addmethod2(sigrzero_class, sigrzero_set, "set","F"); + class_addmethod2(sigrzrev_class, sigrzrev_set, "set","F"); + class_addmethod2(sigrpole_class, sigrpole_clear,"clear",""); + class_addmethod2(sigrzero_class, sigrzero_clear,"clear",""); + class_addmethod2(sigrzrev_class, sigrzrev_clear,"clear",""); + class_addmethod2(sigrpole_class, sigrpole_dsp, "dsp",""); + class_addmethod2(sigrzero_class, sigrzero_dsp, "dsp",""); + class_addmethod2(sigrzrev_class, sigrzrev_dsp, "dsp",""); +} + +/* -------------- cpole~ - complex one-pole filter (raw) --------------- */ +/* -------------- czero~ - complex one-pole filter (raw) --------------- */ +/* ---------- czero_rev~ - complex one-pole filter (raw) --------------- */ + +t_class *sigcpole_class; struct t_sigcpole : t_object {float a; float lastre; float lastim;}; +t_class *sigczero_class; struct t_sigczero : t_object {float a; float lastre; float lastim;}; +t_class *sigczrev_class; struct t_sigczrev : t_object {float a; float lastre; float lastim;}; + +static void *sigcpole_new(t_float re, t_float im) { + t_sigcpole *x = (t_sigcpole *)pd_new(sigcpole_class); + inlet_new(x, x, &s_signal, &s_signal); + pd_float((t_pd *)inlet_new(x, x, &s_signal, &s_signal), re); + pd_float((t_pd *)inlet_new(x, x, &s_signal, &s_signal), im); + outlet_new(x, &s_signal); + outlet_new(x, &s_signal); + x->lastre = x->lastim = 0; + x->a = 0; + return x; +} +static void *sigczero_new(t_float re, t_float im) { + t_sigczero *x = (t_sigczero *)pd_new(sigczero_class); + inlet_new(x, x, &s_signal, &s_signal); + pd_float((t_pd *)inlet_new(x, x, &s_signal, &s_signal), re); + pd_float((t_pd *)inlet_new(x, x, &s_signal, &s_signal), im); + outlet_new(x, &s_signal); + outlet_new(x, &s_signal); + x->lastre = x->lastim = 0; + x->a = 0; + return x; +} +static void *sigczrev_new(t_float re, t_float im) { + t_sigczrev *x = (t_sigczrev *)pd_new(sigczrev_class); + inlet_new(x, x, &s_signal, &s_signal); + pd_float((t_pd *)inlet_new(x, x, &s_signal, &s_signal), re); + pd_float((t_pd *)inlet_new(x, x, &s_signal, &s_signal), im); + outlet_new(x, &s_signal); + outlet_new(x, &s_signal); + x->lastre = x->lastim = 0; + x->a = 0; + return x; +} + +static t_int *sigcpole_perform(t_int *w) { + float *inre1 = (float *)w[1], *inim1 = (float *)w[2]; + float *inre2 = (float *)w[3], *inim2 = (float *)w[4]; + float *outre = (float *)w[5], *outim = (float *)w[6]; + t_sigcpole *x = (t_sigcpole *)w[7]; + int n = (t_int)w[8]; + float lastre = x->lastre; + float lastim = x->lastim; + for (int i = 0; i < n; i++) { + float nextre = *inre1++, nextim = *inim1++; + float coefre = *inre2++, coefim = *inim2++; + float tempre = *outre++ = nextre + lastre * coefre - lastim * coefim; + lastim = *outim++ = nextim + lastre * coefim + lastim * coefre; + lastre = tempre; + } + if (PD_BIGORSMALL(lastre)) lastre = 0; + if (PD_BIGORSMALL(lastim)) lastim = 0; + x->lastre = lastre; + x->lastim = lastim; + return w+9; +} +static t_int *sigczero_perform(t_int *w) { + float *inre1 = (float *)w[1], *inim1 = (float *)w[2]; + float *inre2 = (float *)w[3], *inim2 = (float *)w[4]; + float *outre = (float *)w[5], *outim = (float *)w[6]; + t_sigczero *x = (t_sigczero *)w[7]; + int n = (t_int)w[8]; + float lastre = x->lastre; + float lastim = x->lastim; + for (int i = 0; i < n; i++) { + float nextre = *inre1++, nextim = *inim1++; + float coefre = *inre2++, coefim = *inim2++; + *outre++ = nextre - lastre * coefre + lastim * coefim; + *outim++ = nextim - lastre * coefim - lastim * coefre; + lastre = nextre; + lastim = nextim; + } + x->lastre = lastre; + x->lastim = lastim; + return w+9; +} +static t_int *sigczrev_perform(t_int *w) { + float *inre1 = (float *)w[1], *inim1 = (float *)w[2]; + float *inre2 = (float *)w[3], *inim2 = (float *)w[4]; + float *outre = (float *)w[5], *outim = (float *)w[6]; + t_sigczrev *x = (t_sigczrev *)w[7]; + int n = (t_int)w[8]; + float lastre = x->lastre; + float lastim = x->lastim; + for (int i = 0; i < n; i++) { + float nextre = *inre1++, nextim = *inim1++; + float coefre = *inre2++, coefim = *inim2++; + /* transfer function is (A bar) - Z^-1, for the same frequency response as 1 - AZ^-1 from czero_tilde. */ + *outre++ = lastre - nextre * coefre - nextim * coefim; + *outim++ = lastim - nextre * coefim + nextim * coefre; + lastre = nextre; + lastim = nextim; + } + x->lastre = lastre; + x->lastim = lastim; + return w+9; +} + +static void sigcpole_dsp(t_sigcpole *x, t_signal **sp) { + dsp_add(sigcpole_perform, 8, sp[0]->v, sp[1]->v, sp[2]->v, sp[3]->v, sp[4]->v, sp[5]->v, x, sp[0]->n);} +static void sigczero_dsp(t_sigczero *x, t_signal **sp) { + dsp_add(sigczero_perform, 8, sp[0]->v, sp[1]->v, sp[2]->v, sp[3]->v, sp[4]->v, sp[5]->v, x, sp[0]->n);} +static void sigczrev_dsp(t_sigczrev *x, t_signal **sp) { + dsp_add(sigczrev_perform, 8, sp[0]->v, sp[1]->v, sp[2]->v, sp[3]->v, sp[4]->v, sp[5]->v, x, sp[0]->n);} + +static void sigcpole_clear(t_sigcpole *x) {x->lastre = x->lastim = 0;} +static void sigczero_clear(t_sigczero *x) {x->lastre = x->lastim = 0;} +static void sigczrev_clear(t_sigczrev *x) {x->lastre = x->lastim = 0;} +static void sigcpole_set(t_sigcpole *x, t_float re, t_float im) {x->lastre = re; x->lastim = im;} +static void sigczero_set(t_sigczero *x, t_float re, t_float im) {x->lastre = re; x->lastim = im;} +static void sigczrev_set(t_sigczrev *x, t_float re, t_float im) {x->lastre = re; x->lastim = im;} + +void sigc_setup() { + sigcpole_class = class_new2("cpole~", sigcpole_new,0,sizeof(t_sigcpole),0,"FF"); + sigczero_class = class_new2("czero~", sigczero_new,0,sizeof(t_sigczero),0,"FF"); + sigczrev_class = class_new2("czero_rev~",sigczrev_new,0,sizeof(t_sigczrev),0,"FF"); + CLASS_MAINSIGNALIN(sigcpole_class, t_sigcpole, a); + CLASS_MAINSIGNALIN(sigczero_class, t_sigczero, a); + CLASS_MAINSIGNALIN(sigczrev_class, t_sigczrev, a); + class_addmethod2(sigcpole_class, sigcpole_set, "set","FF"); + class_addmethod2(sigczero_class, sigczero_set, "set","FF"); + class_addmethod2(sigczrev_class, sigczrev_set, "set","FF"); + class_addmethod2(sigcpole_class, sigcpole_clear,"clear",""); + class_addmethod2(sigczero_class, sigczero_clear, "clear",""); + class_addmethod2(sigczrev_class, sigczrev_clear, "clear",""); + class_addmethod2(sigcpole_class, sigcpole_dsp, "dsp",""); + class_addmethod2(sigczero_class, sigczero_dsp, "dsp",""); + class_addmethod2(sigczrev_class, sigczrev_dsp, "dsp",""); +} + +/* ----------------------------- send~ ----------------------------- */ +static t_class *sigsend_class; +struct t_sigsend : t_object { + t_symbol *sym; + int n; + float *vec; + float a; +}; +static void *sigsend_new(t_symbol *s) { + t_sigsend *x = (t_sigsend *)pd_new(sigsend_class); + pd_bind(x, s); + x->sym = s; + x->n = DEFSENDVS; + x->vec = (float *)getalignedbytes(DEFSENDVS * sizeof(float)); + memset((char *)(x->vec), 0, DEFSENDVS * sizeof(float)); + x->a = 0; + return x; +} +static t_int *sigsend_perform(t_int *w) { + testcopyvec((t_float *)w[2],(t_float *)w[1],w[3]); + return w+4; +} +/* T.Grill - SIMD version */ +static t_int *sigsend_perfsimd(t_int *w) { + testcopyvec_simd((t_float *)w[2],(t_float *)w[1],w[3]); + return w+4; +} +static void sigsend_dsp(t_sigsend *x, t_signal **sp) { + const int n = x->n; + if(n != sp[0]->n) {error("sigsend %s: unexpected vector size", x->sym->name); return;} + if(SIMD_CHECK1(n,sp[0]->v)) /* x->vec is aligned in any case */ + dsp_add(sigsend_perfsimd, 3, sp[0]->v, x->vec, n); + else dsp_add(sigsend_perform, 3, sp[0]->v, x->vec, n); +} +static void sigsend_free(t_sigsend *x) { + pd_unbind(x, x->sym); + freealignedbytes(x->vec,x->n* sizeof(float)); +} +static void sigsend_setup() { + sigsend_class = class_new2("send~",sigsend_new,sigsend_free,sizeof(t_sigsend),0,"S"); + class_addcreator2("s~",sigsend_new,"S"); + CLASS_MAINSIGNALIN(sigsend_class, t_sigsend, a); + class_addmethod2(sigsend_class, sigsend_dsp,"dsp",""); +} + +/* ----------------------------- receive~ ----------------------------- */ +static t_class *sigreceive_class; +struct t_sigreceive : t_object { + t_symbol *sym; + t_float *wherefrom; + int n; +}; +static void *sigreceive_new(t_symbol *s) { + t_sigreceive *x = (t_sigreceive *)pd_new(sigreceive_class); + x->n = DEFSENDVS; /* LATER find our vector size correctly */ + x->sym = s; + x->wherefrom = 0; + outlet_new(x, &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->wherefrom; + if (in) { + while (n--) *out++ = *in++; + } else { + while (n--) *out++ = 0; + } + return w+4; +} +/* tb: vectorized receive function */ +static t_int *sigreceive_perf8(t_int *w) { + t_sigreceive *x = (t_sigreceive *)w[1]; + t_float *in = x->wherefrom; + if (in) copyvec_8((t_float *)w[2],in,w[3]); + else zerovec_8((t_float *)w[2],w[3]); + return w+4; +} +/* T.Grill - SIMD version */ +static t_int *sigreceive_perfsimd(t_int *w) { + t_sigreceive *x = (t_sigreceive *)w[1]; + t_float *in = x->wherefrom; + if(in) copyvec_simd((t_float *)w[2],in,w[3]); + else zerovec_simd((t_float *)w[2],w[3]); + return w+4; +} +static void sigreceive_set(t_sigreceive *x, t_symbol *s) { + t_sigsend *sender = (t_sigsend *)pd_findbyclass((x->sym = s), + sigsend_class); + if (sender) { + if (sender->n == x->n) + x->wherefrom = sender->vec; + else { + error("receive~ %s: vector size mismatch", x->sym->name); + x->wherefrom = 0; + } + } else { + error("receive~ %s: no matching send", x->sym->name); + x->wherefrom = 0; + } +} +static void sigreceive_dsp(t_sigreceive *x, t_signal **sp) { + const int n = x->n; + if (sp[0]->n != n) {error("receive~ %s: vector size mismatch", x->sym->name); return;} + sigreceive_set(x, x->sym); + /* x->wherefrom is aligned because we aligned the sender memory buffer */ + if(n&7) dsp_add(sigreceive_perform, 3, x, sp[0]->v, n); + else if(SIMD_CHECK1(n,sp[0]->v)) + dsp_add(sigreceive_perfsimd, 3, x, sp[0]->v, n); + else dsp_add(sigreceive_perf8, 3, x, sp[0]->v, n); +} +static void sigreceive_setup() { + sigreceive_class = class_new2("receive~",sigreceive_new,0,sizeof(t_sigreceive),0,"S"); + class_addcreator2("r~",sigreceive_new,"S"); + class_addmethod2(sigreceive_class, sigreceive_set, "set","s"); + class_addmethod2(sigreceive_class, sigreceive_dsp, "dsp",""); + class_sethelpsymbol(sigreceive_class, gensym("send~")); +} + +/* ----------------------------- catch~ ----------------------------- */ +static t_class *sigcatch_class; +struct t_sigcatch : t_object { + t_symbol *sym; + int n; + float *vec; +}; +static void *sigcatch_new(t_symbol *s) { + t_sigcatch *x = (t_sigcatch *)pd_new(sigcatch_class); + pd_bind(x, s); + x->sym = s; + x->n = DEFSENDVS; + x->vec = (float *)getalignedbytes(DEFSENDVS * sizeof(float)); + memset((char *)(x->vec), 0, DEFSENDVS * sizeof(float)); + outlet_new(x, &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; +} +/* tb: vectorized catch function */ +static t_int *sigcatch_perf8(t_int *w) { + copyvec_8((t_float *)w[2],(t_float *)w[1],w[3]); + zerovec_8((t_float *)w[1],w[3]); + return w+4; +} +/* T.Grill: SIMD catch function */ +static t_int *sigcatch_perfsimd(t_int *w) { + copyvec_simd((t_float *)w[2],(t_float *)w[1],w[3]); + zerovec_simd((t_float *)w[1],w[3]); + return w+4; +} +static void sigcatch_dsp(t_sigcatch *x, t_signal **sp) { + const int n = sp[0]->n; + if (x->n != n) {error("sigcatch %s: unexpected vector size", x->sym->name); return;} + if(n&7) dsp_add(sigcatch_perform, 3, x->vec, sp[0]->v, n); + else if(SIMD_CHECK2(n,x->vec,sp[0]->v)) + dsp_add(sigcatch_perfsimd, 3, x->vec, sp[0]->v, n); + else dsp_add(sigcatch_perf8, 3, x->vec, sp[0]->v, n); +} +static void sigcatch_free(t_sigcatch *x) { + pd_unbind(x, x->sym); + freealignedbytes(x->vec,x->n*sizeof(float)); +} +static void sigcatch_setup() { + sigcatch_class = class_new2("catch~",sigcatch_new,sigcatch_free,sizeof(t_sigcatch),CLASS_NOINLET,"S"); + class_addmethod2(sigcatch_class, sigcatch_dsp, "dsp",""); + class_sethelpsymbol(sigcatch_class, gensym("throw~")); +} + +/* ----------------------------- throw~ ----------------------------- */ +static t_class *sigthrow_class; +struct t_sigthrow : t_object { + t_symbol *sym; + t_float *whereto; + int n; + t_float a; +}; +static void *sigthrow_new(t_symbol *s) { + t_sigthrow *x = (t_sigthrow *)pd_new(sigthrow_class); + x->sym = s; + x->whereto = 0; + x->n = DEFSENDVS; + x->a = 0; + return x; +} +static t_int *sigthrow_perform(t_int *w) { + t_sigthrow *x = (t_sigthrow *)w[1]; + t_float *out = x->whereto; + if(out) testaddvec(out,(t_float *)w[2],w[3]); + return w+4; +} +/* T.Grill - SIMD version */ +static t_int *sigthrow_perfsimd(t_int *w) { + t_sigthrow *x = (t_sigthrow *)w[1]; + t_float *out = x->whereto; + if(out) testaddvec_simd(out,(t_float *)w[2],w[3]); + return w+4; +} +static void sigthrow_set(t_sigthrow *x, t_symbol *s) { + x->sym = s; + t_sigcatch *catcher = (t_sigcatch *)pd_findbyclass(s,sigcatch_class); + x->whereto = 0; + if (catcher) { + if (catcher->n == x->n) x->whereto = catcher->vec; + else error("throw~ %s: vector size mismatch", x->sym->name); + } else error("throw~ %s: no matching catch", x->sym->name); +} +static void sigthrow_dsp(t_sigthrow *x, t_signal **sp) { + const int n = x->n; + if (sp[0]->n != n) {error("throw~ %s: vector size mismatch", x->sym->name); return;} + sigthrow_set(x, x->sym); + if(SIMD_CHECK1(n,sp[0]->v)) /* the memory of the catcher is aligned in any case */ + dsp_add(sigthrow_perfsimd, 3, x, sp[0]->v, n); + else dsp_add(sigthrow_perform, 3, x, sp[0]->v, n); +} +static void sigthrow_setup() { + sigthrow_class = class_new2("throw~",sigthrow_new,0,sizeof(t_sigthrow),0,"S"); + class_addmethod2(sigthrow_class, sigthrow_set, "set","s"); + CLASS_MAINSIGNALIN(sigthrow_class, t_sigthrow, a); + class_addmethod2(sigthrow_class, sigthrow_dsp, "dsp",""); +} + +/* ------------------------- clip~ -------------------------- */ +static t_class *clip_class; +struct t_clip : t_object { + float a; + t_sample lo; + t_sample hi; +}; +static void *clip_new(t_floatarg lo, t_floatarg hi) { + t_clip *x = (t_clip *)pd_new(clip_class); + x->lo = lo; + x->hi = hi; + outlet_new(x, &s_signal); + floatinlet_new(x, &x->lo); + floatinlet_new(x, &x->hi); + x->a = 0; + return x; +} +/* T.Grill - changed function interface so that class pointer needn't be passed */ +t_int *clip_perform(t_int *w) { + t_float *in = (t_float *)w[1]; + t_float *out = (t_float *)w[2]; + const t_float lo = *(t_float *)w[3],hi = *(t_float *)w[4]; + int n = (int)w[5]; + while (n--) *out++ = clip(*in++,lo,hi); + return w+6; +} +static void clip_dsp(t_clip *x, t_signal **sp) { + if(SIMD_CHECK2(sp[0]->n,sp[0]->v,sp[1]->v)) + dsp_add(clip_perf_simd, 5, sp[0]->v, sp[1]->v, &x->lo, &x->hi, sp[0]->n); + else dsp_add(clip_perform, 5, sp[0]->v, sp[1]->v, &x->lo, &x->hi, sp[0]->n); +} +static void clip_setup() { + clip_class = class_new2("clip~",clip_new,0,sizeof(t_clip),0,"FF"); + CLASS_MAINSIGNALIN(clip_class, t_clip, a); + class_addmethod2(clip_class, clip_dsp, "dsp",""); +} + +/* sigrsqrt - reciprocal square root good to 8 mantissa bits */ +/* sigsqrt - 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() { + for (int i = 0; i < DUMTAB1SIZE; i++) { + float f; + long l = (i ? (i == DUMTAB1SIZE-1 ? DUMTAB1SIZE-2 : i) : 1)<< 23; + *(int *)(&f) = l; + rsqrt_exptab[i] = 1./sqrt(f); + } + for (int 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; + 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; + 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 +static t_class *sigrsqrt_class; struct t_sigrsqrt : t_object {float a;}; +static t_class * sigsqrt_class; struct t_sigsqrt : t_object {float a;}; +static void *sigrsqrt_new() { + t_sigrsqrt *x = (t_sigrsqrt *)pd_new(sigrsqrt_class); + outlet_new(x, &s_signal); + x->a = 0; + return x; +} +static void *sigsqrt_new() { + t_sigsqrt *x = (t_sigsqrt *)pd_new(sigsqrt_class); + outlet_new(x, &s_signal); + x->a = 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; +} +/* not static; also used in d_fft.c */ +t_int *sigsqrt_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++ = f * (1.5 * g - 0.5 * g * g * g * f); + } + } + return w+4; +} + +static void sigrsqrt_dsp(t_sigrsqrt *x, t_signal **sp) { + if(SIMD_CHECK2(sp[0]->n,sp[0]->v,sp[1]->v)) + dsp_add(sigrsqrt_perf_simd, 3, sp[0]->v, sp[1]->v, sp[0]->n); + else dsp_add(sigrsqrt_perform, 3, sp[0]->v, sp[1]->v, sp[0]->n); +} +static void sigsqrt_dsp(t_sigsqrt *x, t_signal **sp) { + if(SIMD_CHECK2(sp[0]->n,sp[0]->v,sp[1]->v)) + dsp_add(sigsqrt_perf_simd, 3, sp[0]->v, sp[1]->v, sp[0]->n); + else dsp_add(sigsqrt_perform, 3, sp[0]->v, sp[1]->v, sp[0]->n); +} + +void sigsqrt_setup() { + init_rsqrt(); + sigrsqrt_class = class_new2("rsqrt~",sigrsqrt_new,0,sizeof(t_sigrsqrt),0,""); + sigsqrt_class = class_new2( "sqrt~", sigsqrt_new,0, sizeof(t_sigsqrt),0,""); + CLASS_MAINSIGNALIN(sigrsqrt_class,t_sigrsqrt,a); + CLASS_MAINSIGNALIN( sigsqrt_class, t_sigsqrt,a); + class_addmethod2( sigsqrt_class, sigsqrt_dsp,"dsp",""); + class_addmethod2(sigrsqrt_class,sigrsqrt_dsp,"dsp",""); + class_addcreator2("q8_rsqrt~",sigrsqrt_new,""); + class_addcreator2("q8_sqrt~", sigsqrt_new,""); +} + +/* ------------------------------ wrap~ -------------------------- */ +struct t_sigwrap : t_object {float a;}; +t_class *sigwrap_class; +static void *sigwrap_new() { + t_sigwrap *x = (t_sigwrap *)pd_new(sigwrap_class); + outlet_new(x, &s_signal); + x->a = 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 = (int)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) { + if(SIMD_CHECK2(sp[0]->n,sp[0]->v,sp[1]->v)) + dsp_add(sigwrap_perf_simd, 3, sp[0]->v, sp[1]->v, sp[0]->n); + else dsp_add(sigwrap_perform, 3, sp[0]->v, sp[1]->v, sp[0]->n); +} +void sigwrap_setup() { + sigwrap_class = class_new2("wrap~",sigwrap_new,0,sizeof(t_sigwrap),0,""); + CLASS_MAINSIGNALIN(sigwrap_class, t_sigwrap, a); + class_addmethod2(sigwrap_class, sigwrap_dsp, "dsp",""); +} + +/* ------------------------------ mtof_tilde~ and such -------------------------- */ +struct t_func1 : t_object {float a;}; +t_class *mtof_tilde_class, *ftom_tilde_class; +t_class *dbtorms_tilde_class, *rmstodb_tilde_class; +t_class *dbtopow_tilde_class, *powtodb_tilde_class; + +#define FUNC1(NAME,EXPR) \ +static t_int *NAME##_perform(t_int *w) { \ + float *in = *(t_float **)(w+1), *out = *(t_float **)(w+2); \ + for (t_int n = *(t_int *)(w+3); n--; in++, out++) { float a = *in; *out = (EXPR); } \ + return w+4;} \ +static void *NAME##_new() {t_func1 *x = (t_func1 *)pd_new(NAME##_class); \ + outlet_new(x,&s_signal); x->a = 0; return x;} \ +static void NAME##_dsp(t_func1 *x, t_signal **sp) { \ + dsp_add(NAME##_perform, 3, sp[0]->v, sp[1]->v, sp[0]->n);} + + +FUNC1(mtof_tilde, a<=-1500 ? 0 : 8.17579891564 * exp(.0577622650 * min(a,1499.f))) +FUNC1(ftom_tilde, a>0 ? 17.3123405046 * log(.12231220585 * a) : -1500) +FUNC1(dbtorms_tilde, a<=0 ? 0 : exp((LOGTEN * 0.05) * (min(a,485.f)-100.))) +FUNC1(dbtopow_tilde, a<=0 ? 0 : max(100 + 20./LOGTEN * log(a),0.)) +FUNC1(rmstodb_tilde, a<=0 ? 0 : exp((LOGTEN * 0.1) * (min(a,870.f)-100.))) +FUNC1(powtodb_tilde, a<=0 ? 0 : max(100 + 10./LOGTEN * log(a),0.)) + +#define FUNC1DECL(NAME,SYM) \ + NAME##_class = class_new2(SYM,NAME##_new,0,sizeof(t_func1),0,""); \ + CLASS_MAINSIGNALIN(NAME##_class,t_func1,a); \ + class_addmethod2(NAME##_class, NAME##_dsp, "dsp",""); + +void mtof_tilde_setup() { + FUNC1DECL(mtof_tilde,"mtof~") + FUNC1DECL(ftom_tilde,"ftom~") + FUNC1DECL(dbtorms_tilde,"dbtorms~") + FUNC1DECL(dbtopow_tilde,"dbtopow~") + FUNC1DECL(rmstodb_tilde,"rmstodb~") + FUNC1DECL(powtodb_tilde,"powtodb~") + t_symbol *s = gensym("acoustics~.pd"); + 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); +} + +static t_class *print_class; +struct t_print : t_object { + float a; + t_symbol *sym; + int count; +}; +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->count) { + post("%s:", x->sym->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->count--; + } + return w+4; +} +static void print_dsp(t_print *x, t_signal **sp) { + dsp_add(print_perform, 3, x, sp[0]->v, sp[0]->n); +} +static void print_float(t_print *x, t_float f) {x->count = max(0,(int)f);} +static void print_bang(t_print *x) {x->count = 1;} +static void *print_new(t_symbol *s) { + t_print *x = (t_print *)pd_new(print_class); + x->sym = s->name[0] ? s : gensym("print~"); + x->count = 0; + x->a = 0; + return x; +} +static void print_setup() { + print_class = class_new2("print~",print_new,0,sizeof(t_print),0,"S"); + CLASS_MAINSIGNALIN(print_class, t_print, a); + class_addmethod2(print_class, print_dsp, "dsp",""); + class_addbang(print_class, print_bang); + class_addfloat(print_class, print_float); +} + +/* ------------------------ bang~ -------------------------- */ +static t_class *bang_tilde_class; +struct t_bang : t_object { + t_clock *clock; +}; +static t_int *bang_tilde_perform(t_int *w) { + t_bang *x = (t_bang *)w[1]; + clock_delay(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->outlet);} +static void bang_tilde_free(t_bang *x) {clock_free(x->clock);} +static void *bang_tilde_new(t_symbol *s) { + t_bang *x = (t_bang *)pd_new(bang_tilde_class); + x->clock = clock_new(x, bang_tilde_tick); + outlet_new(x, &s_bang); + return x; +} +static void bang_tilde_setup() { + bang_tilde_class = class_new2("bang~",bang_tilde_new,bang_tilde_free,sizeof(t_bang),0,""); + class_addmethod2(bang_tilde_class, bang_tilde_dsp, "dsp",""); +} + +/* -------------------------- phasor~ ------------------------------ */ +static t_class *phasor_class; +/* in the style of R. Hoeldrich (ICMC 1995 Banff) */ +struct t_phasor : t_object { + double phase; + float conv; + float a; /* scalar frequency */ +}; +static void *phasor_new(t_floatarg f) { + t_phasor *x = (t_phasor *)pd_new(phasor_class); + x->a = f; + inlet_new(x, x, &s_float, gensym("ft1")); + x->phase = 0; + x->conv = 0; + outlet_new(x, &s_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->phase + UNITBIT32; + union tabfudge tf; + int normhipart; + float conv = x->conv; + tf.d = UNITBIT32; + normhipart = tf.i[HIOFFSET]; + tf.d = dphase; + while (n--) { + tf.i[HIOFFSET] = normhipart; + dphase += *in++ * conv; + *out++ = tf.d - UNITBIT32; + tf.d = dphase; + } + tf.i[HIOFFSET] = normhipart; + x->phase = tf.d - UNITBIT32; + return w+5; +} +static void phasor_dsp(t_phasor *x, t_signal **sp) { + x->conv = 1./sp[0]->sr; + dsp_add(phasor_perform, 4, x, sp[0]->v, sp[1]->v, sp[0]->n); +} +static void phasor_ft1(t_phasor *x, t_float f) { + x->phase = f; +} +static void phasor_setup() { + phasor_class = class_new2("phasor~",phasor_new,0,sizeof(t_phasor),0,"F"); + CLASS_MAINSIGNALIN(phasor_class, t_phasor, a); + class_addmethod2(phasor_class, phasor_dsp, "dsp",""); + class_addmethod2(phasor_class, phasor_ft1,"ft1","f"); +} +/* </Hoeldrich-version> */ + +/* ------------------------ cos~ ----------------------------- */ +float *cos_table; +static t_class *cos_class; +struct t_cos : t_object { + float a; +}; +static void *cos_new() { + t_cos *x = (t_cos *)pd_new(cos_class); + outlet_new(x,&s_signal); + x->a = 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.d = UNITBIT32; + normhipart = tf.i[HIOFFSET]; + +#if 0 /* this is the readable version of the code. */ + while (n--) { + dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32; + tf.d = dphase; + addr = tab + (tf.i[HIOFFSET] & (COSTABSIZE-1)); + tf.i[HIOFFSET] = normhipart; + frac = tf.d - UNITBIT32; + f1 = addr[0]; + f2 = addr[1]; + *out++ = f1 + frac * (f2 - f1); + } +#else /* this is the same, unwrapped by hand. */ + dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32; + tf.d = dphase; + addr = tab + (tf.i[HIOFFSET] & (COSTABSIZE-1)); + tf.i[HIOFFSET] = normhipart; + while (--n) { + dphase = (double)(*in++ * (float)(COSTABSIZE)) + UNITBIT32; + frac = tf.d - UNITBIT32; + tf.d = dphase; + f1 = addr[0]; + f2 = addr[1]; + addr = tab + (tf.i[HIOFFSET] & (COSTABSIZE-1)); + *out++ = f1 + frac * (f2 - f1); + tf.i[HIOFFSET] = normhipart; + } + frac = 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]->v, sp[1]->v, sp[0]->n); +} +static void cos_maketable() { + float phsinc = (2. * 3.14159) / COSTABSIZE; + union tabfudge tf; + if (cos_table) return; + cos_table = (float *)getbytes(sizeof(float) * (COSTABSIZE+1)); + float phase=0; + float *fp = cos_table; + for (int i = COSTABSIZE + 1; 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.d = UNITBIT32 + 0.5; + if ((unsigned)tf.i[LOWOFFSET] != 0x80000000) bug("cos~: unexpected machine alignment"); +} +static void cos_setup() { + cos_class = class_new2("cos~",cos_new,0,sizeof(t_cos),0,"F"); + CLASS_MAINSIGNALIN(cos_class, t_cos, a); + class_addmethod2(cos_class, cos_dsp, "dsp",""); + cos_maketable(); +} + +/* ------------------------ osc~ ----------------------------- */ +static t_class *osc_class; +struct t_osc : t_object { + double phase; + float conv; + float a; /* frequency if scalar */ +}; +static void *osc_new(t_floatarg f) { + t_osc *x = (t_osc *)pd_new(osc_class); + x->a = f; + outlet_new(x,&s_signal); + inlet_new(x, x, &s_float, gensym("ft1")); + inlet_settip(x->inlet,gensym("phase")); + x->phase = 0; + 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->phase + UNITBIT32; + int normhipart; + union tabfudge tf; + float conv = x->conv; + tf.d = UNITBIT32; + normhipart = tf.i[HIOFFSET]; +#if 0 + while (n--) { + tf.d = dphase; + dphase += *in++ * conv; + addr = tab + (tf.i[HIOFFSET] & (COSTABSIZE-1)); + tf.i[HIOFFSET] = normhipart; + frac = tf.d - UNITBIT32; + f1 = addr[0]; + f2 = addr[1]; + *out++ = f1 + frac * (f2 - f1); + } +#else + tf.d = dphase; + dphase += *in++ * conv; + addr = tab + (tf.i[HIOFFSET] & (COSTABSIZE-1)); + tf.i[HIOFFSET] = normhipart; + frac = tf.d - UNITBIT32; + while (--n) { + tf.d = dphase; + f1 = addr[0]; + dphase += *in++ * conv; + f2 = addr[1]; + addr = tab + (tf.i[HIOFFSET] & (COSTABSIZE-1)); + tf.i[HIOFFSET] = normhipart; + *out++ = f1 + frac * (f2 - f1); + frac = tf.d - UNITBIT32; + } + f1 = addr[0]; + f2 = addr[1]; + *out++ = f1 + frac * (f2 - f1); +#endif + tf.d = UNITBIT32 * COSTABSIZE; + normhipart = tf.i[HIOFFSET]; + tf.d = dphase + (UNITBIT32 * COSTABSIZE - UNITBIT32); + tf.i[HIOFFSET] = normhipart; + x->phase = tf.d - UNITBIT32 * COSTABSIZE; + return w+5; +} +static void osc_dsp(t_osc *x, t_signal **sp) { + x->conv = COSTABSIZE/sp[0]->sr; + dsp_add(osc_perform, 4, x, sp[0]->v, sp[1]->v, sp[0]->n); +} +static void osc_ft1(t_osc *x, t_float f) { + x->phase = COSTABSIZE * f; +} +static void osc_setup() { + osc_class = class_new2("osc~",osc_new,0,sizeof(t_osc),0,"F"); + CLASS_MAINSIGNALIN(osc_class, t_osc, a); + class_addmethod2(osc_class, osc_dsp, "dsp",""); + class_addmethod2(osc_class, osc_ft1, "ft1","f"); + class_settip(osc_class,gensym("frequency")); + cos_maketable(); +} + +/* ---------------- vcf~ - 2-pole bandpass filter. ----------------- */ +struct t_vcfctl { + float re; + float im; + float q; + float isr; +}; +struct t_sigvcf : t_object { + t_vcfctl cspace; + t_vcfctl *ctl; + float a; +}; +t_class *sigvcf_class; +static void *sigvcf_new(t_floatarg q) { + t_sigvcf *x = (t_sigvcf *)pd_new(sigvcf_class); + inlet_new(x, x, &s_signal, &s_signal); + inlet_new(x, x, &s_float, gensym("ft1")); + outlet_new(x, &s_signal); + outlet_new(x, &s_signal); + x->ctl = &x->cspace; + x->cspace.re = 0; + x->cspace.im = 0; + x->cspace.q = q; + x->cspace.isr = 0; + x->a = 0; + return x; +} +static void sigvcf_ft1(t_sigvcf *x, t_floatarg f) { + x->ctl->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]; + float re = c->re, re2; + float im = c->im; + float q = c->q; + float qinv = (q > 0? 1.0f/q : 0); + float ampcorrect = 2.0f - 2.0f / (q + 2.0f); + float isr = c->isr; + float coefr, coefi; + float *tab = cos_table, *addr, f1, f2, frac; + double dphase; + int normhipart, tabindex; + union tabfudge tf; + tf.d = UNITBIT32; + normhipart = tf.i[HIOFFSET]; + for (int 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.d = dphase; + tabindex = tf.i[HIOFFSET] & (COSTABSIZE-1); + addr = tab + tabindex; + tf.i[HIOFFSET] = normhipart; + frac = tf.d - UNITBIT32; + f1 = addr[0]; f2 = addr[1]; coefr = r * (f1 + frac * (f2 - f1)); + addr = tab + ((tabindex - (COSTABSIZE/4)) & (COSTABSIZE-1)); + f1 = addr[0]; f2 = addr[1]; coefi = r * (f1 + frac * (f2 - f1)); + f1 = *in1++; + re2 = re; + *out1++ = re = ampcorrect * oneminusr * f1 + coefr * re2 - coefi * im; + *out2++ = im = coefi * re2 + coefr * im; + } + if (PD_BIGORSMALL(re)) re = 0; + if (PD_BIGORSMALL(im)) im = 0; + c->re = re; + c->im = im; + return w+7; +} +static void sigvcf_dsp(t_sigvcf *x, t_signal **sp) { + x->ctl->isr = 6.28318f/sp[0]->sr; + dsp_add(sigvcf_perform, 6, sp[0]->v, sp[1]->v, sp[2]->v, sp[3]->v, x->ctl, sp[0]->n); + +} +void sigvcf_setup() { + sigvcf_class = class_new2("vcf~",sigvcf_new,0,sizeof(t_sigvcf),0,"F"); + CLASS_MAINSIGNALIN(sigvcf_class, t_sigvcf, a); + class_addmethod2(sigvcf_class, sigvcf_dsp, "dsp",""); + class_addmethod2(sigvcf_class, sigvcf_ft1, "ft1","f"); +} + +/* -------------------------- noise~ ------------------------------ */ +static t_class *noise_class; +struct t_noise : t_object { + int val; +}; +static void *noise_new() { + t_noise *x = (t_noise *)pd_new(noise_class); + static int init = 307; + x->val = (init *= 1319); + outlet_new(x, &s_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]->v, &x->val, sp[0]->n); +} +static void noise_setup() { + noise_class = class_new2("noise~",noise_new,0,sizeof(t_noise),0,""); + class_addmethod2(noise_class, noise_dsp, "dsp",""); +} +void builtins_dsp_setup() { + plus_setup(); + minus_setup(); + times_setup(); + over_setup(); + max_setup(); + min_setup(); + lt_setup(); + gt_setup(); + le_setup(); + ge_setup(); + eq_setup(); + ne_setup(); + //abs_setup(); + + tab_tilde_setup(); + tabosc4_tilde_setup(); + tabsend_setup(); + tabreceive_setup(); + tabread_setup(); + tabread4_setup(); + tabwrite_setup(); + + sig_tilde_setup(); + line_tilde_setup(); snapshot_tilde_setup(); + vline_tilde_setup(); vsnapshot_tilde_setup(); + env_tilde_setup(); + threshold_tilde_setup(); + + dac_setup(); + adc_setup(); + + sigdelwrite_setup(); + sigdelread_setup(); + sigvd_setup(); + + sigframp_setup(); +#ifdef HAVE_LIBFFTW3F + sigfftw_setup(); /* added by Tim Blechmann to support fftw */ +#else + sigfft_setup(); +#endif /* HAVE_LIBFFTW3F */ + + sighip_setup(); + siglop_setup(); + sigbp_setup(); + sigbiquad_setup(); + sigsamphold_setup(); + sigr_setup(); + sigc_setup(); + + sigsend_setup(); + sigreceive_setup(); + sigcatch_setup(); + sigthrow_setup(); + + clip_setup(); + sigsqrt_setup(); + sigwrap_setup(); + mtof_tilde_setup(); + print_setup(); + bang_tilde_setup(); + + phasor_setup(); + cos_setup(); + osc_setup(); + sigvcf_setup(); + noise_setup(); +} diff --git a/desiredata/src/config.h.in b/desiredata/src/config.h.in new file mode 100644 index 00000000..cf88ef4a --- /dev/null +++ b/desiredata/src/config.h.in @@ -0,0 +1,5 @@ + +#undef HAVE_ALLOCA + +#undef HAVE_ALLOCA_H + diff --git a/desiredata/src/configure b/desiredata/src/configure new file mode 100755 index 00000000..205d26e7 --- /dev/null +++ b/desiredata/src/configure @@ -0,0 +1,6562 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.59. +# +# Copyright (C) 2003 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2 + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +exec 6>&1 + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_config_libobj_dir=. +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Maximum number of lines to put in a shell here document. +# This variable seems obsolete. It should probably be removed, and +# only ac_max_sed_lines should be used. +: ${ac_max_here_lines=38} + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= + +ac_unique_file="kernel.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include <stdio.h> +#if HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif +#if HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#if STDC_HEADERS +# include <stdlib.h> +# include <stddef.h> +#else +# if HAVE_STDLIB_H +# include <stdlib.h> +# endif +#endif +#if HAVE_STRING_H +# if !STDC_HEADERS && HAVE_MEMORY_H +# include <memory.h> +# endif +# include <string.h> +#endif +#if HAVE_STRINGS_H +# include <strings.h> +#endif +#if HAVE_INTTYPES_H +# include <inttypes.h> +#else +# if HAVE_STDINT_H +# include <stdint.h> +# endif +#endif +#if HAVE_UNISTD_H +# include <unistd.h> +#endif" + +ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS oss alsa jack portaudio portmidi fftw CPPFLAGS MORECFLAGS EXT USE_DEBUG_CFLAGS AUDIOSRC MIDISRC STRIPFLAG EXTERNTARGET LIBSUFFIX LDSOFLAGS CC CFLAGS LDFLAGS ac_ct_CC EXEEXT OBJEXT INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA SET_MAKE CPP EGREP ALLOCA LIBOBJS LTLIBOBJS' +ac_subst_files='' + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +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' + +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 + + ac_optarg=`expr "x$ac_option" : 'x[^=]*=\(.*\)'` + + # 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_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$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 ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad | --data | --dat | --da) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ + | --da=*) + datadir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + eval "enable_$ac_feature=no" ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) 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 | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$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 | -n) + 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_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package| sed 's/-/_/g'` + case $ac_option in + *=*) ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"`;; + *) ac_optarg=yes ;; + esac + eval "with_$ac_package='$ac_optarg'" ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + 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 "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + ac_optarg=`echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` + eval "$ac_envvar='$ac_optarg'" + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute paths. +for ac_var in exec_prefix prefix +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* | NONE | '' ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# Be sure to have absolute paths. +for ac_var in bindir sbindir libexecdir datadir sysconfdir sharedstatedir \ + localstatedir libdir includedir oldincludedir infodir mandir +do + eval ac_val=$`echo $ac_var` + case $ac_val in + [\\/$]* | ?:[\\/]* ) ;; + *) { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; };; + esac +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +# 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_confdir=`(dirname "$0") 2>/dev/null || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + 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 "$as_me: error: cannot find sources ($ac_unique_file) in $ac_confdir or .." >&2 + { (exit 1); exit 1; }; } + else + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } + fi +fi +(cd $srcdir && test -r ./$ac_unique_file) 2>/dev/null || + { echo "$as_me: error: sources are in $srcdir, but \`cd $srcdir' does not work" >&2 + { (exit 1); exit 1; }; } +srcdir=`echo "$srcdir" | sed 's%\([^\\/]\)[\\/]*$%\1%'` +ac_env_build_alias_set=${build_alias+set} +ac_env_build_alias_value=$build_alias +ac_cv_env_build_alias_set=${build_alias+set} +ac_cv_env_build_alias_value=$build_alias +ac_env_host_alias_set=${host_alias+set} +ac_env_host_alias_value=$host_alias +ac_cv_env_host_alias_set=${host_alias+set} +ac_cv_env_host_alias_value=$host_alias +ac_env_target_alias_set=${target_alias+set} +ac_env_target_alias_value=$target_alias +ac_cv_env_target_alias_set=${target_alias+set} +ac_cv_env_target_alias_value=$target_alias +ac_env_CC_set=${CC+set} +ac_env_CC_value=$CC +ac_cv_env_CC_set=${CC+set} +ac_cv_env_CC_value=$CC +ac_env_CFLAGS_set=${CFLAGS+set} +ac_env_CFLAGS_value=$CFLAGS +ac_cv_env_CFLAGS_set=${CFLAGS+set} +ac_cv_env_CFLAGS_value=$CFLAGS +ac_env_LDFLAGS_set=${LDFLAGS+set} +ac_env_LDFLAGS_value=$LDFLAGS +ac_cv_env_LDFLAGS_set=${LDFLAGS+set} +ac_cv_env_LDFLAGS_value=$LDFLAGS +ac_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_env_CPPFLAGS_value=$CPPFLAGS +ac_cv_env_CPPFLAGS_set=${CPPFLAGS+set} +ac_cv_env_CPPFLAGS_value=$CPPFLAGS +ac_env_CPP_set=${CPP+set} +ac_env_CPP_value=$CPP +ac_cv_env_CPP_set=${CPP+set} +ac_cv_env_CPP_value=$CPP + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # 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 <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +_ACEOF + + cat <<_ACEOF +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --datadir=DIR read-only architecture-independent data [PREFIX/share] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --infodir=DIR info documentation [PREFIX/info] + --mandir=DIR man documentation [PREFIX/man] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-oss audio via OSS + --enable-alsa audio via ALSA + --enable-jack audio via JACK + --enable-portaudio audio via PortAudio + --enable-portmidi MIDI via PortMidi + --enable-debug debugging support + --enable-static link statically + --enable-fftw use FFTW package + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + CPPFLAGS C/C++ preprocessor flags, e.g. -I<include dir> if you have + headers in a nonstandard directory <include dir> + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +_ACEOF +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + ac_popdir=`pwd` + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d $ac_dir || continue + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + cd $ac_dir + # Check for guested configure; otherwise get Cygnus style configure. + if test -f $ac_srcdir/configure.gnu; then + echo + $SHELL $ac_srcdir/configure.gnu --help=recursive + elif test -f $ac_srcdir/configure; then + echo + $SHELL $ac_srcdir/configure --help=recursive + elif test -f $ac_srcdir/configure.ac || + test -f $ac_srcdir/configure.in; then + echo + $ac_configure --help + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi + cd "$ac_popdir" + done +fi + +test -n "$ac_init_help" && exit 0 +if $ac_init_version; then + cat <<\_ACEOF + +Copyright (C) 2003 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit 0 +fi +exec 5>config.log +cat >&5 <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.59. Invocation command line was + + $ $0 $@ + +_ACEOF +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +hostinfo = `(hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_sep= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args$ac_sep'$ac_arg'" + # Get rid of the leading space. + ac_sep=" " + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Be sure not to use single quotes in there, as some shells, +# such as our DU 5.0 friend, will then `close' the trap. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +{ + (set) 2>&1 | + case `(ac_space='"'"' '"'"'; set | grep ac_space) 2>&1` in + *ac_space=\ *) + sed -n \ + "s/'"'"'/'"'"'\\\\'"'"''"'"'/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='"'"'\\2'"'"'/p" + ;; + *) + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------- ## +## Output files. ## +## ------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=$`echo $ac_var` + echo "$ac_var='"'"'$ac_val'"'"'" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + sed "/^$/d" confdefs.h | sort + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core && + rm -rf conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status + ' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# 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 + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# 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 "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . $cache_file;; + *) . ./$cache_file;; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in `(set) 2>&1 | + sed -n 's/^ac_env_\([a-zA-Z_0-9]*\)_set=.*/\1/p'`; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val="\$ac_cv_env_${ac_var}_value" + eval ac_new_val="\$ac_env_${ac_var}_value" + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?\"\']*) + ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + + + + + + + + +oss=yes + +alsa=yes + +jack=yes + +portaudio=yes + +portmidi=yes + +fftw=yes + + + + +USE_DEBUG_CFLAGS=no + + + + + + + + +# Check whether --enable-alsa or --disable-alsa was given. +if test "${enable_alsa+set}" = set; then + enableval="$enable_alsa" + alsa=$enableval +fi; +# Check whether --enable-alsa or --disable-alsa was given. +if test "${enable_alsa+set}" = set; then + enableval="$enable_alsa" + alsa=$enableval +fi; +# Check whether --enable-jack or --disable-jack was given. +if test "${enable_jack+set}" = set; then + enableval="$enable_jack" + jack=$enableval +fi; +# Check whether --enable-portaudio or --disable-portaudio was given. +if test "${enable_portaudio+set}" = set; then + enableval="$enable_portaudio" + portaudio=$enableval +fi; +# Check whether --enable-portmidi or --disable-portmidi was given. +if test "${enable_portmidi+set}" = set; then + enableval="$enable_portmidi" + portmidi=$enableval +fi; +# Check whether --enable-debug or --disable-debug was given. +if test "${enable_debug+set}" = set; then + enableval="$enable_debug" + USE_DEBUG_CFLAGS=$enableval +fi; +# Check whether --enable-static or --disable-static was given. +if test "${enable_static+set}" = set; then + enableval="$enable_static" + static=$enableval +fi; +# Check whether --enable-fftw or --disable-fftw was given. +if test "${enable_fftw+set}" = set; then + enableval="$enable_fftw" + fftw=$enableval +fi; + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + CC=$ac_ct_CC +else + CC="$ac_cv_prog_CC" +fi + +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 "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +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 $# != 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 + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6 +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6 +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 +fi + + test -n "$ac_ct_CC" && break +done + + CC=$ac_ct_CC +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO:" \ + "checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5 + (eval $ac_compiler --version </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -v </dev/null >&5\"") >&5 + (eval $ac_compiler -v </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (eval echo "$as_me:$LINENO: \"$ac_compiler -V </dev/null >&5\"") >&5 + (eval $ac_compiler -V </dev/null >&5) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6 +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +if { (eval echo "$as_me:$LINENO: \"$ac_link_default\"") >&5 + (eval $ac_link_default) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Find the output, starting from the most likely. This scheme is +# not robust to junk in `.', hence go to wildcards (a.*) only as a last +# resort. + +# Be careful to initialize this variable, since it used to be cached. +# Otherwise an old cache value of `no' led to `EXEEXT = no' in a Makefile. +ac_cv_exeext= +# b.out is created by i960 compilers. +for ac_file in a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) + ;; + conftest.$ac_ext ) + # This is the source file. + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + # FIXME: I believe we export ac_cv_exeext for Libtool, + # but it would be cool to find out if it's true. Does anybody + # maintain Libtool? --akim. + export ac_cv_exeext + break;; + * ) + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6 + +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6 +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6 +echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6 + +echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6 +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + export ac_cv_exeext + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6 + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6 +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in `(ls conftest.o conftest.obj; ls conftest.*) 2>/dev/null`; do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6 +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6 +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_compiler_gnu=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6 +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +CFLAGS="-g" +echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_prog_cc_g=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&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 +echo "$as_me:$LINENO: checking for $CC option to accept ANSI C" >&5 +echo $ECHO_N "checking for $CC option to accept ANSI C... $ECHO_C" >&6 +if test "${ac_cv_prog_cc_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_stdc=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std1 is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std1. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +# Don't try gcc -ansi; that turns off useful extensions and +# breaks some systems' header files. +# AIX -qlanglvl=ansi +# Ultrix and OSF/1 -std1 +# HP-UX 10.20 and later -Ae +# HP-UX older versions -Aa -D_HPUX_SOURCE +# SVR4 -Xc -D__EXTENSIONS__ +for ac_arg in "" -qlanglvl=ansi -std1 -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_prog_cc_stdc=$ac_arg +break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext +done +rm -f conftest.$ac_ext conftest.$ac_objext +CC=$ac_save_CC + +fi + +case "x$ac_cv_prog_cc_stdc" in + x|xno) + echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6 ;; + *) + echo "$as_me:$LINENO: result: $ac_cv_prog_cc_stdc" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_stdc" >&6 + CC="$CC $ac_cv_prog_cc_stdc" ;; +esac + +# Some people use a C++ compiler to compile C. Since we use `exit', +# in C++ we need to declare it. In case someone uses the same compiler +# for both compiling C and C++ we need to have the C++ compiler decide +# the declaration of exit, since it's the most demanding environment. +cat >conftest.$ac_ext <<_ACEOF +#ifndef __cplusplus + choke me +#endif +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + for ac_declaration in \ + '' \ + 'extern "C" void std::exit (int) throw (); using std::exit;' \ + 'extern "C" void std::exit (int); using std::exit;' \ + 'extern "C" void exit (int) throw ();' \ + 'extern "C" void exit (int);' \ + 'void exit (int);' +do + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +#include <stdlib.h> +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +continue +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_declaration +int +main () +{ +exit (42); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + break +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +done +rm -f conftest* +if test -n "$ac_declaration"; then + echo '#ifdef __cplusplus' >>confdefs.h + echo $ac_declaration >>confdefs.h + echo '#endif' >>confdefs.h +fi + +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +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 + elif test -f $ac_dir/shtool; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&5 +echo "$as_me: error: cannot find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." >&2;} + { (exit 1); exit 1; }; } +fi +ac_config_guess="$SHELL $ac_aux_dir/config.guess" +ac_config_sub="$SHELL $ac_aux_dir/config.sub" +ac_configure="$SHELL $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 +# AmigaOS /C/install, which installs bootblocks on floppy discs +# 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" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 +echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6 +if test -z "$INSTALL"; then +if test "${ac_cv_path_install+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in + ./ | .// | /cC/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + /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 + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + done + done + ;; +esac +done + + +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 "$as_me:$LINENO: result: $INSTALL" >&5 +echo "${ECHO_T}$INSTALL" >&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}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6 +set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y,:./+-,___p_,'` +if eval "test \"\${ac_cv_prog_make_${ac_make}_set+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.make <<\_ACEOF +all: + @echo 'ac_maketemp="$(MAKE)"' +_ACEOF +# GNU make sometimes prints "make[1]: Entering...", which would confuse us. +eval `${MAKE-make} -f conftest.make 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 conftest.make +fi +if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then + echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6 + SET_MAKE= +else + echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6 + SET_MAKE="MAKE=${MAKE-make}" +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6 +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6 +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether non-existent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6 +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset x; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *ccp; + char **p; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + ccp = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++ccp; + p = (char**) ccp; + ccp = (char const *const *) p; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + } +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_const=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6 +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + + +echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6 +if test "${ac_cv_prog_egrep+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | (grep -E '(a|b)') >/dev/null 2>&1 + then ac_cv_prog_egrep='grep -E' + else ac_cv_prog_egrep='egrep' + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_egrep" >&5 +echo "${ECHO_T}$ac_cv_prog_egrep" >&6 + EGREP=$ac_cv_prog_egrep + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + 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 <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + 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 <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ctype.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#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); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_Header=no" +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +echo "$as_me:$LINENO: checking for pid_t" >&5 +echo $ECHO_N "checking for pid_t... $ECHO_C" >&6 +if test "${ac_cv_type_pid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((pid_t *) 0) + return 0; +if (sizeof (pid_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_pid_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_pid_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 +echo "${ECHO_T}$ac_cv_type_pid_t" >&6 +if test $ac_cv_type_pid_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6 +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +if ((size_t *) 0) + return 0; +if (sizeof (size_t)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_size_t=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6 +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned +_ACEOF + +fi + +echo "$as_me:$LINENO: checking whether time.h and sys/time.h may both be included" >&5 +echo $ECHO_N "checking whether time.h and sys/time.h may both be included... $ECHO_C" >&6 +if test "${ac_cv_header_time+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/time.h> +#include <time.h> + +int +main () +{ +if ((struct tm *) 0) +return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_time=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_time=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_header_time" >&5 +echo "${ECHO_T}$ac_cv_header_time" >&6 +if test $ac_cv_header_time = yes; then + +cat >>confdefs.h <<\_ACEOF +#define TIME_WITH_SYS_TIME 1 +_ACEOF + +fi + + +echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6 +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <float.h> + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_header_stdc=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <string.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + 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 <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <stdlib.h> + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + 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 <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <ctype.h> +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#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); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6 +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + + + + + + + + +for ac_header in fcntl.h limits.h malloc.h sys/ioctl.h sys/time.h unistd.h bstring.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6 +if eval "test \"\${$as_ac_Header+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6 + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +if test $ac_cv_c_compiler_gnu = yes; then + echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5 +echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6 +if test "${ac_cv_prog_gcc_traditional+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_pattern="Autoconf.*'x'" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sgtty.h> +Autoconf TIOCGETP +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +else + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <termio.h> +Autoconf TCGETA +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi +echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5 +echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6 + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6 +if test "${ac_cv_type_signal+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <signal.h> +#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; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_type_signal=void +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_type_signal=int +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +echo "${ECHO_T}$ac_cv_type_signal" >&6 + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + + +for ac_func in vprintf +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +echo "$as_me:$LINENO: checking for _doprnt" >&5 +echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6 +if test "${ac_cv_func__doprnt+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define _doprnt to an innocuous variant, in case <limits.h> declares _doprnt. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define _doprnt innocuous__doprnt + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char _doprnt (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef _doprnt + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char _doprnt (); +/* 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 +char (*f) () = _doprnt; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != _doprnt; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func__doprnt=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func__doprnt=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5 +echo "${ECHO_T}$ac_cv_func__doprnt" >&6 +if test $ac_cv_func__doprnt = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_DOPRNT 1 +_ACEOF + +fi + +fi +done + + + + + + +for ac_func in gettimeofday select socket strerror +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works +# for constant arguments. Useless! +echo "$as_me:$LINENO: checking for working alloca.h" >&5 +echo $ECHO_N "checking for working alloca.h... $ECHO_C" >&6 +if test "${ac_cv_working_alloca_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <alloca.h> +int +main () +{ +char *p = (char *) alloca (2 * sizeof (int)); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_working_alloca_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_working_alloca_h=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_working_alloca_h" >&5 +echo "${ECHO_T}$ac_cv_working_alloca_h" >&6 +if test $ac_cv_working_alloca_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ALLOCA_H 1 +_ACEOF + +fi + +echo "$as_me:$LINENO: checking for alloca" >&5 +echo $ECHO_N "checking for alloca... $ECHO_C" >&6 +if test "${ac_cv_func_alloca_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __GNUC__ +# define alloca __builtin_alloca +#else +# ifdef _MSC_VER +# include <malloc.h> +# define alloca _alloca +# else +# if HAVE_ALLOCA_H +# include <alloca.h> +# else +# ifdef _AIX + #pragma alloca +# else +# ifndef alloca /* predefined by HP cc +Olibcalls */ +char *alloca (); +# endif +# endif +# endif +# endif +#endif + +int +main () +{ +char *p = (char *) alloca (1); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_alloca_works=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_func_alloca_works=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_func_alloca_works" >&5 +echo "${ECHO_T}$ac_cv_func_alloca_works" >&6 + +if test $ac_cv_func_alloca_works = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ALLOCA 1 +_ACEOF + +else + # The SVR3 libPW and SVR4 libucb both contain incompatible functions +# that cause trouble. Some versions do not even contain alloca or +# contain a buggy version. If you still want to use their alloca, +# use ar to extract alloca.o from them instead of compiling alloca.c. + +ALLOCA=alloca.$ac_objext + +cat >>confdefs.h <<\_ACEOF +#define C_ALLOCA 1 +_ACEOF + + +echo "$as_me:$LINENO: checking whether \`alloca.c' needs Cray hooks" >&5 +echo $ECHO_N "checking whether \`alloca.c' needs Cray hooks... $ECHO_C" >&6 +if test "${ac_cv_os_cray+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#if defined(CRAY) && ! defined(CRAY2) +webecray +#else +wenotbecray +#endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "webecray" >/dev/null 2>&1; then + ac_cv_os_cray=yes +else + ac_cv_os_cray=no +fi +rm -f conftest* + +fi +echo "$as_me:$LINENO: result: $ac_cv_os_cray" >&5 +echo "${ECHO_T}$ac_cv_os_cray" >&6 +if test $ac_cv_os_cray = yes; then + for ac_func in _getb67 GETB67 getb67; do + as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6 +if eval "test \"\${$as_ac_var+set}\" = set"; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func. + For example, HP-UX 11i <limits.h> declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + <limits.h> exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + +#undef $ac_func + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +{ +#endif +/* 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 (); +/* 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 +char (*f) () = $ac_func; +#endif +#ifdef __cplusplus +} +#endif + +int +main () +{ +return f != $ac_func; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +eval "$as_ac_var=no" +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5 +echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6 +if test `eval echo '${'$as_ac_var'}'` = yes; then + +cat >>confdefs.h <<_ACEOF +#define CRAY_STACKSEG_END $ac_func +_ACEOF + + break +fi + + done +fi + +echo "$as_me:$LINENO: checking stack direction for C alloca" >&5 +echo $ECHO_N "checking stack direction for C alloca... $ECHO_C" >&6 +if test "${ac_cv_c_stack_direction+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_c_stack_direction=0 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +int +find_stack_direction () +{ + static char *addr = 0; + auto char dummy; + if (addr == 0) + { + addr = &dummy; + return find_stack_direction (); + } + else + return (&dummy > addr) ? 1 : -1; +} + +int +main () +{ + exit (find_stack_direction () < 0); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_stack_direction=1 +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_c_stack_direction=-1 +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +echo "$as_me:$LINENO: result: $ac_cv_c_stack_direction" >&5 +echo "${ECHO_T}$ac_cv_c_stack_direction" >&6 + +cat >>confdefs.h <<_ACEOF +#define STACK_DIRECTION $ac_cv_c_stack_direction +_ACEOF + + +fi + + +# audio APIs may accumulate, but there must be only one MIDI API (??) +AUDIOSRC="" +MIDISRC="s_midi_none.c" + +if test `uname -s` = Linux; then + LDFLAGS="-Wl,-export-dynamic " + EXT=pd_linux + LIBSUFFIX=.so + CPPFLAGS="-DDL_OPEN -DPA_USE_OSS -DUNIX -DUNISTD" + MORECFLAGS="-fno-strict-aliasing" + STRIPFLAG=-s + LDSOFLAGS="-shared" +fi + +if test `uname -s` = Darwin; then + LDFLAGS="" + EXT=pd_darwin + LIBSUFFIX=.dylib + CPPFLAGS="-DMACOSX -DUNISTD -I/usr/X11R6/include -DPA_USE_COREAUDIO" + CPPFLAGS=$CPPFLAGS" -DDL_OPEN" # 0.40 + MORECFLAGS="" + STRIPFLAG="" +# LDSOFLAGS="-dylib -bundle -flat_namespace -undefined suppress" +# LDSOFLAGS="-dylib -flat_namespace" +# Charles Turner told me to use this: + LDSOFLAGS="-dynamiclib -Wl,-single_module" + EXTERNTARGET=pd_darwin +### this comes from pd 0.40 +# if test `uname -r` = 7.9.0; then +# CPPFLAGS=$CPPFLAGS" -DMACOSX3" +# EXTERNTARGET=d_ppc +# else +# CPPFLAGS=$CPPFLAGS" -isysroot /Developer/SDKs/MacOSX10.4u.sdk" +# MORECFLAGS=$MORECFLAGS" -arch i386 -arch ppc" +# EXTERNTARGET=d_fat +# LDFLAGS=$LDFLAGS" -arch i386 -arch ppc" +# fi +fi + +if test `uname -s | cut -f1 -d_` = CYGWIN; then + LDFLAGS="-Wl,-export-dynamic " + EXT=pd_linux + LIBSUFFIX=.dll + CPPFLAGS="-DDL_OPEN -DPA_USE_OSS -DUNIX -DUNISTD" + MORECFLAGS="-fno-strict-aliasing" + STRIPFLAG=-s + LDSOFLAGS="-shared" +fi + +if test `uname -m` = x86_64; then + MORECFLAGS=$MORECFLAGS" -fPIC" +fi + +if test "$static" = yes; then LDFLAGS="$LDFLAGS -static"; fi + +echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 +echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6 +if test "${ac_cv_lib_dl_dlopen+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char dlopen (); +int +main () +{ +dlopen (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_dl_dlopen=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_dl_dlopen=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 +echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6 +if test $ac_cv_lib_dl_dlopen = yes; then + LDFLAGS="$LDFLAGS -ldl" +else + echo "dynamic link support required" || exit 1 +fi + +echo "$as_me:$LINENO: checking for sin in -lffm" >&5 +echo $ECHO_N "checking for sin in -lffm... $ECHO_C" >&6 +if test "${ac_cv_lib_ffm_sin+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lffm $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char sin (); +int +main () +{ +sin (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_ffm_sin=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_ffm_sin=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_ffm_sin" >&5 +echo "${ECHO_T}$ac_cv_lib_ffm_sin" >&6 +if test $ac_cv_lib_ffm_sin = yes; then + LDFLAGS="$LDFLAGS -lffm" +fi + +echo "$as_me:$LINENO: checking for sin in -lm" >&5 +echo $ECHO_N "checking for sin in -lm... $ECHO_C" >&6 +if test "${ac_cv_lib_m_sin+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lm $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char sin (); +int +main () +{ +sin (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_m_sin=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_m_sin=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_m_sin" >&5 +echo "${ECHO_T}$ac_cv_lib_m_sin" >&6 +if test $ac_cv_lib_m_sin = yes; then + LDFLAGS="$LDFLAGS -lm" +else + echo "math library required" || exit 1 +fi + +echo "$as_me:$LINENO: checking for pthread_create in -lpthread" >&5 +echo $ECHO_N "checking for pthread_create in -lpthread... $ECHO_C" >&6 +if test "${ac_cv_lib_pthread_pthread_create+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lpthread $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char pthread_create (); +int +main () +{ +pthread_create (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_pthread_pthread_create=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_pthread_pthread_create=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_pthread_pthread_create" >&5 +echo "${ECHO_T}$ac_cv_lib_pthread_pthread_create" >&6 +if test $ac_cv_lib_pthread_pthread_create = yes; then + LDFLAGS="$LDFLAGS -lpthread" +else + echo "pthreads required" || exit 1 +fi + + +if test x$oss == xyes; then + if test "${ac_cv_header_linux_soundcard_h+set}" = set; then + echo "$as_me:$LINENO: checking for linux/soundcard.h" >&5 +echo $ECHO_N "checking for linux/soundcard.h... $ECHO_C" >&6 +if test "${ac_cv_header_linux_soundcard_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +echo "$as_me:$LINENO: result: $ac_cv_header_linux_soundcard_h" >&5 +echo "${ECHO_T}$ac_cv_header_linux_soundcard_h" >&6 +else + # Is the header compilable? +echo "$as_me:$LINENO: checking linux/soundcard.h usability" >&5 +echo $ECHO_N "checking linux/soundcard.h usability... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <linux/soundcard.h> +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_header_compiler=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6 + +# Is the header present? +echo "$as_me:$LINENO: checking linux/soundcard.h presence" >&5 +echo $ECHO_N "checking linux/soundcard.h presence... $ECHO_C" >&6 +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <linux/soundcard.h> +_ACEOF +if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5 + (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null; then + if test -s conftest.err; then + ac_cpp_err=$ac_c_preproc_warn_flag + ac_cpp_err=$ac_cpp_err$ac_c_werror_flag + else + ac_cpp_err= + fi +else + ac_cpp_err=yes +fi +if test -z "$ac_cpp_err"; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi +rm -f conftest.err conftest.$ac_ext +echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6 + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: linux/soundcard.h: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: linux/soundcard.h: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: present but cannot be compiled" >&5 +echo "$as_me: WARNING: linux/soundcard.h: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: linux/soundcard.h: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: linux/soundcard.h: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: linux/soundcard.h: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: linux/soundcard.h: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: linux/soundcard.h: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: linux/soundcard.h: in the future, the compiler will take precedence" >&2;} + ( + cat <<\_ASBOX +## ------------------------------------------ ## +## Report this to the AC_PACKAGE_NAME lists. ## +## ------------------------------------------ ## +_ASBOX + ) | + sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +echo "$as_me:$LINENO: checking for linux/soundcard.h" >&5 +echo $ECHO_N "checking for linux/soundcard.h... $ECHO_C" >&6 +if test "${ac_cv_header_linux_soundcard_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_header_linux_soundcard_h=$ac_header_preproc +fi +echo "$as_me:$LINENO: result: $ac_cv_header_linux_soundcard_h" >&5 +echo "${ECHO_T}$ac_cv_header_linux_soundcard_h" >&6 + +fi +if test $ac_cv_header_linux_soundcard_h = yes; then + oss="yes" +else + oss="no" +fi + + +fi + +if test x$alsa == xyes; then + echo "$as_me:$LINENO: checking for snd_pcm_info in -lasound" >&5 +echo $ECHO_N "checking for snd_pcm_info in -lasound... $ECHO_C" >&6 +if test "${ac_cv_lib_asound_snd_pcm_info+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lasound $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char snd_pcm_info (); +int +main () +{ +snd_pcm_info (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_asound_snd_pcm_info=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_asound_snd_pcm_info=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_asound_snd_pcm_info" >&5 +echo "${ECHO_T}$ac_cv_lib_asound_snd_pcm_info" >&6 +if test $ac_cv_lib_asound_snd_pcm_info = yes; then + LDFLAGS="$LDFLAGS -lasound" ; alsa="yes" +else + alsa="no" +fi + +fi + +if test x$jack == xyes; then + echo "$as_me:$LINENO: checking for shm_open in -lrt" >&5 +echo $ECHO_N "checking for shm_open in -lrt... $ECHO_C" >&6 +if test "${ac_cv_lib_rt_shm_open+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lrt $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char shm_open (); +int +main () +{ +shm_open (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_rt_shm_open=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_rt_shm_open=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_rt_shm_open" >&5 +echo "${ECHO_T}$ac_cv_lib_rt_shm_open" >&6 +if test $ac_cv_lib_rt_shm_open = yes; then + LIBS="$LIBS -lrt" +fi + + echo "$as_me:$LINENO: checking for jack_set_xrun_callback in -ljack" >&5 +echo $ECHO_N "checking for jack_set_xrun_callback in -ljack... $ECHO_C" >&6 +if test "${ac_cv_lib_jack_jack_set_xrun_callback+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ljack $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char jack_set_xrun_callback (); +int +main () +{ +jack_set_xrun_callback (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_jack_jack_set_xrun_callback=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_jack_jack_set_xrun_callback=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_jack_jack_set_xrun_callback" >&5 +echo "${ECHO_T}$ac_cv_lib_jack_jack_set_xrun_callback" >&6 +if test $ac_cv_lib_jack_jack_set_xrun_callback = yes; then + jack=xrun +else + jack=no +fi + + echo "$as_me:$LINENO: checking for jack_set_error_function in -ljack" >&5 +echo $ECHO_N "checking for jack_set_error_function in -ljack... $ECHO_C" >&6 +if test "${ac_cv_lib_jack_jack_set_error_function+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ljack $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char jack_set_error_function (); +int +main () +{ +jack_set_error_function (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_jack_jack_set_error_function=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_jack_jack_set_error_function=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_jack_jack_set_error_function" >&5 +echo "${ECHO_T}$ac_cv_lib_jack_jack_set_error_function" >&6 +if test $ac_cv_lib_jack_jack_set_error_function = yes; then + jack=yes +else + jack=no +fi + +fi + +if test x$portaudio == xyes; then + echo "$as_me:$LINENO: checking for Pa_GetDeviceCount in -lportaudio" >&5 +echo $ECHO_N "checking for Pa_GetDeviceCount in -lportaudio... $ECHO_C" >&6 +if test "${ac_cv_lib_portaudio_Pa_GetDeviceCount+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lportaudio $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char Pa_GetDeviceCount (); +int +main () +{ +Pa_GetDeviceCount (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_portaudio_Pa_GetDeviceCount=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_portaudio_Pa_GetDeviceCount=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_portaudio_Pa_GetDeviceCount" >&5 +echo "${ECHO_T}$ac_cv_lib_portaudio_Pa_GetDeviceCount" >&6 +if test $ac_cv_lib_portaudio_Pa_GetDeviceCount = yes; then + LDFLAGS=$LDFLAGS" -lportaudio" ; portaudio=yes +else + portaudio=no +fi + +fi + +if test x$portmidi == xyes; then + echo "$as_me:$LINENO: checking for Pt_Started in -lporttime" >&5 +echo $ECHO_N "checking for Pt_Started in -lporttime... $ECHO_C" >&6 +if test "${ac_cv_lib_porttime_Pt_Started___+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lporttime $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char Pt_Started (); +int +main () +{ +Pt_Started (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_porttime_Pt_Started___=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_porttime_Pt_Started___=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_porttime_Pt_Started___" >&5 +echo "${ECHO_T}$ac_cv_lib_porttime_Pt_Started___" >&6 +if test $ac_cv_lib_porttime_Pt_Started___ = yes; then + LDFLAGS=$LDFLAGS" -lporttime" +fi + + echo "$as_me:$LINENO: checking for Pm_Initialize in -lportmidi" >&5 +echo $ECHO_N "checking for Pm_Initialize in -lportmidi... $ECHO_C" >&6 +if test "${ac_cv_lib_portmidi_Pm_Initialize+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lportmidi $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char Pm_Initialize (); +int +main () +{ +Pm_Initialize (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_portmidi_Pm_Initialize=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_portmidi_Pm_Initialize=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_portmidi_Pm_Initialize" >&5 +echo "${ECHO_T}$ac_cv_lib_portmidi_Pm_Initialize" >&6 +if test $ac_cv_lib_portmidi_Pm_Initialize = yes; then + LDFLAGS=$LDFLAGS" -lportmidi" ; portmidi=yes +else + portmidi=no +fi + +fi + +if test x$USE_DEBUG_CFLAGS == xyes; then + MORECFLAGS=$MORECFLAGS" -O1 -g" +else + MORECFLAGS=$MORECFLAGS" -O3 -funroll-loops -fomit-frame-pointer" +fi + +if test x$oss == xyes; then + AUDIOSRC=$AUDIOSRC" s_audio_oss.c" + MIDISRC="s_midi_oss.c" + CPPFLAGS=$CPPFLAGS" -DUSEAPI_OSS" +fi + +if test x$alsa == xyes; then + # the alsa midi is weird, it needs to have another MIDI module at once, so i put it in "audio" instead. + AUDIOSRC=$AUDIOSRC" s_audio_alsa.c s_audio_alsamm.c s_midi_alsa.c" + CPPFLAGS=$CPPFLAGS" -DPA_USE_ALSA -DUSEAPI_ALSA" + LDFLAGS=$LDFLAGS" -lasound" +fi + +if test x$jack == xyes; then + AUDIOSRC=$AUDIOSRC" s_audio_jack.c" + if test x$jack != xno; then + if test `uname -s` = Linux; then + if test x$jack == "xyes"; then LDFLAGS=$LDFLAGS" -lrt -ljack"; fi + if test x$jack == "xrun"; then LDFLAGS=$LDFLAGS" -lrt -ljack"; fi + fi + if test `uname -s` = Darwin; then + LDFLAGS=$LDFLAGS" -framework Jack" # 0.39 + LDFLAGS=$LDFLAGS" -weak_framework Jack" # 0.40 + else + LIBS="$LIBS -ljack" + fi + CPPFLAGS=$CPPFLAGS" -DUSEAPI_JACK" + fi + if test x$jack == "xrun"; then CPPFLAGS=$CPPFLAGS" -DJACK_XRUN"; fi +fi + +if test x$portaudio == xyes; then + CPPFLAGS=$CPPFLAGS" -DUSEAPI_PORTAUDIO -DPA19" + AUDIOSRC=$AUDIOSRC" s_audio_portaudio.c" + if test `uname -s` = Darwin; then + LDFLAGS=$LDFLAGS" -framework CoreAudio -framework AudioUnit -framework AudioToolbox -framework Carbon" + fi +fi + +if test x$portmidi == xyes; then + MIDISRC="s_midi_pm.c" + MIDIFLAGS="-DUSEAPI_PORTMIDI" + if test `uname -s` = Darwin; then + LDFLAGS=$LDFLAGS" -framework CoreMIDI" + fi +fi + +CPPFLAGS=$CPPFLAGS" $MIDIFLAGS" + +if test x$fftw == "xyes"; then + echo "$as_me:$LINENO: checking for fftwf_forget_wisdom in -lfftw3f" >&5 +echo $ECHO_N "checking for fftwf_forget_wisdom in -lfftw3f... $ECHO_C" >&6 +if test "${ac_cv_lib_fftw3f_fftwf_forget_wisdom_+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lfftw3f $LIBS" +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +/* Override any gcc2 internal prototype to avoid an error. */ +#ifdef __cplusplus +extern "C" +#endif +/* We use char because int might match the return type of a gcc2 + builtin and then its argument prototype would still apply. */ +char fftwf_forget_wisdom (); +int +main () +{ +fftwf_forget_wisdom (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_lib_fftw3f_fftwf_forget_wisdom_=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_lib_fftw3f_fftwf_forget_wisdom_=no +fi +rm -f conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +echo "$as_me:$LINENO: result: $ac_cv_lib_fftw3f_fftwf_forget_wisdom_" >&5 +echo "${ECHO_T}$ac_cv_lib_fftw3f_fftwf_forget_wisdom_" >&6 +if test $ac_cv_lib_fftw3f_fftwf_forget_wisdom_ = yes; then + LDFLAGS=$LDFLAGS" -lfftw3f" +else + fftw=no +fi + +fi + +echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 +echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6 +if test "${ac_cv_c_bigendian+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # See if sys/param.h defines the BYTE_ORDER macro. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/param.h> + +int +main () +{ +#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + # It does; now see whether it defined to BIG_ENDIAN or not. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <sys/types.h> +#include <sys/param.h> + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_bigendian=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +# It does not; compile a test program. +if test "$cross_compiling" = yes; then + # try to guess the endianness by grepping values into an object file + ac_cv_c_bigendian=unknown + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; +short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; +void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; } +short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; +short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; +void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; } +int +main () +{ + _ascii (); _ebcdic (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then + ac_cv_c_bigendian=yes +fi +if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi +fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +int +main () +{ + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long l; + char c[sizeof (long)]; + } u; + u.l = 1; + exit (u.c[sizeof (long) - 1] == 1); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_c_bigendian=yes +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 +echo "${ECHO_T}$ac_cv_c_bigendian" >&6 +case $ac_cv_c_bigendian in + yes) + bigendian=yes ;; + no) + bigendian=no ;; + *) + bigendian=maybe ;; +esac + +if test x$bigendian = "xyes"; then + CPPFLAGS=$CPPFLAGS" -DBIGENDIAN" +fi + + ac_config_files="$ac_config_files makefile" +cat >confcache <<\_ACEOF +# 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, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# 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 \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n \ + "s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1=\\2/p" + ;; + esac; +} | + sed ' + t clear + : clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + /^ac_cv_env/!s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + : end' >>confcache +if diff $cache_file confcache >/dev/null 2>&1; then :; else + if test -w $cache_file; then + test "x$cache_file" != "x/dev/null" && echo "updating cache $cache_file" + cat confcache >$cache_file + else + echo "not updating unwritable cache $cache_file" + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/; +s/:*\${srcdir}:*/:/; +s/:*@srcdir@:*/:/; +s/^\([^=]*=[ ]*\):*/\1/; +s/:*$//; +s/^[^=]*=[ ]*$//; +}' +fi + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then we branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +cat >confdef2opt.sed <<\_ACEOF +t clear +: clear +s,^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\),-D\1=\2,g +t quote +s,^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\),-D\1=\2,g +t quote +d +: quote +s,[ `~#$^&*(){}\\|;'"<>?],\\&,g +s,\[,\\&,g +s,\],\\&,g +s,\$,$$,g +p +_ACEOF +# We use echo to avoid assuming a particular line-breaking character. +# The extra dot is to prevent the shell from consuming trailing +# line-breaks from the sub-command output. A line-break within +# single-quotes doesn't work because, if this script is created in a +# platform that uses two characters for line-breaks (e.g., DOS), tr +# would break. +ac_LF_and_DOT=`echo; echo .` +DEFS=`sed -n -f confdef2opt.sed confdefs.h | tr "$ac_LF_and_DOT" ' .'` +rm -f confdef2opt.sed + + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_i=`echo "$ac_i" | + sed 's/\$U\././;s/\.o$//;s/\.obj$//'` + # 2. Add them. + ac_libobjs="$ac_libobjs $ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs $ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' +elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then + set -o posix +fi +DUALCASE=1; export DUALCASE # for MKS sh + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# Work around bugs in pre-3.0 UWIN ksh. +$as_unset ENV MAIL MAILPATH +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +if (basename /) >/dev/null 2>&1 && test "X`basename / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)$' \| \ + . : '\(.\)' 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/; q; } + /^X\/\(\/\/\)$/{ s//\1/; q; } + /^X\/\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + + +# PATH needs CR, and LINENO needs CR and PATH. +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" || { + # Find who we are. Look in the path if we contain no path at all + # relative or not. + case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done + + ;; + esac + # We did not find ourselves, most probably we were run as `sh COMMAND' + # in which case we are not to be found in the path. + if test "x$as_myself" = x; then + as_myself=$0 + fi + if test ! -f "$as_myself"; then + { { echo "$as_me:$LINENO: error: cannot find myself; rerun with an absolute path" >&5 +echo "$as_me: error: cannot find myself; rerun with an absolute path" >&2;} + { (exit 1); exit 1; }; } + fi + case $CONFIG_SHELL in + '') + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for as_base in sh bash ksh sh5; do + case $as_dir in + /*) + if ("$as_dir/$as_base" -c ' + as_lineno_1=$LINENO + as_lineno_2=$LINENO + as_lineno_3=`(expr $as_lineno_1 + 1) 2>/dev/null` + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x$as_lineno_3" = "x$as_lineno_2" ') 2>/dev/null; then + $as_unset BASH_ENV || test "${BASH_ENV+set}" != set || { BASH_ENV=; export BASH_ENV; } + $as_unset ENV || test "${ENV+set}" != set || { ENV=; export ENV; } + CONFIG_SHELL=$as_dir/$as_base + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$0" ${1+"$@"} + fi;; + esac + done +done +;; + esac + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line before each line; the second 'sed' does the real + # work. The second script uses 'N' to pair each line-number line + # with the numbered line, and appends trailing '-' during + # substitution so that $LINENO is not a special case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # second 'sed' script. Blame Lee E. McMahon for sed's syntax. :-) + sed '=' <$as_myself | + sed ' + N + s,$,-, + : loop + s,^\(['$as_cr_digits']*\)\(.*\)[$]LINENO\([^'$as_cr_alnum'_]\),\1\2\1\3, + t loop + s,-$,, + s,^['$as_cr_digits']*\n,, + ' >$as_me.lineno && + chmod +x $as_me.lineno || + { { echo "$as_me:$LINENO: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&5 +echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2;} + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensible to this). + . ./$as_me.lineno + # Exit status is that of the last command. + exit +} + + +case `echo "testing\c"; echo 1,2,3`,`echo -n testing; echo 1,2,3` in + *c*,-n*) ECHO_N= ECHO_C=' +' ECHO_T=' ' ;; + *c*,* ) ECHO_N=-n ECHO_C= ECHO_T= ;; + *) ECHO_N= ECHO_C='\c' ECHO_T= ;; +esac + +if expr a : '\(a\)' >/dev/null 2>&1; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + # We could just check for DJGPP; but this test a) works b) is more generic + # and c) will remain valid once DJGPP supports symlinks (DJGPP 2.04). + if test -f conf$$.exe; then + # Don't use ln at all; we don't have any links + as_ln_s='cp -p' + else + as_ln_s='ln -s' + fi +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.file + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_executable_p="test -f" + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +# IFS +# We need space, tab and new line, in precisely that order. +as_nl=' +' +IFS=" $as_nl" + +# CDPATH. +$as_unset CDPATH + +exec 6>&1 + +# Open the log real soon, to keep \$[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. Logging --version etc. is OK. +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX +} >&5 +cat >&5 <<_CSEOF + +This file was extended by $as_me, which was +generated by GNU Autoconf 2.59. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +_CSEOF +echo "on `(hostname || uname -n) 2>/dev/null | sed 1q`" >&5 +echo >&5 +_ACEOF + +# Files that config.status was made for. +if test -n "$ac_config_files"; then + echo "config_files=\"$ac_config_files\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_headers"; then + echo "config_headers=\"$ac_config_headers\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_links"; then + echo "config_links=\"$ac_config_links\"" >>$CONFIG_STATUS +fi + +if test -n "$ac_config_commands"; then + echo "config_commands=\"$ac_config_commands\"" >>$CONFIG_STATUS +fi + +cat >>$CONFIG_STATUS <<\_ACEOF + +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to <bug-autoconf@gnu.org>." +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.59, + with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2003 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." +srcdir=$srcdir +INSTALL="$INSTALL" +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "x$1" : 'x\([^=]*\)='` + ac_optarg=`expr "x$1" : 'x[^=]*=\(.*\)'` + ac_shift=: + ;; + -*) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + *) # This is not an option, so the user has probably given explicit + # arguments. + ac_option=$1 + ac_need_defaults=false;; + esac + + case $ac_option in + # Handling of the options. +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --vers* | -V ) + echo "$ac_cs_version"; exit 0 ;; + --he | --h) + # Conflict between --help and --header + { { echo "$as_me:$LINENO: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { { echo "$as_me:$LINENO: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&5 +echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2;} + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running $SHELL $0 " $ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + exec $SHELL $0 $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF + + + + + +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_config_target in $ac_config_targets +do + case "$ac_config_target" in + # Handling of arguments. + "makefile" ) CONFIG_FILES="$CONFIG_FILES makefile" ;; + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason to put it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Create a temporary directory, and hook for its removal unless debugging. +$debug || +{ + trap 'exit_status=$?; rm -rf $tmp && exit $exit_status' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} + +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d -q "./confstatXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./confstat$$-$RANDOM + (umask 077 && mkdir $tmp) +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF + +# +# CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "\$CONFIG_FILES"; then + # Protect against being on the right side of a sed subst in config.status. + sed 's/,@/@@/; s/@,/@@/; s/,;t t\$/@;t t/; /@;t t\$/s/[\\\\&,]/\\\\&/g; + s/@@/,@/; s/@@/@,/; s/@;t t\$/,;t t/' >\$tmp/subs.sed <<\\CEOF +s,@SHELL@,$SHELL,;t t +s,@PATH_SEPARATOR@,$PATH_SEPARATOR,;t t +s,@PACKAGE_NAME@,$PACKAGE_NAME,;t t +s,@PACKAGE_TARNAME@,$PACKAGE_TARNAME,;t t +s,@PACKAGE_VERSION@,$PACKAGE_VERSION,;t t +s,@PACKAGE_STRING@,$PACKAGE_STRING,;t t +s,@PACKAGE_BUGREPORT@,$PACKAGE_BUGREPORT,;t t +s,@exec_prefix@,$exec_prefix,;t t +s,@prefix@,$prefix,;t t +s,@program_transform_name@,$program_transform_name,;t t +s,@bindir@,$bindir,;t t +s,@sbindir@,$sbindir,;t t +s,@libexecdir@,$libexecdir,;t t +s,@datadir@,$datadir,;t t +s,@sysconfdir@,$sysconfdir,;t t +s,@sharedstatedir@,$sharedstatedir,;t t +s,@localstatedir@,$localstatedir,;t t +s,@libdir@,$libdir,;t t +s,@includedir@,$includedir,;t t +s,@oldincludedir@,$oldincludedir,;t t +s,@infodir@,$infodir,;t t +s,@mandir@,$mandir,;t t +s,@build_alias@,$build_alias,;t t +s,@host_alias@,$host_alias,;t t +s,@target_alias@,$target_alias,;t t +s,@DEFS@,$DEFS,;t t +s,@ECHO_C@,$ECHO_C,;t t +s,@ECHO_N@,$ECHO_N,;t t +s,@ECHO_T@,$ECHO_T,;t t +s,@LIBS@,$LIBS,;t t +s,@oss@,$oss,;t t +s,@alsa@,$alsa,;t t +s,@jack@,$jack,;t t +s,@portaudio@,$portaudio,;t t +s,@portmidi@,$portmidi,;t t +s,@fftw@,$fftw,;t t +s,@CPPFLAGS@,$CPPFLAGS,;t t +s,@MORECFLAGS@,$MORECFLAGS,;t t +s,@EXT@,$EXT,;t t +s,@USE_DEBUG_CFLAGS@,$USE_DEBUG_CFLAGS,;t t +s,@AUDIOSRC@,$AUDIOSRC,;t t +s,@MIDISRC@,$MIDISRC,;t t +s,@STRIPFLAG@,$STRIPFLAG,;t t +s,@EXTERNTARGET@,$EXTERNTARGET,;t t +s,@LIBSUFFIX@,$LIBSUFFIX,;t t +s,@LDSOFLAGS@,$LDSOFLAGS,;t t +s,@CC@,$CC,;t t +s,@CFLAGS@,$CFLAGS,;t t +s,@LDFLAGS@,$LDFLAGS,;t t +s,@ac_ct_CC@,$ac_ct_CC,;t t +s,@EXEEXT@,$EXEEXT,;t t +s,@OBJEXT@,$OBJEXT,;t t +s,@INSTALL_PROGRAM@,$INSTALL_PROGRAM,;t t +s,@INSTALL_SCRIPT@,$INSTALL_SCRIPT,;t t +s,@INSTALL_DATA@,$INSTALL_DATA,;t t +s,@SET_MAKE@,$SET_MAKE,;t t +s,@CPP@,$CPP,;t t +s,@EGREP@,$EGREP,;t t +s,@ALLOCA@,$ALLOCA,;t t +s,@LIBOBJS@,$LIBOBJS,;t t +s,@LTLIBOBJS@,$LTLIBOBJS,;t t +CEOF + +_ACEOF + + cat >>$CONFIG_STATUS <<\_ACEOF + # 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_lines=48 + ac_sed_frag=1 # Number of current file. + ac_beg=1 # First line for current file. + ac_end=$ac_max_sed_lines # 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" $tmp/subs.sed >$tmp/subs.frag + else + sed "${ac_end}q" $tmp/subs.sed >$tmp/subs.frag + fi + if test ! -s $tmp/subs.frag; then + ac_more_lines=false + else + # The purpose of the label and of the branching condition is to + # speed up the sed processing (if there are no `@' at all, there + # is no need to browse any of the substitutions). + # These are the two extra sed commands mentioned above. + (echo ':t + /@[a-zA-Z_][a-zA-Z_0-9]*@/!b' && cat $tmp/subs.frag) >$tmp/subs-$ac_sed_frag.sed + if test -z "$ac_sed_cmds"; then + ac_sed_cmds="sed -f $tmp/subs-$ac_sed_frag.sed" + else + ac_sed_cmds="$ac_sed_cmds | sed -f $tmp/subs-$ac_sed_frag.sed" + fi + ac_sed_frag=`expr $ac_sed_frag + 1` + ac_beg=$ac_end + ac_end=`expr $ac_end + $ac_max_sed_lines` + fi + done + if test -z "$ac_sed_cmds"; then + ac_sed_cmds=cat + fi +fi # test -n "$CONFIG_FILES" + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +for ac_file in : $CONFIG_FILES; do test "x$ac_file" = x: && continue + # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". + case $ac_file in + - | *:- | *:-:* ) # input from stdin + cat >$tmp/stdin + ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + *:* ) ac_file_in=`echo "$ac_file" | sed 's,[^:]*:,,'` + ac_file=`echo "$ac_file" | sed 's,:.*,,'` ;; + * ) ac_file_in=$ac_file.in ;; + esac + + # Compute @srcdir@, @top_srcdir@, and @INSTALL@ for subdirectories. + ac_dir=`(dirname "$ac_file") 2>/dev/null || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + { if $as_mkdir_p; then + mkdir -p "$ac_dir" + else + as_dir="$ac_dir" + as_dirs= + while test ! -d "$as_dir"; do + as_dirs="$as_dir $as_dirs" + as_dir=`(dirname "$as_dir") 2>/dev/null || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| \ + . : '\(.\)' 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/; q; } + /^X\(\/\/\)[^/].*/{ s//\1/; q; } + /^X\(\/\/\)$/{ s//\1/; q; } + /^X\(\/\).*/{ s//\1/; q; } + s/.*/./; q'` + done + test ! -n "$as_dirs" || mkdir $as_dirs + fi || { { echo "$as_me:$LINENO: error: cannot create directory \"$ac_dir\"" >&5 +echo "$as_me: error: cannot create directory \"$ac_dir\"" >&2;} + { (exit 1); exit 1; }; }; } + + ac_builddir=. + +if test "$ac_dir" != .; then + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A "../" for each directory in $ac_dir_suffix. + ac_top_builddir=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,../,g'` +else + ac_dir_suffix= ac_top_builddir= +fi + +case $srcdir in + .) # No --srcdir option. We are building in place. + ac_srcdir=. + if test -z "$ac_top_builddir"; then + ac_top_srcdir=. + else + ac_top_srcdir=`echo $ac_top_builddir | sed 's,/$,,'` + fi ;; + [\\/]* | ?:[\\/]* ) # Absolute path. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir ;; + *) # Relative path. + ac_srcdir=$ac_top_builddir$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_builddir$srcdir ;; +esac + +# Do not use `cd foo && pwd` to compute absolute paths, because +# the directories may not exist. +case `pwd` in +.) ac_abs_builddir="$ac_dir";; +*) + case "$ac_dir" in + .) ac_abs_builddir=`pwd`;; + [\\/]* | ?:[\\/]* ) ac_abs_builddir="$ac_dir";; + *) ac_abs_builddir=`pwd`/"$ac_dir";; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_builddir=${ac_top_builddir}.;; +*) + case ${ac_top_builddir}. in + .) ac_abs_top_builddir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_builddir=${ac_top_builddir}.;; + *) ac_abs_top_builddir=$ac_abs_builddir/${ac_top_builddir}.;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_srcdir=$ac_srcdir;; +*) + case $ac_srcdir in + .) ac_abs_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_srcdir=$ac_srcdir;; + *) ac_abs_srcdir=$ac_abs_builddir/$ac_srcdir;; + esac;; +esac +case $ac_abs_builddir in +.) ac_abs_top_srcdir=$ac_top_srcdir;; +*) + case $ac_top_srcdir in + .) ac_abs_top_srcdir=$ac_abs_builddir;; + [\\/]* | ?:[\\/]* ) ac_abs_top_srcdir=$ac_top_srcdir;; + *) ac_abs_top_srcdir=$ac_abs_builddir/$ac_top_srcdir;; + esac;; +esac + + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_builddir$INSTALL ;; + esac + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + if test x"$ac_file" = x-; then + configure_input= + else + configure_input="$ac_file. " + fi + configure_input=$configure_input"Generated from `echo $ac_file_in | + sed 's,.*/,,'` by configure." + + # First look for the input files in the build tree, otherwise in the + # src tree. + ac_file_inputs=`IFS=: + for f in $ac_file_in; do + case $f in + -) echo $tmp/stdin ;; + [\\/$]*) + # Absolute (can't be DOS-style, as IFS=:) + test -f "$f" || { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + echo "$f";; + *) # Relative + if test -f "$f"; then + # Build tree + echo "$f" + elif test -f "$srcdir/$f"; then + # Source tree + echo "$srcdir/$f" + else + # /dev/null tree + { { echo "$as_me:$LINENO: error: cannot find input file: $f" >&5 +echo "$as_me: error: cannot find input file: $f" >&2;} + { (exit 1); exit 1; }; } + fi;; + esac + done` || { (exit 1); exit 1; } + + if test x"$ac_file" != x-; then + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + rm -f "$ac_file" + fi +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s,@configure_input@,$configure_input,;t t +s,@srcdir@,$ac_srcdir,;t t +s,@abs_srcdir@,$ac_abs_srcdir,;t t +s,@top_srcdir@,$ac_top_srcdir,;t t +s,@abs_top_srcdir@,$ac_abs_top_srcdir,;t t +s,@builddir@,$ac_builddir,;t t +s,@abs_builddir@,$ac_abs_builddir,;t t +s,@top_builddir@,$ac_top_builddir,;t t +s,@abs_top_builddir@,$ac_abs_top_builddir,;t t +s,@INSTALL@,$ac_INSTALL,;t t +" $ac_file_inputs | (eval "$ac_sed_cmds") >$tmp/out + rm -f $tmp/stdin + if test x"$ac_file" != x-; then + mv $tmp/out $ac_file + else + cat $tmp/out + rm -f $tmp/out + fi + +done +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + + diff --git a/desiredata/src/configure.in b/desiredata/src/configure.in new file mode 100644 index 00000000..752551ec --- /dev/null +++ b/desiredata/src/configure.in @@ -0,0 +1,213 @@ +# This file is part of DesireData +# If you want to build Thomas Grill's devel_0_39, you need to use scons instead. +# +# ./configure is not a source file (not written by someone), it's generated from +# ./configure.in, but it's still put in the CVS and releases so that people don't +# have to have autoconf installed. + +AC_INIT(kernel.c) +AC_SUBST(oss, yes) +AC_SUBST(alsa, yes) +AC_SUBST(jack, yes) +AC_SUBST(portaudio, yes) +AC_SUBST(portmidi, yes) +AC_SUBST(fftw, yes) +AC_SUBST(CPPFLAGS) +AC_SUBST(MORECFLAGS) +AC_SUBST(EXT) +AC_SUBST(USE_DEBUG_CFLAGS, no) +AC_SUBST(AUDIOSRC) +AC_SUBST(MIDISRC) +AC_SUBST(STRIPFLAG) +AC_SUBST(EXTERNTARGET) +AC_SUBST(LIBSUFFIX) +AC_SUBST(LDSOFLAGS) + +dnl Checks for features. +AC_ARG_ENABLE(alsa, [ --enable-oss audio via OSS], alsa=$enableval) +AC_ARG_ENABLE(alsa, [ --enable-alsa audio via ALSA], alsa=$enableval) +AC_ARG_ENABLE(jack, [ --enable-jack audio via JACK], jack=$enableval) +AC_ARG_ENABLE(portaudio,[ --enable-portaudio audio via PortAudio], portaudio=$enableval) +AC_ARG_ENABLE(portmidi, [ --enable-portmidi MIDI via PortMidi], portmidi=$enableval) +AC_ARG_ENABLE(debug, [ --enable-debug debugging support], USE_DEBUG_CFLAGS=$enableval) +AC_ARG_ENABLE(static, [ --enable-static link statically], static=$enableval) +AC_ARG_ENABLE(fftw, [ --enable-fftw use FFTW package], fftw=$enableval) + +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) +AC_FUNC_ALLOCA + +# audio APIs may accumulate, but there must be only one MIDI API (??) +AUDIOSRC="" +MIDISRC="s_midi_none.c" + +if test `uname -s` = Linux; then + LDFLAGS="-Wl,-export-dynamic " + EXT=pd_linux + LIBSUFFIX=.so + CPPFLAGS="-DDL_OPEN -DPA_USE_OSS -DUNIX -DUNISTD" + MORECFLAGS="-fno-strict-aliasing" + STRIPFLAG=-s + LDSOFLAGS="-shared" +fi + +if test `uname -s` = Darwin; then + LDFLAGS="" + EXT=pd_darwin + LIBSUFFIX=.dylib + CPPFLAGS="-DMACOSX -DUNISTD -I/usr/X11R6/include -DPA_USE_COREAUDIO" + CPPFLAGS=$CPPFLAGS" -DDL_OPEN" # 0.40 + MORECFLAGS="" + STRIPFLAG="" +# LDSOFLAGS="-dylib -bundle -flat_namespace -undefined suppress" +# LDSOFLAGS="-dylib -flat_namespace" +# Charles Turner told me to use this: + LDSOFLAGS="-dynamiclib -Wl,-single_module" + EXTERNTARGET=pd_darwin +### this comes from pd 0.40 +# if test `uname -r` = 7.9.0; then +# CPPFLAGS=$CPPFLAGS" -DMACOSX3" +# EXTERNTARGET=d_ppc +# else +# CPPFLAGS=$CPPFLAGS" -isysroot /Developer/SDKs/MacOSX10.4u.sdk" +# MORECFLAGS=$MORECFLAGS" -arch i386 -arch ppc" +# EXTERNTARGET=d_fat +# LDFLAGS=$LDFLAGS" -arch i386 -arch ppc" +# fi +fi + +if test `uname -s | cut -f1 -d_` = CYGWIN; then + LDFLAGS="-Wl,-export-dynamic " + EXT=pd_linux + LIBSUFFIX=.dll + CPPFLAGS="-DDL_OPEN -DPA_USE_OSS -DUNIX -DUNISTD" + MORECFLAGS="-fno-strict-aliasing" + STRIPFLAG=-s + LDSOFLAGS="-shared" +fi + +if test `uname -m` = x86_64; then + MORECFLAGS=$MORECFLAGS" -fPIC" +fi + +if test "$static" = yes; then LDFLAGS="$LDFLAGS -static"; fi + +dnl Checking for `dlopen' function in -ldl: +AC_CHECK_LIB(dl, dlopen,LDFLAGS="$LDFLAGS -ldl", echo "dynamic link support required" || exit 1) +dnl Checking for `sin' function in -lffm (ffm is the fast math library on the alpha) +AC_CHECK_LIB(ffm, sin,LDFLAGS="$LDFLAGS -lffm") +dnl Checking for `sin' function in -lm: +AC_CHECK_LIB(m, sin,LDFLAGS="$LDFLAGS -lm", echo "math library required" || exit 1) +dnl Checking for `pthread_create' function in -pthread +AC_CHECK_LIB(pthread, pthread_create,LDFLAGS="$LDFLAGS -lpthread", echo "pthreads required" || exit 1) + +if test x$oss = xyes; then + AC_CHECK_HEADER(linux/soundcard.h,oss="yes",oss="no") +fi + +dnl This should be fixed so Pd can use ALSA shared libraries where appropriate. +if test x$alsa = xyes; then + AC_CHECK_LIB(asound,snd_pcm_info,LDFLAGS="$LDFLAGS -lasound" ; alsa="yes",alsa="no") +fi + +if test x$jack = xyes; then + AC_CHECK_LIB(rt,shm_open,LIBS="$LIBS -lrt") + AC_CHECK_LIB(jack,jack_set_xrun_callback,jack=xrun,jack=no) + AC_CHECK_LIB(jack,jack_set_error_function,jack=yes,jack=no) +fi + +dnl This should be fixed so Pd can use ALSA shared libraries where appropriate. +if test x$portaudio = xyes; then + AC_CHECK_LIB(portaudio,Pa_GetDeviceCount,LDFLAGS=$LDFLAGS" -lportaudio" ; portaudio=yes,portaudio=no) +fi + +if test x$portmidi = xyes; then + AC_CHECK_LIB(porttime,Pt_Started ,LDFLAGS=$LDFLAGS" -lporttime") + AC_CHECK_LIB(portmidi,Pm_Initialize,LDFLAGS=$LDFLAGS" -lportmidi" ; portmidi=yes,portmidi=no) +fi + +if test x$USE_DEBUG_CFLAGS = xyes; then + MORECFLAGS=$MORECFLAGS" -O1 -g" +else + MORECFLAGS=$MORECFLAGS" -O3 -funroll-loops -fomit-frame-pointer" +fi + +if test x$oss = xyes; then + AUDIOSRC=$AUDIOSRC" s_audio_oss.c" + MIDISRC="s_midi_oss.c" + CPPFLAGS=$CPPFLAGS" -DUSEAPI_OSS" +fi + +if test x$alsa = xyes; then + # the alsa midi is weird, it needs to have another MIDI module at once, so i put it in "audio" instead. + AUDIOSRC=$AUDIOSRC" s_audio_alsa.c s_audio_alsamm.c s_midi_alsa.c" + CPPFLAGS=$CPPFLAGS" -DPA_USE_ALSA -DUSEAPI_ALSA" + LDFLAGS=$LDFLAGS" -lasound" +fi + +if test x$jack = xyes; then + AUDIOSRC=$AUDIOSRC" s_audio_jack.c" + if test x$jack != xno; then + if test `uname -s` = Linux; then + if test x$jack = "xyes"; then LDFLAGS=$LDFLAGS" -lrt -ljack"; fi + if test x$jack = "xrun"; then LDFLAGS=$LDFLAGS" -lrt -ljack"; fi + fi + if test `uname -s` = Darwin; then + LDFLAGS=$LDFLAGS" -framework Jack" # 0.39 + LDFLAGS=$LDFLAGS" -weak_framework Jack" # 0.40 + else + LIBS="$LIBS -ljack" + fi + CPPFLAGS=$CPPFLAGS" -DUSEAPI_JACK" + fi + if test x$jack = "xrun"; then CPPFLAGS=$CPPFLAGS" -DJACK_XRUN"; fi +fi + +if test x$portaudio = xyes; then + CPPFLAGS=$CPPFLAGS" -DUSEAPI_PORTAUDIO -DPA19" + AUDIOSRC=$AUDIOSRC" s_audio_portaudio.c" + if test `uname -s` = Darwin; then + LDFLAGS=$LDFLAGS" -framework CoreAudio -framework AudioUnit -framework AudioToolbox -framework Carbon" + fi +fi + +if test x$portmidi = xyes; then + MIDISRC="s_midi_pm.c" + MIDIFLAGS="-DUSEAPI_PORTMIDI" + if test `uname -s` = Darwin; then + LDFLAGS=$LDFLAGS" -framework CoreMIDI" + fi +fi + +CPPFLAGS=$CPPFLAGS" $MIDIFLAGS" + +if test x$fftw = "xyes"; then + AC_CHECK_LIB(fftw3f, fftwf_forget_wisdom ,LDFLAGS=$LDFLAGS" -lfftw3f",fftw=no) +fi + +AC_C_BIGENDIAN(bigendian=yes,bigendian=no,bigendian=maybe) +if test x$bigendian = "xyes"; then + CPPFLAGS=$CPPFLAGS" -DBIGENDIAN" +fi + +AC_OUTPUT(makefile) + diff --git a/desiredata/src/cvs-switch-user b/desiredata/src/cvs-switch-user new file mode 100755 index 00000000..99cfc7c0 --- /dev/null +++ b/desiredata/src/cvs-switch-user @@ -0,0 +1,5 @@ +echo $1@pure-data.cvs.sourceforge.net:/cvsroot/pure-data > CVS/Root +for file in $(find -name Root) +do + cp CVS/Root $file +done diff --git a/desiredata/src/d_fftroutine.c b/desiredata/src/d_fftroutine.c new file mode 100644 index 00000000..4a44ee61 --- /dev/null +++ b/desiredata/src/d_fftroutine.c @@ -0,0 +1,999 @@ +/*****************************************************************************/ +/* */ +/* 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 <stdio.h> +#include <math.h> +#include <stdlib.h> + + /* the following is needed only to declare pd_fft() as exportable in MSW */ +#include "m_pd.h" + +/* some basic definitions */ +#ifndef BOOL +#define BOOL int +#define TRUE 1 +#define FALSE 0 +#endif + +#define SAMPLE float /* data type used in calculation */ + +#define SHORT_SIZE sizeof(short) +#define INT_SIZE sizeof(int) +#define FLOAT_SIZE sizeof(float) +#define SAMPLE_SIZE sizeof(SAMPLE) +#define PNTR_SIZE sizeof(char *) + +#define PI 3.1415927 +#define TWO_PI 6.2831854 + +/* type definitions for I/O buffers */ +#define REAL 0 /* real only */ +#define IMAG 2 /* imaginary only */ +#define RECT 8 /* real and imaginary */ +#define MAG 16 /* magnitude only */ +#define PHASE 32 /* phase only */ +#define POLAR 64 /* magnitude and phase*/ + +/* scale definitions for I/O buffers */ +#define LINEAR 0 +#define DB 1 /* 20log10 */ + +/* transform direction definition */ +#define FORWARD 1 /* Forward FFT */ +#define INVERSE 2 /* Inverse FFT */ + +/* window type definitions */ +#define HANNING 1 +#define RECTANGULAR 0 + + + +/* network structure definition */ + +typedef struct Tfft_net { + int n; + int stages; + int bps; + int direction; + int window_type; + int *load_index; + SAMPLE *window, *inv_window; + SAMPLE *regr; + SAMPLE *regi; + SAMPLE **indexpr; + SAMPLE **indexpi; + SAMPLE **indexqr; + SAMPLE **indexqi; + SAMPLE *coeffr, *inv_coeffr; + SAMPLE *coeffi, *inv_coeffi; + struct Tfft_net *next; +} FFT_NET; + + +void cfft(int trnsfrm_dir, int npnt, int window, + float *source_buf, int source_form, int source_scale, + float *result_buf, int result_form, int result_scale, int debug); + + +/*****************************************************************************/ +/* GLOBAL DECLARATIONS */ +/*****************************************************************************/ + +static FFT_NET *firstnet; + +/* prototypes */ + +void net_alloc(FFT_NET *fft_net); +void net_dealloc(FFT_NET *fft_net); +int power_of_two(int n); +void create_hanning(SAMPLE *window, int n, SAMPLE scale); +void create_rectangular(SAMPLE *window, int n, SAMPLE scale); +void short_to_float(short *short_buf, float *float_buf, int n); +void load_registers(FFT_NET *fft_net, float *buf, int buf_form, + int buf_scale, int trnsfrm_dir); +void compute_fft(FFT_NET *fft_net); +void store_registers(FFT_NET *fft_net, float *buf, int buf_form, + int buf_scale, int debug); +void build_fft_network(FFT_NET *fft_net, int n, int window_type); + +/*****************************************************************************/ +/* GENERALIZED FAST FOURIER TRANSFORM MODULE */ +/*****************************************************************************/ + +void cfft(int trnsfrm_dir, int npnt, int window, + float *source_buf, int source_form, int source_scale, + float *result_buf, int result_form, int result_scale, int debug) + +/* modifies: result_buf + effects: Computes npnt FFT specified by form, scale, and dir parameters. + Source samples (single precision float) are taken from soure_buf and + the transfrmd representation is stored in result_buf (single precision + float). The parameters are defined as follows: + + trnsfrm_dir = FORWARD | INVERSE + npnt = 2^k for some any positive integer k + window = HANNING | RECTANGULAR + (RECT = real and imag parts, POLAR = magnitude and phase) + source_form = REAL | IMAG | RECT | POLAR + result_form = REAL | IMAG | RECT | MAG | PHASE | POLAR + xxxxxx_scale= LINEAR | DB ( 20log10 |mag| ) + + The input/output buffers are stored in a form appropriate to the type. + For example: REAL => {real, real, real ...}, + MAG => {mag, mag, mag, ... }, + RECT => {real, imag, real, imag, ... }, + POLAR => {mag, phase, mag, phase, ... }. + + To look at the magnitude (in db) of a 1024 point FFT of a real time + signal we have: + + fft(FORWARD, 1024, RECTANGULAR, input, REAL, LINEAR, output, MAG, DB) + + All possible input and output combinations are possible given the + choice of type and scale parameters. +*/ + +{ + FFT_NET *thisnet = (FFT_NET *)0; + FFT_NET *lastnet = (FFT_NET *)0; + + /* A linked list of fft networks of different sizes is maintained to + avoid building with every call. The network is built on the first + call but reused for subsequent calls requesting the same size + transformation. + */ + + thisnet=firstnet; + while (thisnet) { + if (!(thisnet->n == npnt) || !(thisnet->window_type == window)) { + /* current net doesn't match size or window type */ + lastnet=thisnet; + thisnet=thisnet->next; + continue; /* keep looking */ + } + + else { /* network matches desired size */ + load_registers(thisnet, source_buf, source_form, source_scale, + trnsfrm_dir); + compute_fft(thisnet); /* do transformation */ + store_registers(thisnet, result_buf, result_form, result_scale,debug); + return; + } + } + + /* none of existing networks match required size*/ + + if (lastnet) { /* add new network to end of list */ + thisnet = (FFT_NET *)malloc(sizeof(FFT_NET)); /* allocate */ + thisnet->next = 0; + lastnet->next = thisnet; /* add to end of list */ + } + else { /* first network to be created */ + thisnet=firstnet=(FFT_NET *)malloc(sizeof(FFT_NET)); /* alloc. */ + thisnet->next = 0; + } + + /* build new network and compute transformation */ + build_fft_network(thisnet, npnt, window); + load_registers(thisnet, source_buf, source_form, source_scale, + trnsfrm_dir); + compute_fft(thisnet); + store_registers(thisnet, result_buf, result_form, result_scale,debug); + return; +} + +void fft_clear(void) + +/* effects: Deallocates all preserved FFT networks. Should be used when + finished with all computations. +*/ + +{ + FFT_NET *thisnet, *nextnet; + + if (firstnet) { + thisnet=firstnet; + do { + nextnet = thisnet->next; + net_dealloc(thisnet); + free((char *)thisnet); + } while (thisnet = nextnet); + } +} + + +/*****************************************************************************/ +/* NETWORK CONSTRUCTION */ +/*****************************************************************************/ + +void build_fft_network(FFT_NET *fft_net, int n, int window_type) + + +/* modifies:fft_net + effects: Constructs the fft network as described in fft.h. Butterfly + coefficients, read/write indicies, bit reversed load indicies, + and array allocations are computed. +*/ + +{ + int cntr, i, j, s; + int stages, bps; + int **p, **q, *pp, *qp; + SAMPLE two_pi_div_n = TWO_PI / n; + + + /* network definition */ + fft_net->n = n; + fft_net->bps = bps = n/2; + for (i = 0, j = n; j > 1; j >>= 1, i++); + fft_net->stages = stages = i; + fft_net->direction = FORWARD; + fft_net->window_type = window_type; + fft_net->next = (FFT_NET *)0; + + /* allocate registers, index, coefficient arrays */ + net_alloc(fft_net); + + + /* create appropriate windows */ + if (window_type==HANNING) { + create_hanning(fft_net->window, n, 1.); + create_hanning(fft_net->inv_window, n, 1./n); + } + else { + create_rectangular(fft_net->window, n, 1.); + create_rectangular(fft_net->inv_window, n, 1./n); + } + + + /* calculate butterfly coefficients */ { + + int num_diff_coeffs, power_inc, power; + SAMPLE *coeffpr = fft_net->coeffr; + SAMPLE *coeffpi = fft_net->coeffi; + SAMPLE *inv_coeffpr = fft_net->inv_coeffr; + SAMPLE *inv_coeffpi = fft_net->inv_coeffi; + + /* stage one coeffs are 1 + 0j */ + for (i = 0; i < bps; i++) { + *coeffpr = *inv_coeffpr = 1.; + *coeffpi = *inv_coeffpi = 0.; + coeffpr++; inv_coeffpr++; + coeffpi++; inv_coeffpi++; + } + + /* stage 2 to last stage coeffs need calculation */ + /* (1<<r <=> 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; + + 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; + 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(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; + 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/desiredata/src/d_mayer_fft.c b/desiredata/src/d_mayer_fft.c new file mode 100644 index 00000000..74ee1c76 --- /dev/null +++ b/desiredata/src/d_mayer_fft.c @@ -0,0 +1,422 @@ +/* +** FFT and FHT routines +** Copyright 1988, 1993; Ron Mayer +** +** mayer_fht(fz,n); +** Does a hartley transform of "n" points in the array "fz". +** mayer_fft(n,real,imag) +** Does a fourier transform of "n" points of the "real" and +** "imag" arrays. +** mayer_ifft(n,real,imag) +** Does an inverse fourier transform of "n" points of the "real" +** and "imag" arrays. +** mayer_realfft(n,real) +** Does a real-valued fourier transform of "n" points of the +** "real" array. The real part of the transform ends +** up in the first half of the array and the imaginary part of the +** transform ends up in the second half of the array. +** mayer_realifft(n,real) +** The inverse of the realfft() routine above. +** +** +** NOTE: This routine uses at least 2 patented algorithms, and may be +** under the restrictions of a bunch of different organizations. +** Although I wrote it completely myself, it is kind of a derivative +** of a routine I once authored and released under the GPL, so it +** may fall under the free software foundation's restrictions; +** it was worked on as a Stanford Univ project, so they claim +** some rights to it; it was further optimized at work here, so +** I think this company claims parts of it. The patents are +** held by R. Bracewell (the FHT algorithm) and O. Buneman (the +** trig generator), both at Stanford Univ. +** If it were up to me, I'd say go do whatever you want with it; +** but it would be polite to give credit to the following people +** if you use this anywhere: +** Euler - probable inventor of the fourier transform. +** Gauss - probable inventor of the FFT. +** Hartley - probable inventor of the hartley transform. +** Buneman - for a really cool trig generator +** Mayer(me) - for authoring this particular version and +** including all the optimizations in one package. +** Thanks, +** Ron Mayer; mayer@acuson.com +** +*/ + +/* This is a slightly modified version of Mayer's contribution; write +* msp@ucsd.edu for the original code. Kudos to Mayer for a fine piece +* of work. -msp +*/ + +#ifdef MSW +#pragma warning( disable : 4305 ) /* uncast const double to float */ +#pragma warning( disable : 4244 ) /* uncast double to float */ +#pragma warning( disable : 4101 ) /* unused local variables */ +#endif + +/* the following is needed only to declare pd_fft() as exportable in MSW */ +#include "m_pd.h" + +#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<<i)&t_lam) ; i++); \ + i = k-i; \ + s = sinwrk[i]; \ + c = coswrk[i]; \ + if (i>1) \ + { \ + for (j=k-i+2 ; (1<<j)&t_lam ; j++); \ + j = k - j; \ + sinwrk[i] = halsec[i] * (sinwrk[i-1] + sinwrk[j]); \ + coswrk[i] = halsec[i] * (coswrk[i-1] + coswrk[j]); \ + } \ + } +#define TRIG_RESET(k,c,s) +#endif + +#if defined(FAST_TRIG) +#define TRIG_VARS \ + REAL t_c,t_s; +#define TRIG_INIT(k,c,s) \ + { \ + t_c = costab[k]; \ + t_s = sintab[k]; \ + c = 1; \ + s = 0; \ + } +#define TRIG_NEXT(k,c,s) \ + { \ + REAL t = c; \ + c = t*t_c - s*t_s; \ + s = t*t_s + s*t_c; \ + } +#define TRIG_RESET(k,c,s) +#endif + +static REAL halsec[20]= + { + 0, + 0, + .54119610014619698439972320536638942006107206337801, + .50979557910415916894193980398784391368261849190893, + .50241928618815570551167011928012092247859337193963, + .50060299823519630134550410676638239611758632599591, + .50015063602065098821477101271097658495974913010340, + .50003765191554772296778139077905492847503165398345, + .50000941253588775676512870469186533538523133757983, + .50000235310628608051401267171204408939326297376426, + .50000058827484117879868526730916804925780637276181, + .50000014706860214875463798283871198206179118093251, + .50000003676714377807315864400643020315103490883972, + .50000000919178552207366560348853455333939112569380, + .50000000229794635411562887767906868558991922348920, + .50000000057448658687873302235147272458812263401372 + }; +static REAL costab[20]= + { + .00000000000000000000000000000000000000000000000000, + .70710678118654752440084436210484903928483593768847, + .92387953251128675612818318939678828682241662586364, + .98078528040323044912618223613423903697393373089333, + .99518472667219688624483695310947992157547486872985, + .99879545620517239271477160475910069444320361470461, + .99969881869620422011576564966617219685006108125772, + .99992470183914454092164649119638322435060646880221, + .99998117528260114265699043772856771617391725094433, + .99999529380957617151158012570011989955298763362218, + .99999882345170190992902571017152601904826792288976, + .99999970586288221916022821773876567711626389934930, + .99999992646571785114473148070738785694820115568892, + .99999998161642929380834691540290971450507605124278, + .99999999540410731289097193313960614895889430318945, + .99999999885102682756267330779455410840053741619428 + }; +static REAL sintab[20]= + { + 1.0000000000000000000000000000000000000000000000000, + .70710678118654752440084436210484903928483593768846, + .38268343236508977172845998403039886676134456248561, + .19509032201612826784828486847702224092769161775195, + .09801714032956060199419556388864184586113667316749, + .04906767432741801425495497694268265831474536302574, + .02454122852291228803173452945928292506546611923944, + .01227153828571992607940826195100321214037231959176, + .00613588464915447535964023459037258091705788631738, + .00306795676296597627014536549091984251894461021344, + .00153398018628476561230369715026407907995486457522, + .00076699031874270452693856835794857664314091945205, + .00038349518757139558907246168118138126339502603495, + .00019174759731070330743990956198900093346887403385, + .00009587379909597734587051721097647635118706561284, + .00004793689960306688454900399049465887274686668768 + }; +static REAL coswrk[20]= + { + .00000000000000000000000000000000000000000000000000, + .70710678118654752440084436210484903928483593768847, + .92387953251128675612818318939678828682241662586364, + .98078528040323044912618223613423903697393373089333, + .99518472667219688624483695310947992157547486872985, + .99879545620517239271477160475910069444320361470461, + .99969881869620422011576564966617219685006108125772, + .99992470183914454092164649119638322435060646880221, + .99998117528260114265699043772856771617391725094433, + .99999529380957617151158012570011989955298763362218, + .99999882345170190992902571017152601904826792288976, + .99999970586288221916022821773876567711626389934930, + .99999992646571785114473148070738785694820115568892, + .99999998161642929380834691540290971450507605124278, + .99999999540410731289097193313960614895889430318945, + .99999999885102682756267330779455410840053741619428 + }; +static REAL sinwrk[20]= + { + 1.0000000000000000000000000000000000000000000000000, + .70710678118654752440084436210484903928483593768846, + .38268343236508977172845998403039886676134456248561, + .19509032201612826784828486847702224092769161775195, + .09801714032956060199419556388864184586113667316749, + .04906767432741801425495497694268265831474536302574, + .02454122852291228803173452945928292506546611923944, + .01227153828571992607940826195100321214037231959176, + .00613588464915447535964023459037258091705788631738, + .00306795676296597627014536549091984251894461021344, + .00153398018628476561230369715026407907995486457522, + .00076699031874270452693856835794857664314091945205, + .00038349518757139558907246168118138126339502603495, + .00019174759731070330743990956198900093346887403385, + .00009587379909597734587051721097647635118706561284, + .00004793689960306688454900399049465887274686668768 + }; + + +#define SQRT2_2 0.70710678118654752440084436210484 +#define SQRT2 2*0.70710678118654752440084436210484 + +void mayer_fht(REAL *fz, int n) +{ +/* REAL a,b; +REAL c1,s1,s2,c2,s3,c3,s4,c4; + REAL f0,g0,f1,g1,f2,g2,f3,g3; */ + int k,k1,k2,k3,k4,kx; + REAL *fi,*fn,*gi; + TRIG_VARS; + + for (k1=1,k2=0;k1<n;k1++) + { + REAL aa; + for (k=n>>1; (!((k2^=k)&k)); k>>=1); + if (k1>k2) + { + aa=fz[k1];fz[k1]=fz[k2];fz[k2]=aa; + } + } + for ( k=0 ; (1<<k)<n ; k++ ); + k &= 1; + if (k==0) + { + for (fi=fz,fn=fz+n;fi<fn;fi+=4) + { + REAL f0,f1,f2,f3; + f1 = fi[0 ]-fi[1 ]; + f0 = fi[0 ]+fi[1 ]; + f3 = fi[2 ]-fi[3 ]; + f2 = fi[2 ]+fi[3 ]; + fi[2 ] = (f0-f2); + fi[0 ] = (f0+f2); + fi[3 ] = (f1-f3); + fi[1 ] = (f1+f3); + } + } + else + { + for (fi=fz,fn=fz+n,gi=fi+1;fi<fn;fi+=8,gi+=8) + { + REAL bs1,bc1,bs2,bc2,bs3,bc3,bs4,bc4, + bg0,bf0,bf1,bg1,bf2,bg2,bf3,bg3; + bc1 = fi[0 ] - gi[0 ]; + bs1 = fi[0 ] + gi[0 ]; + bc2 = fi[2 ] - gi[2 ]; + bs2 = fi[2 ] + gi[2 ]; + bc3 = fi[4 ] - gi[4 ]; + bs3 = fi[4 ] + gi[4 ]; + bc4 = fi[6 ] - gi[6 ]; + bs4 = fi[6 ] + gi[6 ]; + bf1 = (bs1 - bs2); + bf0 = (bs1 + bs2); + bg1 = (bc1 - bc2); + bg0 = (bc1 + bc2); + bf3 = (bs3 - bs4); + bf2 = (bs3 + bs4); + bg3 = SQRT2*bc4; + bg2 = SQRT2*bc3; + fi[4 ] = bf0 - bf2; + fi[0 ] = bf0 + bf2; + fi[6 ] = bf1 - bf3; + fi[2 ] = bf1 + bf3; + gi[4 ] = bg0 - bg2; + gi[0 ] = bg0 + bg2; + gi[6 ] = bg1 - bg3; + gi[2 ] = bg1 + bg3; + } + } + if (n<16) return; + + do + { + REAL s1,c1; + int ii; + k += 2; + k1 = 1 << k; + k2 = k1 << 1; + k4 = k2 << 1; + k3 = k2 + k1; + kx = k1 >> 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<fn); + TRIG_INIT(k,c1,s1); + for (ii=1;ii<kx;ii++) + { + REAL c2,s2; + TRIG_NEXT(k,c1,s1); + c2 = c1*c1 - s1*s1; + s2 = 2*(c1*s1); + fn = fz + n; + fi = fz +ii; + gi = fz +k1-ii; + do + { + REAL a,b,g0,f0,f1,g1,f2,g2,f3,g3; + b = s2*fi[k1] - c2*gi[k1]; + a = c2*fi[k1] + s2*gi[k1]; + f1 = fi[0 ] - a; + f0 = fi[0 ] + a; + g1 = gi[0 ] - b; + g0 = gi[0 ] + b; + b = s2*fi[k3] - c2*gi[k3]; + a = c2*fi[k3] + s2*gi[k3]; + f3 = fi[k2] - a; + f2 = fi[k2] + a; + g3 = gi[k2] - b; + g2 = gi[k2] + b; + b = s1*f2 - c1*g3; + a = c1*f2 + s1*g3; + fi[k2] = f0 - a; + fi[0 ] = f0 + a; + gi[k3] = g1 - b; + gi[k1] = g1 + b; + b = c1*g2 - s1*f3; + a = s1*g2 + c1*f3; + gi[k2] = g0 - a; + gi[0 ] = g0 + a; + fi[k3] = f1 - b; + fi[k1] = f1 + b; + gi += k4; + fi += k4; + } while (fi<fn); + } + TRIG_RESET(k,c1,s1); + } while (k4<n); +} + +void mayer_fft(int n, REAL *real, REAL *imag) +{ + REAL a,b,c,d; + REAL q,r,s,t; + int i,j,k; + for (i=1,j=n-1,k=n/2;i<k;i++,j--) { + a = real[i]; b = real[j]; q=a+b; r=a-b; + c = imag[i]; d = imag[j]; s=c+d; t=c-d; + real[i] = (q+t)*.5; real[j] = (q-t)*.5; + imag[i] = (s-r)*.5; imag[j] = (s+r)*.5; + } + mayer_fht(real,n); + mayer_fht(imag,n); +} + +void mayer_ifft(int n, REAL *real, REAL *imag) +{ + REAL a,b,c,d; + REAL q,r,s,t; + int i,j,k; + mayer_fht(real,n); + mayer_fht(imag,n); + for (i=1,j=n-1,k=n/2;i<k;i++,j--) { + a = real[i]; b = real[j]; q=a+b; r=a-b; + c = imag[i]; d = imag[j]; s=c+d; t=c-d; + imag[i] = (s+r)*0.5; imag[j] = (s-r)*0.5; + real[i] = (q-t)*0.5; real[j] = (q+t)*0.5; + } +} + +void mayer_realfft(int n, REAL *real) +{ + REAL a,b; + int i,j,k; + mayer_fht(real,n); + for (i=1,j=n-1,k=n/2;i<k;i++,j--) { + a = real[i]; + b = real[j]; + real[j] = (a-b)*0.5; + real[i] = (a+b)*0.5; + } +} + +void mayer_realifft(int n, REAL *real) +{ + REAL a,b; + int i,j,k; + for (i=1,j=n-1,k=n/2;i<k;i++,j--) { + a = real[i]; + b = real[j]; + real[j] = (a-b); + real[i] = (a+b); + } + mayer_fht(real,n); +} diff --git a/desiredata/src/d_soundfile.c b/desiredata/src/d_soundfile.c new file mode 100644 index 00000000..2a281869 --- /dev/null +++ b/desiredata/src/d_soundfile.c @@ -0,0 +1,2059 @@ +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* this file contains, first, a collection of soundfile access routines, a +sort of soundfile library. Second, the "soundfiler" object is defined which +uses the routines to read or write soundfiles, synchronously, from garrays. +These operations are not to be done in "real time" as they may have to wait +for disk accesses (even the write routine.) Finally, the realtime objects +readsf~ and writesf~ are defined which confine disk operations to a separate +thread so that they can be used in real time. The readsf~ and writesf~ +objects use Posix-like threads. */ + +/* threaded soundfiler by Tim Blechmann */ +// #define THREADED_SF + +#ifndef MSW +#include <unistd.h> +#include <fcntl.h> +#endif +#include <pthread.h> +#ifdef MSW +#include <io.h> +#endif +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +#define PD_PLUSPLUS_FACE +#include "desire.h" +#define a_symbol a_w.w_symbol +#define a_float a_w.w_float + +#define MAXSFCHANS 64 + +#ifdef _LARGEFILE64_SOURCE +# define open open64 +# define lseek lseek64 +#endif + +static bool debug=0; + +#define EAT_ARG(ATYPE,VAR) if (argc<1 || argv->a_type != ATYPE) goto usage; else {VAR = *argv++; argc--;} + +/***************** soundfile header structures ************************/ + +typedef unsigned short uint16; +typedef unsigned int uint32; /* long isn't 32-bit on amd64 */ + +#define FORMAT_WAVE 0 +#define FORMAT_AIFF 1 +#define FORMAT_NEXT 2 + +/* the NeXTStep sound header structure; can be big or little endian */ + +struct t_nextstep { + char fileid[4]; /* magic number '.snd' if file is big-endian */ + uint32 onset; /* byte offset of first sample */ + uint32 length; /* length of sound in bytes */ + uint32 format; /* format; see below */ + uint32 sr; /* sample rate */ + uint32 nchans; /* number of channels */ + char info[4]; /* comment */ +}; + +#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. */ + +struct t_wave { + char fileid[4]; /* chunk id 'RIFF' */ + uint32 chunksize; /* chunk size */ + char waveid[4]; /* wave chunk id 'WAVE' */ + char fmtid[4]; /* format chunk id 'fmt ' */ + uint32 fmtchunksize; /* format chunk size */ + uint16 fmttag; /* format tag (WAV_INT etc) */ + uint16 nchannels; /* number of channels */ + uint32 samplespersec; /* sample rate in hz */ + uint32 navgbytespersec; /* average bytes per second */ + uint16 nblockalign; /* number of bytes per frame */ + uint16 nbitspersample; /* number of bits in a sample */ + char datachunkid[4]; /* data chunk id 'data' */ + uint32 datachunksize; /* length of data chunk */ +}; + +struct t_fmt { /* format chunk */ + uint16 fmttag; /* format tag, 1 for PCM */ + uint16 nchannels; /* number of channels */ + uint32 samplespersec; /* sample rate in hz */ + uint32 navgbytespersec; /* average bytes per second */ + uint16 nblockalign; /* number of bytes per frame */ + uint16 nbitspersample; /* number of bits in a sample */ +}; + +struct t_wavechunk { /* ... and the last two items */ + char id[4]; /* data chunk id, e.g., 'data' or 'fmt ' */ + uint32 size; /* length of data chunk */ +}; + +#define WAV_INT 1 +#define WAV_FLOAT 3 + +typedef unsigned char byte; + +/* the AIFF header. I'm assuming AIFC is compatible but don't really know that. */ + +struct t_datachunk { + char id[4]; // data chunk id 'SSND' + uint32 size; // length of data chunk + uint32 offset; // additional offset in bytes + uint32 block; // block size +}; + +struct t_comm { + uint16 nchannels; // number of channels + uint16 nframeshi; // # of sample frames (hi) + uint16 nframeslo; // # of sample frames (lo) + uint16 bitspersamp; // bits per sample + byte samprate[10];// sample rate, 80-bit float! +}; + +/* this version is more convenient for writing them out: */ +struct t_aiff { + char fileid[4]; // chunk id 'FORM' + uint32 chunksize; // chunk size + char aiffid[4]; // aiff chunk id 'AIFF' + char fmtid[4]; // format chunk id 'COMM' + uint32 fmtchunksize; // format chunk size, 18 + uint16 nchannels; // number of channels + uint16 nframeshi; // # of sample frames (hi) + uint16 nframeslo; // # of sample frames (lo) + uint16 bitspersamp; // bits per sample + byte samprate[10]; // sample rate, 80-bit float! +}; + +struct t_param { + int bytespersample; + int bigendian; + int nchannels; + long bytelimit; + int bytesperchannel() {return bytespersample * nchannels;} +}; + +#define AIFFHDRSIZE 38 /* probably not what sizeof() gives */ +#define AIFFPLUS (AIFFHDRSIZE + 16) /* header size including SSND 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) + +#ifdef MSW +#include <fcntl.h> +#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(); + +/* byte swappers */ + +static uint32 swap4(uint32 n, int doit) { + if (doit) return ((n & 0xff) << 24) | ((n & 0xff00) << 8) | ((n & 0xff0000) >> 8) | ((n & 0xff000000) >> 24); + else return n; +} + +static uint16 swap2(uint32 n, int doit) { + if (doit) return ((n & 0xff) << 8) | ((n & 0xff00) >> 8); + else return n; +} + +static void swapstring(char *foo, int doit) { + if (doit) { + char a = foo[0], b = foo[1], c = foo[2], d = foo[3]; + foo[0] = d; foo[1] = c; foo[2] = b; foo[3] = a; + } +} + +/******************** soundfile access routines **********************/ +/* This routine opens a file, looks for either a nextstep or "wave" header, +* seeks to end of it, and fills in bytes per sample and number of channels. +* Only 2- and 3-byte fixed-point samples and 4-byte floating point samples +* are supported. If "headersize" is nonzero, the +* caller should supply the number of channels, endinanness, and bytes per +* sample; the header is ignored. Otherwise, the routine tries to read the +* header and fill in the properties. +*/ + +int open_soundfile_via_fd(int fd, int headersize, t_param *p, long skipframes) { + int swap, sysrtn; + errno = 0; + t_param q; + q.bytelimit = 0x7fffffff; + if (headersize >= 0) { /* header detection overridden */ + q = *p; + } else { + char buf[MAXPDSTRING]; + int bytesread = read(fd, buf, READHDRSIZE); + int format; + if (bytesread < 4) goto badheader; + if (!strncmp(buf, ".snd", 4)) {format = FORMAT_NEXT; q.bigendian = 1;} + else if (!strncmp(buf, "dns.", 4)) {format = FORMAT_NEXT; q.bigendian = 0;} + else if (!strncmp(buf, "RIFF", 4)) { + if (bytesread < 12 || strncmp(buf + 8, "WAVE", 4)) goto badheader; + format = FORMAT_WAVE; q.bigendian = 0; + } + else if (!strncmp(buf, "FORM", 4)) { + if (bytesread < 12 || strncmp(buf + 8, "AIFF", 4)) goto badheader; + format = FORMAT_AIFF; q.bigendian = 1; + } else goto badheader; + swap = (q.bigendian != garray_ambigendian()); + if (format == FORMAT_NEXT) { /* nextstep header */ + if (bytesread < (int)sizeof(t_nextstep)) goto badheader; + q.nchannels = swap4(((t_nextstep *)buf)->nchans, swap); + format = swap4(((t_nextstep *)buf)->format, swap); + headersize = swap4(((t_nextstep *)buf)->onset, swap); + if (format == NS_FORMAT_LINEAR_16) q.bytespersample = 2; + else if (format == NS_FORMAT_LINEAR_24) q.bytespersample = 3; + else if (format == NS_FORMAT_FLOAT) q.bytespersample = 4; + else goto badheader; + q.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. */ + q.nchannels = 1; + q.bytespersample = 2; + /* copy the first chunk header to beginnning of buffer. */ + memcpy(buf, buf + headersize, sizeof(t_wavechunk)); + /* read chunks in loop until we get to the data chunk */ + while (strncmp(((t_wavechunk *)buf)->id, "data", 4)) { + long chunksize = swap4(((t_wavechunk *)buf)->size, swap), seekto = headersize + chunksize + 8, seekout; + if (!strncmp(((t_wavechunk *)buf)->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; + q.nchannels = swap2(((t_fmt *)buf)->nchannels, swap); + format = swap2(((t_fmt *)buf)->nbitspersample, swap); + if (format == 16) q.bytespersample = 2; + else if (format == 24) q.bytespersample = 3; + else if (format == 32) q.bytespersample = 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; + headersize = seekto; + } + q.bytelimit = swap4(((t_wavechunk *)buf)->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. */ + q.nchannels = 1; + q.bytespersample = 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)->id, "SSND", 4)) { + long chunksize = swap4(((t_datachunk *)buf)->size, swap), seekto = headersize + chunksize + 8, seekout; + if (!strncmp(((t_datachunk *)buf)->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; + q.nchannels = swap2(((t_comm *)buf)->nchannels, swap); + format = swap2(((t_comm *)buf)->bitspersamp, swap); + if (format == 16) q.bytespersample = 2; + else if (format == 24) q.bytespersample = 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; + } + q.bytelimit = swap4(((t_datachunk *)buf)->size, swap); + headersize += 8; + } + } + /* seek past header and any sample frames to skip */ + sysrtn = lseek(fd, q.bytesperchannel() * skipframes + headersize, 0); + if (sysrtn != q.bytesperchannel() * skipframes + headersize) return -1; + q.bytelimit -= q.bytesperchannel() * skipframes; + if (q.bytelimit < 0) q.bytelimit = 0; + *p = q; + return fd; +badheader: + /* the header wasn't recognized. We're threadable here so let's not print out the error... */ + errno = EIO; + return -1; +} + +/* open a soundfile, using open_via_path(). This is used by readsf~ in + a not-perfectly-threadsafe way. LATER replace with a thread-hardened version of open_soundfile_via_canvas() */ +static int open_soundfile(const char *dirname, const char *filename, int headersize, t_param *p, long skipframes) { + char *buf, *bufptr; + int fd = open_via_path2(dirname, filename, "", &buf, &bufptr, 1); + if (fd < 0) return -1; + free(buf); + return open_soundfile_via_fd(fd, headersize, p, skipframes); +} + +/* open a soundfile, using open_via_canvas(). This is used by readsf~ in + a not-perfectly-threadsafe way. LATER replace with a thread-hardened version of open_soundfile_via_canvas() */ +static int open_soundfile_via_canvas(t_canvas *canvas, const char *filename, int headersize, t_param *p, long skipframes) { + char *buf, *bufptr; + int fd = canvas_open2(canvas, filename, "", &buf, &bufptr, 1); + if (fd < 0) return -1; + free(buf); + return open_soundfile_via_fd(fd, headersize, p, skipframes); +} + +static void soundfile_xferin(int sfchannels, int nvecs, float **vecs, + long itemsread, unsigned char *buf, int nitems, int bytespersample, int bigendian) { + unsigned char *sp, *sp2; + int nchannels = (sfchannels < nvecs ? sfchannels : nvecs); + int bytesperframe = bytespersample * sfchannels; + sp = buf; + for (int i=0; i < nchannels; i++, sp += bytespersample) { + int j; + sp2=sp; + float *fp=vecs[i] + itemsread; + #define LOOP for (j=0; j<nitems; j++, sp2 += bytesperframe, fp++) + if (bytespersample == 2) { + if (bigendian) LOOP {*fp = SCALE * ((sp2[0]<<24) | (sp2[1]<<16));} + else LOOP {*fp = SCALE * ((sp2[1]<<24) | (sp2[0]<<16));} + } else if (bytespersample == 3) { + if (bigendian) LOOP {*fp = SCALE * ((sp2[0]<<24) | (sp2[1]<<16) | (sp2[2]<<8));} + else LOOP {*fp = SCALE * ((sp2[2]<<24) | (sp2[1]<<16) | (sp2[0]<<8));} + } else if (bytespersample == 4) { + if (bigendian) LOOP {*(long *)fp = (sp2[0]<<24) | (sp2[1]<<16) | (sp2[2]<<8) | sp2[3];} + else LOOP {*(long *)fp = (sp2[3]<<24) | (sp2[2]<<16) | (sp2[1]<<8) | sp2[0];} + } + #undef LOOP + } + /* zero out other outputs */ + for (int i=sfchannels; i < nvecs; i++) { + float *fp=vecs[i]; + for (int j=nitems; j--; ) *fp++ = 0; + } +} + +/* soundfiler_write ... + usage: write [flags] filename table ... + flags: -nframes <frames> -skip <frames> -bytes <bytes per sample> -normalize -nextstep -wave -big -little + the routine which actually does the work should LATER also be called from garray_write16. + Parse arguments for writing. The "obj" argument is only for flagging + errors. For streaming to a file the "normalize", "onset" and "nframes" + arguments shouldn't be set but the calling routine flags this. */ +static int soundfiler_writeargparse(void *obj, int *p_argc, t_atom **p_argv, t_symbol **p_filesym, +int *p_filetype, int *p_bytespersamp, int *p_swap, int *p_bigendian, +int *p_normalize, long *p_onset, long *p_nframes, float *p_rate) { + int argc = *p_argc; + t_atom *argv = *p_argv; + int bytespersample = 2, bigendian = 0, endianness = -1, swap, filetype = -1, normalize = 0; + long onset = 0, nframes = 0x7fffffff; + t_symbol *filesym; + float rate = -1; + while (argc > 0 && argv->a_type == A_SYMBOL && *argv->a_symbol->name == '-') { + char *flag = argv->a_symbol->name + 1; + argc--; argv++; + if (!strcmp(flag, "skip")) { + EAT_ARG(A_FLOAT,onset); if (onset<0) goto usage; + } else if (!strcmp(flag, "nframes")) { + EAT_ARG(A_FLOAT,nframes); if (nframes<0) goto usage; + } else if (!strcmp(flag, "bytes")) { + EAT_ARG(A_FLOAT,bytespersample); if (bytespersample<2 || bytespersample>4) goto usage; + } else if (!strcmp(flag, "normalize")) {normalize = 1; + } else if (!strcmp(flag, "wave")) {filetype = FORMAT_WAVE; + } else if (!strcmp(flag, "nextstep")) {filetype = FORMAT_NEXT; + } else if (!strcmp(flag, "aiff")) {filetype = FORMAT_AIFF; + } else if (!strcmp(flag, "big")) {endianness = 1; + } else if (!strcmp(flag, "little")) {endianness = 0; + } else if (!strcmp(flag, "r") || !strcmp(flag, "rate")) { + EAT_ARG(A_FLOAT,rate); if (rate<0) goto usage; + } else goto usage; + } + if (!argc || argv->a_type != A_SYMBOL) goto usage; + filesym = argv->a_symbol; + /* check if format not specified and fill in */ + if (filetype < 0) { + const char *s = filesym->name + strlen(filesym->name); + if (strlen(filesym->name) >= 5 && !strcasecmp(s-4, ".aif" )) filetype = FORMAT_AIFF; + if (strlen(filesym->name) >= 6 && !strcasecmp(s-5, ".aiff")) filetype = FORMAT_AIFF; + if (strlen(filesym->name) >= 5 && !strcasecmp(s-4, ".snd" )) filetype = FORMAT_NEXT; + if (strlen(filesym->name) >= 4 && !strcasecmp(s-3, ".au" )) filetype = FORMAT_NEXT; + if (filetype < 0) filetype = FORMAT_WAVE; + } + /* don't handle AIFF floating point samples */ + if (bytespersample == 4) { + if (filetype == FORMAT_AIFF) { + error("AIFF floating-point file format unavailable"); + goto usage; + } + } + /* for WAVE force little endian; for nextstep use machine native */ + if (filetype == FORMAT_WAVE) { + bigendian = 0; + if (endianness == 1) error("WAVE file forced to little endian"); + } else if (filetype == FORMAT_AIFF) { + bigendian = 1; + if (endianness == 0) error("AIFF file forced to big endian"); + } else if (endianness == -1) { + bigendian = garray_ambigendian(); + } else bigendian = endianness; + swap = (bigendian != garray_ambigendian()); + argc--; argv++; + *p_argc = argc; + *p_argv = argv; + *p_filesym = filesym; + *p_filetype = filetype; + *p_bytespersamp = bytespersample; + *p_swap = swap; + *p_normalize = normalize; + *p_onset = onset; + *p_nframes = nframes; + *p_bigendian = bigendian; + *p_rate = rate; + return 0; +usage: + return -1; +} + +static bool strcaseends(const char *a, const char *b) {return strcasecmp(a+strlen(a)-strlen(b),b)==0;} + +static int create_soundfile(t_canvas *canvas, const char *filename, int filetype, int nframes, int bytespersample, +int bigendian, int nchannels, int swap, float samplerate) { + char filenamebuf[strlen(filename)+10]; + char headerbuf[WRITEHDRSIZE]; + int fd, headersize = 0; + strcpy(filenamebuf, filename); + if (filetype == FORMAT_NEXT) { + t_nextstep *nexthdr = (t_nextstep *)headerbuf; + if (!strcaseends(filenamebuf,".snd")) strcat(filenamebuf, ".snd"); + if (bigendian) strncpy(nexthdr->fileid, bigendian?".snd":"dns.", 4); + nexthdr->onset = swap4(sizeof(*nexthdr), swap); + nexthdr->length = 0; + nexthdr->format = swap4(bytespersample == 3 ? NS_FORMAT_LINEAR_24 : bytespersample == 4 ? NS_FORMAT_FLOAT : NS_FORMAT_LINEAR_16, swap); + nexthdr->sr = swap4((size_t)samplerate, swap); + nexthdr->nchans = swap4((size_t)nchannels, swap); + strcpy(nexthdr->info, "Pd "); + swapstring(nexthdr->info, swap); + headersize = sizeof(t_nextstep); + } else if (filetype == FORMAT_AIFF) { + long datasize = nframes * nchannels * bytespersample; + long longtmp; + static unsigned char dogdoo[] = {0x40, 0x0e, 0xac, 0x44, 0, 0, 0, 0, 0, 0, 'S', 'S', 'N', 'D'}; + t_aiff *aiffhdr = (t_aiff *)headerbuf; + if (!strcaseends(filenamebuf,".aif") && !strcaseends(filenamebuf,".aiff")) strcat(filenamebuf, ".aif"); + strncpy(aiffhdr->fileid, "FORM", 4); + aiffhdr->chunksize = swap4(datasize + sizeof(*aiffhdr) + 4, swap); + strncpy(aiffhdr->aiffid, "AIFF", 4); + strncpy(aiffhdr->fmtid, "COMM", 4); + aiffhdr->fmtchunksize = swap4(18, swap); + aiffhdr->nchannels = swap2(nchannels, swap); + longtmp = swap4(nframes, swap); + memcpy(&aiffhdr->nframeshi, &longtmp, 4); + aiffhdr->bitspersamp = swap2(8 * bytespersample, swap); + memcpy(aiffhdr->samprate, dogdoo, sizeof(dogdoo)); + longtmp = swap4(datasize, swap); + memcpy(aiffhdr->samprate + sizeof(dogdoo), &longtmp, 4); + memset(aiffhdr->samprate + sizeof(dogdoo) + 4, 0, 8); + headersize = AIFFPLUS; + /* fix by matju for hfeli, 2007.07.04, but really, dogdoo should be removed */ + while (samplerate >= 0x10000) {aiffhdr->samprate[1]++; samplerate/=2;} + aiffhdr->samprate[2] = (long)samplerate>>8; + aiffhdr->samprate[3] = (long)samplerate; + } else { /* WAVE format */ + long datasize = nframes * nchannels * bytespersample; + if (!strcaseends(filenamebuf,".wav")) strcat(filenamebuf, ".wav"); + t_wave *wavehdr = (t_wave *)headerbuf; + strncpy(wavehdr->fileid, "RIFF", 4); + wavehdr->chunksize = swap4(datasize + sizeof(*wavehdr) - 8, swap); + strncpy(wavehdr->waveid, "WAVE", 4); + strncpy(wavehdr->fmtid, "fmt ", 4); + wavehdr->fmtchunksize = swap4(16, swap); + wavehdr->fmttag = swap2((bytespersample == 4 ? WAV_FLOAT : WAV_INT), swap); + wavehdr->nchannels = swap2(nchannels, swap); + wavehdr->samplespersec = swap4(size_t(samplerate), swap); + wavehdr->navgbytespersec = swap4((int)(samplerate * nchannels * bytespersample), swap); + wavehdr->nblockalign = swap2(nchannels * bytespersample, swap); + wavehdr->nbitspersample = swap2(8 * bytespersample, swap); + strncpy(wavehdr->datachunkid, "data", 4); + wavehdr->datachunksize = swap4(datasize, swap); + headersize = sizeof(t_wave); + } + char *buf2 = canvas_makefilename(canvas, filenamebuf,0,0); + sys_bashfilename(buf2,buf2); + if ((fd = open(buf2, BINCREATE, 0666)) < 0) {free(buf2); 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) + error("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, v; + if (lseek(fd, ((char *)(&((t_wave *)0)->chunksize)) - (char *)0, SEEK_SET) == 0) goto baddonewrite; + v = swap4(datasize + sizeof(t_wave) - 8, swap); + if (write(fd, (char *)&v, 4) < 4) goto baddonewrite; + if (lseek(fd, ((char *)(&((t_wave *)0)->datachunksize)) - (char *)0, SEEK_SET) == 0) goto baddonewrite; + v = swap4(datasize, swap); + if (write(fd, (char *)&v, 4) < 4) goto baddonewrite; + } + if (filetype == FORMAT_AIFF) { + long v; + if (lseek(fd, ((char *)(&((t_aiff *)0)->nframeshi)) - (char *)0, SEEK_SET) == 0) goto baddonewrite; + v = swap4(itemswritten, swap); + if (write(fd, (char *)&v,4) < 4) goto baddonewrite; + if (lseek(fd, ((char *)(&((t_aiff *)0)->chunksize)) - (char *)0, SEEK_SET) == 0) goto baddonewrite; + v = swap4(itemswritten*bytesperframe+AIFFHDRSIZE, swap); + if (write(fd, (char *)&v,4) < 4) goto baddonewrite; + if (lseek(fd, (AIFFHDRSIZE+4), SEEK_SET) == 0) goto baddonewrite; + v = swap4(itemswritten*bytesperframe, swap); + if (write(fd, (char *)&v,4) < 4) goto baddonewrite; + } + if (filetype == FORMAT_NEXT) { + /* do it the lazy way: just set the size field to 'unknown size'*/ + uint32 nextsize = 0xffffffff; + if (lseek(fd, 8, SEEK_SET) == 0) goto baddonewrite; + if (write(fd, &nextsize, 4) < 4) goto baddonewrite; + } + } + return; +baddonewrite: + error("%s: %s", filename, strerror(errno)); +} + +static void soundfile_xferout(int nchannels, float **vecs, unsigned char *buf, int nitems, long onset, int bytespersample, +int bigendian, float normalfactor) { + unsigned char *sp=buf, *sp2; + float *fp; + int bytesperframe = bytespersample * nchannels; + #define LOOP for (int j=0; j<nitems; j++, sp2 += bytesperframe, fp++) + for (int i = 0; i < nchannels; i++, sp += bytespersample) { + sp2 = sp; fp = vecs[i] + onset; + if (bytespersample == 2) { + float ff = normalfactor * 32768.; + if (bigendian) LOOP { + int xx = clip(int(32768. + *fp * ff) - 0x8000,-0x7fff,+0x7fff); + sp2[0] = xx>>8; sp2[1] = xx; + } else LOOP { + int xx = clip(int(32768. + *fp * ff) - 0x8000,-0x7fff,+0x7fff); + sp2[1] = xx>>8; sp2[0] = xx; + } + } else if (bytespersample == 3) { + float ff = normalfactor * 8388608.; + if (bigendian) LOOP { + int xx = clip(int(8388608. + *fp * ff) - 0x800000,-0x7fffff,+0x7fffff); + sp2[0] = xx>>16; sp2[1] = xx>>8; sp2[2] = xx; + } else LOOP { + int xx = clip(int(8388608. + *fp * ff) - 0x800000,-0x7fffff,+0x7fffff); + sp2[2] = xx>>16; sp2[1] = xx>>8; sp2[0] = xx; + } + } else if (bytespersample == 4) { + if (bigendian) LOOP { + float f2 = *fp * normalfactor; long xx = *(long *)&f2; + sp2[0] = xx >> 24; sp2[1] = xx >> 16; sp2[2] = xx >> 8; sp2[3] = xx; + } else LOOP { + float f2 = *fp * normalfactor; long xx = *(long *)&f2; + sp2[3] = xx >> 24; sp2[2] = xx >> 16; sp2[1] = xx >> 8; sp2[0] = xx; + } + } + } + #undef LOOP +} + +/* ------- soundfiler - reads and writes soundfiles to/from "garrays" ---- */ + +#define DEFMAXSIZE (16*1024*1024*4) /* default maximum 16 million floats per channel */ +#define SAMPBUFSIZE 1024 + +static t_class *soundfiler_class; + +struct t_soundfiler : t_object {t_canvas *canvas;}; + +#ifdef THREADED_SF +#include <sched.h> +#if (_POSIX_MEMLOCK - 0) >= 200112L +#include <sys/mman.h> +#else +#define munlockall() /* ignore */ +#define mlockall() /* ignore */ +#endif /* _POSIX_MEMLOCK */ + +static pthread_t sf_thread_id; /* id of soundfiler thread */ + +struct t_sfprocess { + void (*process)(t_soundfiler *,t_symbol *, int, t_atom *); /* function to call */ + t_soundfiler *x; + int argc; + t_atom *argv; + struct t_sfprocess *next; /* next object in queue */ + pthread_mutex_t mutex; +}; + +/* this is the queue for all soundfiler objects */ +struct t_sfqueue { + t_sfprocess *begin; + t_sfprocess *end; + pthread_mutex_t mutex; + pthread_cond_t cond; +}; + +static t_sfqueue *soundfiler_queue; + +/* we fill the queue */ +void soundfiler_queue_add(void (* process) (t_soundfiler *,t_symbol *,int,t_atom *), void * x, int argc, t_atom * argv) { + /* preparing entry */ + t_sfprocess * last_entry = (t_sfprocess*)getbytes(sizeof(t_sfprocess)); + if (debug) post("adding process to queue"); + pthread_mutex_init(&(last_entry->mutex), NULL); + pthread_mutex_lock(&(last_entry->mutex)); + last_entry->process = process; + last_entry->x = (t_soundfiler *)x; + last_entry->argc = argc; + last_entry->argv = (t_atom *)copybytes(argv, argc * sizeof(t_atom)); + last_entry->next = NULL; + pthread_mutex_unlock(&(last_entry->mutex)); + /* add new entry to queue */ + pthread_mutex_lock(&(soundfiler_queue->mutex)); + if (soundfiler_queue->begin==NULL) { + soundfiler_queue->begin=last_entry; + soundfiler_queue->end=last_entry; + } else { + pthread_mutex_lock(&(soundfiler_queue->end->mutex)); + soundfiler_queue->end->next=last_entry; + pthread_mutex_unlock(&(soundfiler_queue->end->mutex)); + soundfiler_queue->end=last_entry; + } + if ( soundfiler_queue->begin == soundfiler_queue->end ) { + if (debug) post("signaling"); + pthread_mutex_unlock(&(soundfiler_queue->mutex)); + /* and signal the helper thread */ + pthread_cond_signal(&(soundfiler_queue->cond)); + } else { + if (debug) post("not signaling"); + pthread_mutex_unlock(&(soundfiler_queue->mutex)); + } + return; +} + +/* global soundfiler thread ... sleeping until signaled */ +void soundfiler_thread() { + t_sfprocess *me; + t_sfprocess *next; + if (debug) post("soundfiler_thread ID: %d", pthread_self()); + while (1) { + if (debug) post("Soundfiler sleeping"); + pthread_cond_wait(&soundfiler_queue->cond, &soundfiler_queue->mutex); + if (debug) post("Soundfiler awake"); + /* work on the queue */ + while (soundfiler_queue->begin!=NULL) { + post("soundfiler: locked queue"); + /* locking process */ + pthread_mutex_lock(&(soundfiler_queue->begin->mutex)); + me = soundfiler_queue->begin; + pthread_mutex_unlock(&(me->mutex)); + pthread_mutex_unlock(&(soundfiler_queue->mutex)); + if (debug) post("soundfiler: mutex unlocked, running process"); + /* running the specific function */ + me->process(me->x, NULL, me->argc, me->argv); + if (debug) post("soundfiler: process done, locking mutex"); + pthread_mutex_lock(&(soundfiler_queue->mutex)); + pthread_mutex_lock(&(me->mutex)); + free(me->argv); + /* the process struct */ + next=me->next; + soundfiler_queue->begin=next; + free(me); + } + soundfiler_queue->end=NULL; + } +} + +extern int sys_hipriority; /* real-time flag, true if priority boosted */ + +/* create soundfiler thread */ +void sys_start_sfthread() { + pthread_attr_t sf_attr; + struct sched_param sf_param; + int status; + // initialize queue + soundfiler_queue = (t_sfqueue *)getbytes(sizeof(t_sfqueue)); + pthread_mutex_init(&soundfiler_queue->mutex,NULL); + pthread_cond_init(&soundfiler_queue->cond,NULL); + soundfiler_queue->begin=soundfiler_queue->end=NULL; +/* pthread_mutex_unlock(&(soundfiler_queue->mutex)); */ + // initialize thread + pthread_attr_init(&sf_attr); + sf_param.sched_priority=sched_get_priority_min(SCHED_OTHER); + pthread_attr_setschedparam(&sf_attr,&sf_param); +/* pthread_attr_setinheritsched(&sf_attr,PTHREAD_EXPLICIT_SCHED); */ +#ifdef UNIX + if (sys_hipriority == 1 && getuid() == 0) { + sf_param.sched_priority=sched_get_priority_min(SCHED_RR); + pthread_attr_setschedpolicy(&sf_attr,SCHED_RR); + } else { +/* pthread_attr_setschedpolicy(&sf_attr,SCHED_OTHER); */ +/* sf_param.sched_priority=sched_get_priority_min(SCHED_OTHER); */ + } +#endif /* UNIX */ + //start thread + status = pthread_create(&sf_thread_id, &sf_attr, (void *(*)(void *)) soundfiler_thread,NULL); + if (status != 0) error("Couldn't create soundfiler thread: %d",status); + else post("global soundfiler thread launched, priority: %d", sf_param.sched_priority); +} + +static void soundfiler_t_write( t_soundfiler *x, t_symbol *s, int argc, t_atom *argv); +static void soundfiler_t_write_addq(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) { + soundfiler_queue_add(soundfiler_t_write,(void *)x,argc, argv); +} +static void soundfiler_t_read( t_soundfiler *x, t_symbol *s, int argc, t_atom *argv); +static void soundfiler_t_read_addq(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) { + soundfiler_queue_add(soundfiler_t_read,(void *)x,argc, argv); +} + +/* soundfiler_read + usage: read [flags] filename table ... + flags: -skip <frames> ... frames to skip in file + -nframes <frames> -onset <frames> ... onset in table to read into (NOT DONE YET) + -raw <headersize channels bytes endian> -resize -maxsize <max-size> + TB: adapted for threaded use */ +static t_int soundfiler_read_update_garray(t_int *w); +static t_int soundfiler_read_update_graphics(t_int *w); +static t_int soundfiler_read_output(t_int *w); +static void soundfiler_t_read(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) { + t_param p; + int headersize = -1; + p.nchannels = 0; p.bytespersample = 0; p.bigendian = 0; + int resize = 0, i, j; + long skipframes = 0, nframes = 0, finalsize = 0, maxsize = DEFMAXSIZE, itemsread = 0; + p.bytelimit = 0x7fffffff; + int fd = -1; + char endianness, *filename; + t_garray *garrays[MAXSFCHANS]; + t_float *vecs[MAXSFCHANS]; /* the old array */ + t_float *nvecs[MAXSFCHANS]; /* the new array */ + int vecsize[MAXSFCHANS]; /* the old array size */ + char sampbuf[SAMPBUFSIZE]; + int bufframes, nitems; + FILE *fp; + pthread_cond_t resume_after_callback = PTHREAD_COND_INITIALIZER; + pthread_mutex_t resume_after_callback_mutex = PTHREAD_MUTEX_INITIALIZER; /* dummy */ + t_int* outargs; + while (argc > 0 && argv->a_type == A_SYMBOL && *argv->a_symbol->name == '-') { + char *flag = argv->a_symbol->name + 1; + argc--; argv++; + if (!strcmp(flag, "skip")) { + EAT_ARG(A_FLOAT,skipframes); if (skipframes<0) goto usage; + } else if (!strcmp(flag, "nframes")) { + EAT_ARG(A_FLOAT,nframes); if (nframes<0) goto usage; + } else if (!strcmp(flag, "raw")) { + EAT_ARG(A_FLOAT,headersize); if (headersize<0) goto usage; + EAT_ARG(A_FLOAT,p.nchannels); if (p.nchannels<1) goto usage; + EAT_ARG(A_FLOAT,p.bytespersample); if (p.bytespersample<2 || p.bytespersample>4) goto usage; + EAT_ARG(A_SYMBOL,endianness); if (endianness!='b' && endianness!='l' && endianness!='n') goto usage; + if (endianness == 'b') p.bigendian = 1; + else if (endianness == 'l') p.bigendian = 0; + else p.bigendian = garray_ambigendian(); + } else if (!strcmp(flag, "resize")) { + resize = 1; + } else if (!strcmp(flag, "maxsize")) { + EAT_ARG(A_FLOAT,maxsize); if (maxsize<0) goto usage; + resize = 1; /* maxsize implies resize. */ + } else goto usage; + } + if (argc < 2 || argc > MAXSFCHANS + 1 || argv[0].a_type != A_SYMBOL) goto usage; + filename = argv[0].a_symbol->name; + argc--; argv++; + for (int i=0; i<argc; i++) { + if (argv[i].a_type != A_SYMBOL) goto usage; + garrays[i] = (t_garray *)pd_findbyclass(argv[i].a_symbol, garray_class); + if (!garrays[i]) { + error("%s: no such table", argv[i].a_symbol->name); + goto done; + } else if (!garray_getfloatarray(garrays[i], &vecsize[i], &vecs[i])) + error("%s: bad template for tabwrite", argv[i].a_symbol->name); + if (finalsize && finalsize != vecsize[i] && !resize) { + post("soundfiler_read: arrays have different lengths; resizing..."); + resize = 1; + } + finalsize = vecsize[i]; + } + fd = open_soundfile(canvas_getdir(x->canvas)->name, filename, headersize, &p, skipframes); + if (fd < 0) { + error("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) {error("lseek failed"); goto done;} + lseek(fd, poswas, SEEK_SET); + framesinfile = (eofis - poswas) / p.bytesperchannel(); + if (framesinfile > maxsize) { + error("soundfiler_read: truncated to %d elements", maxsize); + framesinfile = maxsize; + } + framesinfile = min(framesinfile, p.bytelimit / p.bytesperchannel()); + finalsize = framesinfile; + } + if (!finalsize) finalsize = 0x7fffffff; + finalsize = min(finalsize, p.bytelimit / p.bytesperchannel()); + fp = fdopen(fd, "rb"); + bufframes = SAMPBUFSIZE / p.bytesperchannel(); + if (debug) { + post("buffers: %d", argc); + post("channels: %d", p.nchannels); + } + munlockall(); + /* allocate memory for new array */ + if (resize) + for (int i=0; i<argc; i++) { + nvecs[i] = (float *)getalignedbytes(finalsize * sizeof(t_float)); + /* if we are out of memory, free it again and quit */ + if (nvecs[i]==0) { + error("resize failed"); + /* if the resizing fails, we'll have to free all arrays again */ + for (j=0; j!=i;++j) freealignedbytes (nvecs[i],finalsize * sizeof(t_float)); + goto done; + } + } + else + for (int i=0; i<argc; i++) { + nvecs[i] = (float *)getalignedbytes(vecsize[i] * sizeof(t_float)); + /* if we are out of memory, free it again and quit */ + if (nvecs[i]==0) { + error("resize failed"); + /* if the resizing fails, we'll have to free all arrays again */ + for (j=0; j!=i;++j) freealignedbytes (nvecs[i],vecsize[i] * sizeof(t_float)); + goto done; + } + } + if(i > p.nchannels) memset(nvecs[i],0,vecsize[i] * sizeof(t_float)); + if (debug) post("transfer soundfile"); + for (itemsread = 0; itemsread < finalsize; ) { + int thisread = finalsize - itemsread; + thisread = (thisread > bufframes ? bufframes : thisread); + nitems = fread(sampbuf, p.bytesperchannel(), thisread, fp); + if (nitems <= 0) break; + soundfile_xferin(p.nchannels, argc, nvecs, itemsread, (unsigned char *)sampbuf, nitems, p.bytespersample, p.bigendian); + itemsread += nitems; + } + if (debug) post("zeroing remaining elements"); + /* zero out remaining elements of vectors */ + for (int i=0; i<argc; i++) for (int j=itemsread; j<finalsize; j++) nvecs[i][j] = 0; + /* set idle callback to switch pointers */ + if (debug) post("locked"); + for (int i=0; i<argc; i++) { + t_int *w = (t_int*)getbytes(4*sizeof(t_int)); + w[0] = (t_int)(garrays[i]); + w[1] = (t_int)nvecs[i]; + w[2] = (t_int)finalsize; + w[3] = (t_int)(&resume_after_callback); + sys_callback(&soundfiler_read_update_garray, w, 4); + pthread_cond_wait(&resume_after_callback, &resume_after_callback_mutex); + } + if (debug) post("unlocked, doing graphics updates"); + /* do all graphics updates. run this in the main thread via callback */ + for (int i=0; i<argc; i++) { + t_int *w = (t_int*)getbytes(2*sizeof(t_int)); + w[0] = (t_int)(garrays[i]); + w[1] = (t_int)finalsize; + sys_callback(&soundfiler_read_update_graphics, w, 2); + } + /* free the old arrays */ + for (int i=0; i<argc; i++) freealignedbytes(vecs[i], vecsize[i] * sizeof(t_float)); + fclose(fp); + fd = -1; + goto done; +usage: + error("usage: read [flags] filename tablename..."); + post("flags: -skip <n> -nframes <n> -resize -maxsize <n> ..."); + post("-raw <headerbytes> <channels> <bytespersample> <endian (b, l, or n)>."); +done: + if (fd>=0) close(fd); + mlockall(MCL_FUTURE); + outargs = (t_int*)getbytes(2*sizeof(t_int)); + outargs[0] = (t_int)x->outlet; + outargs[1] = (t_int)itemsread; + sys_callback(&soundfiler_read_output, outargs, 2); +} + +/* idle callback for threadsafe synchronisation */ +static t_int soundfiler_read_update_garray(t_int *w) { + t_garray *garray = (t_garray*)w[0]; + t_int nvec = w[1]; + t_int finalsize = w[2]; + pthread_cond_t *conditional = (pthread_cond_t*) w[3]; + t_array *a = garray_getarray(garray); + a->vec = (char *) nvec; + a->n = finalsize; + if (garray->usedindsp) canvas_update_dsp(); + /* signal helper thread */ + pthread_cond_broadcast(conditional); + return 0; +} + +static t_int soundfiler_read_update_graphics(t_int *w) { + t_garray *garray = (t_garray*) w[0]; + t_canvas *gl; + int n = w[1]; + /* if this is the only array in the graph, reset the graph's coordinates */ + if (debug) post("redraw array %p", garray); + gl = garray->canvas; + if (gl->list == garray && !garray->next) { + vmess(gl, gensym("bounds"), "ffff", 0., gl->y1, double(n > 1 ? n-1 : 1), gl->y2); + /* close any dialogs that might have the wrong info now... */ + } else garray_redraw(garray); + return 0; +} + +static t_int soundfiler_read_output(t_int * w) { + t_outlet* outlet = (t_outlet*) w[0]; + float itemsread = (float) w[1]; + if (debug) post("bang %p", outlet); + outlet_float (outlet, itemsread); + return 0; +} + +/* this is broken out from soundfiler_write below so garray_write can call it too... not done yet though. */ +long soundfiler_t_dowrite(void *obj, t_canvas *canvas, int argc, t_atom *argv) { + int bytespersample, bigendian, swap, filetype, normalize, nchannels; + long onset, nframes, itemswritten = 0; + t_garray *garrays[MAXSFCHANS]; + t_float *vecs[MAXSFCHANS]; + char sampbuf[SAMPBUFSIZE]; + int bufframes; + int fd = -1; + float normfactor, biggest = 0, samplerate; + t_symbol *filesym; + if (soundfiler_writeargparse(obj, &argc, &argv, &filesym, &filetype, + &bytespersample, &swap, &bigendian, &normalize, &onset, &nframes, &samplerate)) + goto usage; + nchannels = argc; + if (nchannels < 1 || nchannels > MAXSFCHANS) goto usage; + if (samplerate < 0) samplerate = sys_getsr(); + for (int 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_symbol, garray_class))) { + error("%s: no such table", argv[i].a_symbol->name); + goto fail; + } + else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i])) + error("%s: bad template for tabwrite", argv[i].a_symbol->name); + if (nframes > vecsize - onset) + nframes = vecsize - onset; + for (int 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) { + error("soundfiler_write: no samples at onset %ld", onset); + goto fail; + } + if ((fd = create_soundfile(canvas, filesym->name, filetype, nframes, bytespersample, bigendian, nchannels, swap, samplerate)) < 0) { + post("%s: %s", filesym->name, strerror(errno)); + goto fail; + } + if (!normalize) { + if ((bytespersample != 4) && (biggest > 1)) { + post("%s: normalizing max amplitude %f to 1", filesym->name, biggest); + normalize = 1; + } else post("%s: biggest amplitude = %f", filesym->name, biggest); + } + if (normalize) normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1); + else normfactor = 1; + bufframes = SAMPBUFSIZE / (nchannels * bytespersample); + for (itemswritten = 0; itemswritten < nframes; ) { + int thiswrite = nframes - itemswritten, nbytes; + thiswrite = (thiswrite > bufframes ? bufframes : thiswrite); + soundfile_xferout(argc, vecs, (unsigned char *)sampbuf, thiswrite, onset, bytespersample, bigendian, normfactor); + nbytes = write(fd, sampbuf, nchannels * bytespersample * thiswrite); + if (nbytes < nchannels * bytespersample * thiswrite) { + post("%s: %s", filesym->name, strerror(errno)); + if (nbytes > 0) itemswritten += nbytes / (nchannels * bytespersample); + break; + } + itemswritten += thiswrite; + onset += thiswrite; + } + if (fd >= 0) { + soundfile_finishwrite(obj, filesym->name, fd, filetype, nframes, itemswritten, nchannels * bytespersample, swap); + close (fd); + } + return itemswritten; +usage: + error("usage: write [flags] filename tablename..."); + post("flags: -skip <n> -nframes <n> -bytes <n> -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_t_write(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) { + long bozo = soundfiler_t_dowrite(x, x->canvas, argc, argv); + sys_lock(); + outlet_float(x->outlet, (float)bozo); + sys_lock(); +} + +static void soundfiler_t_resize(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv); +static void soundfiler_t_resize_addq(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) { + soundfiler_queue_add(soundfiler_t_resize,(void *)x,argc, argv); +} + +/* TB: soundfiler_t_resize ... usage: resize table size; adapted from garray_resize */ +static void soundfiler_t_resize(t_soundfiler *y, t_symbol *s, int argc, t_atom *argv) { + int was, elemsize; /* array contains was elements of size elemsize */ + t_float *vec; /* old array */ + t_canvas *gl; + int n; /* resize of n elements */ + char *nvec; /* new array */ + t_garray *x = (t_garray *)pd_findbyclass(argv[0].a_symbol, garray_class); + t_array *a = garray_getarray(x); + if (!x) {error("%s: no such table", argv[0].a_symbol->name); goto usage;} + vec = (t_float*) a->vec; + was = a->n; + if ((argv+1)->a_type == A_FLOAT) { + n = (int) (argv+1)->a_float; + } else goto usage; + if (n == was) return; + if (n < 1) n = 1; + elemsize = template_findbyname(a->templatesym)->t_n * sizeof(t_word); + munlockall(); + if (was > n) { + nvec = (char *)copyalignedbytes(a->vec, was * elemsize); + } else { + nvec = (char *)getalignedbytes(n * elemsize); + memcpy (nvec, a->vec, was * elemsize); + memset(nvec + was*elemsize, 0, (n - was) * elemsize); + } + if (!nvec) {error("array resize failed: out of memory"); mlockall(MCL_FUTURE); return;} + /* TB: we'll have to be sure that no one is accessing the array */ + sys_lock(); + a->vec = nvec; + a->n = n; + if (x->usedindsp) canvas_update_dsp(); + sys_unlock(); + /* if this is the only array in the graph, reset the graph's coordinates */ + gl = x->canvas; + if (gl->list == x && !x->next) { + vmess(gl, gensym("bounds"), "ffff", 0., gl->y1, (double)(n > 1 ? n-1 : 1), gl->y2); + /* close any dialogs that might have the wrong info now... */ + } else garray_redraw(x); + freealignedbytes (vec, was * elemsize); + mlockall(MCL_FUTURE); + sys_lock(); + outlet_float(y->outlet, (float)atom_getintarg(1,argc,argv)); + sys_unlock(); + return; +usage: + error("usage: resize tablename size"); +} + +static void soundfiler_t_const(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv); +static void soundfiler_t_const_addq(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) { + soundfiler_queue_add(soundfiler_t_const,(void *)x,argc, argv); +} + +/* TB: soundfiler_t_const ... usage: const table value */ +static void soundfiler_t_const(t_soundfiler *y, t_symbol *s, int argc, t_atom *argv) { + int size, elemsize; /* array contains was elements of size elemsize */ + t_float *vec; /* old array */ + t_canvas *gl; + int val; /* value */ + char *nvec; /* new array */ + t_garray * x = (t_garray *)pd_findbyclass(argv[0].a_symbol, garray_class); + t_array *a = garray_getarray(x); + if (!x) {error("%s: no such table", argv[0].a_symbol->name); goto usage;} + vec = (t_float*) a->vec; + size = a->n; + if ((argv+1)->a_type == A_FLOAT) { + val = (int) (argv+1)->a_float; + } else goto usage; + elemsize = template_findbyname(a->templatesym)->t_n * sizeof(t_word); + /* allocating memory */ + munlockall(); + nvec = (char *)getalignedbytes(size * elemsize); + if (!nvec) { + error("array resize failed: out of memory"); + mlockall(MCL_FUTURE); + return; + } + /* setting array */ + for (int i=0; i!=size; ++i) nvec[i]=val; + /* TB: we'll have to be sure that no one is accessing the array */ + sys_lock(); + a->vec = nvec; + if (x->usedindsp) canvas_update_dsp(); + sys_unlock(); + /* if this is the only array in the graph, reset the graph's coordinates */ + gl = x->canvas; + if (gl->list == x && !x->next) { + vmess(gl, gensym("bounds"), "ffff", 0., gl->y1, (double)(size > 1 ? size-1 : 1), gl->y2); + /* close any dialogs that might have the wrong info now... */ + } else garray_redraw(x); + freealignedbytes (vec, size * elemsize); + mlockall(MCL_FUTURE); + sys_lock(); + outlet_float(y->outlet, size); + sys_unlock(); + return; + usage: + error("usage: const tablename value"); +} + +#endif /* THREADED_SF */ + +static t_soundfiler *soundfiler_new() { + t_soundfiler *x = (t_soundfiler *)pd_new(soundfiler_class); + x->canvas = canvas_getcurrent(); + outlet_new(x,&s_float); +#ifdef THREADED_SF + post("warning: threaded soundfiler is not synchronous"); +#endif /* THREADED_SF */ + return x; +} + +/* soundfiler_read ... + usage: read [flags] filename table ... + flags: -skip <frames> ... frames to skip in file + -nframes <frames> -onset <frames> ... onset in table to read into (NOT DONE YET) + -raw <headersize channels bytes endian> -resize -maxsize <max-size> */ +static void soundfiler_read(t_soundfiler *x, t_symbol *s, int argc, t_atom *argv) { + t_param p; + p.bytespersample = 0; + p.bigendian = 0; + p.nchannels = 0; + p.bytelimit = 0x7fffffff; + int headersize = -1, resize = 0; + long skipframes = 0, nframes = 0, finalsize = 0, maxsize = DEFMAXSIZE, itemsread = 0; + 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_symbol->name == '-') { + char *flag = argv->a_symbol->name + 1; + argc--; argv++; + if (!strcmp(flag, "skip")) { + EAT_ARG(A_FLOAT,skipframes); if (skipframes<0) goto usage; + } else if (!strcmp(flag, "nframes")) { + EAT_ARG(A_FLOAT,nframes); if (nframes<0) goto usage; + } else if (!strcmp(flag, "raw")) { + EAT_ARG(A_FLOAT,headersize); if (headersize<0) goto usage; + EAT_ARG(A_FLOAT,p.nchannels); if (p.nchannels<1) goto usage; + EAT_ARG(A_FLOAT,p.bytespersample); if (p.bytespersample<2 || p.bytespersample>4) goto usage; + EAT_ARG(A_SYMBOL,endianness); if (endianness!='b' && endianness!='l' && endianness!='n') goto usage; + if (endianness == 'b') p.bigendian = 1; + else if (endianness == 'l') p.bigendian = 0; + else p.bigendian = garray_ambigendian(); + } else if (!strcmp(flag, "resize")) { + resize = 1; + } else if (!strcmp(flag, "maxsize")) { + EAT_ARG(A_FLOAT,maxsize); if (maxsize<0) goto usage; + resize = 1; /* maxsize implies resize. */ + } else goto usage; + } + if (argc < 2 || argc > MAXSFCHANS + 1 || argv[0].a_type != A_SYMBOL) goto usage; + filename = argv[0].a_symbol->name; + argc--; argv++; + for (int i=0; i<argc; i++) { + int vecsize; + if (argv[i].a_type != A_SYMBOL) goto usage; + garrays[i] = (t_garray *)pd_findbyclass(argv[i].a_symbol, garray_class); + if (!garrays) { + error("%s: no such table", argv[i].a_symbol->name); + goto done; + } else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i])) + error("%s: bad template for tabwrite", argv[i].a_symbol->name); + if (finalsize && finalsize != vecsize && !resize) { + post("soundfiler_read: arrays have different lengths; resizing..."); + resize = 1; + } + finalsize = vecsize; + } + fd = open_soundfile_via_canvas(x->canvas, filename, headersize, &p, skipframes); + if (fd < 0) { + error("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) {error("lseek failed"); goto done;} + lseek(fd, poswas, SEEK_SET); + framesinfile = (eofis - poswas) / (p.nchannels * p.bytespersample); + if (framesinfile > maxsize) { + error("soundfiler_read: truncated to %d elements", maxsize); + framesinfile = maxsize; + } + framesinfile = min(framesinfile, p.bytelimit / (p.nchannels * p.bytespersample)); + finalsize = framesinfile; + for (int 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) {error("resize failed"); goto done;} + } + } + if (!finalsize) finalsize = 0x7fffffff; + finalsize = min(finalsize, p.bytelimit / (p.nchannels * p.bytespersample)); + fp = fdopen(fd, "rb"); + bufframes = SAMPBUFSIZE / (p.nchannels * p.bytespersample); + for (itemsread = 0; itemsread < finalsize; ) { + int thisread = finalsize - itemsread; + thisread = min(thisread,bufframes); + nitems = fread(sampbuf, p.nchannels * p.bytespersample, thisread, fp); + if (nitems <= 0) break; + soundfile_xferin(p.nchannels, argc, vecs, itemsread, (unsigned char *)sampbuf, nitems, p.bytespersample, p.bigendian); + itemsread += nitems; + } + /* zero out remaining elements of vectors */ + for (int i=0; i<argc; i++) { + int vecsize; garray_getfloatarray(garrays[i], &vecsize, &vecs[i]); + for (int j=itemsread; j<vecsize; j++) vecs[i][j]=0; + } + /* zero out vectors in excess of number of channels */ + for (int i=p.nchannels; i<argc; i++) { + int vecsize; float *foo; garray_getfloatarray(garrays[i], &vecsize, &foo); + for (int j=0; j<vecsize; j++) foo[j]=0; + } + /* do all graphics updates */ + for (int i=0; i<argc; i++) garray_redraw(garrays[i]); + fclose(fp); + fd = -1; + goto done; +usage: + error("usage: read [flags] filename tablename..."); + post("flags: -skip <n> -nframes <n> -resize -maxsize <n> ..."); + post("-raw <headerbytes> <channels> <bytespersample> <endian (b, l, or n)>."); +done: + if (fd >= 0) close (fd); + outlet_float(x->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 bytespersample, bigendian, swap, filetype, normalize, nchannels; + long onset, nframes, itemswritten = 0; + t_garray *garrays[MAXSFCHANS]; + t_float *vecs[MAXSFCHANS]; + char sampbuf[SAMPBUFSIZE]; + int bufframes; + int fd = -1; + float normfactor, biggest = 0, samplerate; + t_symbol *filesym; + if (soundfiler_writeargparse(obj, &argc, &argv, &filesym, &filetype, + &bytespersample, &swap, &bigendian, &normalize, &onset, &nframes, + &samplerate)) + goto usage; + nchannels = argc; + if (nchannels < 1 || nchannels > MAXSFCHANS) goto usage; + if (samplerate < 0) samplerate = sys_getsr(); + for (int 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_symbol, garray_class))) { + error("%s: no such table", argv[i].a_symbol->name); + goto fail; + } + else if (!garray_getfloatarray(garrays[i], &vecsize, &vecs[i])) + error("%s: bad template for tabwrite", argv[i].a_symbol->name); + nframes = min(nframes, vecsize - onset); + for (int 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) { + error("soundfiler_write: no samples at onset %ld", onset); + goto fail; + } + if ((fd = create_soundfile(canvas, filesym->name, filetype, nframes, bytespersample, bigendian, nchannels, swap, samplerate)) < 0) { + post("%s: %s", filesym->name, strerror(errno)); + goto fail; + } + if (!normalize) { + if ((bytespersample != 4) && (biggest > 1)) { + post("%s: normalizing max amplitude %f to 1", filesym->name, biggest); + normalize = 1; + } else post("%s: biggest amplitude = %f", filesym->name, biggest); + } + if (normalize) normfactor = (biggest > 0 ? 32767./(32768. * biggest) : 1); else normfactor = 1; + bufframes = SAMPBUFSIZE / (nchannels * bytespersample); + for (itemswritten = 0; itemswritten < nframes; ) { + int thiswrite = nframes - itemswritten, nbytes; + thiswrite = (thiswrite > bufframes ? bufframes : thiswrite); + soundfile_xferout(argc, vecs, (unsigned char *)sampbuf, thiswrite, onset, bytespersample, bigendian, normfactor); + nbytes = write(fd, sampbuf, nchannels * bytespersample * thiswrite); + if (nbytes < nchannels * bytespersample * thiswrite) { + post("%s: %s", filesym->name, strerror(errno)); + if (nbytes > 0) itemswritten += nbytes / (nchannels * bytespersample); + break; + } + itemswritten += thiswrite; + onset += thiswrite; + } + if (fd >= 0) { + soundfile_finishwrite(obj, filesym->name, fd, filetype, nframes, itemswritten, nchannels * bytespersample, swap); + close (fd); + } + return itemswritten; +usage: + error("usage: write [flags] filename tablename..."); + post("flags: -skip <n> -nframes <n> -bytes <n> -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->canvas, argc, argv); + outlet_float(x->outlet, (float)bozo); +} + +static void soundfiler_setup() { + t_class *c = soundfiler_class = class_new2("soundfiler", (t_newmethod)soundfiler_new, 0, sizeof(t_soundfiler), 0, ""); +#ifdef THREADED_SF + class_addmethod2(c, (t_method)soundfiler_t_read_addq, "read", "*"); +/* class_addmethod2(c, (t_method)soundfiler_t_write_addq, "write", "*"); */ + class_addmethod2(c, (t_method)soundfiler_t_resize_addq, "resize", "*"); + class_addmethod2(c, (t_method)soundfiler_t_const_addq, "const", "*"); +#else + class_addmethod2(c, (t_method)soundfiler_read, "read", "*"); +#endif /* THREADED_SF */ + class_addmethod2(c, (t_method)soundfiler_write, "write", "*"); +} + +/************************* readsf object ******************************/ + +/* READSF uses the Posix threads package; for the moment we're Linux +only although this should be portable to the other platforms. + +Each instance of readsf~ owns a "child" thread for doing the unix (MSW?) file +reading. The parent thread signals the child each time: + (1) a file wants opening or closing; + (2) we've eaten another 1/16 of the shared buffer (so that the + child thread should check if it's time to read some more.) +The child signals the parent whenever a read has completed. Signalling +is done by setting "conditions" and putting data in mutex-controlled common +areas. +*/ + +#define MAXBYTESPERSAMPLE 4 +#define MAXVECSIZE 128 + +#define READSIZE 65536 +#define WRITESIZE 65536 +#define DEFBUFPERCHAN 262144 +#define MINBUFSIZE (4 * READSIZE) +#define MAXBUFSIZE 16777216 /* arbitrary; just don't want to hang malloc */ + +#define REQUEST_NOTHING 0 +#define REQUEST_OPEN 1 +#define REQUEST_CLOSE 2 +#define REQUEST_QUIT 3 +#define REQUEST_BUSY 4 + +#define STATE_IDLE 0 +#define STATE_STARTUP 1 +#define STATE_STREAM 2 + +static t_class *readsf_class; + +struct t_readsf : t_object { + t_canvas *canvas; + t_clock *clock; + char *buf; /* soundfile buffer */ + int bufsize; /* buffer size in bytes */ + int noutlets; /* number of audio outlets */ + t_sample *(outvec[MAXSFCHANS]); /* audio vectors */ + int vecsize; /* vector size for transfers */ + t_outlet *bangout; /* bang-on-done outlet */ + int state; /* opened, running, or idle */ + float insamplerate; /* sample rate of input signal if known */ + /* parameters to communicate with subthread */ + int requestcode; /* pending request from parent to I/O thread */ + char *filename; /* file to open (string is permanently allocated) */ + int fileerror; /* slot for "errno" return */ + int skipheaderbytes; /* size of header we'll skip */ + t_param p; + float samplerate; /* sample rate of soundfile */ + long onsetframes; /* number of sample frames to skip */ + int fd; /* filedesc */ + int fifosize; /* buffer size appropriately rounded down */ + int fifohead; /* index of next byte to get from file */ + int fifotail; /* index of next byte the ugen will read */ + int eof; /* true if fifohead has stopped changing */ + int sigcountdown; /* counter for signalling child for more data */ + int sigperiod; /* number of ticks per signal */ + int filetype; /* writesf~ only; type of file to create */ + int itemswritten; /* writesf~ only; items writen */ + int swap; /* writesf~ only; true if byte swapping */ + float f; /* writesf~ only; scalar for signal inlet */ + pthread_mutex_t mutex; + pthread_cond_t requestcondition; + pthread_cond_t answercondition; + pthread_t childthread; +}; + + +/************** the child thread which performs file I/O ***********/ + +#if 1 +#define sfread_cond_wait pthread_cond_wait +#define sfread_cond_signal pthread_cond_signal +#else +#include <sys/time.h> /* debugging version... */ +#include <sys/types.h> +static void readsf_fakewait(pthread_mutex_t *b) { + struct timeval timout; + timout.tv_sec = 0; + timout.tv_usec = 1000000; + pthread_mutex_unlock(b); + select(0, 0, 0, 0, &timout); + pthread_mutex_lock(b); +} + +#define sfread_cond_wait(a,b) readsf_fakewait(b) +#define sfread_cond_signal(a) +#endif + +static void *readsf_child_main(void *zz) { + t_readsf *x = (t_readsf *)zz; + pthread_mutex_lock(&x->mutex); + while (1) { + int fd, fifohead; + char *buf; + if (x->requestcode == REQUEST_NOTHING) { + sfread_cond_signal(&x->answercondition); + sfread_cond_wait(&x->requestcondition, &x->mutex); + } else if (x->requestcode == REQUEST_OPEN) { + 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->onsetframes; + t_param p; + p.bytelimit = 0x7fffffff; + int skipheaderbytes = x->skipheaderbytes; + p.bytespersample = x->p.bytespersample; + p.bigendian = x->p.bigendian; + /* alter the request code so that an ensuing "open" will get noticed. */ + x->requestcode = REQUEST_BUSY; + x->fileerror = 0; + /* if there's already a file open, close it */ + if (x->fd >= 0) { + fd = x->fd; + pthread_mutex_unlock(&x->mutex); + close (fd); + pthread_mutex_lock(&x->mutex); + x->fd = -1; + if (x->requestcode != REQUEST_BUSY) goto lost; + } + /* open the soundfile with the mutex unlocked */ + pthread_mutex_unlock(&x->mutex); + fd = open_soundfile(canvas_getdir(x->canvas)->name, x->filename, skipheaderbytes, &p, onsetframes); + pthread_mutex_lock(&x->mutex); + /* copy back into the instance structure. */ + x->p = p; + x->fd = fd; + if (fd < 0) { + x->fileerror = errno; + x->eof = 1; + goto lost; + } + /* check if another request has been made; if so, field it */ + if (x->requestcode != REQUEST_BUSY) goto lost; + 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->fifosize = x->bufsize - (x->bufsize % (x->p.bytesperchannel() * MAXVECSIZE)); + /* arrange for the "request" condition to be signalled 16 times per buffer */ + x->sigcountdown = x->sigperiod = x->fifosize / (16 * x->p.bytesperchannel() * x->vecsize); + /* in a loop, wait for the fifo to get hungry and feed it */ + while (x->requestcode == REQUEST_BUSY) { + int fifosize = x->fifosize; + if (x->eof) break; + if (x->fifohead >= 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->fifotail || (fifosize - x->fifohead > READSIZE)) { + wantbytes = min(min(fifosize - x->fifohead, READSIZE),(int)x->p.bytelimit); + } else { + sfread_cond_signal(&x->answercondition); + sfread_cond_wait(&x->requestcondition, &x->mutex); + continue; + } + } else { + /* otherwise check if there are at least READSIZE bytes to read. If not, wait and loop back. */ + wantbytes = x->fifotail - x->fifohead - 1; + if (wantbytes < READSIZE) { + sfread_cond_signal(&x->answercondition); + sfread_cond_wait(&x->requestcondition, &x->mutex); + continue; + } else wantbytes = READSIZE; + wantbytes = min(wantbytes,(int)x->p.bytelimit); + } + fd = x->fd; + buf = x->buf; + fifohead = x->fifohead; + pthread_mutex_unlock(&x->mutex); + sysrtn = read(fd, buf + fifohead, wantbytes); + pthread_mutex_lock(&x->mutex); + if (x->requestcode != REQUEST_BUSY) break; + if (sysrtn < 0) {x->fileerror = errno; break;} + else if (sysrtn == 0) {x->eof = 1; break;} + else { + x->fifohead += sysrtn; + x->p.bytelimit -= sysrtn; + if (x->p.bytelimit <= 0) {x->eof = 1; break;} + if (x->fifohead == fifosize) x->fifohead = 0; + } + /* signal parent in case it's waiting for data */ + sfread_cond_signal(&x->answercondition); + } + lost: + if (x->requestcode == REQUEST_BUSY) x->requestcode = REQUEST_NOTHING; + /* fell out of read loop: close file if necessary, set EOF and signal once more */ + if (x->fd >= 0) { + fd = x->fd; + pthread_mutex_unlock(&x->mutex); + close (fd); + pthread_mutex_lock(&x->mutex); + x->fd = -1; + } + sfread_cond_signal(&x->answercondition); + } else if (x->requestcode == REQUEST_CLOSE || x->requestcode == REQUEST_QUIT) { + if (x->fd >= 0) { + fd = x->fd; + pthread_mutex_unlock(&x->mutex); + close (fd); + pthread_mutex_lock(&x->mutex); + x->fd = -1; + } + x->requestcode = REQUEST_NOTHING; + sfread_cond_signal(&x->answercondition); + } + if (x->requestcode == REQUEST_QUIT) break; + } + pthread_mutex_unlock(&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) { + int nchannels = int(fnchannels), bufsize = int(fbufsize); + 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; + char *buf = (char *)getbytes(bufsize); + if (!buf) return 0; + t_readsf *x = (t_readsf *)pd_new(readsf_class); + for (int i=0; i<nchannels; i++) outlet_new(x,gensym("signal")); + x->noutlets = nchannels; + x->bangout = outlet_new(x,&s_bang); + pthread_mutex_init(&x->mutex, 0); + pthread_cond_init(&x->requestcondition, 0); + pthread_cond_init(&x->answercondition, 0); + x->vecsize = MAXVECSIZE; + x->state = STATE_IDLE; + x->clock = clock_new(x, (t_method)readsf_tick); + x->canvas = canvas_getcurrent(); + x->p.bytespersample = 2; + x->p.nchannels = 1; + x->fd = -1; + x->buf = buf; + x->bufsize = bufsize; + x->fifosize = x->fifohead = x->fifotail = x->requestcode = 0; + pthread_create(&x->childthread, 0, readsf_child_main, x); + return x; +} + +static void readsf_tick(t_readsf *x) {outlet_bang(x->bangout);} + +static t_int *readsf_perform(t_int *w) { + t_readsf *x = (t_readsf *)(w[1]); + int vecsize = x->vecsize, noutlets = x->noutlets; + if (x->state == STATE_STREAM) { + int wantbytes; + pthread_mutex_lock(&x->mutex); + wantbytes = vecsize * x->p.bytesperchannel(); + while (!x->eof && x->fifohead >= x->fifotail && x->fifohead < x->fifotail + wantbytes-1) { + sfread_cond_signal(&x->requestcondition); + sfread_cond_wait(&x->answercondition, &x->mutex); + } + if (x->eof && x->fifohead >= x->fifotail && x->fifohead < x->fifotail + wantbytes-1) { + if (x->fileerror) { + error("dsp: %s: %s", x->filename, x->fileerror == EIO ? "unknown or bad header format" : strerror(x->fileerror)); + } + clock_delay(x->clock, 0); + x->state = STATE_IDLE; + /* if there's a partial buffer left, copy it out. */ + int xfersize = (x->fifohead - x->fifotail + 1) / x->p.bytesperchannel(); + if (xfersize) { + soundfile_xferin(x->p.nchannels, noutlets, x->outvec, 0, + (unsigned char *)(x->buf + x->fifotail), xfersize, x->p.bytespersample, x->p.bigendian); + vecsize -= xfersize; + } + /* then zero out the (rest of the) output */ + for (int i=0; i<noutlets; i++) { + float *fp = x->outvec[i] + xfersize; + for (int j=vecsize; j--; ) *fp++ = 0; + } + sfread_cond_signal(&x->requestcondition); + pthread_mutex_unlock(&x->mutex); + return w+2; + } + soundfile_xferin(x->p.nchannels, noutlets, x->outvec, 0, (unsigned char *)(x->buf + x->fifotail), vecsize, x->p.bytespersample, x->p.bigendian); + x->fifotail += wantbytes; + if (x->fifotail >= x->fifosize) x->fifotail = 0; + if ((--x->sigcountdown) <= 0) { + sfread_cond_signal(&x->requestcondition); + x->sigcountdown = x->sigperiod; + } + pthread_mutex_unlock(&x->mutex); + } else { + for (int i=0; i<noutlets; i++) { + float *fp = x->outvec[i]; + for (int j=vecsize; 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->state == STATE_STARTUP) x->state = STATE_STREAM; + else error("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->mutex); + x->state = STATE_IDLE; + x->requestcode = REQUEST_CLOSE; + sfread_cond_signal(&x->requestcondition); + pthread_mutex_unlock(&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 bytespersample 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 bytespersample = atom_getfloatarg(4, argc, argv); + t_symbol *endian = atom_getsymbolarg(5, argc, argv); + if (!*filesym->name) return; + pthread_mutex_lock(&x->mutex); + x->requestcode = REQUEST_OPEN; + x->filename = filesym->name; + x->fifotail = 0; + x->fifohead = 0; + if (*endian->name == 'b') x->p.bigendian = 1; + else if (*endian->name == 'l') x->p.bigendian = 0; + else if (*endian->name) error("endianness neither 'b' nor 'l'"); + else x->p.bigendian = garray_ambigendian(); + x->onsetframes = max(long(onsetframes),0L); + x->skipheaderbytes = int(headerbytes > 0 ? headerbytes : headerbytes == 0 ? -1 : 0); + x->p.nchannels = max(int(channels),1); + x->p.bytespersample = max(int(bytespersample),2); + x->eof = 0; + x->fileerror = 0; + x->state = STATE_STARTUP; + sfread_cond_signal(&x->requestcondition); + pthread_mutex_unlock(&x->mutex); +} + +static void readsf_dsp(t_readsf *x, t_signal **sp) { + int i, noutlets = x->noutlets; + pthread_mutex_lock(&x->mutex); + x->vecsize = sp[0]->n; + x->sigperiod = (x->fifosize / (x->p.bytesperchannel() * x->vecsize)); + for (i = 0; i < noutlets; i++) x->outvec[i] = sp[i]->v; + pthread_mutex_unlock(&x->mutex); + dsp_add(readsf_perform, 1, x); +} + +static void readsf_print(t_readsf *x) { + post("state %d", x->state); + post("fifo head %d", x->fifohead); + post("fifo tail %d", x->fifotail); + post("fifo size %d", x->fifosize); + post("fd %d", x->fd); + post("eof %d", x->eof); +} + +static void readsf_free(t_readsf *x) { + /* request QUIT and wait for acknowledge */ + void *threadrtn; + pthread_mutex_lock(&x->mutex); + x->requestcode = REQUEST_QUIT; + sfread_cond_signal(&x->requestcondition); + while (x->requestcode != REQUEST_NOTHING) { + sfread_cond_signal(&x->requestcondition); + sfread_cond_wait(&x->answercondition, &x->mutex); + } + pthread_mutex_unlock(&x->mutex); + if (pthread_join(x->childthread, &threadrtn)) error("readsf_free: join failed"); + pthread_cond_destroy(&x->requestcondition); + pthread_cond_destroy(&x->answercondition); + pthread_mutex_destroy(&x->mutex); + free(x->buf); + clock_free(x->clock); +} + +static void readsf_setup() { + t_class *c = readsf_class = class_new2("readsf~", (t_newmethod)readsf_new, (t_method)readsf_free, sizeof(t_readsf), 0, "FF"); + class_addfloat(c, (t_method)readsf_float); + class_addmethod2(c, (t_method)readsf_start, "start",""); + class_addmethod2(c, (t_method)readsf_stop, "stop",""); + class_addmethod2(c, (t_method)readsf_dsp, "dsp",""); + class_addmethod2(c, (t_method)readsf_open, "open","*"); + class_addmethod2(c, (t_method)readsf_print, "print",""); +} + +/******************************* writesf *******************/ + +static t_class *writesf_class; +typedef t_readsf t_writesf; /* just re-use the structure */ + +/************** the child thread which performs file I/O ***********/ + +static void *writesf_child_main(void *zz) { + t_writesf *x = (t_writesf*)zz; + pthread_mutex_lock(&x->mutex); + while (1) { + if (x->requestcode == REQUEST_NOTHING) { + sfread_cond_signal(&x->answercondition); + sfread_cond_wait(&x->requestcondition, &x->mutex); + } else if (x->requestcode == REQUEST_OPEN) { + int fd, sysrtn, writebytes; + /* copy file stuff out of the data structure so we can relinquish the mutex while we're in open_soundfile(). */ + int filetype = x->filetype; + char *filename = x->filename; + t_canvas *canvas = x->canvas; + float samplerate = x->samplerate; + /* alter the request code so that an ensuing "open" will get noticed. */ + x->requestcode = REQUEST_BUSY; + x->fileerror = 0; + /* if there's already a file open, close it. This should never happen since + writesf_open() calls stop if needed and then waits until we're idle. */ + if (x->fd >= 0) { + int bytesperframe = x->p.bytesperchannel(); + char *filename = x->filename; + int fd = x->fd; + int filetype = x->filetype; + int itemswritten = x->itemswritten; + int swap = x->swap; + pthread_mutex_unlock(&x->mutex); + soundfile_finishwrite(x, filename, fd, filetype, 0x7fffffff, itemswritten, bytesperframe, swap); + close (fd); + pthread_mutex_lock(&x->mutex); + x->fd = -1; + if (x->requestcode != REQUEST_BUSY) continue; + } + /* open the soundfile with the mutex unlocked */ + pthread_mutex_unlock(&x->mutex); + fd = create_soundfile(canvas, filename, filetype, 0, + x->p.bytespersample, x->p.bigendian, x->p.nchannels, garray_ambigendian() != x->p.bigendian, samplerate); + pthread_mutex_lock(&x->mutex); + if (fd < 0) { + x->fd = -1; + x->eof = 1; + x->fileerror = errno; + x->requestcode = REQUEST_NOTHING; + continue; + } + /* check if another request has been made; if so, field it */ + if (x->requestcode != REQUEST_BUSY) continue; + x->fd = fd; + x->fifotail = 0; + x->itemswritten = 0; + x->swap = garray_ambigendian() != x->p.bigendian; + /* in a loop, wait for the fifo to have data and write it to disk */ + while (x->requestcode == REQUEST_BUSY || (x->requestcode == REQUEST_CLOSE && x->fifohead != x->fifotail)) { + int fifosize = x->fifosize, fifotail; + char *buf = x->buf; + /* 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->fifohead < x->fifotail || x->fifohead >= x->fifotail + WRITESIZE + || (x->requestcode == REQUEST_CLOSE && x->fifohead != x->fifotail)) { + writebytes = (x->fifohead < x->fifotail ? fifosize : x->fifohead) - x->fifotail; + if (writebytes > READSIZE) writebytes = READSIZE; + } else { + sfread_cond_signal(&x->answercondition); + sfread_cond_wait(&x->requestcondition,&x->mutex); + continue; + } + fifotail = x->fifotail; + fd = x->fd; + pthread_mutex_unlock(&x->mutex); + sysrtn = write(fd, buf + fifotail, writebytes); + pthread_mutex_lock(&x->mutex); + if (x->requestcode != REQUEST_BUSY && x->requestcode != REQUEST_CLOSE) break; + if (sysrtn < writebytes) {x->fileerror = errno; break;} + else { + x->fifotail += sysrtn; + if (x->fifotail == fifosize) x->fifotail = 0; + } + x->itemswritten += sysrtn / x->p.bytesperchannel(); + /* signal parent in case it's waiting for data */ + sfread_cond_signal(&x->answercondition); + } + } else if (x->requestcode == REQUEST_CLOSE || x->requestcode == REQUEST_QUIT) { + if (x->fd >= 0) { + int bytesperframe = x->p.bytesperchannel(); + char *filename = x->filename; + int fd = x->fd; + int filetype = x->filetype; + int itemswritten = x->itemswritten; + int swap = x->swap; + pthread_mutex_unlock(&x->mutex); + soundfile_finishwrite(x, filename, fd, filetype, 0x7fffffff, itemswritten, bytesperframe, swap); + close(fd); + pthread_mutex_lock(&x->mutex); + x->fd = -1; + } + x->requestcode = REQUEST_NOTHING; + sfread_cond_signal(&x->answercondition); + } + if (x->requestcode == REQUEST_QUIT) break; + } + pthread_mutex_unlock(&x->mutex); + return 0; +} + +/******** the object proper runs in the calling (parent) thread ****/ + +static void *writesf_new(t_floatarg fnchannels, t_floatarg fbufsize) { + int nchannels = int(fnchannels), bufsize = int(fbufsize), i; + 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; + char *buf = (char *)getbytes(bufsize); + if (!buf) return 0; + t_writesf *x = (t_writesf *)pd_new(writesf_class); + for (i = 1; i < nchannels; i++) inlet_new(x,x, &s_signal, &s_signal); + x->f = 0; + x->p.nchannels = nchannels; + pthread_mutex_init(&x->mutex, 0); + pthread_cond_init(&x->requestcondition, 0); + pthread_cond_init(&x->answercondition, 0); + x->vecsize = MAXVECSIZE; + x->insamplerate = x->samplerate = 0; + x->state = STATE_IDLE; + x->clock = 0; /* no callback needed here */ + x->canvas = canvas_getcurrent(); + x->p.bytespersample = 2; + x->fd = -1; + x->buf = buf; + x->bufsize = bufsize; + x->fifosize = x->fifohead = x->fifotail = x->requestcode = 0; + pthread_create(&x->childthread, 0, writesf_child_main, x); + return x; +} + +static t_int *writesf_perform(t_int *w) { + t_writesf *x = (t_writesf *)(w[1]); + if (x->state == STATE_STREAM) { + int wantbytes; + pthread_mutex_lock(&x->mutex); + wantbytes = x->vecsize * x->p.bytesperchannel(); + while (x->fifotail > x->fifohead && x->fifotail < x->fifohead + wantbytes + 1) { + sfread_cond_signal(&x->requestcondition); + sfread_cond_wait(&x->answercondition, &x->mutex); + /* resync local cariables -- bug fix thanks to Shahrokh */ + wantbytes = x->vecsize * x->p.bytesperchannel(); + } + soundfile_xferout(x->p.nchannels, x->outvec, (unsigned char *)(x->buf + x->fifohead), x->vecsize, 0, x->p.bytespersample, x->p.bigendian, 1.); + x->fifohead += wantbytes; + if (x->fifohead >= x->fifosize) x->fifohead = 0; + if ((--x->sigcountdown) <= 0) { + sfread_cond_signal(&x->requestcondition); + x->sigcountdown = x->sigperiod; + } + pthread_mutex_unlock(&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->state == STATE_STARTUP) x->state = STATE_STREAM; + else error("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->mutex); + x->state = STATE_IDLE; + x->requestcode = REQUEST_CLOSE; + sfread_cond_signal(&x->requestcondition); + pthread_mutex_unlock(&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, bytespersample, swap, bigendian, normalize; + long onset, nframes; + float samplerate; + if (x->state != STATE_IDLE) writesf_stop(x); + if (soundfiler_writeargparse(x, &argc, &argv, &filesym, &filetype, &bytespersample, &swap, &bigendian, + &normalize, &onset, &nframes, &samplerate)) { + error("writesf~: usage: open [-bytes [234]] [-wave,-nextstep,-aiff] ..."); + post("... [-big,-little] [-rate ####] filename"); + return; + } + if (normalize || onset || (nframes != 0x7fffffff)) + error("normalize/onset/nframes argument to writesf~: ignored"); + if (argc) error("extra argument(s) to writesf~: ignored"); + pthread_mutex_lock(&x->mutex); + while (x->requestcode != REQUEST_NOTHING) { + sfread_cond_signal(&x->requestcondition); + sfread_cond_wait(&x->answercondition, &x->mutex); + } + x->p.bytespersample = max(bytespersample,2); + x->swap = swap; + x->p.bigendian = bigendian; + x->filename = filesym->name; + x->filetype = filetype; + x->itemswritten = 0; + x->requestcode = REQUEST_OPEN; + x->fifotail = 0; + x->fifohead = 0; + x->eof = 0; + x->fileerror = 0; + x->state = STATE_STARTUP; + x->samplerate = samplerate>0 ? samplerate : x->insamplerate>0 ? x->insamplerate : sys_getsr(); + /* set fifosize from bufsize. fifosize must be a multiple of the number of bytes eaten for each DSP tick. */ + x->fifosize = x->bufsize - x->bufsize % (x->p.bytesperchannel() * MAXVECSIZE); + /* arrange for the "request" condition to be signalled 16 times per buffer */ + x->sigcountdown = x->sigperiod = x->fifosize / (16 * x->p.bytesperchannel() * x->vecsize); + sfread_cond_signal(&x->requestcondition); + pthread_mutex_unlock(&x->mutex); +} + +static void writesf_dsp(t_writesf *x, t_signal **sp) { + int ninlets = x->p.nchannels; + pthread_mutex_lock(&x->mutex); + x->vecsize = sp[0]->n; + x->sigperiod = x->fifosize / (x->p.bytesperchannel() * x->vecsize); + for (int i=0; i<ninlets; i++) x->outvec[i] = sp[i]->v; + x->insamplerate = sp[0]->sr; + pthread_mutex_unlock(&x->mutex); + dsp_add(writesf_perform, 1, x); +} + +static void writesf_print(t_writesf *x) { + post("state %d", x->state); + post("fifo head %d", x->fifohead); + post("fifo tail %d", x->fifotail); + post("fifo size %d", x->fifosize); + post("fd %d", x->fd); + post("eof %d", x->eof); +} + +static void writesf_free(t_writesf *x) { + /* request QUIT and wait for acknowledge */ + void *threadrtn; + pthread_mutex_lock(&x->mutex); + x->requestcode = REQUEST_QUIT; + /* post("stopping writesf thread..."); */ + sfread_cond_signal(&x->requestcondition); + while (x->requestcode != REQUEST_NOTHING) { + /* post("signalling..."); */ + sfread_cond_signal(&x->requestcondition); + sfread_cond_wait(&x->answercondition, &x->mutex); + } + pthread_mutex_unlock(&x->mutex); + if (pthread_join(x->childthread, &threadrtn)) error("writesf_free: join failed"); + /* post("... done."); */ + pthread_cond_destroy(&x->requestcondition); + pthread_cond_destroy(&x->answercondition); + pthread_mutex_destroy(&x->mutex); + free(x->buf); +} + +static void writesf_setup() { + t_class *c = writesf_class = class_new2("writesf~", (t_newmethod)writesf_new, (t_method)writesf_free, sizeof(t_writesf), 0, "FF"); + class_addmethod2(c, (t_method)writesf_start, "start", ""); + class_addmethod2(c, (t_method)writesf_stop, "stop", ""); + class_addmethod2(c, (t_method)writesf_dsp, "dsp", ""); + class_addmethod2(c, (t_method)writesf_open, "open", "*"); + class_addmethod2(c, (t_method)writesf_print, "print", ""); + CLASS_MAINSIGNALIN(c, t_writesf, f); +} + +/* ------------------------ global setup routine ------------------------- */ + +void d_soundfile_setup() { + if (sizeof(uint16)!=2) bug("uint16 should really be 16 bits"); + if (sizeof(uint32)!=4) bug("uint32 should really be 32 bits"); + soundfiler_setup(); + readsf_setup(); + writesf_setup(); +} diff --git a/desiredata/src/d_ugen.c b/desiredata/src/d_ugen.c new file mode 100644 index 00000000..4835b49d --- /dev/null +++ b/desiredata/src/d_ugen.c @@ -0,0 +1,1016 @@ +/* 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 power-of-2 samplerates - mfg.gfd.uil IOhannes */ + +#define PD_PLUSPLUS_FACE +#include "desire.h" +#include <stdlib.h> +#include <stdarg.h> + +/* T.Grill - include SIMD functionality */ +#include "m_simd.h" + +extern t_class *vinlet_class, *voutlet_class, *canvas_class; +t_sample *obj_findsignalscalar(t_object *x, int m); +static int ugen_loud; +static t_int *dsp_chain; +static int dsp_chainsize; +struct _vinlet; +struct _voutlet; + +extern "C" { +void vinlet_dspprolog( struct _vinlet *x, t_signal **parentsigs, int myvecsize, int calcsize, int phase, int period, int frequency, + int downsample, int upsample, int reblock, int switched); +void voutlet_dspprolog(struct _voutlet *x, t_signal **parentsigs, int myvecsize, int calcsize, int phase, int period, int frequency, + int downsample, int upsample, int reblock, int switched); +void voutlet_dspepilog(struct _voutlet *x, t_signal **parentsigs, int myvecsize, int calcsize, int phase, int period, int frequency, + int downsample, int upsample, int reblock, int switched); +}; + +/* zero out a vector */ +t_int *zero_perform(t_int *w) { + 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 if(SIMD_CHECK1(n,out)) dsp_add(zero_perf_simd, 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; + +struct t_block : t_object { + int vecsize; /* size of audio signals in this block */ + int calcsize; /* number of samples actually to compute */ + int overlap; + int phase; /* from 0 to period-1; when zero we run the block */ + int period; /* submultiple of containing canvas */ + int frequency; /* supermultiple of comtaining canvas */ + int count; /* number of times parent block has called us */ + int chainonset; /* beginning of code in DSP chain */ + int blocklength; /* length of dspchain for this block */ + int epiloglength; /* length of epilog */ + char switched; /* true if we're acting as a a switch */ + char switchon; /* true if we're switched on */ + char reblock; /* true if inlets and outlets are reblocking */ + int upsample; /* IOhannes: upsampling-factor */ + int downsample; /* IOhannes: downsampling-factor */ + int x_return; /* stop right after this block (for one-shots) */ +}; + +static void block_set(t_block *x, t_floatarg fvecsize, t_floatarg foverlap, t_floatarg fupsample); + +static void *block_new(t_floatarg fcalcsize, t_floatarg foverlap, t_floatarg fupsample) /* IOhannes */ { + t_block *x = (t_block *)pd_new(block_class); + x->phase = 0; + x->period = 1; + x->frequency = 1; + x->switched = 0; + x->switchon = 1; + block_set(x, fcalcsize, foverlap, fupsample); + return x; +} + +static void block_set(t_block *x, t_floatarg fcalcsize, t_floatarg foverlap, t_floatarg fupsample) { + int upsample, downsample; /* IOhannes */ + int calcsize = (int)fcalcsize; + int overlap = (int)foverlap; + int dspstate = canvas_suspend_dsp(); + if (overlap < 1) overlap = 1; + if (calcsize < 0) calcsize = 0; /* this means we'll get it from parent later. */ + /* IOhannes { */ + if (fupsample <= 0) upsample = downsample = 1; + else if (fupsample >= 1) { + upsample = (int)fupsample; + downsample = 1; + } else { + downsample = int(1.0 / fupsample); + upsample = 1; + } + /* } IOhannes */ + /* vecsize is smallest power of 2 large enough to hold calcsize */ + int vecsize = 0; + if (calcsize) { + vecsize = (1 << ilog2(calcsize)); + if (vecsize != calcsize) vecsize *= 2; + } + if (vecsize && vecsize != (1 << ilog2(vecsize))) {error("block~: vector size not a power of 2"); vecsize = 64;} + if ( overlap != (1 << ilog2(overlap))) {error("block~: overlap not a power of 2"); overlap = 1;} + /* IOhannes { */ + if (downsample != (1 << ilog2(downsample))) {error("block~: downsampling not a power of 2"); downsample = 1;} + if ( upsample != (1 << ilog2( upsample))) {error("block~: upsampling not a power of 2"); upsample = 1;} + /* } IOhannes */ + x->calcsize = calcsize; + x->vecsize = vecsize; + x->overlap = overlap; + /* IOhannes { */ + x->upsample = upsample; + x->downsample = downsample; + /* } IOhannes */ + canvas_resume_dsp(dspstate); +} + +static void *switch_new(t_floatarg fvecsize, t_floatarg foverlap, t_floatarg fupsample) /* IOhannes */ { + t_block *x = (t_block *)(block_new(fvecsize, foverlap, fupsample)); /* IOhannes */ + x->switched = 1; + x->switchon = 0; + return x; +} + +static void block_float(t_block *x, t_floatarg f) { + if (x->switched) x->switchon = f!=0; +} + +static void block_bang(t_block *x) { + if (x->switched && !x->switchon) { + x->x_return = 1; + for (t_int *ip = dsp_chain + x->chainonset; ip; ) ip = t_perfroutine(*ip)(ip); + x->x_return = 0; + } else error("bang to block~ or on-state switch~ has no effect"); +} + +#define PROLOGCALL 2 +#define EPILOGCALL 2 + +static t_int *block_prolog(t_int *w) { + t_block *x = (t_block *)w[1]; + int phase = x->phase; + /* if we're switched off, jump past the epilog code */ + if (!x->switchon) return w+x->blocklength; + if (phase) { + phase++; + if (phase == x->period) phase = 0; + x->phase = phase; + return w+x->blocklength; /* skip block; jump past epilog */ + } else { + x->count = x->frequency; + x->phase = (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->count - 1; + if (x->x_return) return 0; + if (!x->reblock) return w+x->epiloglength+EPILOGCALL; + if (count) { + x->count = count; + return w - (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 */} + +void block_tilde_setup() { + block_class = class_new2("block~", (t_newmethod)block_new, 0, sizeof(t_block), 0,"FFF"); + class_addcreator2("switch~",(t_newmethod)switch_new,"FFF"); + class_addmethod2(block_class, (t_method)block_set,"set","FFF"); + class_addmethod2(block_class, (t_method)block_dsp,"dsp",""); + class_addfloat(block_class, block_float); + class_addbang(block_class, block_bang); +} + +/* ------------------ DSP call list ----------------------- */ + +static t_int dsp_done(t_int *w) {return 0;} + +void dsp_add(t_perfroutine f, int n, ...) { + va_list ap; + va_start(ap, n); + int newsize = dsp_chainsize + n+1; + dsp_chain = (t_int *)resizebytes(dsp_chain, dsp_chainsize * sizeof (t_int), newsize * sizeof (t_int)); + dsp_chain[dsp_chainsize-1] = (t_int)f; + for (int i=0; i<n; i++) dsp_chain[dsp_chainsize + i] = va_arg(ap, t_int); + dsp_chain[newsize-1] = (t_int)dsp_done; + dsp_chainsize = newsize; + va_end(ap); +} + +/* 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; + dsp_chain = (t_int *)resizebytes(dsp_chain, dsp_chainsize * sizeof (t_int), newsize * sizeof (t_int)); + dsp_chain[dsp_chainsize-1] = (t_int)f; + for (int i=0; i<n; i++) dsp_chain[dsp_chainsize + i] = vec[i]; + dsp_chain[newsize-1] = (t_int)dsp_done; + dsp_chainsize = newsize; +} + +void dsp_tick() { + if (dsp_chain) { + for (t_int *ip = dsp_chain; ip; ) ip = ((t_perfroutine)*ip)(ip); + dsp_phase++; + } +} + +/* ---------------- signals ---------------------------- */ + +int ilog2(int n) { + int r = -1; + if (n <= 0) return 0; + while (n) { + r++; + n >>= 1; + } + return r; +} + +/* list of signals which can be reused, sorted by buffer size */ +static t_signal *signal_freelist[MAXLOGSIG+1]; +/* list of reusable "borrowed" signals (which don't own sample buffers) */ +static t_signal *signal_freeborrowed; +/* list of all signals allocated (not including "borrowed" ones) */ +static t_signal *signal_usedlist; + +/* call this when DSP is stopped to free all the signals */ +void signal_cleanup() { + t_signal *sig; + while ((sig = signal_usedlist)) { + signal_usedlist = sig->nextused; + if (!sig->isborrowed) { +#ifndef VECTORALIGNMENT + free(sig->v); +#else + freealignedbytes(sig->v, sig->vecsize * sizeof (*sig->v)); +#endif + } + free(sig); + } + for (int i=0; i<=MAXLOGSIG; i++) signal_freelist[i] = 0; + signal_freeborrowed = 0; +} + +/* mark the signal "reusable." */ +extern "C" void signal_makereusable(t_signal *sig) { + int logn = ilog2(sig->vecsize); +#if 1 + for (t_signal *s5 = signal_freeborrowed; s5; s5 = s5->nextfree) {if (s5 == sig) {bug("signal_free 3"); return;}} + for (t_signal *s5 = signal_freelist[logn]; s5; s5 = s5->nextfree) {if (s5 == sig) {bug("signal_free 4"); return;}} +#endif + if (ugen_loud) post("free %lx: %d", sig, sig->isborrowed); + if (sig->isborrowed) { + /* if the signal is borrowed, decrement the borrowed-from signal's reference count, possibly marking it reusable too */ + t_signal *s2 = sig->borrowedfrom; + if ((s2 == sig) || !s2) bug("signal_free"); + s2->refcount--; + if (!s2->refcount) signal_makereusable(s2); + sig->nextfree = signal_freeborrowed; + signal_freeborrowed = sig; + } else { + /* if it's a real signal (not borrowed), put it on the free list so we can reuse it. */ + if (signal_freelist[logn] == sig) bug("signal_free 2"); + sig->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 vecsize = 0; + t_signal *ret, **whichlist; + int logn = ilog2(n); + if (n) { + if ((vecsize = (1<<logn)) != n) vecsize *= 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 */ + ret = *whichlist; + if (ret) *whichlist = ret->nextfree; + else { + /* LATER figure out what to do for out-of-space here! */ + ret = (t_signal *)t_getbytes(sizeof *ret); + if (n) { +#ifndef VECTORALIGNMENT + ret->v = (t_sample *)getbytes(vecsize * sizeof (*ret->v)); +#else + /* T.Grill - make signal vectors aligned! */ + ret->v = (t_sample *)getalignedbytes(vecsize * sizeof (*ret->v)); +#endif + ret->isborrowed = 0; + } else { + ret->v = 0; + ret->isborrowed = 1; + } + ret->nextused = signal_usedlist; + signal_usedlist = ret; + } + ret->n = n; + ret->vecsize = vecsize; + ret->sr = sr; + ret->refcount = 0; + ret->borrowedfrom = 0; + if (ugen_loud) post("new %lx: %d", ret, ret->isborrowed); + return ret; +} + +static t_signal *signal_newlike(const t_signal *sig) {return signal_new(sig->n, sig->sr);} + +extern "C" void signal_setborrowed(t_signal *sig, t_signal *sig2) { + if (!sig->isborrowed || sig->borrowedfrom) bug("signal_setborrowed"); + if (sig == sig2) bug("signal_setborrowed 2"); + sig->borrowedfrom = sig2; + sig->v = sig2->v; + sig->n = sig2->n; + sig->vecsize = sig2->vecsize; +} + +int signal_compatible(t_signal *s1, t_signal *s2) {return s1->n == s2->n && s1->sr == s2->sr;} + +/* ------------------ ugen ("unit generator") sorting ----------------- */ + +struct t_ugenbox { + struct t_siginlet *in; int nin; + struct t_sigoutlet *out; int nout; + int u_phase; + t_ugenbox *next; + t_object *obj; + int done; +}; + +struct t_siginlet { + int nconnect; + int ngot; + t_signal *signal; +}; + +struct t_sigoutconnect { + t_ugenbox *who; + int inno; + t_sigoutconnect *next; +}; + +struct t_sigoutlet { + int nconnect; + int nsent; + t_signal *signal; + t_sigoutconnect *connections; +}; + +struct t_dspcontext { + t_ugenbox *ugenlist; + t_dspcontext *parentcontext; + int ninlets; + int noutlets; + t_signal **iosigs; + float srate; + int vecsize; /* vector size, power of two */ + int calcsize; /* number of elements to calculate */ + char toplevel; /* true if "iosigs" is invalid. */ + char reblock; /* true if we have to reblock inlets/outlets */ + char switched; /* true if we're switched */ +}; + +static int ugen_sortno = 0; +static t_dspcontext *ugen_currentcontext; + +void ugen_stop() { + if (dsp_chain) { + free(dsp_chain); + dsp_chain = 0; + } + signal_cleanup(); +} + +void ugen_start() { + ugen_stop(); + ugen_sortno++; + dsp_chain = (t_int *)getbytes(sizeof(*dsp_chain)); + dsp_chain[0] = (t_int)dsp_done; + dsp_chainsize = 1; + if (ugen_currentcontext) bug("ugen_start"); +} + +int ugen_getsortno() {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->nextused) {} + post("used signals %d", count); + for (i = 0; i < MAXLOGSIG; i++) { + for (count = 0, sig = signal_freelist[i]; sig; count++, sig = sig->nextfree) {} + if (count) post("size %d: free %d", (1 << i), count); + } + for (count = 0, sig = signal_freeborrowed; sig; count++, sig = sig->nextfree) {} + post("free borrowed %d", count); + ugen_loud = argc; +} +#endif + +/* start building the graph for a canvas */ +extern "C" t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp, int ninlets, int noutlets) { + t_dspcontext *dc = (t_dspcontext *)getbytes(sizeof(*dc)); + if (ugen_loud) post("ugen_start_graph..."); + dc->ugenlist = 0; + dc->toplevel = toplevel; + dc->iosigs = sp; + dc->ninlets = ninlets; + dc->noutlets = noutlets; + dc->parentcontext = ugen_currentcontext; + ugen_currentcontext = dc; + return dc; +} + +/* first the canvas calls this to create all the boxes... */ +extern "C" void ugen_add(t_dspcontext *dc, t_object *obj) { + t_ugenbox *x = (t_ugenbox *)getbytes(sizeof *x); + int i; + t_sigoutlet *uout; + t_siginlet *uin; + x->next = dc->ugenlist; + dc->ugenlist = x; + x->obj = obj; + x->nin = obj_nsiginlets(obj); + x->in = (t_siginlet *)getbytes(x->nin * sizeof (*x->in)); + for (uin = x->in, i = x->nin; i--; uin++) uin->nconnect = 0; + x->nout = obj_nsigoutlets(obj); + x->out = (t_sigoutlet *)getbytes(x->nout * sizeof (*x->out)); + for (uout = x->out, i = x->nout; i--; uout++) uout->connections = 0, uout->nconnect = 0; +} + +/* and then this to make all the connections. */ +extern "C" void ugen_connect(t_dspcontext *dc, t_object *x1, int outno, t_object *x2, int inno) { + t_ugenbox *u1, *u2; + 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->ugenlist; u1 && u1->obj != x1; u1 = u1->next); + for (u2 = dc->ugenlist; u2 && u2->obj != x2; u2 = u2->next); + if (!u1 || !u2 || siginno < 0) { + pd_error(u1->obj, "signal outlet connect to nonsignal inlet (ignored)"); + return; + } + if (sigoutno < 0 || sigoutno >= u1->nout || siginno >= u2->nin) { + bug("ugen_connect %s %s %d %d (%d %d)", + class_getname(x1->ob_pd), class_getname(x2->ob_pd), sigoutno, siginno, u1->nout, u2->nin); + } + t_sigoutlet *uout = u1->out + sigoutno; + t_siginlet * uin = u2->in + siginno; + /* add a new connection to the outlet's list */ + t_sigoutconnect *oc = (t_sigoutconnect *)getbytes(sizeof *oc); + oc->next = uout->connections; + uout->connections = oc; + oc->who = u2; + oc->inno = siginno; + /* update inlet and outlet counts */ + uout->nconnect++; + uin->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=0; + for (t_ugenbox *u = dc->ugenlist; 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; + t_class *klass = pd_class(u->obj); + 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 = klass==canvas_class || klass==vinlet_class && !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 = klass==canvas_class || klass==voutlet_class && !(dc->reblock || dc->switched); + t_signal **insig, **outsig, **sig, *s1, *s2, *s3; + t_ugenbox *u2; + if (ugen_loud) post("doit %s %d %d", class_getname(klass), nofreesigs, nonewsigs); + for (i = 0, uin = u->in; i < u->nin; i++, uin++) { + if (!uin->nconnect) { + t_sample *scalar; + s3 = signal_new(dc->vecsize, dc->srate); + /* post("%s: unconnected signal inlet set to zero", class_getname(u->obj->ob_pd)); */ + if ((scalar = obj_findsignalscalar(u->obj, i))) dsp_add_scalarcopy(scalar, s3->v, s3->n); + else dsp_add_zero(s3->v, s3->n); + uin->signal = s3; + s3->refcount = 1; + } + } + insig = (t_signal **)getbytes((u->nin + u->nout) * sizeof(t_signal *)); + outsig = insig + u->nin; + for (sig = insig, uin = u->in, i = u->nin; i--; sig++, uin++) { + int newrefcount; + *sig = uin->signal; + newrefcount = --(*sig)->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)->refcount++; + else if (!newrefcount) signal_makereusable(*sig); + } + for (sig = outsig, uout = u->out, i = 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." */ + *sig = uout->signal = nonewsigs ? + signal_new(0, dc->srate) : + signal_new(dc->vecsize, dc->srate); + (*sig)->refcount = uout->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->obj, 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->out, i = u->nout; i--; sig++, uout++) { + if (!(*sig)->refcount) signal_makereusable(*sig); + } + if (ugen_loud) { + if (u->nin+u->nout==0) post("put %s %d", class_getname(u->obj->ob_pd), ugen_index(dc,u)); + else if (u->nin+u->nout==1) post("put %s %d (%lx)", class_getname(u->obj->ob_pd), ugen_index(dc,u),sig[0]); + else if (u->nin+u->nout==2) post("put %s %d (%lx %lx)", class_getname(u->obj->ob_pd), ugen_index(dc,u),sig[0],sig[1]); + else post("put %s %d (%lx %lx %lx ...)", class_getname(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->out, i = u->nout; i--; uout++) { + s1 = uout->signal; + for (oc = uout->connections; oc; oc = oc->next) { + u2 = oc->who; + uin = &u2->in[oc->inno]; + /* if there's already someone here, sum the two */ + s2 = uin->signal; + if (s2) { + s1->refcount--; + s2->refcount--; + if (!signal_compatible(s1, s2)) {pd_error(u->obj, "%s: incompatible signal inputs", class_getname(u->obj->ob_pd)); return;} + s3 = signal_newlike(s1); + dsp_add_plus(s1->v, s2->v, s3->v, s1->n); + uin->signal = s3; + s3->refcount = 1; + if (!s1->refcount) signal_makereusable(s1); + if (!s2->refcount) signal_makereusable(s2); + } else uin->signal = s1; + uin->ngot++; + if (uin->ngot < uin->nconnect) goto notyet; + if (u2->nin > 1) for (uin = u2->in, n = u2->nin; n--; uin++) if (uin->ngot < uin->nconnect) goto notyet; + ugen_doit(dc, u2); + notyet: ; + } + } + free(insig); + u->done = 1; +} + +/* once the DSP graph is built, we call this routine to sort it. This routine also deletes the graph; later we might + want to leave the graph around, in case the user is editing the DSP network, to save having to recreate it all the + time. But not today. */ +extern "C" void ugen_done_graph(t_dspcontext *dc) { + t_sigoutlet *uout; + t_siginlet *uin; + int i, n; + t_block *blk; + int period, frequency, phase, vecsize, calcsize; + float srate; + int reblock = 0, switched; + int downsample = 1, upsample = 1; /* IOhannes */ + /* debugging printout */ + if (ugen_loud) { + post("ugen_done_graph..."); + for (t_ugenbox *u = dc->ugenlist; u; u = u->next) { + post("ugen: %s", class_getname(u->obj->ob_pd)); + for (uout = u->out, i = 0; i < u->nout; uout++, i++) + for (t_sigoutconnect *oc = uout->connections; oc; oc = oc->next) { + post("... out %d to %s, index %d, inlet %d", i, class_getname(oc->who->obj->ob_pd), ugen_index(dc, oc->who), oc->inno); + } + } + } + /* search for an object of class "block~" */ + blk = 0; + for (t_ugenbox *u = dc->ugenlist; u; u = u->next) { + t_pd *zz = u->obj; + if (pd_class(zz) == block_class) { + if (blk) pd_error(blk, "conflicting block~ objects in same page"); + else blk = (t_block *)zz; + } + } + t_dspcontext *parent_context = dc->parentcontext; + float parent_srate = parent_context ? parent_context->srate : sys_getsr(); + int parent_vecsize = parent_context ? parent_context->vecsize : sys_getblksize(); + if (blk) { + int realoverlap; + vecsize = blk->vecsize; if (!vecsize) vecsize = parent_vecsize; + calcsize = blk->calcsize;if (!calcsize) calcsize = vecsize; + realoverlap = blk->overlap; if (realoverlap > vecsize) realoverlap = vecsize; + /* IOhannes { */ + downsample = blk->downsample; + upsample = blk->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->phase; + srate = parent_srate * realoverlap * upsample / downsample; + /* IOhannes */ + if (period < 1) period = 1; + if (frequency < 1) frequency = 1; + blk->frequency = frequency; + blk->period = period; + blk->phase = dsp_phase & (period - 1); + if (!parent_context || realoverlap!=1 || vecsize!=parent_vecsize || downsample!=1 || upsample!=1) /* IOhannes */ + reblock = 1; + switched = blk->switched; + } else { + srate = parent_srate; + vecsize = parent_vecsize; + calcsize = parent_context ? parent_context->calcsize : vecsize; + downsample = upsample = 1;/* IOhannes */ + period = frequency = 1; + phase = 0; + if (!parent_context) reblock = 1; + switched = 0; + } + dc->reblock = reblock; + dc->switched = switched; + dc->srate = srate; + dc->vecsize = vecsize; + dc->calcsize = calcsize; + /* if we're reblocking or switched, we now have to create output signals to fill in for the "borrowed" ones we + have now. This is also possibly true even if we're not blocked/switched, in the case that there was a + signal loop. But we don't know this yet. */ + if (dc->iosigs && (switched || reblock)) { + t_signal **sigp; + for (i = 0, sigp = dc->iosigs + dc->ninlets; i < dc->noutlets; i++, sigp++) { + if ((*sigp)->isborrowed && !(*sigp)->borrowedfrom) { + signal_setborrowed(*sigp, signal_new(parent_vecsize, parent_srate)); + (*sigp)->refcount++; + if (ugen_loud) post("set %lx->%lx", *sigp, (*sigp)->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 (t_ugenbox *u = dc->ugenlist; u; u = u->next) { + t_pd *zz = u->obj; + t_signal **outsigs = dc->iosigs; + if (outsigs) outsigs += dc->ninlets; + if (pd_class(zz) == vinlet_class) + vinlet_dspprolog((struct _vinlet *)zz, dc->iosigs, vecsize, calcsize, dsp_phase, period, frequency, + downsample, upsample, reblock, switched); + else if (pd_class(zz) == voutlet_class) + voutlet_dspprolog((struct _voutlet *)zz, outsigs, vecsize, calcsize, dsp_phase, period, frequency, + downsample, upsample, reblock, switched); + } + int chainblockbegin = dsp_chainsize; /* DSP chain onset before block prolog code */ + if (blk && (reblock || switched)) { /* add the block DSP prolog */ + dsp_add(block_prolog, 1, blk); + blk->chainonset = dsp_chainsize - 1; + } + /* Initialize for sorting */ + for (t_ugenbox *u = dc->ugenlist; u; u = u->next) { + u->done = 0; + for (uout = u->out, i = u->nout; i--; uout++) uout->nsent = 0; + for (uin = u->in, i = u->nin; i--; uin++) uin->ngot = 0, uin->signal = 0; + } + /* Do the sort */ + for (t_ugenbox *u = dc->ugenlist; u; u = u->next) { + /* check that we have no connected signal inlets */ + if (u->done) continue; + for (uin = u->in, i = u->nin; i--; uin++) + if (uin->nconnect) goto next; + ugen_doit(dc, u); + next: ; + } + /* check for a DSP loop, which is evidenced here by the presence of ugens not yet scheduled. */ + for (t_ugenbox *u = dc->ugenlist; u; u = u->next) if (!u->done) { + t_signal **sigp; + pd_error(u->obj, "DSP loop detected (some tilde objects not scheduled)"); + /* this might imply that we have unfilled "borrowed" outputs which we'd better fill in now. */ + for (i = 0, sigp = dc->iosigs + dc->ninlets; i < dc->noutlets; i++, sigp++) { + if ((*sigp)->isborrowed && !(*sigp)->borrowedfrom) { + t_signal *s3 = signal_new(parent_vecsize, parent_srate); + signal_setborrowed(*sigp, s3); + (*sigp)->refcount++; + dsp_add_zero(s3->v, s3->n); + if (ugen_loud) post("oops, belatedly set %lx->%lx", *sigp, (*sigp)->borrowedfrom); + } + } + break; /* don't need to keep looking. */ + } + /* add block DSP epilog */ + if (blk && (reblock || switched)) dsp_add(block_epilog, 1, blk); + int chainblockend = dsp_chainsize; /* and after block epilog code */ + /* add epilogs for outlets. */ + for (t_ugenbox *u = dc->ugenlist; u; u = u->next) { + t_pd *zz = u->obj; + if (pd_class(zz) == voutlet_class) { + t_signal **iosigs = dc->iosigs; + if (iosigs) iosigs += dc->ninlets; + voutlet_dspepilog((struct _voutlet *)zz, iosigs, vecsize, calcsize, dsp_phase, period, frequency, + downsample, upsample, reblock, switched); + } + } + int chainafterall = dsp_chainsize; /* and after signal outlet epilog */ + if (blk) { + blk->blocklength = chainblockend - chainblockbegin; + blk->epiloglength = chainafterall - chainblockend; + blk->reblock = reblock; + } + if (ugen_loud) { + t_int *ip; + if (!dc->parentcontext) + for (i = dsp_chainsize, ip = dsp_chain; i--; ip++) post("chain %lx", *ip); + post("... ugen_done_graph done."); + } + /* now delete everything. */ + while (dc->ugenlist) { + for (uout = dc->ugenlist->out, n = dc->ugenlist->nout; n--; uout++) { + t_sigoutconnect *oc = uout->connections, *oc2; + while (oc) { + oc2 = oc->next; + free(oc); + oc = oc2; + } + } + free(dc->ugenlist->out); + free(dc->ugenlist->in); + t_ugenbox *u = dc->ugenlist; + dc->ugenlist = u->next; + free(u); + } + if (ugen_currentcontext == dc) ugen_currentcontext = dc->parentcontext; + else bug("ugen_currentcontext"); + free(dc); +} + +t_signal *ugen_getiosig(int index, int inout) { + if (!ugen_currentcontext) bug("ugen_getiosig"); + if (ugen_currentcontext->toplevel) return 0; + if (inout) index += ugen_currentcontext->ninlets; + return ugen_currentcontext->iosigs[index]; +} + +/* resampling code originally by Johannes Zmlnig in 2001 */ +/* also "block-resampling" added by Johannes in 2004.09 */ +/* --------------------- up/down-sampling --------------------- */ +/* LATER: add some downsampling-filters for HOLD and LINEAR */ + +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 *downsampling_perform_block(t_int *w) { + /* the downsampled vector is exactly the first part of the parent vector + * the rest of the parent is just skipped + * cool for FFT-data, where you only want to process the significant (1st) part of the vector + */ + 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++; + 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; + t_float *dum_out = out; + t_float *dum_in = in; + while (i--) { + int 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 */ + const int up = int(w[4]); /* upsampling factor */ + const int parent = int(w[5]); /* original vectorsize */ + const int length = parent*up; + t_float *fp; + t_float a=*x->buffer, b=*in; + const t_float up_inv = (t_float)1.0/up; + t_float findex = 0.f; + for (int n=0; n<length; n++) { + const int index = int(findex+=up_inv); + t_float frac=findex-index; + if(frac==0.)frac=1.; + *out++ = frac * b + (1.-frac) * a; + fp=in+index; + b=*fp; + // do we still need the last sample of the previous pointer for interpolation ? + a=(index)?*(fp-1):a; + } + *x->buffer = a; + return w+6; +} + +t_int *upsampling_perform_block(t_int *w) { + /* 1st part of the upsampled signal-vector will be the original one + * 2nd part of the upsampled signal-vector is just 0 + * cool for FFT-data, where you only want to process the significant (1st) part of the vector */ + 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=parent; + int n=parent*(up-1); + while (i--) *out++=*in++; + while (n--) *out++=0.f; + return w+5; +} + +/* ----------------------- public -------------------------------- */ +/* utils */ + +void resample_init(t_resample *x) { + x->method=0; + x->downsample=x->upsample=1; + x->n = x->coefsize = x->bufsize = 0; + x->v = x->coeffs = x->buffer = 0; +} +void resample_free(t_resample *x) { + if (x->n) free(x->v); + if (x->coefsize) free(x->coeffs); + if (x->bufsize) free(x->buffer); + x->n = x->coefsize = x->bufsize = 0; + x->v = x->coeffs = x->buffer = 0; +} +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) { + case RESAMPLE_BLOCK: dsp_add(downsampling_perform_block, 4, in, out, insize/outsize, insize); break; + 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 RESAMPLE_HOLD: dsp_add(upsampling_perform_hold, 4, in, out, outsize/insize, insize); break; + case RESAMPLE_LINEAR: + if (x->bufsize != 1) { + free(x->buffer); + x->bufsize = 1; + x->buffer = (t_float *)t_getbytes(x->bufsize*sizeof(*x->buffer)); + } + dsp_add(upsampling_perform_linear, 5, x, in, out, outsize/insize, insize); + break; + case RESAMPLE_BLOCK: dsp_add(upsampling_perform_block, 4, 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) { free(x->v); x->n = 0; x->v = in; return;} + if (x->n != outsize) { + free(x->v); + x->v = (t_float *)t_getbytes(outsize * sizeof(*x->v)); + x->n = outsize; + } + resample_dsp(x, in, insize, x->v, x->n, method); +} +void resampleto_dsp(t_resample *x, t_sample *out, int insize, int outsize, int method) { + if (insize==outsize) {if (x->n) free(x->v); x->n = 0; x->v = out; return;} + if (x->n != insize) { + free(x->v); + x->v = (t_float *)t_getbytes(insize * sizeof(*x->v)); + x->n = insize; + } + resample_dsp(x, x->v, x->n, out, outsize, method); +} + +/* ------------------------ samplerate~ -------------------------- */ + +static t_class *samplerate_tilde_class; +struct t_samplerate : t_object { + t_canvas *canvas; +}; +extern "C" void *canvas_getblock(t_class *blockclass, t_canvas **canvasp); +static void samplerate_tilde_bang(t_samplerate *x) { + float srate = sys_getsr(); + t_canvas *canvas = x->canvas; + while (canvas) { + t_block *b = (t_block *)canvas_getblock(block_class, &canvas); + if (b) srate *= float(b->upsample) / float(b->downsample); + } + outlet_float(x->ob_outlet, srate); +} +static void *samplerate_tilde_new(t_symbol *s) { + t_samplerate *x = (t_samplerate *)pd_new(samplerate_tilde_class); + outlet_new(x,&s_float); + x->canvas = canvas_getcurrent(); + return x; +} +static void samplerate_tilde_setup() { + samplerate_tilde_class = class_new2("samplerate~",samplerate_tilde_new,0,sizeof(t_samplerate),0,""); + class_addbang(samplerate_tilde_class, samplerate_tilde_bang); +} + +/* -------------------- setup routine -------------------------- */ + +void d_ugen_setup () { + block_tilde_setup(); + samplerate_tilde_setup(); +} diff --git a/desiredata/src/defaults.ddrc b/desiredata/src/defaults.ddrc new file mode 100644 index 00000000..fdee7d78 --- /dev/null +++ b/desiredata/src/defaults.ddrc @@ -0,0 +1,193 @@ +look { + TextBox { + bgedit #ffffff + } + View { + language english + } + Client { + console 666 + } + Box { + extrapix 1 + } + Canvas { + pointer_sense 5 + bgedit #dddddd + bgrun #ffffff + grid #ffffff + buttonbar 1 + compbg #ffffff + compfg #000000 + compselectbg #000000 + compselectfg #ffffff + crosshair #ff14ff + hairsnap 1 + hairstate 1 + gridstate 1 + grid_size 20 + snap_grid 1 + showcomp 10 + statusbar 1 + menubar 1 + scrollbar 0 + } + Comment { + bg #cccccc + fg #000000 + frame1 #ffffff + frame2 #888888 + frame3 #cccccc + } + FutureWire { + dash #ee0012 + } + SelRect { + rect #ff12ff + } + Slider { + bg #ccebff + } + TextBox { + bgedit {#ffffff} + fg {#000000} + } + View { + bg #ffffff + fg #000000 + font {Courier -12} + frame1 #99cccc + frame2 #668888 + frame3 #000000 + selectframe #0080ff + tooltip 1 + } + Box { + iowidth 7 + minobjwidth 21 + inletfg #000000 + outletfg #000000 + iopos -1 + } + Wire { + dspfg #890098 + fg #888888 + fg2 #ee0000 + thick 1 + wirearrow 1 + } + KeyboardDialog { + font {Courier -10} + } + Console { + font {Courier -10} + } +} +key { + Canvas { + Array {} + bng Alt+b + cnv Alt+c + Comment Ctrl+5 + Graph {} + hradio Alt+i + hsl Alt+h + Message Ctrl+2 + Number Ctrl+3 + nbx Alt+n + Object Ctrl+1 + Pdwindow {} + Symbol Ctrl+4 + tgl Alt+t + vu Alt+u + vradio Alt+d + vsl Alt+v + about {} + audio_off Ctrl+period + audio_on Ctrl+slash + class_browser {} + close Ctrl+w + copy Ctrl+c + crosshair {} + cut Ctrl+x + decr_scale Ctrl+UNDERSCORE + decr_zoom Ctrl+minus + dropper Alt+y + duplicate Ctrl+d + editmodeswitch Ctrl+e + find Ctrl+f + find_again Ctrl+g + find_last_error {} + font_bomb Ctrl+F + incr_scale Ctrl+PLUS + incr_zoom Ctrl+equal + key_nav_down Ctrl+down + key_nav_down_shift Ctrl+DOWN + key_nav_ioselect Ctrl+tab + key_nav_left Ctrl+left + key_nav_left_shift Ctrl+LEFT + key_nav_right Ctrl+right + key_nav_right_shift Ctrl+RIGHT + key_nav_up Ctrl+up + key_nav_up_shift Ctrl+UP + latency_meter {} + load_meter {} + new_file Ctrl+n + open_file Ctrl+o + parentwindow {} + paste Ctrl+v + paths {} + popup_help {} + popup_open {} + popup_properties {} + print Ctrl+p + quit Ctrl+q + redo Ctrl+Z + redraw {} + reload Ctrl+r + save Ctrl+s + save_as Ctrl+S + select_all Ctrl+a + text_editor Ctrl+t + tidy_up {} + undo Ctrl+z + insert_object Ctrl+i + chain_object Ctrl+6 + clear_wires Ctrl+k + auto_wire Ctrl+j + subpatcherize Alt+s + clear_selection Ctrl+A + auto_test Ctrl+grave + runcommand Alt+x + keyprefix Ctrl+QUESTION + macro_toggle Alt+m + macro_play Ctrl+m + macro_copy Ctrl+M + id_toggle Ctrl+semicolon + } + Client { + Canvas Alt+c + about {} + audio_off Ctrl+period + audio_on Ctrl+slash + audio_settings {} + class_browser {} + client_class_tree Ctrl+grave + client_prefs Ctrl+l + find Ctrl+f + find_again Ctrl+g + find_last_error {} + font_bomb {} + latency_meter {} + load_meter {} + midi_settings {} + new_file Ctrl+n + open_file Ctrl+o + paths {} + server_prefs Ctrl+p + quit Ctrl+q + send_message Ctrl+m + test_audio_and_midi {} + text_editor Ctrl+t + } +} diff --git a/desiredata/src/desire.c b/desiredata/src/desire.c new file mode 100644 index 00000000..cc04ad5e --- /dev/null +++ b/desiredata/src/desire.c @@ -0,0 +1,7332 @@ +/* $Id: desire.c,v 1.1.2.217.2.235 2007-08-21 19:50:25 matju Exp $ + + This file is part of DesireData. + Copyright (c) 2004-2007 by Mathieu Bouchard. + Portions Copyright (c) 1997-2005 Miller Puckette, Günter Geiger, Krzysztof Czaja, + Johannes Zmoelnig, Thomas Musil, Joseph Sarlo, etc. + The remains of IEMGUI Copyright (c) 2000-2001 Thomas Musil (IEM KUG Graz Austria) + For information on usage and redistribution, and for a DISCLAIMER OF ALL + WARRANTIES, see the file, "LICENSE.txt," in this distribution. +*/ + +#define PD_PLUSPLUS_FACE +#include "m_pd.h" +#include "desire.h" +#include "s_stuff.h" +#include <ctype.h> +#include <math.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "m_simd.h" +#include <errno.h> +#include <sys/time.h> +#include <sstream> +#include <map> + +#ifdef MSW +#include <io.h> +#define snprintf _snprintf +#else +#include <unistd.h> +#endif + +/* +#define sys_vgui(args...) do { \ + fprintf(stderr,"\e[0;1;31m"); \ + L fprintf(stderr,args); \ + fprintf(stderr,"\e[0m"); \ + sys_vgui(args); } while(0) +*/ + +#define foreach(ITER,COLL) for(typeof(COLL.begin()) ITER = COLL.begin(); ITER != (COLL).end(); ITER++) + +#define boxes_each(CHILD,BOXES) for(t_gobj *CHILD=(BOXES)->first(); CHILD; CHILD=CHILD->next()) +#define canvas_each(CHILD,CANVAS) for(t_gobj *CHILD=(CANVAS)->boxes->first(); CHILD; CHILD=CHILD->next()) +#define canvas_wires_each(WIRE,TRAV,CANVAS) \ + for (t_outconnect *WIRE=(t_outconnect *)666; WIRE==(t_outconnect *)666; ) \ + for (t_linetraverser TRAV(CANVAS); (WIRE=linetraverser_next(&TRAV)); ) + +#undef SET +#define SET(attr,value) do {gobj_changed(x,#attr); x->attr = (value);} while(0) + +#define a_float a_w.w_float +#define a_symbol a_w.w_symbol + +#define CLAMP(_var,_min,_max) do { if (_var<_min) _var=_min; else if (_var>_max) _var=_max; } while(0) +#define IS_A_FLOAT(atom,index) ((atom+index)->a_type == A_FLOAT) +#define IS_A_SYMBOL(atom,index) ((atom+index)->a_type == A_SYMBOL) + +int imin(int a, int b) {return a<b?a:b;} +int imax(int a, int b) {return a>b?a:b;} +t_symbol *s_empty, *s_pd, *s_Pd; + +std::ostream &operator << (std::ostream &str, t_pd *self) { + t_binbuf *b; + if (self->_class->patchable && (b = ((t_text *)self)->binbuf)) { + char *buf; int bufn; + binbuf_gettext(b,&buf,&bufn); + str << "[" << buf << "]"; + free(buf); + } else str << "<" << self->_class->name->name << ":" << std::hex << (long)self << ">"; + return str; +} + +//-------------------------------------------------------------------------- + +t_class *boxes_class; + +struct t_boxes : t_gobj { + typedef std::map<int,t_gobj *> M; + typedef std::pair<int,t_gobj *> KV; +private: + std::map<int,t_gobj *> map; +public: + void invariant () {size();} + t_boxes() {} + size_t size() { + size_t n=0; + boxes_each(g,this) n++; + if (map.size()!=n) post("map size=%d list size=%d",map.size(),n); + return n; + } + t_gobj *first() {return map.begin() != map.end() ? map.begin()->second : 0;} + t_gobj *last() {M::iterator iter = map.end(); iter--; return iter->second;} + t_gobj *next(t_gobj *x) { + M::iterator iter = map.begin(); + while (iter->second != x) iter++; + iter++; + return iter == map.end() ? 0 : iter->second; + } + void add(t_gobj *x) {map.insert(KV(x->dix->index,x)); invariant();} + void remove(int i) {map.erase(i); invariant();} + void remove_by_value(t_gobj *x) {map.erase(x->dix->index); invariant();} +}; + +t_boxes *boxes_new() { + t_boxes *self = (t_boxes *)pd_new(boxes_class); + new(self) t_boxes; + return self; +} + +void boxes_notice(t_boxes *self, t_gobj *origin, int argc, t_atom *argv) { +} + +void boxes_free(t_boxes *self) {self->~t_boxes();} + +t_gobj *_gobj::next() {return dix->canvas->boxes->next(this);} + +//-------------------------------------------------------------------------- + +t_class *gop_filtre_class; + +struct t_gop_filtre : t_gobj { + t_canvas *canvas; +}; + +// test for half-open interval membership +bool inside (int x, int x0, int x1) {return x0<=x && x<x1;} + +/* this method is always called when a canvas is in gop mode so we don't check for this */ +/* it doesn't filtre out all that needs to be filtred out, but does not filtre out anything that has to stay */ +void gop_filtre_notice(t_gop_filtre *self,t_gobj *origin, int argc, t_atom *argv) { + /* here, assume that the canvas *is* a gop; else we wouldn't be in this method. */ + t_object *o = (t_object *)origin; + t_canvas *c = self->canvas; + if (0/* check for messagebox, comment, but you can't check for objectboxes in general */) return; + if (c->goprect) { + if (!inside(o->x, c->xmargin, c->xmargin + c->pixwidth )) return; + if (!inside(o->y, c->ymargin, c->ymargin + c->pixheight)) return; + } + gobj_changed3(self,origin,argc,argv); +} + +t_gobj *gop_filtre_new(t_canvas *canvas) { + t_gop_filtre *self = (t_gop_filtre *)pd_new(gop_filtre_class); + new(self) t_gop_filtre; + self->canvas = canvas; + gobj_subscribe(canvas->boxes,self); + return self; +} + +void gop_filtre_free(t_boxes *self) {} + +//-------------------------------------------------------------------------- +// t_appendix: an extension to t_gobj made by matju so that all t_gobj's may have new fields +// without sacrificing binary compat with externals compiled for PureMSP. + +typedef t_hash<t_symbol *, t_arglist *> t_visual; + +t_appendix *appendix_new (t_gobj *master) { + //fprintf(stderr,"appendix_new %p\n",master); + t_appendix *self = (t_appendix *)malloc(sizeof(t_appendix)); + self->canvas = 0; + self->nobs = 0; + self->obs = 0; + self->visual = new t_visual(1); + self->index = 0; + self->elapsed = 0; + return self; +} + +void appendix_save (t_gobj *master, t_binbuf *b) { + t_visual *h = master->dix->visual; + //fprintf(stderr,"appendix_save %p size=%ld\n",master,hash_size(h)); + if (!h->size()) return; + t_symbol *k; + t_arglist *v; + int i=0; + binbuf_addv(b,"t","#V"); + hash_foreach(k,v,h) { + t_arglist *al = (t_arglist *)v; + binbuf_addv(b,"s",k); + binbuf_add(b,al->c,al->v); + if (size_t(i+1)==h->size()) binbuf_addv(b, ";"); else binbuf_addv(b,"t",","); + i++; + } +} + +void appendix_free (t_gobj *master) { + t_appendix *self = master->dix; + t_symbol *k; + t_arglist *v; + if (self->visual) { + hash_foreach(k,v,self->visual) free(v); + delete self->visual; + } + free(self); +} + +/* subscribing N spies takes N*N time, but it's not important for now */ +/* subscription could become just a special use of t_outlet in the future, or sometimes become implied. */ +/* perhaps use [bindelem] here? */ +void gobj_subscribe(t_gobj *self, t_gobj *observer) { + t_appendix *d = self->dix; + for (size_t i=0; i<d->nobs; i++) if (d->obs[i]) return; + d->obs=(t_gobj **)realloc(d->obs,sizeof(t_gobj *)*(1+d->nobs)); + d->obs[d->nobs++] = observer; + t_onsubscribe ons = self->_class->onsubscribe; + ons(self,observer); + //post("x%p has %d observers",self,(int)d->nobs); +} + +void gobj_unsubscribe (t_gobj *self, t_gobj *observer) { + t_appendix *d = self->dix; + size_t i; + for (i=0; i<d->nobs; i++) if (d->obs[i]) break; + if (i==d->nobs) return; + d->nobs--; + for (; i<d->nobs; i++) d->obs[i] = d->obs[i+1]; + // should have something like onunsubscribe too, to handle delete?... or just use onsubscribe differently. +} + +void gobj_setcanvas (t_gobj *self, t_canvas *c) { + if (self->dix->canvas == c) return; + if (self->dix->canvas) gobj_unsubscribe(self,self->dix->canvas); + self->dix->canvas = c; + if (self->dix->canvas) gobj_subscribe(self,self->dix->canvas); +} + +/* for future use */ +t_canvas *gobj_canvas (t_gobj *self) {return self->dix->canvas;} + +// if !k then suppose all of the object might have changed. +void gobj_changed (t_gobj *self, const char *k) { + int dirty = k ? (1<<class_getfieldindex(self->_class,k)) : -1; + t_atom argv[1]; + SETFLOAT(argv,(float)dirty); + gobj_changed3(self,self,1,argv); +} +//#define gobj_changed(SELF,K) do {L; gobj_changed(SELF,K);} while(0) + +// if only a float is sent, it's a bitset of at most 25 elements +// else it may mean whatever else... +void gobj_changed2 (t_gobj *self, int argc, t_atom *argv) { + gobj_changed3(self,self,argc,argv); +} + +void gobj_changed3 (t_gobj *self, t_gobj *origin, int argc, t_atom *argv) { + t_appendix *d = self->dix; + std::ostringstream s; + for (int i=0; i<argc; i++) s << " " << &argv[i]; + //fprintf(stderr,"gobj_changed3 self=%lx origin=%lx args=%s\n",(long)self,(long)origin,s.str().data()); + /* TRACE THE DIFFERENTIAL UPLOAD REQUESTS */ + //std::cerr << "gobj_changed3 self=" << self << " origin=" << origin << " args={" << s.str().data()+(!!argc) << "}\n"; + if (!d) {post("gobj_changed3: no appendix in %p",self); return;} + for (size_t i=0; i<d->nobs; i++) { + t_gobj *obs = d->obs[i]; + t_notice ice = obs->_class->notice; + if (ice) ice(obs,origin,argc,argv); + else post("null func ptr for class %s",self->_class->name->name); + } +} + +//-------------------------------------------------------------------------- +// some simple ringbuffer-style queue +// when this becomes too small, use a bigger constant or a better impl. + +#define QUEUE_SIZE 16384 +struct t_queue { + int start,len; + t_pd *o[QUEUE_SIZE]; +}; + +t_queue *queue_new () { + t_queue *self = (t_queue *)getbytes(sizeof(t_queue)); + self->start = self->len = 0; + return self; +} + +static bool debug_queue=0; + +static void pd_print (t_pd *self, char *header) { + if (self->_class->gobj && (object_table->get(self)&1)==0) {printf("%s %p dead\n",header,self); return;} + if (self->_class->patchable) { + t_binbuf *b = ((t_text *)self)->binbuf; + if (b) { + char *buf; int bufn; + binbuf_gettext(b,&buf,&bufn); + printf("%s %p [%.*s]\n",header,self,bufn,buf); + return; + } + } + printf("%s %p (%s)\n",header,self,self->_class->name->name); +} + +void queue_put (t_queue *self, t_pd *stuff) { + if (debug_queue) pd_print(stuff,"queue_put"); + if (self->len==QUEUE_SIZE) {bug("queue full"); return;} + self->o[(self->start+self->len)%QUEUE_SIZE] = stuff; + self->len++; + if (debug_queue) post("queue_put: items in queue: %d",self->len); +} + +void *queue_get (t_queue *self) { + t_pd *stuff = self->o[self->start]; + self->start = (self->start+1)%QUEUE_SIZE; + self->len--; + if (debug_queue) {post("queue_get: items in queue: %d",self->len); pd_print(stuff,"queue_get");} + return stuff; +} + +#define queue_each(i,self) \ + for (int i=self->start; i!=(self->start+self->len)%QUEUE_SIZE; i=(i+1)%QUEUE_SIZE) + +void queue_free (t_queue *self) { + abort(); + free(self); +} + +int queue_empty (t_queue *self) {return self->len==0;} +int queue_full (t_queue *self) {return self->len==QUEUE_SIZE;} + +//-------------------------------------------------------------------------- +// reply system: + +struct t_reply : t_gobj { + short serial; + void *answer; +}; + +t_class *reply_class; + +static t_reply *reply_new (short serial, void *answer) { + t_reply *self = (t_reply *)pd_new(reply_class); + self->dix = appendix_new(self); + self->serial = serial; + self->answer = answer; + return self; +} + +static void reply_send (t_reply *self) { + sys_vgui("serial %ld x%lx\n",(long)self->serial,(long)self->answer); +} + +static void reply_free (t_reply *self) {} + +//-------------------------------------------------------------------------- +// update manager: + +struct t_dirtyentry { + long fields; + size_t start,end; + t_dirtyentry() {} +}; + +typedef t_hash <t_gobj *, t_dirtyentry *> t_dirtyset; + +struct t_manager : t_text { + t_queue *q; + t_clock *clock; + t_binbuf *b; /* reusable, for processing messages from the gui */ + t_dirtyset *dirty; +}; + +static t_class *manager_class; +t_manager *manager; + +void manager_call (void *foo) { + t_manager *self = (t_manager *)foo; + while (!queue_empty(self->q)) { + t_gobj *o = (t_gobj *)queue_get(self->q); + if (!o) continue; /* cancelled notice */ + //fprintf(stderr,"manager_call, o->_class=%s\n",o->_class->c_name->name); + if (o->_class == reply_class) { + reply_send((t_reply *)o); + pd_free(o); + } else { + if (self->dirty->exists(o)) { + pd_upload(o); + self->dirty->del(o); + } + } + } + clock_delay(self->clock,50); +} + +void manager_notice (t_gobj *self_, t_gobj *origin, int argc, t_atom *argv) { + t_manager *self = (t_manager *)self_; + if (!self->dirty->exists(origin)) { + //std::cerr << "manager_notice:"; + //for (int i=0; i<argc; i++) std::cerr << " " << &argv[i]; + //std::cerr << "\n"; + queue_put(self->q,origin); + self->dirty->set(origin,0); + } +} + +t_manager *manager_new (t_symbol *s, int argc, t_atom *argv) { + t_manager *self = (t_manager *)pd_new(manager_class); + self->q = queue_new(); + self->clock = clock_new(self,(t_method)manager_call); + self->b = binbuf_new(); + clock_delay(self->clock,0); + self->dirty = new t_dirtyset(127); + return self; +} + +void manager_free (t_manager *self) { + clock_free(self->clock); + queue_free(self->q); + binbuf_free(self->b); + pd_free(self); +} + +extern "C" void manager_anything (t_manager *self, t_symbol *s, int argc, t_atom *argv) { + binbuf_clear(self->b); + binbuf_addv(self->b,"s",s); + binbuf_add(self->b,argc,argv); + binbuf_eval(self->b,0,0,0); +} + +//-------------------------------------------------------------------------- +/* +IOhannes changed the canvas_restore, so that it might accept $args as well +(like "pd $0_test") so you can make multiple & distinguishable templates. +*/ + +#define CANVAS_DEFCANVASWIDTH 450 +#define CANVAS_DEFCANVASHEIGHT 300 + +#ifdef __APPLE__ +#define CANVAS_DEFCANVASYLOC 22 +#else +#define CANVAS_DEFCANVASYLOC 0 +#endif + +extern short next_object; +extern t_pd *newest; +t_class *canvas_class; +int canvas_dspstate; /* whether DSP is on or off */ +t_canvas *canvas_whichfind; /* last canvas we did a find in */ +std::map<t_canvas *,int> windowed_canvases; /* where int is dummy */ +static void canvas_setbounds(t_canvas *x, t_floatarg x1, t_floatarg y1, t_floatarg x2, t_floatarg y2); +static t_symbol *canvas_newfilename = &s_; +static t_symbol *canvas_newdirectory = &s_; +static int canvas_newargc; +static t_atom *canvas_newargv; + +/* add a canvas the list of "root" canvases (toplevels without parents.) */ +/* should those two functions still exist? */ +static void canvas_addtolist(t_canvas *x) { + windowed_canvases.insert(std::pair<t_canvas *,int>(x,42)); + if (x->havewindow) gobj_subscribe(x,manager); +} +static void canvas_takeofflist(t_canvas *x) {windowed_canvases.erase(x);} + +/* 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(). */ +void canvas_setargs(int argc, t_atom *argv) { + if (canvas_newargv) free(canvas_newargv); + canvas_newargc = argc; + canvas_newargv = (t_atom *)copybytes(argv, argc * sizeof(t_atom)); +} + +void glob_setfilename(void *self, t_symbol *filesym, t_symbol *dirsym) { + canvas_newfilename = filesym; + canvas_newdirectory = dirsym; +} + +t_canvas *canvas_getcurrent () {return ((t_canvas *)pd_findbyclass(&s__X, canvas_class));} + +t_canvasenvironment *canvas_getenv(t_canvas *x) { + while (!x->env) x = x->dix->canvas; + return x->env; +} + +int canvas_getdollarzero () { + t_canvas *x = canvas_getcurrent(); + t_canvasenvironment *env = x ? canvas_getenv(x) : 0; + return env ? env->dollarzero : 0; +} + +t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s) { + if (strchr(s->name,'$')) { + t_canvasenvironment *env = canvas_getenv(x); + pd_pushsym(x); + t_symbol *ret = binbuf_realizedollsym(s, env->argc, env->argv, 1); + pd_popsym(x); + return ret; + } + return s; +} + +t_symbol *canvas_getcurrentdir () {return canvas_getenv(canvas_getcurrent())->dir;} +t_symbol *canvas_getdir(t_canvas *x) {return canvas_getenv( x)->dir;} + +char *canvas_makefilename(t_canvas *x, char *file, char *result, int resultsize) { + char *dir = canvas_getenv(x)->dir->name; + if (file[0] == '/' || (file[0] && file[1] == ':') || !*dir) { + if (!result) return strdup(file); + strncpy(result, file, resultsize); + result[resultsize-1] = 0; + } else { + if (result) {snprintf(result,resultsize,"%s/%s",dir,file); result[resultsize-1] = 0;} + else asprintf(&result, "%s/%s",dir,file); + } + return result; +} + +static void canvas_rename(t_canvas *x, t_symbol *s, t_symbol *dir) { + t_symbol *bs = canvas_makebindsym(x->name); + if (x->name!=s_Pd) pd_unbind(x, bs); + SET(name,s); + if (x->name!=s_Pd) pd_bind(x, bs); + if (dir && dir != &s_) { + canvas_getenv(x)->dir = dir; + gobj_changed(x,"dir"); + } +} + +/* --------------- traversing the set of lines in a canvas ----------- */ + +t_linetraverser::t_linetraverser(t_canvas *canvas) {linetraverser_start(this,canvas);} + +void linetraverser_start(t_linetraverser *t, t_canvas *x) { + t->from = 0; + t->canvas = x; + t->next = 0; + t->nextoutno = t->nout = 0; +} + +t_outconnect *linetraverser_next(t_linetraverser *t) { + t_outconnect *rval = t->next; + while (!rval) { + int outno = t->nextoutno; + while (outno == t->nout) { + t_object *ob = 0; + t_gobj *y = t->from ? t->from->next() : t->canvas->boxes->first(); + for (; y; y = y->next()) if ((ob = pd_checkobject(y))) break; + if (!ob) return 0; + t->from = ob; + t->nout = obj_noutlets(ob); + outno = 0; + } + t->nextoutno = outno + 1; + rval = obj_starttraverseoutlet(t->from, &t->outletp, outno); + t->outlet = outno; + } + t->next = obj_nexttraverseoutlet(rval, &t->to, &t->inletp, &t->inlet); + t->nin = obj_ninlets(t->to); + if (!t->nin) bug("linetraverser_next"); + return rval; +} + +/* -------------------- the canvas object -------------------------- */ +static int hack = 1; + +static t_canvas *canvas_new2() { + t_canvas *x = (t_canvas *)pd_new(canvas_class); + /* zero out every field except "pd" and "dix" */ + memset(((char *)x) + sizeof(t_gobj), 0, sizeof(*x) - sizeof(t_gobj)); + x->xlabel = (t_symbol **)getbytes(0); + x->ylabel = (t_symbol **)getbytes(0); + // only manage this canvas if it's not one of the 3 invisible builtin canvases + x->boxes = boxes_new(); + return x; +} + +static void canvas_vis(t_canvas *x, t_floatarg f); + +/* make a new canvas. It will either be a "root" canvas or else it appears as + a "text" object in another window (canvas_getcurrent() tells us which.) */ +static t_canvas *canvas_new(void *self, t_symbol *sel, int argc, t_atom *argv) { + t_canvas *x = canvas_new2(); + t_canvas *owner = canvas_getcurrent(); + t_symbol *s = &s_; + int width = CANVAS_DEFCANVASWIDTH, xloc = 0; + int height = CANVAS_DEFCANVASHEIGHT, yloc = CANVAS_DEFCANVASYLOC; + int vis=0, font = owner?owner->font:10; + if (!owner) canvas_addtolist(x); + /* toplevel vs subwindow */ + if (argc==5) pd_scanargs(argc,argv,"iiiii", &xloc,&yloc,&width,&height,&font); + else if (argc==6) pd_scanargs(argc,argv,"iiiisi",&xloc,&yloc,&width,&height,&s,&vis); + /* (otherwise assume we're being created from the menu.) */ + if (canvas_newdirectory->name[0]) { + static long dollarzero = 1000; + t_canvasenvironment *env = x->env = (t_canvasenvironment *)getbytes(sizeof(*x->env)); + if (!canvas_newargv) canvas_newargv = (t_atom *)getbytes(0); + env->dir = canvas_newdirectory; + env->argc = canvas_newargc; + env->argv = canvas_newargv; + env->dollarzero = dollarzero++; + env->path = 0; + canvas_newdirectory = &s_; + canvas_newargc = 0; + canvas_newargv = 0; + } else x->env = 0; + + if (yloc < CANVAS_DEFCANVASYLOC) yloc = CANVAS_DEFCANVASYLOC; + if (xloc < 0) xloc = 0; + x->x1 = 0; x->y1 = 0; + x->x2 = 1; x->y2 = 1; + canvas_setbounds(x, xloc, yloc, xloc + width, yloc + height); + gobj_setcanvas(x,owner); + x->name = *s->name ? s : canvas_newfilename ? canvas_newfilename : s_Pd; + if (x->name != s_Pd) pd_bind(x, canvas_makebindsym(x->name)); + x->goprect = 0; /* no GOP rectangle unless it's turned on later */ + /* cancel "vis" flag if we're a subpatch of an abstraction inside another patch. + A separate mechanism prevents the toplevel abstraction from showing up. */ + if (vis && gensym("#X")->thing && gensym("#X")->thing->_class == canvas_class) { + t_canvas *z = (t_canvas *)(gensym("#X")->thing); + while (z && !z->env) z = z->dix->canvas; + if (z && canvas_isabstraction(z) && z->dix->canvas) vis = 0; + } + if (vis) canvas_vis(x,vis); + x->font = 10 /*sys_nearestfontsize(font)*/; + pd_pushsym(x); + newest = x; + return x; +} + +void canvas_setgraph(t_canvas *x, int flag, int nogoprect); + +static void canvas_coords(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { + pd_scanargs(argc,argv,"ffffii*",&x->x1,&x->y1,&x->x2,&x->y2,&x->pixwidth,&x->pixheight); + if (argc <= 7) canvas_setgraph(x, atom_getintarg(6, argc, argv), 1); + else { + canvas_setgraph(x, atom_getintarg(6, argc, argv), 0); + SET(xmargin, atom_getintarg(7, argc, argv)); + SET(ymargin, atom_getintarg(8, argc, argv)); + } + gobj_changed(x,0); +} + +template <class T> void swap(T &a, T &b) {T c=a; a=b; b=c;} + +#define CANVAS_DEFGRAPHWIDTH 200 +#define CANVAS_DEFGRAPHHEIGHT 140 +/* make a new canvas and add it to this canvas. It will appear as a "graph", not a text object. */ +static t_canvas *canvas_addcanvas(t_canvas *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 menu = 0; + t_canvas *x = canvas_new2(); + if (!*sym->name) { + sym = symprintf("graph%d", ++gcount); + menu = 1; + } else if (!strncmp(sym->name,"graph",5)) { + int zz = atoi(sym->name+5); + if (zz>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) {swap(y1,y2); swap(py1,py2);} + if (x1 == x2 || y1 == y2) {x1=0; x2=100; y1=1; y2=-1;} + if (px1 >= px2 || py1 >= py2) { + px1=100; px2=px1+CANVAS_DEFGRAPHWIDTH; + py1=20; py2=py1+CANVAS_DEFGRAPHHEIGHT; + } + x->name = sym; + SET(x1,x1); SET(y1,y1); SET(x,short(px1)); SET(pixwidth ,int(px2-px1)); + SET(x2,x2); SET(y2,y2); SET(y,short(py1)); SET(pixheight,int(py2-py1)); + x->font = (canvas_getcurrent() ? canvas_getcurrent()->font : 42 /*sys_defaultfont*/); + x->screenx1 = x->screeny1 = 0; x->screenx2 = 450; x->screeny2 = 300; + if (x->name != s_Pd) pd_bind(x, canvas_makebindsym(x->name)); + gobj_setcanvas(x,g); + x->gop = 1; + x->goprect = 0; + x->binbuf = binbuf_new(); + binbuf_addv(x->binbuf,"t","graph"); + if (!menu) pd_pushsym(x); + canvas_add(g,x); + return x; +} + +static void canvas_canvas(t_canvas *g, t_symbol *s, int argc, t_atom *argv) { + t_symbol *sym; + float x1,y1,x2,y2,px1,py1,px2,py2; + pd_scanargs(argc,argv,"sffffffff",&sym,&x1,&y1,&x2,&y2,&px1,&py1,&px2,&py2); + canvas_addcanvas(g, sym, x1, y1, x2, y2, px1, py1, px2, py2); +} + +static void canvas_redraw(t_canvas *x) { + gobj_changed(x,0); + canvas_each(y,x) if (y->_class==canvas_class) canvas_redraw((t_canvas *)y); else gobj_changed(y,0); +} + +/* 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, t_floatarg x1, t_floatarg y1, t_floatarg x2, t_floatarg y2) { + int heightwas = int(y2-y1); + if (x->screenx1 == x1 && x->screeny1 == y1 && + x->screenx2 == x2 && x->screeny2 == y2) return; + x->screenx1 = int(x1); x->screeny1 = int(y1); + x->screenx2 = int(x2); x->screeny2 = int(y2); + if (!x->gop && x->y2 < x->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->y1 - x->y2; + x->y1 = heightwas * diff; + x->y2 = x->y1 - diff; + canvas_redraw(x); + } +} + +t_symbol *canvas_makebindsym(t_symbol *s) {return symprintf("pd-%s",s->name);} + +static void canvas_vis(t_canvas *x, t_floatarg f) { + int hadwindow = x->havewindow; + SET(havewindow,!!f); + if (hadwindow && !x->havewindow) gobj_unsubscribe(x,manager); + if (!hadwindow && x->havewindow) gobj_subscribe(x,manager); +} + +/* we call this on a non-toplevel canvas to "open" it into its own window. */ +static void canvas_menu_open(t_canvas *x) { + if (canvas_isvisible(x) && !canvas_istoplevel(x)) { + if (!x->dix->canvas) {error("this works only on subpatch or abstraction"); return;} + SET(havewindow,1); + } +} + +int canvas_isvisible(t_canvas *x) {return gstack_empty() && canvas_getcanvas(x)->havewindow;} + +/* 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. */ +int canvas_istoplevel(t_canvas *x) {return x->havewindow || !x->gop;} + +static void canvas_free(t_canvas *x) { + int dspstate = canvas_suspend_dsp(); + if (canvas_whichfind == x) canvas_whichfind = 0; + t_gobj *y; + while ((y = x->boxes->first())) canvas_delete(x, y); + canvas_vis(x, 0); + if (x->name != s_Pd) pd_unbind(x,canvas_makebindsym(x->name)); + if (x->env) { + free(x->env->argv); + free(x->env); + } + canvas_resume_dsp(dspstate); + free(x->xlabel); + free(x->ylabel); + if (!x->dix->canvas) canvas_takeofflist(x); +} + +/* kill all lines for one inlet or outlet */ +void canvas_deletelinesforio(t_canvas *x, t_text *text, t_inlet *inp, t_outlet *outp) { + canvas_wires_each(oc,t,x) + if ((t.from == text && t.outletp == outp) || + (t.to == text && t.inletp == inp)) + obj_disconnect(t.from, t.outlet, t.to, t.inlet); +} +void canvas_deletelinesfor(t_canvas *x, t_text *text) { + canvas_wires_each(oc,t,x) + if (t.from == text || t.to == text) + obj_disconnect(t.from, t.outlet, t.to, t.inlet); +} + +static void canvas_resortinlets(t_canvas *x); +static void canvas_resortoutlets(t_canvas *x); +static void canvas_push(t_canvas *x, t_floatarg f) {pd_pushsym(x);} +/* assuming that this only ever gets called on toplevel canvases (?) */ +static void canvas_pop(t_canvas *x, t_floatarg fvis) { + pd_popsym(x); canvas_resortinlets(x); canvas_resortoutlets(x); + if (fvis) canvas_vis(x, 1); +} +/* called by m_class.c */ +extern "C" void canvas_popabstraction(t_canvas *x) { + pd_set_newest(x); + pd_popsym(x); canvas_resortinlets(x); canvas_resortoutlets(x); +} + +void canvas_objfor(t_canvas *gl, t_text *x, int argc, t_atom *argv); + +void canvas_restore(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { + if (argc > 3) { + t_atom *ap=argv+3; + if (ap->a_type == A_SYMBOL) { + t_canvasenvironment *e = canvas_getenv(canvas_getcurrent()); + canvas_rename(x, binbuf_realizedollsym(ap->a_symbol, e->argc, e->argv, 1), 0); + } + } + canvas_pop(x,0); /* 0 means "don't touch" here. */ + t_pd *z = gensym("#X")->thing; + if (!z) {error("out of context"); return;} + if (z->_class != canvas_class) {error("wasn't a canvas"); return;} + gobj_setcanvas(x,(t_canvas *)z); + canvas_objfor((t_canvas *)z, x, argc, argv); + newest = x; +} + +static void canvas_loadbang(t_canvas *x); + +static void canvas_loadbangabstractions(t_canvas *x) { + canvas_each(y,x) if (y->_class == canvas_class) { + t_canvas *z = (t_canvas *)y; + if (canvas_isabstraction(z)) canvas_loadbang(z); else canvas_loadbangabstractions(z); + } +} + +void canvas_loadbangsubpatches(t_canvas *x) { + t_symbol *s = gensym("loadbang"); + canvas_each(y,x) if (y->_class == canvas_class) { + t_canvas *z = (t_canvas *)y; + if (!canvas_isabstraction(z)) canvas_loadbangsubpatches(z); + } + canvas_each(y,x) if ((y->_class != canvas_class) && zgetfn(y,s)) pd_vmess(y,s,""); +} + +static void canvas_loadbang(t_canvas *x) { + canvas_loadbangabstractions(x); + canvas_loadbangsubpatches(x); +} + +/* When you ask a canvas its size the result is 2 pixels more than what you gave it to open it; + perhaps there's a 1-pixel border all around it or something. Anyway, we just add the 2 pixels back here; + seems we have to do this for linux but not MSW; not sure about MacOS. */ +#ifdef MSW +#define HORIZBORDER 0 +#define VERTBORDER 0 +#else +#define HORIZBORDER 2 +#define VERTBORDER 2 +#endif + +static void canvas_relocate(t_canvas *x, t_symbol *canvasgeom, t_symbol *topgeom) { + int cxpix, cypix, cw, ch, txpix, typix, tw, th; + if (sscanf(canvasgeom->name, "%dx%d+%d+%d", &cw, &ch, &cxpix, &cypix) < 4 || + sscanf( topgeom->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 pd_set_newest (t_pd *x) {newest = x;} + +static void *subcanvas_new(t_symbol *s) { + t_atom a[6]; + t_canvas *z = canvas_getcurrent(); + if (!*s->name) s = gensym("/SUBPATCH/"); + SETFLOAT(a, 0); + SETFLOAT(a+1, CANVAS_DEFCANVASYLOC); + SETFLOAT(a+2, CANVAS_DEFCANVASWIDTH); + SETFLOAT(a+3, CANVAS_DEFCANVASHEIGHT); + SETSYMBOL(a+4, s); + SETFLOAT(a+5, 1); + t_canvas *x = canvas_new(0, 0, 6, a); + gobj_setcanvas(x,z); + canvas_pop(x, 1); + return x; +} + +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_symbol, 0); + else if (ac && av->a_type == A_DOLLSYM) { + t_canvasenvironment *e = canvas_getenv(x); + pd_pushsym(x); + canvas_rename(x, binbuf_realizedollsym(av->a_symbol, e->argc, e->argv, 1), 0); + pd_popsym(x); + } else canvas_rename(x, gensym("Pd"), 0); +} + +/* ------------------ table ---------------------------*/ + +static t_garray *graph_array(t_canvas *gl, t_symbol *s, t_symbol *tmpl, t_floatarg f, t_floatarg saveit); + +static int tabcount = 0; + +static void *table_new(t_symbol *s, t_floatarg f) { + t_atom a[9]; + t_canvas *z = canvas_getcurrent(); + if (s == &s_) s = symprintf("table%d", tabcount++); + if (f <= 1) f = 100; + SETFLOAT(a, 0); + SETFLOAT(a+1, CANVAS_DEFCANVASYLOC); + SETFLOAT(a+2, 600); + SETFLOAT(a+3, 400); + SETSYMBOL(a+4, s); + SETFLOAT(a+5, 0); + t_canvas *x = canvas_new(0,0,6,a); + gobj_setcanvas(x,z); + /* create a graph for the table */ + t_canvas *gl = canvas_addcanvas(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->env!=0;} + +/* return true if the "canvas" object is a "table". */ +int canvas_istable(t_canvas *x) { + t_atom *argv = x->binbuf ? binbuf_getvec( x->binbuf) : 0; + int argc = x->binbuf ? binbuf_getnatom(x->binbuf) : 0; + return argc && argv[0].a_type == A_SYMBOL && argv[0].a_symbol == gensym("table"); +} + +/* return true if the "canvas" object should be treated as a text + object. This is true for abstractions but also for "table"s... */ +/* JMZ: add a flag to gop-abstractions to hide the title */ +static int canvas_showtext(t_canvas *x) { + t_atom *argv = x->binbuf? binbuf_getvec( x->binbuf) : 0; + int argc = x->binbuf? binbuf_getnatom(x->binbuf) : 0; + int isarray = argc && argv[0].a_type == A_SYMBOL && argv[0].a_symbol == gensym("graph"); + return x->hidetext ? 0 : !isarray; +} + +/* get the document containing this canvas */ +t_canvas *canvas_getrootfor(t_canvas *x) { + if (!x->dix->canvas || canvas_isabstraction(x)) return x; + return canvas_getrootfor(x->dix->canvas); +} + +/* ------------------------- DSP chain handling ------------------------- */ + +typedef struct _dspcontext t_dspcontext; + +extern void ugen_start (); +extern void ugen_stop (); +extern "C" t_dspcontext *ugen_start_graph(int toplevel, t_signal **sp, int ninlets, int noutlets); +extern "C" void ugen_add(t_dspcontext *dc, t_object *x); +extern "C" void ugen_connect(t_dspcontext *dc, t_object *from, int outlet, t_object *to, int inlet); +extern "C" void ugen_done_graph(t_dspcontext *dc); + +/* schedule one canvas for DSP. This is called below for all "root" + canvases, but is also called from the "dsp" method for sub- + canvases, which are treated almost like any other tilde object. */ +static void canvas_dodsp(t_canvas *x, int toplevel, t_signal **sp) { + t_object *ob; + t_symbol *dspsym = gensym("dsp"); + /* create a new "DSP graph" object to use in sorting this canvas. + If we aren't toplevel, there are already other dspcontexts around. */ + t_dspcontext *dc = ugen_start_graph(toplevel, sp, obj_nsiginlets(x), obj_nsigoutlets(x)); + /* find all the "dsp" boxes and add them to the graph */ + canvas_each(y,x) if ((ob = pd_checkobject(y)) && zgetfn(y,dspsym)) ugen_add(dc, ob); + /* ... and all dsp interconnections */ + canvas_wires_each(oc,t,x) + if (obj_issignaloutlet(t.from, t.outlet)) + ugen_connect(dc, t.from, t.outlet, t.to, t.inlet); + /* finally, sort them and add them to the DSP chain */ + ugen_done_graph(dc); +} + +static void canvas_dsp(t_canvas *x, t_signal **sp) {canvas_dodsp(x, 0, sp);} + +/* this routine starts DSP for all root canvases. */ +static void canvas_start_dsp() { + if (canvas_dspstate) ugen_stop(); + else sys_gui("pdtk_pd_dsp 1\n"); + ugen_start(); + //timeval v0,v1; gettimeofday(&v0,0); + foreach(x,windowed_canvases) canvas_dodsp(x->first,1,0); + //gettimeofday(&v1,0); printf("canvas_start_dsp took %ld us\n",(v1.tv_sec-v0.tv_sec)*1000000+(v1.tv_usec-v0.tv_usec)); + canvas_dspstate = 1; +} + +/*static*/ void canvas_stop_dsp() { + if (canvas_dspstate) { + ugen_stop(); + sys_gui("pdtk_pd_dsp 0\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 afterwards, so that DSP doesn't get resorted for every DSP object in the patch. */ +int canvas_suspend_dsp () { + 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 () {if (canvas_dspstate) canvas_start_dsp();} + +extern "C" void glob_dsp(void *self, t_symbol *s, int argc, t_atom *argv) { + if (argc) { + int newstate = atom_getintarg(0, argc, argv); + if (newstate && !canvas_dspstate) { + canvas_start_dsp(); + sys_set_audio_state(1); + } else if (!newstate && canvas_dspstate) { + sys_set_audio_state(0); + canvas_stop_dsp(); + } + } else post("dsp state %d", canvas_dspstate); +} + +extern "C" void *canvas_getblock(t_class *blockclass, t_canvas **canvasp) { + t_canvas *canvas = *canvasp; + void *ret = 0; + canvas_each(g,canvas) if (g->_class == blockclass) ret = g; + *canvasp = canvas->dix->canvas; + return ret; +} + +/******************* redrawing data *********************/ + +static t_float slot_getcoord(t_slot *f, t_template *, t_word *wp, int loud); +static void slot_setcoord(t_slot *f, t_template *, t_word *wp, float pix, int loud); +static t_float slot_cvttocoord(t_slot *f, float val); +static t_template *template_new(t_symbol *sym, int argc, t_atom *argv); +static void template_free(t_template *x); +static int template_match(t_template *x1, t_template *x2); +static int template_find_field(t_template *x, t_symbol*name, int*p_onset, int*p_type, t_symbol **p_arraytype); +static t_float template_getfloat( t_template *x, t_symbol *fieldname, t_word *wp, int loud); +static void template_setfloat( t_template *x, t_symbol *fieldname, t_word *wp, t_float f, int loud); +static t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, t_word *wp, int loud); +static void template_setsymbol(t_template *x, t_symbol *fieldname, t_word *wp, t_symbol *s, int loud); +static t_template *gtemplate_get(t_gtemplate *x); +static t_template *template_findbyname(t_symbol *s); +static t_canvas *template_findcanvas(t_template *tmpl); +static void template_notify(t_template *, t_symbol *s, int argc, t_atom *argv); + +/* find the template defined by a canvas, and redraw all elements for that */ +void canvas_redrawallfortemplatecanvas(t_canvas *x, int action) { + t_template *tmpl; + t_symbol *s1 = gensym("struct"); + canvas_each(g,x) { + t_object *ob = pd_checkobject(g); + if (!ob || /* ob->type != T_OBJECT || */ binbuf_getnatom(ob->binbuf) < 2) continue; + t_atom *argv = binbuf_getvec(ob->binbuf); + if (argv[0].a_type != A_SYMBOL || argv[1].a_type != A_SYMBOL || argv[0].a_symbol != s1) + continue; + tmpl = template_findbyname(argv[1].a_symbol); + //canvas_redrawallfortemplate(tmpl, action); + } + //canvas_redrawallfortemplate(0, action); +} + +//#define canvas_each2(CHILD,CANVAS) for(CHILD=&(CANVAS)->list; *CHILD; CHILD=&(*CHILD)->next()) +/* just a raw remove, no other business */ +/* doesn't work (why?) */ +static t_gobj *canvas_remove_nth(t_canvas *x, int n) { +/* + t_gobj **y; + canvas_each2(y,x) { + fprintf(stderr,"n=%i *y=%p\n",n,*y); + if (!n) { + t_gobj *z = *y; + *y = z->next(); + z->next() = 0; + return z; + } else n--; + } + return 0; +*/ +} + +/* just a raw insert, no other business */ +/* doesn't work (why?) */ +static void canvas_insert_nth(t_canvas *x, int n, t_gobj *nu) { +/* + t_gobj **y; + canvas_each2(y,x) if (!n) { + t_gobj *z = *y; + *y = nu; + nu->next() = z; + return; + } else n--; + *y = nu; +*/ +} + +void canvas_disconnect(t_canvas *x, float from_, float outlet, float to_, float inlet) { + int ifrom=(int)from_, ito=int(to_); + t_gobj *from=0, *to=0; + canvas_each(gfrom,x) if (gfrom->dix->index == ifrom) {from=gfrom; break;} + canvas_each( gto,x) if ( gto->dix->index == ito) { to= gto; break;} + if (!from || !to) goto bad; + obj_disconnect((t_object *)from, int(outlet), (t_object *)to, int(inlet)); + return; +bad: + post("dumb."); +} + +static t_binbuf *canvas_cut_wires(t_canvas *x, t_gobj *o); +static void canvas_paste_wires(t_canvas *x, t_binbuf *buf); + +/* recursively check for abstractions to reload as result of a save. + Don't reload the one we just saved ("except") though. */ +/* LATER try to do the same trick for externs. */ +static void canvas_doreload(t_canvas *gl, t_symbol *name, t_symbol *dir, t_gobj *except) { + int i=0, nobj = gl->boxes->size(); + for (t_gobj *g = gl->boxes->first(); g && i < nobj; i++) { + if (g != except && g->_class == canvas_class) { + t_canvas *c = (t_canvas *)g; + if (canvas_isabstraction(c) && c->name==name && canvas_getdir(c) == dir) { + /* we're going to remake the object, so "g" will go stale. Get its index here, and afterwards restore g. + Also, the replacement will be at the end of the list, so we don't do g = g->next() in this case. */ + int j = g->dix->index; + int hadwindow = gl->havewindow; + if (!hadwindow) canvas_vis(canvas_getcanvas(gl), 1); + t_binbuf *buf = canvas_cut_wires(gl,g); + gl->boxes->remove(j); + //MISSING: remake the object here. + canvas_paste_wires(gl,buf); + if (!hadwindow) canvas_vis(canvas_getcanvas(gl), 0); + continue; + } + canvas_doreload(c,name,dir,except); + } + g = g->next(); + } +} + +void canvas_reload(t_symbol *name, t_symbol *dir, t_gobj *except) { + foreach(x,windowed_canvases) canvas_doreload(x->first, name, dir, except); +} + +/* ------------------------ event handling ------------------------ */ + +/* set a canvas up as a graph-on-parent. + Set reasonable defaults for any missing paramters and redraw things if necessary. */ +void canvas_setgraph(t_canvas *x, int flag, int nogoprect) { + if (!flag && x->gop) { + x->gop = 0; + } else if (flag) { + if (x->pixwidth <= 0) x->pixwidth = CANVAS_DEFGRAPHWIDTH; + if (x->pixheight <= 0) x->pixheight = CANVAS_DEFGRAPHHEIGHT; + SET(gop,1); + SET(hidetext,!!(flag&2)); + if (!nogoprect && !x->goprect) canvas_each(g,x) if (pd_checkobject(g)) {SET(goprect,1); break;} + if (canvas_isvisible(x) && x->goprect) gobj_changed(x,0); + } +} + +/* keep me */ +static int canvas_isconnected (t_canvas *x, t_text *ob1, int n1, t_text *ob2, int n2) { + canvas_wires_each(oc,t,x) + if (t.from == ob1 && t.outlet == n1 && t.to == ob2 && t.inlet == n2) return 1; + return 0; +} + +/* ----------------------------- window stuff ----------------------- */ + +void canvas_close(t_canvas *x) { + if (x->dix->canvas) canvas_vis(x, 0); else pd_free(x); +} + +static int canvas_find_index1, canvas_find_index2; +static t_binbuf *canvas_findbuf; +int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf); + +/* find an atom or string of atoms */ +static int canvas_dofind(t_canvas *x, int *myindex1p) { + int myindex1 = *myindex1p, myindex2=0; + if (myindex1 >= canvas_find_index1) { + canvas_each(y,x) { + t_object *ob = pd_checkobject(y); + if (ob && 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; + vmess(x,gensym("menu-open"),""); + return 1; + } + } + myindex2++; + } + } + myindex2=0; + canvas_each(y,x) { + if (y->_class == canvas_class) { + (*myindex1p)++; + if (canvas_dofind((t_canvas *)y, myindex1p)) return 1; + } + myindex2++; + } + return 0; +} + +static void canvas_find_parent(t_canvas *x) { + if (x->dix->canvas) canvas_vis(canvas_getcanvas(x->dix->canvas), 1); +} + +static int canvas_dofinderror(t_canvas *gl, void *error_object) { + canvas_each(g,gl) { + if (g==error_object) { + /* got it... now show it. */ + canvas_vis(canvas_getcanvas(gl), 1); + return 1; + } else if (g->_class == canvas_class) { + if (canvas_dofinderror((t_canvas *)g, error_object)) return 1; + } + } + return 0; +} + +void canvas_finderror(void *error_object) { + foreach(x,windowed_canvases) if (canvas_dofinderror(x->first, error_object)) return; + post("... sorry, I couldn't find the source of that error."); +} + +extern t_class *text_class; +extern t_class *dummy_class; + +static int is_dummy (t_text *x) {return x->_class==dummy_class;} + +long canvas_base_o_index(void); + +void canvas_connect(t_canvas *x, t_floatarg ffrom, t_floatarg foutlet, t_floatarg fto,t_floatarg finlet) { + int base = canvas_base_o_index(); + int ifrom=base+int(ffrom), outlet=(int)foutlet; + int ito=base+int(fto), inlet=(int)finlet; + t_gobj *gfrom=0, *gto=0; + t_object *from=0, *to=0; + t_outconnect *oc; + if (ifrom<0) goto bad; + if (ito <0) goto bad; + canvas_each(zfrom,x) if (zfrom->dix->index == ifrom) {gfrom=zfrom; break;} + if (!gfrom) goto bad; + canvas_each( zto,x) if ( zto->dix->index == ito) { gto= zto; break;} + if (!gto) goto bad; + from = pd_checkobject(gfrom); + to = pd_checkobject( gto); + if (!from || !to) goto bad; + /* if object creation failed, make dummy inlets or outlets as needed */ + if (is_dummy(from)) while (outlet >= obj_noutlets(from)) outlet_new(from, &s_); + if (is_dummy(to)) while ( inlet >= obj_ninlets(to)) inlet_new(to,to,&s_,&s_); + if (!(oc = obj_connect(from,outlet,to,inlet))) goto bad; + pd_set_newest(oc); + gobj_setcanvas(oc,x); + oc->dix->index = x->next_w_index++; + return; +bad: + post("%s %d %d %d %d (%s->%s) connection failed", x->name->name,ifrom,outlet,ito,inlet, + from ? class_getname(from->_class) : "???", + to ? class_getname( to->_class) : "???"); +} + +#define ARRAYPAGESIZE 1000 /* this should match the page size in u_main.tk */ +/* 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->name != '#') return s; + return symprintf("$%s",s->name+1); +} + +/* --------- "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_class *array_class; + +static t_array *array_new(t_symbol *templatesym, t_gpointer *parent) { + t_array *x = (t_array *)pd_new(array_class); + t_template *t = template_findbyname(templatesym); + x->templatesym = templatesym; + x->n = 1; + x->elemsize = sizeof(t_word) * t->n; + /* aligned allocation */ + x->vec = (char *)getalignedbytes(x->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->gp = *parent; + word_init((t_word *)x->vec, t, parent); + return x; +} + +void array_resize(t_array *x, int n) { + t_template *t = template_findbyname(x->templatesym); + if (n < 1) n = 1; + int oldn = x->n; + int elemsize = sizeof(t_word) * t->n; + x->vec = (char *)resizealignedbytes(x->vec, oldn * elemsize, n * elemsize); + x->n = n; + if (n > oldn) { + char *cp = x->vec + elemsize * oldn; + for (int i = n-oldn; i--; cp += elemsize) { + t_word *wp = (t_word *)cp; + word_init(wp, t, &x->gp); + } + } +} + +static void array_resize_and_redraw(t_array *array, int n) { +/* what was that for??? */ + array_resize(array,n); + gobj_changed(array,0); +} + +void word_free(t_word *wp, t_template *t); + +static void array_free(t_array *x) { + t_template *scalartemplate = template_findbyname(x->templatesym); + for (int i=0; i < x->n; i++) word_free((t_word *)(x->vec + x->elemsize*i), scalartemplate); + freealignedbytes(x->vec, x->elemsize * x->n); +} + +/* --------------------- graphical arrays (garrays) ------------------- */ + +t_class *garray_class; + +static t_pd *garray_arraytemplatecanvas; + +/* create invisible, built-in canvases to determine the templates for floats +and float-arrays. */ + +void pd_eval_text2(char *s) {pd_eval_text(s,strlen(s));} + +extern "C" void garray_init () { + hack = 0; /* invisible canvases must be, uh, invisible */ + if (garray_arraytemplatecanvas) return; + t_binbuf *b = binbuf_new(); + glob_setfilename(0, gensym("_float"), gensym(".")); + pd_eval_text2( + "#N canvas 0 0 458 153 10;\n" + "#X obj 43 31 struct _float_array array z float float style float linewidth float color;\n" + "#X obj 43 70 plot z color linewidth 0 0 1 style;\n"); + vmess(s__X.thing, gensym("pop"), "i", 0); + glob_setfilename(0, gensym("_float_array"), gensym(".")); + pd_eval_text2( + "#N canvas 0 0 458 153 10;\n" + "#X obj 39 26 struct float float y;\n"); + garray_arraytemplatecanvas = s__X.thing; + vmess(s__X.thing, gensym("pop"), "i", 0); + glob_setfilename(0, &s_, &s_); + binbuf_free(b); + hack = 1; /* enable canvas visibility for upcoming canvases */ +} + +/* create a new scalar attached to a symbol. Used to make floating-point +arrays (the scalar will be of type "_float_array"). Currently this is +always called by graph_array() below; but when we make a more general way +to save and create arrays this might get called more directly. */ + +static t_garray *graph_scalar(t_canvas *gl, t_symbol *s, t_symbol *templatesym, int saveit) { + if (!template_findbyname(templatesym)) return 0; + t_garray *x = (t_garray *)pd_new(garray_class); + x->scalar = scalar_new(gl, templatesym); + x->realname = s; + x->realname = canvas_realizedollar(gl, s); + pd_bind(x,x->realname); + x->usedindsp = 0; + x->saveit = saveit; + x->listviewing = 0; + canvas_add(gl,x); + x->canvas = gl; + return x; +} + +#define TEMPLATE_CHECK(tsym,ret) if (!t) {\ + error("couldn't find template %s", tsym->name); return ret;} + +#define TEMPLATE_FLOATY(a,ret) if (!a) {\ + error("%s: needs floating-point 'y' field", x->realname); return ret;} + + /* get a garray's "array" structure. */ +t_array *garray_getarray(t_garray *x) { + int zonset, ztype; + t_symbol *zarraytype; + t_scalar *sc = x->scalar; + t_template *t = template_findbyname(sc->t); + TEMPLATE_CHECK(sc->t,0) + if (!template_find_field(t, gensym("z"), &zonset, &ztype, &zarraytype)) { + error("template %s has no 'z' field", sc->t->name); + return 0; + } + if (ztype != DT_ARRAY) { + error("template %s, 'z' field is not an array", sc->t->name); + return 0; + } + return sc->v[zonset].w_array; +} + + /* get the "array" structure and furthermore check it's float */ +static t_array *garray_getarray_floatonly(t_garray *x, int *yonsetp, int *elemsizep) { + t_array *a = garray_getarray(x); + int yonset, type; + t_symbol *arraytype; + t_template *t = template_findbyname(a->templatesym); + if (!template_find_field(t,&s_y,&yonset,&type,&arraytype) || type != DT_FLOAT) + return 0; + *yonsetp = yonset; + *elemsizep = a->elemsize; + return a; +} + +/* get the array's name. Return nonzero if it should be hidden */ +int garray_getname(t_garray *x, t_symbol **namep) { +// *namep = x->name; + *namep = x->realname; + return x->hidename; +} + + +/* if there is one garray in a graph, reset the graph's coordinates + to fit a new size and style for the garray */ +static void garray_fittograph(t_garray *x, int n, int style) { + t_array *array = garray_getarray(x); + t_canvas *gl = x->canvas; + if (gl->boxes->first() == x && !x->next()) { + vmess(gl,gensym("bounds"),"ffff",0.,gl->y1, double(style == PLOTSTYLE_POINTS || n == 1 ? n : n-1), gl->y2); + /* close any dialogs that might have the wrong info now... */ + } + array_resize_and_redraw(array, n); +} + +/* handle "array" message to canvases; call graph_scalar above with +an appropriate template; then set size and flags. This is called +from the menu and in the file format for patches. LATER replace this +by a more coherent (and general) invocation. */ + +t_garray *graph_array(t_canvas *gl, t_symbol *s, t_symbol *templateargsym, t_floatarg fsize, t_floatarg fflags) { + int n = (int)fsize, zonset, ztype, saveit; + t_symbol *zarraytype; + t_symbol *templatesym = gensym("pd-_float_array"); + int flags = (int)fflags; + int filestyle = (flags & 6)>>1; + int style = filestyle == 0 ? PLOTSTYLE_POLY : filestyle == 1 ? PLOTSTYLE_POINTS : filestyle; + if (templateargsym != &s_float) {error("%s: only 'float' type understood", templateargsym->name); return 0;} + t_template *t = template_findbyname(templatesym); + TEMPLATE_CHECK(templatesym,0) + if (!template_find_field(t, gensym("z"), &zonset, &ztype, &zarraytype)) { + error("template %s has no 'z' field", templatesym->name); + return 0; + } + if (ztype != DT_ARRAY) {error("template %s, 'z' field is not an array", templatesym->name); return 0;} + t_template *ztemplate = template_findbyname(zarraytype); + if (!ztemplate) {error("no template of type %s", zarraytype->name); return 0;} + saveit = (flags & 1) != 0; + t_garray *x = graph_scalar(gl, s, templatesym, saveit); + x->hidename = (flags>>3)&1; + if (n <= 0) n = 100; + array_resize(x->scalar->v[zonset].w_array, n); + template_setfloat(t, gensym("style"), x->scalar->v, style, 1); + template_setfloat(t, gensym("linewidth"), x->scalar->v, style==PLOTSTYLE_POINTS?2:1, 1); + t_pd *x2 = pd_findbyclass(gensym("#A"), garray_class); + if (x2) pd_unbind(x2,gensym("#A")); + pd_bind(x,gensym("#A")); + garray_redraw(x); + return x; +} + +/* find the graph most recently added to this canvas; if none exists, return 0. */ +static t_canvas *canvas_findgraph(t_canvas *x) { + t_gobj *y = 0; + canvas_each(z,x) if (z->_class==canvas_class && ((t_canvas *)z)->gop) y = z; + return (t_canvas *)y; +} + +/* 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. */ +static void canvas_arraydialog(t_canvas *parent, t_symbol *name, t_floatarg size, t_floatarg fflags, t_floatarg otherflag) { + t_canvas *gl; + int flags = (int)fflags; + if (size < 1) size = 1; + if (otherflag == 0 || !(gl = canvas_findgraph(parent))) + gl = canvas_addcanvas(parent, &s_, 0, 1, (size>1 ? size-1 : size), -1, 0, 0, 0, 0); + graph_array(gl, sharptodollar(name), &s_float, size, flags); +} + +void garray_arrayviewlist_close(t_garray *x) { + x->listviewing = 0; + sys_vgui("pdtk_array_listview_closeWindow %s\n", x->realname->name); +} + +/* 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 fflags, t_floatarg deleteit) { + int flags = (int)fflags; + int saveit = (flags&1)!=0; + int style = (flags>>1)&3; + float stylewas = template_getfloat(template_findbyname(x->scalar->t), gensym("style"), x->scalar->v, 1); + if (deleteit) {canvas_delete(x->canvas,x); return;} + t_symbol *argname = sharptodollar(name); + t_array *a = garray_getarray(x); + t_template *scalartemplate; + if (!a) {error("can't find array"); return;} + if (!(scalartemplate = template_findbyname(x->scalar->t))) {error("no template of type %s", x->scalar->t->name); return;} + if (argname != x->realname) { + if (x->listviewing) garray_arrayviewlist_close(x); + x->realname = argname; /* is this line supposed to exist? */ + pd_unbind(x,x->realname); + x->realname = canvas_realizedollar(x->canvas, argname); + pd_bind(x,x->realname); + gobj_changed(x,0); + } + int size = max(1,int(fsize)); + if (size != a->n) garray_resize(x, size); + else if (style != stylewas) garray_fittograph(x, size, style); + template_setfloat(scalartemplate, gensym("style"), x->scalar->v, (float)style, 0); + garray_setsaveit(x, saveit!=0); + garray_redraw(x); +} + +void garray_arrayviewlist_new(t_garray *x) { + char *s = x->realname->name; + int yonset=0, elemsize=0; + char cmdbuf[200]; + t_array *a = garray_getarray_floatonly(x, &yonset, &elemsize); + if (!a) {error("garray_arrayviewlist_new()"); return;} + x->listviewing = 1; + sprintf(cmdbuf, "pdtk_array_listview_new %%s %s %d\n",s,0); + for (int i=0; i < ARRAYPAGESIZE && i < a->n; i++) { + float yval = *(float *)(a->vec + elemsize*i + yonset); + sys_vgui(".%sArrayWindow.lb insert %d {%d) %g}\n",s,i,i,yval); + } +} + +void garray_arrayviewlist_fillpage(t_garray *x, t_float page, t_float fTopItem) { + char *s = x->realname->name; + int yonset=0, elemsize=0, topItem=(int)fTopItem; + t_array *a = garray_getarray_floatonly(x, &yonset, &elemsize); + if (!a) {error("garray_arrayviewlist_fillpage()"); return;} + if (page < 0) { + page = 0; + sys_vgui("pdtk_array_listview_setpage %s %d\n",s,(int)page); + } else if ((page * ARRAYPAGESIZE) >= a->n) { + page = (int)(((int)a->n - 1)/ (int)ARRAYPAGESIZE); + sys_vgui("pdtk_array_listview_setpage %s %d\n",s,(int)page); + } + sys_vgui(".%sArrayWindow.lb delete 0 %d\n",s,ARRAYPAGESIZE-1); + for (int i = (int)page * ARRAYPAGESIZE; (i < (page+1)*ARRAYPAGESIZE && i < a->n); i++) { + float yval = *(float *)(a->vec + elemsize*i + yonset); + sys_vgui(".%sArrayWindow.lb insert %d {%d) %g}\n",s,i%ARRAYPAGESIZE,i,yval); + } + sys_vgui(".%sArrayWindow.lb yview %d\n",s,topItem); +} + +static void garray_free(t_garray *x) { + if (x->listviewing) garray_arrayviewlist_close(x); + pd_unbind(x,x->realname); + /* LATER find a way to get #A unbound earlier (at end of load?) */ + t_pd *x2; + while ((x2 = pd_findbyclass(gensym("#A"), garray_class))) pd_unbind(x2, gensym("#A")); +} + +/* ------------- code used by both array and plot widget functions ---- */ + +static void array_redraw(t_array *a, t_canvas *canvas) { + /* what was that for? */ + scalar_redraw(a->gp.scalar, canvas); +} + +static int canvas_xtopixels(t_canvas *x, float xval); +static int canvas_ytopixels(t_canvas *x, float yval); + + /* routine to get screen coordinates of a point in an array */ +static void array_getcoordinate(t_canvas *canvas, char *elem, int xonset, int yonset, int wonset, int indx, +float basex, float basey, float xinc, t_slot *xslot, t_slot *yslot, t_slot *wslot, +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 = canvas_ytopixels(canvas, basey + slot_cvttocoord(yslot, yval)); + if (wonset >= 0) { + /* found "w" field which controls linewidth. */ + float wval = *(float *)(elem + wonset); + wpix = canvas_ytopixels(canvas, basey + slot_cvttocoord(yslot,yval) + slot_cvttocoord(wslot,wval)) - ypix; + if (wpix < 0) wpix = -wpix; + } else wpix = 1; + *xp = canvas_xtopixels(canvas, basex + slot_cvttocoord(xslot, xval)); + *yp = ypix; + *wp = wpix; +} + +static struct { + float xcumulative, ycumulative; + t_slot *xfield, *yfield; + t_canvas *canvas; + t_scalar *scalar; + t_array *array; + t_word *wp; + t_template *t; + int npoints, elemsize; + float initx, xperpix, yperpix; + int lastx, fatten; +} ammo; + +/* LATER protect against the template changing or the scalar disappearing + probably by attaching a gpointer here ... */ +#if 0 +static void array_motion(void *z, t_floatarg dx, t_floatarg dy) { + ammo.xcumulative += dx * ammo.xperpix; + ammo.ycumulative += dy * ammo.yperpix; + if (ammo.xfield) {// xy plot + for (int i=0; i<ammo.npoints; i++) { + t_word *thisword = (t_word *)(((char *)ammo.wp) + i*ammo.elemsize); + float xwas = slot_getcoord(ammo.xfield, ammo.t, thisword, 1); + float ywas = ammo.yfield ? slot_getcoord(ammo.yfield, ammo.t, thisword, 1) : 0; + slot_setcoord(ammo.xfield, ammo.t, thisword, xwas + dx, 1); + if (ammo.yfield) { + if (ammo.fatten) { + if (i == 0) { + float newy = max(0.f,ywas+dy*ammo.yperpix); + slot_setcoord(ammo.yfield, ammo.t, thisword, newy, 1); + } + } else slot_setcoord(ammo.yfield, ammo.t, thisword, ywas + dy*ammo.yperpix, 1); + } + } + } else if (ammo.yfield) {// y plot + int thisx = int(ammo.initx + ammo.xcumulative + 0.5), x2; + int increment, nchange; + float newy = ammo.ycumulative; + float oldy = slot_getcoord(ammo.yfield, ammo.t, (t_word *)(((char *)ammo.wp) + ammo.elemsize * ammo.lastx), 1); + float ydiff = newy-oldy; + CLAMP(thisx,0,1); + increment = thisx > ammo.lastx ? -1 : 1; + nchange = 1 + increment * (ammo.lastx - thisx); + x2 = thisx; + for (int i=0; i<nchange; i++, x2 += increment) { + slot_setcoord(ammo.yfield, ammo.t, (t_word *)(((char *)ammo.wp) + ammo.elemsize * x2), newy, 1); + if (nchange > 1) newy -= ydiff/(nchange-1); + } + ammo.lastx = thisx; + } + if (ammo.scalar) scalar_redraw(ammo.scalar, ammo.canvas); + if (ammo.array) array_redraw(ammo.array, ammo.canvas); +} +#endif + +int scalar_doclick(t_word *data, t_template *t, t_scalar *sc, t_array *ap, t_canvas *owner, float xloc, float yloc, +int xpix, int ypix, int shift, int alt, int dbl, int doit); + +static int array_getfields(t_symbol *elemtemplatesym, t_canvas **elemtemplatecanvasp, +t_template **elemtemplatep, int *elemsizep, t_slot *xslot, t_slot *yslot, t_slot *wslot, +int *xonsetp, int *yonsetp, int *wonsetp); + +/* try clicking on an element of the array as a scalar (if clicking + on the trace of the array failed) */ +static int array_doclick_element(t_array *array, t_canvas *canvas, t_scalar *sc, t_array *ap, +t_symbol *elemtemplatesym, float linewidth, float xloc, float xinc, float yloc, +t_slot *xfield, t_slot *yfield, t_slot *wfield, int xpix, int ypix, int shift, int alt, int dbl, int doit) { + t_canvas *elemtemplatecanvas; + t_template *elemtemplate; + int elemsize, yonset, wonset, xonset, incr; + //float xsum=0; + if (elemtemplatesym == &s_float) return 0; + if (array_getfields(elemtemplatesym, &elemtemplatecanvas, + &elemtemplate, &elemsize, xfield, yfield, wfield, &xonset, &yonset, &wonset)) + return 0; + /* if it has more than 2000 points, just check 300 of them. */ + if (array->n < 2000) incr=1; else incr = array->n/300; + for (int i=0; i < array->n; i += incr) { + //float usexloc = xonset>=0 ? xloc + slot_cvttocoord(xfield, *(float *)&array->vec[elemsize*i+xonset]) : xloc + xsum; + //if (xonset>=0) xsum += xinc; + //float useyloc = yloc + (yonset>=0 ? slot_cvttocoord(yfield, *(float *)&array->vec[elemsize*i+yonset]):0); + int hit = 0; + /* hit = scalar_doclick((t_word *)&array->vec[elemsize*i], + elemtemplate, 0, array, canvas, usexloc, useyloc, xpix, ypix, shift, alt, dbl, doit);*/ + if (hit) return hit; + } + return 0; +} + +static float canvas_pixelstox(t_canvas *x, float xpix); +static float canvas_pixelstoy(t_canvas *x, float xpix); + +/* convert an X screen distance to an X coordinate increment. */ +static float canvas_dpixtodx(t_canvas*x,float dxpix){return dxpix*(canvas_pixelstox(x,1)-canvas_pixelstox(x,0));} +static float canvas_dpixtody(t_canvas*x,float dypix){return dypix*(canvas_pixelstoy(x,1)-canvas_pixelstoy(x,0));} + +/* LATER move this and others back into plot parentwidget code, so + they can be static (look in g_canvas.h for candidates). */ +int array_doclick(t_array *array, t_canvas *canvas, t_scalar *sc, t_array *ap, +t_symbol *elemtemplatesym, float linewidth, float xloc, float xinc, float yloc, float scalarvis, +t_slot *xfield, t_slot *yfield, t_slot *wfield, int xpix, int ypix, int shift, int alt, int dbl, int doit) { + t_canvas *elemtemplatecanvas; + t_template *elemtemplate; + int elemsize, yonset, wonset, xonset; + if (!array_getfields(elemtemplatesym, &elemtemplatecanvas, &elemtemplate, &elemsize, + xfield, yfield, wfield, &xonset, &yonset, &wonset)) { + float best = 100; + /* if it has more than 2000 points, just check 1000 of them. */ + int incr = (array->n <= 2000 ? 1 : array->n / 1000); + for (int i=0; i < array->n; i += incr) { + float pxpix, pypix, pwpix, dx, dy; + array_getcoordinate(canvas, &array->vec[elemsize*i], xonset, yonset, wonset, i, + xloc, yloc, xinc, xfield, yfield, wfield, &pxpix, &pypix, &pwpix); + if (pwpix < 4) pwpix = 4; + dx = fabs(pxpix-xpix); if (dx>8) continue; + dy = fabs(pypix-ypix); if (dx+dy<best) best=dx+dy; + if (wonset >= 0) { + dy = fabs(pypix+pwpix-ypix); if (dx+dy < best) best = dx+dy; + dy = fabs(pypix-pwpix-ypix); if (dx+dy < best) best = dx+dy; + } + } if (best > 8) { + if (scalarvis != 0) return array_doclick_element(array, canvas, sc, ap, elemtemplatesym, + linewidth, xloc, xinc, yloc, xfield, yfield, wfield, xpix, ypix, shift, alt, dbl, doit); + return 0; + } + best += 0.001; /* add truncation error margin */ + for (int i=0; i < array->n; i += incr) { + float pxpix, pypix, pwpix, dx, dy, dy2, dy3; + array_getcoordinate(canvas, &array->vec[elemsize*i], xonset, yonset, wonset, i, + xloc, yloc, xinc, xfield, yfield, wfield, &pxpix, &pypix, &pwpix); + if (pwpix < 4) pwpix = 4; + dx = fabs(pxpix-xpix); + dy = fabs(pypix-ypix); + if (wonset >= 0) { + dy2 = fabs(pypix+pwpix-ypix); + dy3 = fabs(pypix-pwpix-ypix); + if (yonset < 0) dy = 100; + } else dy2 = dy3 = 100; + if (dx + dy <= best || dx + dy2 <= best || dx + dy3 <= best) { + if (dy<dy2 && dy<dy3) ammo.fatten = 0; + else if (dy2<dy3) ammo.fatten = -1; + else ammo.fatten = 1; + if (doit) { + char *elem = array->vec; + ammo.elemsize = elemsize; + ammo.canvas = canvas; + ammo.scalar = sc; + ammo.array = ap; + ammo.t = elemtemplate; + ammo.xperpix = canvas_dpixtodx(canvas, 1); + ammo.yperpix = canvas_dpixtody(canvas, 1); + if (alt && xpix < pxpix) { /* delete a point */ + if (array->n <= 1) return 0; + memmove(&array->vec[elemsize*i], &array->vec[elemsize*(i+1)], (array->n-1-i) * elemsize); + array_resize_and_redraw(array, array->n - 1); + return 0; + } else if (alt) { + /* add a point (after the clicked-on one) */ + array_resize_and_redraw(array, array->n + 1); + elem = array->vec; + memmove(elem + elemsize * (i+1), elem + elemsize*i, (array->n-i-1) * elemsize); + i++; + } + if (xonset >= 0) { + ammo.xfield = xfield; + ammo.xcumulative = slot_getcoord(xfield,ammo.t,(t_word *)(elem+elemsize*i),1); + ammo.wp = (t_word *)(elem + elemsize*i); + if (shift) ammo.npoints = array->n - i; + else ammo.npoints = 1; + } else { + ammo.xfield = 0; + ammo.xcumulative = 0; + ammo.wp = (t_word *)elem; + ammo.npoints = array->n; + ammo.initx = i; + ammo.lastx = i; + ammo.xperpix *= (xinc == 0 ? 1 : 1./xinc); + } + if (ammo.fatten) { + ammo.yfield = wfield; + ammo.ycumulative = slot_getcoord(wfield,ammo.t,(t_word *)(elem+elemsize*i),1); + ammo.yperpix *= -ammo.fatten; + } else if (yonset >= 0) { + ammo.yfield = yfield; + ammo.ycumulative = slot_getcoord(yfield,ammo.t,(t_word *)(elem+elemsize*i),1); + } else { + ammo.yfield = 0; + ammo.ycumulative = 0; + } + /* canvas_grab(canvas, 0, array_motion, 0, xpix, ypix); */ + } + return 0; + } + } + } + return 0; +} + +static void garray_save(t_gobj *z, t_binbuf *b) { + t_garray *x = (t_garray *)z; + t_array *array = garray_getarray(x); + t_template *scalartemplate; + /* LATER "save" the scalar as such */ + if (x->scalar->t != gensym("pd-_float_array")) {error("can't save arrays of type %s yet", x->scalar->t->name); return;} + if (!(scalartemplate = template_findbyname(x->scalar->t))) {error("no template of type %s", x->scalar->t->name); return;} + int style = (int)template_getfloat(scalartemplate, gensym("style"), x->scalar->v, 0); + int filestyle = (style == PLOTSTYLE_POINTS ? 1 : (style == PLOTSTYLE_POLY ? 0 : style)); + binbuf_addv(b, "ttsisi;","#X","array", x->realname, array->n, &s_float, x->saveit+2*filestyle+8*x->hidename); + if (x->saveit) { + int n = array->n, n2 = 0; + while (n2 < n) { + int chunk = imin(n-n2,1000); + binbuf_addv(b,"ti","#A",n2); + for (int i=0; i<chunk; i++) binbuf_addv(b, "f", ((float *)array->vec)[n2+i]); + binbuf_addv(b, ";"); + n2 += chunk; + } + } +} + +/* required by d_array.c and d_soundfile */ +void garray_redraw(t_garray *x) {gobj_changed(x,0);} + +/* those three required by d_array.c */ +void garray_usedindsp(t_garray *x) {x->usedindsp = 1;} +int garray_npoints(t_garray *x) {return garray_getarray(x)->n;} /* get the length */ +char *garray_vec(t_garray *x) {return (char *)garray_getarray(x)->vec;} /* get the contents */ + +/* 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) { + int yonset, elemsize; + t_array *a = garray_getarray_floatonly(x, &yonset, &elemsize); + TEMPLATE_FLOATY(a,0) + if (elemsize != sizeof(t_word)) {error("%s: has more than one field", x->realname); return 0;} + *size = garray_npoints(x); + *vec = (float *)garray_vec(x); + return 1; +} + +/* set the "saveit" flag */ +void garray_setsaveit(t_garray *x, int saveit) { + if (x->saveit && !saveit) post("warning: array %s: clearing save-in-patch flag", x->realname->name); + x->saveit = saveit; +} + +static void garray_const(t_garray *x, t_floatarg g) { + int yonset, elemsize; + t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); + TEMPLATE_FLOATY(array,) + for (int i=0; i<array->n; i++) *((float *)(array->vec + elemsize*i) + yonset) = g; + garray_redraw(x); +} + +/* sum of Fourier components; called from functions below */ +static void garray_dofo(t_garray *x, int npoints, float dcval, int nsin, t_float *vsin, int sineflag) { + double phase, fj; + int yonset, i, j, elemsize; + t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); + TEMPLATE_FLOATY(array,) + if (npoints == 0) npoints = 512; /* dunno what a good default would be... */ + if (npoints != (1 << ilog2(npoints))) + post("%s: rounnding to %d points", array->templatesym->name, (npoints = (1<<ilog2(npoints)))); + garray_resize(x, npoints + 3); + double phaseincr = 2. * 3.14159 / npoints; + for (i=0, phase = -phaseincr; i < array->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 *)(array->vec + elemsize*i) + yonset) = sum; + } + garray_redraw(x); +} + +static void garray_sinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv) { + if (argc < 2) {error("%s: need number of points and partial strengths", x->realname->name); return;} + t_float *svec = (t_float *)getbytes(sizeof(t_float) * argc); + int npoints = atom_getintarg(0,argc--,argv++); + argv++, argc--; /* is it normal that this happens a second time? */ + for (int i=0; i < argc; i++) svec[i] = atom_getfloatarg(i, argc, argv); + garray_dofo(x, npoints, 0, argc, svec, 1); + free(svec); +} +static void garray_cosinesum(t_garray *x, t_symbol *s, int argc, t_atom *argv) { + if (argc < 2) {error("%s: need number of points and partial strengths", x->realname->name); return;} + t_float *svec = (t_float *)getbytes(sizeof(t_float) * argc); + int npoints = atom_getintarg(0,argc--,argv++); + for (int i=0; i < argc; i++) svec[i] = atom_getfloatarg(i, argc, argv); + garray_dofo(x, npoints, 0, argc, svec, 0); + free(svec); +} + +static void garray_normalize(t_garray *x, t_float f) { + double maxv=0; + int yonset, elemsize; + t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); + TEMPLATE_FLOATY(array,) + if (f <= 0) f = 1; + for (int i=0; i < array->n; i++) { + double v = *((float *)(array->vec + elemsize*i) + yonset); + if ( v > maxv) maxv = v; + if (-v > maxv) maxv = -v; + } + if (maxv > 0) { + double renormer = f/maxv; + for (int i=0; i < array->n; i++) *((float *)(array->vec + elemsize*i) + yonset) *= renormer; + } + garray_redraw(x); +} + +/* list: the first value is an index; subsequent values are put in the "y" slot of the array. */ +static void garray_list(t_garray *x, t_symbol *s, int argc, t_atom *argv) { + int yonset, elemsize; + t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); + TEMPLATE_FLOATY(array,) + if (argc < 2) return; + else { + int firstindex = atom_getintarg(0,argc--,argv++); + if (firstindex < 0) { /* drop negative x values */ + argc += firstindex; + argv -= firstindex; + firstindex = 0; + } + if (argc + firstindex > array->n) argc = array->n - firstindex; + for (int i=0; i < argc; i++) + *((float *)(array->vec + elemsize * (i + firstindex)) + yonset) = atom_getfloat(argv + i); + } + garray_redraw(x); +} + +static void garray_bounds(t_garray *x, t_floatarg x1, t_floatarg y1, t_floatarg x2, t_floatarg y2) +{vmess(x->canvas, gensym("bounds"), "ffff", x1, y1, x2, y2);} +static void garray_xticks(t_garray *x, t_floatarg point, t_floatarg inc, t_floatarg f) +{vmess(x->canvas, gensym("xticks"), "fff", point, inc, f);} +static void garray_yticks(t_garray *x, t_floatarg point, t_floatarg inc, t_floatarg f) +{vmess(x->canvas, gensym("yticks"), "fff", point, inc, f);} +static void garray_xlabel(t_garray *x, t_symbol *s, int argc, t_atom *argv) {typedmess(x->canvas, s, argc, argv);} +static void garray_ylabel(t_garray *x, t_symbol *s, int argc, t_atom *argv) {typedmess(x->canvas, s, argc, argv);} + +static void garray_rename(t_garray *x, t_symbol *s) { + if (x->listviewing) garray_arrayviewlist_close(x); + pd_unbind(x,x->realname); + x->realname = s; + pd_bind(x,x->realname); + garray_redraw(x); +} + +static void garray_read(t_garray *x, t_symbol *filename) { + FILE *fd; + char *buf, *bufptr; + int yonset, elemsize; + t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); + TEMPLATE_FLOATY(array,) + int nelem = array->n; + int filedesc = canvas_open2(canvas_getcanvas(x->canvas), filename->name, "", &buf, &bufptr, 0); + if (filedesc<0) {error("%s: can't open", filename->name); free(buf); return;} + if (!(fd = fdopen(filedesc, "r"))) {error("%s: can't open", filename->name); free(buf); return;} + int i; + for (i=0; i < nelem; i++) { + if (!fscanf(fd, "%f", (float *)(array->vec + elemsize*i) + yonset)) { + post("%s: read %d elements into table of size %d", filename->name, i, nelem); + break; + } + } + while (i < nelem) *((float *)(array->vec + elemsize*i) + yonset) = 0, i++; + fclose(fd); + garray_redraw(x); + free(buf); +} + +static void garray_write(t_garray *x, t_symbol *filename) { + int yonset, elemsize; + t_array *array = garray_getarray_floatonly(x, &yonset, &elemsize); + TEMPLATE_FLOATY(array,) + char *buf = canvas_makefilename(canvas_getcanvas(x->canvas),filename->name,0,0); + sys_bashfilename(buf, buf); + FILE *fd = fopen(buf, "w"); + if (!fd) {error("can't create file '%s'", buf); free(buf); return;} + free(buf); + for (int i=0; i < array->n; i++) { + if (fprintf(fd, "%g\n", *(float *)(((array->vec + sizeof(t_word) * i)) + yonset)) < 1) { + post("%s: write error", filename->name); + break; + } + } + fclose(fd); +} + +/* d_soundfile.c uses this! */ +int garray_ambigendian () { + unsigned short s = 1; + unsigned char c = *(char *)(&s); + return c==0; +} + +/* d_soundfile.c uses this! */ +void garray_resize(t_garray *x, t_floatarg f) { + t_array *array = garray_getarray(x); + int n = f<1?1:(int)f; + garray_fittograph(x, n, (int)template_getfloat(template_findbyname(x->scalar->t), gensym("style"), x->scalar->v, 1)); + array_resize_and_redraw(array, n); + if (x->usedindsp) canvas_update_dsp(); +} + +static void garray_print(t_garray *x) { + t_array *array = garray_getarray(x); + post("garray %s: template %s, length %d", x->realname->name, array->templatesym->name, array->n); +} + +static void g_array_setup() { + t_class *c = garray_class = class_new2("array",0,garray_free,sizeof(t_garray),CLASS_GOBJ,""); + class_addlist(garray_class, garray_list); + class_addmethod2(c, garray_const, "const", "F"); + class_addmethod2(c, garray_bounds, "bounds", "ffff"); + class_addmethod2(c, garray_xticks, "xticks", "fff"); + class_addmethod2(c, garray_xlabel, "xlabel", "*"); + class_addmethod2(c, garray_yticks, "yticks", "fff"); + class_addmethod2(c, garray_ylabel, "ylabel", "*"); + class_addmethod2(c, garray_rename, "rename", "s"); + class_addmethod2(c, garray_read, "read", "s"); + class_addmethod2(c, garray_write, "write", "s"); + class_addmethod2(c, garray_resize, "resize", "f"); + class_addmethod2(c, garray_print, "print", ""); + class_addmethod2(c, garray_sinesum, "sinesum", "*"); + class_addmethod2(c, garray_cosinesum, "cosinesum", "*"); + class_addmethod2(c, garray_normalize, "normalize", "F"); + class_addmethod2(c, garray_arraydialog, "arraydialog", "sfff"); + class_addmethod2(c, garray_arrayviewlist_new, "arrayviewlistnew", ""); + class_addmethod2(c, garray_arrayviewlist_fillpage, "arrayviewlistfillpage", "fF"); + class_addmethod2(c, garray_arrayviewlist_close, "arrayviewclose", ""); + class_setsavefn(c, garray_save); + array_class = class_new2("array_really",0,array_free,sizeof(t_array),CLASS_GOBJ,""); +} + +static void graph_graphrect(t_gobj *z, t_canvas *canvas, int *xp1, int *yp1, int *xp2, int *yp2); + +void canvas_add_debug(t_canvas *x, t_gobj *y) { + if (!y->_class->patchable) { + printf("canvas_add %p %p class=%s (non-t_text)\n",x,y,y->_class->name->name); + } else { + t_binbuf *bb = ((t_text *)y)->binbuf; + if (binbuf_getvec(bb)) { + char *buf; int bufn; + binbuf_gettext(bb,&buf,&bufn); + printf("canvas_add %p %p [%.*s]\n",x,y,bufn,buf); + free(buf); + } else { + printf("canvas_add %p %p class=%s (binbuf without b_vec !)\n",x,y,y->_class->name->name); + } + } +} + +void canvas_add(t_canvas *x, t_gobj *y, int index) { + gobj_setcanvas(y,x); + if (index<0) y->dix->index = x->next_o_index++; + else y->dix->index = index; + x->boxes->add(y); + if (x->gop && !x->goprect && pd_checkobject(y)) SET(goprect,1); + //if (class_isdrawcommand(y->_class)) canvas_redrawallfortemplate(template_findbyname(canvas_makebindsym(canvas_getcanvas(x)->name)), 0); +} + +/* delete an object from a canvas and free it */ +void canvas_delete(t_canvas *x, t_gobj *y) { + bool chkdsp = !!zgetfn(y,gensym("dsp")); + //int drawcommand = class_isdrawcommand(y->_class); + /* if we're a drawing command, erase all scalars now, before deleting it; we'll redraw them once it's deleted below. */ + //if (drawcommand) canvas_redrawallfortemplate(template_findbyname(canvas_makebindsym(canvas_getcanvas(x)->name)), 2); + canvas_deletelinesfor(x,(t_text *)y); + x->boxes->remove_by_value(y); + /* BUG: should call gobj_onsubscribe here, to flush the zombie */ + pd_free(y); + if (chkdsp) canvas_update_dsp(); + //if (drawcommand) canvas_redrawallfortemplate(template_findbyname(canvas_makebindsym(canvas_getcanvas(x)->name)), 1); +} + +static void canvas_clear(t_canvas *x) { + t_gobj *y; + int dspstate = 0, suspended = 0; + t_symbol *dspsym = gensym("dsp"); + /* to avoid unnecessary DSP resorting, we suspend DSP only if we find a DSP object. */ + canvas_each(y,x) if (!suspended && pd_checkobject(y) && zgetfn(y,dspsym)) {dspstate = canvas_suspend_dsp(); suspended=1;} + while ((y = x->boxes->first())) x->boxes->remove_by_value(y); + if (suspended) canvas_resume_dsp(dspstate); +} + + +t_canvas *canvas_getcanvas(t_canvas *x) { + while (x->dix->canvas && !x->havewindow && x->gop) x = x->dix->canvas; + return x; +} + +static void scalar_getbasexy(t_scalar *x, float *basex, float *basey); + +static float gobj_getxforsort(t_gobj *g) { + if (g->_class!=scalar_class) return 0; + float x1, y1; + scalar_getbasexy((t_scalar *)g, &x1, &y1); + return x1; +} + +static t_gobj *canvas_merge(t_canvas *x, t_gobj *g1, t_gobj *g2) { +/* + t_gobj *g = 0, *g9 = 0; + float f1 = g1 ? gobj_getxforsort(g1) : 0; + float f2 = g2 ? gobj_getxforsort(g2) : 0; + while (1) { + if (g1 && !(g2 && f1>f2)) { + if (g9) {g9->g_next = g1; g9 = g1;} else g9 = g = g1; + if ((g1 = g1->next())) f1 = gobj_getxforsort(g1); + g9->g_next = 0; + continue; + } + if (g1 || g2) { + if (g9) {g9->g_next = g2; g9 = g2;} else g9 = g = g2; + if ((g2 = g2->next())) f2 = gobj_getxforsort(g2); + g9->g_next = 0; + continue; + } + break; + } + return g; +*/ +} + +/* +static t_gobj *canvas_dosort(t_canvas *x, t_gobj *g, int nitems) { + t_gobj *g2, *g3; + int n1 = nitems/2, n2 = nitems - n1, i; + if (nitems < 2) return g; + int i=n1-1; + for (g2 = g; i--; g2 = g2->next()) {} + g3 = g2->next(); + g2->g_next = 0; + g = canvas_dosort(x, g, n1); + g3 = canvas_dosort(x, g3, n2); + return canvas_merge(x, g, g3); +} + +void canvas_sort(t_canvas *x) { + int nitems = 0, foo = 0; + float lastx = -1e37; + canvas_each(g,x) { + float x1 = gobj_getxforsort(g); + if (x1 < lastx) foo = 1; + lastx = x1; + nitems++; + } + if (foo) x->list = canvas_dosort(x, x->list, nitems); +} +*/ + +static t_inlet *canvas_addinlet(t_canvas *x, t_pd *who, t_symbol *s, t_symbol* h) { + t_inlet *ip = inlet_new(x,who,s,0); inlet_settip(ip,h); + if (gstack_empty()) canvas_resortinlets(x); + gobj_changed(x,0); return ip; +} +static t_outlet *canvas_addoutlet(t_canvas *x, t_pd *who, t_symbol *s) { + t_outlet *op = outlet_new(x,s); + if (gstack_empty()) canvas_resortoutlets(x); + gobj_changed(x,0); return op; +} + +static void canvas_rminlet(t_canvas *x, t_inlet *ip) { + if (x->dix->canvas) canvas_deletelinesforio(x->dix->canvas,x,ip,0); + inlet_free(ip); /*gobj_changed(x,0);*/ +} +static void canvas_rmoutlet(t_canvas *x, t_outlet *op) { + if (x->dix->canvas) canvas_deletelinesforio(x->dix->canvas,x,0,op); + outlet_free(op); /*gobj_changed(x,0);*/ +} + +extern "C" t_inlet *vinlet_getit(t_pd *x); +extern "C" t_outlet *voutlet_getit(t_pd *x); + +typedef int (*t_order)(const void *, const void *); +int gobj_order_x (t_object **a, t_object **b) {return (*a)->x - (*b)->x;} + +//{std::ostringstream s; s<<"disorder:"; for (int i=0; i<n; i++) s<<" "<<vec[i]->x; post("%s",s.str().data());} + +void obj_moveinletfirst(t_object *x, t_inlet *i); +void obj_moveoutletfirst(t_object *x, t_outlet *o); + +static void canvas_resortinlets(t_canvas *x) { + int n=0; canvas_each(y,x) if (y->_class==vinlet_class) n++; + t_object **vec = new t_object *[n], **vp = vec; + canvas_each(y,x) if (y->_class==vinlet_class) *vp++ = (t_object *)y; + qsort(vec,n,sizeof(t_object *),(t_order)gobj_order_x); + for (int i=n; i--;) obj_moveinletfirst(x,vinlet_getit(vec[i])); + delete[] vec; +} +static void canvas_resortoutlets(t_canvas *x) { + int n=0; canvas_each(y,x) if (y->_class==voutlet_class) n++; + t_object **vec = new t_object *[n], **vp = vec; + canvas_each(y,x) if (y->_class==voutlet_class) *vp++ = (t_object *)y; + qsort(vec,n,sizeof(t_object *),(t_order)gobj_order_x); + for (int i=n; i--;) obj_moveoutletfirst(x,voutlet_getit(vec[i])); + delete[] vec; +} + +static void graph_bounds(t_canvas *x, t_floatarg x1, t_floatarg y1, t_floatarg x2, t_floatarg y2) { + x->x1 = x1; x->y1 = y1; + x->x2 = x2; x->y2 = y2; + if (x->x2 == x->x1 || x->y2 == x->y1) { + error("empty bounds rectangle"); + x->x1 = x->y1 = 0; + x->x2 = x->y2 = 1; + } + gobj_changed(x,0); +} + +static void graph_xticks(t_canvas *x, t_floatarg point, t_floatarg inc, t_floatarg f) +{t_tick *t = &x->xtick; t->point = point; t->inc = inc; t->lperb = (int)f; gobj_changed(x,"xticks");} +static void graph_yticks(t_canvas *x, t_floatarg point, t_floatarg inc, t_floatarg f) +{t_tick *t = &x->ytick; t->point = point; t->inc = inc; t->lperb = (int)f; gobj_changed(x,"yticks");} + +static void graph_xlabel(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { + if (argc < 1) {error("graph_xlabel: no y value given"); return;} + x->xlabely = atom_getfloatarg(0,argc--,argv++); + x->xlabel = (t_symbol **)realloc(x->xlabel,argc*sizeof(void*)); + x->nxlabels = argc; + for (int i=0; i < argc; i++) x->xlabel[i] = atom_gensym(&argv[i]); + gobj_changed(x,"xlabel"); +} +static void graph_ylabel(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { + if (argc < 1) {error("graph_ylabel: no x value given"); return;} + x->ylabelx = atom_getfloatarg(0,argc--,argv++); + x->ylabel = (t_symbol **)realloc(x->ylabel,argc*sizeof(void*)); + x->nylabels = argc; + for (int i=0; i < argc; i++) x->ylabel[i] = atom_gensym(&argv[i]); + gobj_changed(x,"ylabel"); +} + +/* 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 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. + otherwise, we appear in a graph within a parent canvas, so get our screen rectangle on parent and transform. */ +static float canvas_pixelstox(t_canvas *x, float xpix) { + int x1, y1, x2, y2; float width = x->x2-x->x1; + if (!x->gop) return x->x1 + width * xpix; + if (x->havewindow) return x->x1 + width * xpix / (x->screenx2-x->screenx1); + graph_graphrect(x, x->dix->canvas, &x1, &y1, &x2, &y2); + return x->x1 + width * (xpix-x1) / (x2-x1); +} +static float canvas_pixelstoy(t_canvas *x, float ypix) { + int x1, y1, x2, y2; float height = x->y2-x->y1; + if (!x->gop) return x->y1 + height * ypix; + if (x->havewindow) return x->y1 + height * ypix / (x->screeny2-x->screeny1); + graph_graphrect(x, x->dix->canvas, &x1, &y1, &x2, &y2); + return x->y1 + height * (ypix-y1) / (y2-y1); +} + +/* convert an x coordinate value to an x pixel location in window */ +static int canvas_xtopixels(t_canvas *x, float xval) { + int x1, y1, x2, y2; float width = x->x2-x->x1; + if (!x->gop) return int((xval-x->x1)/width); + if (x->havewindow) return int((x->screenx2-x->screenx1) * (xval-x->x1) / width); + graph_graphrect(x, x->dix->canvas, &x1, &y1, &x2, &y2); + return int(x1 + (x2-x1) * (xval-x->x1) / width); +} +static int canvas_ytopixels(t_canvas *x, float yval) { + int x1, y1, x2, y2; float height = x->y2-x->y1; + if (!x->gop) return int((yval-x->y1)/height); + if (x->havewindow) return int((x->screeny2-x->screeny1) * (yval-x->y1) / height); + graph_graphrect(x, x->dix->canvas, &x1, &y1, &x2, &y2); + return int(y1 + (y2-y1) * (yval-x->y1) / height); +} + +/* --------------------------- widget behavior ------------------- */ +/* don't remove this code yet: has to be rewritten in tcl */ +#if 1 +#define FONT "pourier" +static void graph_vis(t_gobj *gr, int vis) { + t_canvas *x = (t_canvas *)gr; + t_canvas *c = canvas_getcanvas(x->dix->canvas); + char tag[50]; + int x1=69, y1=69, x2=69, y2=69; + sprintf(tag, "graph%lx", (t_int)x); + if (vis) { + sys_mgui(x,"ninlets=","i",0/*obj_ninlets(x)*/); + sys_mgui(x,"noutlets=","i",0/*obj_noutlets(x)*/); + } + /* if we look like a graph but have been moved to a toplevel, just show the bounding rectangle */ + if (x->havewindow) { + /*if (vis) sys_vgui(".x%lx.c create polygon %d %d %d %d %d %d %d %d %d %d -tags %s -fill #c0c0c0\n", + (long)c, x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag);*/ + return; + } + /* draw a rectangle around the graph */ + sys_vgui(".x%lx.c create line %d %d %d %d %d %d %d %d %d %d -tags %s\n", + (long)c, x1, y1, x1, y2, x2, y2, x2, y1, x1, y1, tag); + /* if there's just one "garray" in the graph, write its name along the top */ + int i = min(y1,y2)-1; + t_symbol *arrayname; + canvas_each(g,x) if (g->g_pd == garray_class && !garray_getname((t_garray *)g, &arrayname)) { + // i -= sys_fontheight(glist_getfont(x)); + sys_vgui(".x%lx.c create text %d %d -text {%s} -anchor nw\ + -font -*-courier-bold--normal--%d-* -tags %s\n", + (long)canvas_getcanvas(x), x1, i, arrayname->name, + 42/*sys_hostfontsize(canvas_getfont(x))*/, tag); + } + + /* draw ticks on horizontal borders. If lperb field is zero, this is disabled. */ + #define DRAWTICK(x1,y1,x2,y2) sys_vgui(".x%lx.c create line %d %d %d %d -tags %s\n", \ + (long)c, int(x1),int(y1),int(x2),int(y2),tag) + float f; + if (x->xtick.lperb) { + float upix, lpix; + if (y2<y1) {upix = y1; lpix = y2;} + else {upix = y2; lpix = y1;} + for (i=0,f=x->xtick.point; f<0.99*x->x2+0.01*x->x1; i++, f+=x->xtick.inc) { + int tickpix = i%x->xtick.lperb?2:4, x0 = canvas_xtopixels(x,f); + DRAWTICK(x0,upix,x0,upix-tickpix); + DRAWTICK(x0,lpix,x0,lpix+tickpix); + } + for (i=1,f=x->xtick.point-x->xtick.inc; f>0.99*x->x1+0.01*x->x2; i++,f-=x->xtick.inc) { + int tickpix = i%x->xtick.lperb?2:4, x0 = canvas_xtopixels(x,f); + DRAWTICK(x0,upix,x0,upix-tickpix); + DRAWTICK(x0,lpix,x0,lpix+tickpix); + } + } + /* draw ticks in vertical borders*/ + if (x->ytick.lperb) { + float ubound, lbound; + if (x->y2<x->y1) {ubound = x->y1; lbound = x->y2;} + else {ubound = x->y2; lbound = x->y1;} + for (i=0,f=x->ytick.point; f<0.99*ubound+0.01*lbound; i++, f += x->ytick.inc) { + int tickpix = i%x->ytick.lperb?2:4, y0 = canvas_ytopixels(x,f); + DRAWTICK(x1,y0,x1+tickpix,y0); + DRAWTICK(x2,y0,x2-tickpix,y0); + } + for (i=1,f=x->ytick.point-x->ytick.inc; f>0.99*lbound+0.01*ubound; i++,f-=x->ytick.inc) { + int tickpix = i%x->ytick.lperb?2:4, y0 = canvas_ytopixels(x,f); + DRAWTICK(x1,y0,x1+tickpix,y0); + DRAWTICK(x2,y0,x2-tickpix,y0); + } + } + /* draw labels */ + #define DRAWLABEL(x1,y1) sys_vgui(".x%lx.c create text %d %d -text {%s} -font "FONT" -tags %s\n", (long)c, \ + int(canvas_xtopixels(x,x1)),int(canvas_ytopixels(x,y1)),s,42,tag); + for (int i=0; i < x->nxlabels; i++) {char *s = x->xlabel[i]->name; DRAWLABEL(atof(s),x->xlabely);} + for (int i=0; i < x->nylabels; i++) {char *s = x->ylabel[i]->name; DRAWLABEL(x->ylabelx,atof(s));} +} +#endif + +static int text_xpix(t_text *x, t_canvas *canvas) { + float width = canvas->x2-canvas->x1; + if (canvas->havewindow || !canvas->gop) return x->x; + if (canvas->goprect) return canvas->x+x->x-canvas->xmargin; + return canvas_xtopixels(canvas, canvas->x1 + width * x->x / (canvas->screenx2-canvas->screenx1)); +} +static int text_ypix(t_text *x, t_canvas *canvas) { + float height = canvas->y2-canvas->y1; + if (canvas->havewindow || !canvas->gop) return x->y; + if (canvas->goprect) return canvas->y+x->y-canvas->ymargin; + return canvas_ytopixels(canvas, canvas->y1 + height* x->y / (canvas->screeny2-canvas->screeny1)); +} +static void graph_graphrect(t_gobj *z, t_canvas *canvas, int *xp1, int *yp1, int *xp2, int *yp2) { + t_canvas *x = (t_canvas *)z; + *xp1 = text_xpix(x,canvas); *xp2 = *xp1+x->pixwidth; + *yp1 = text_ypix(x,canvas); *yp2 = *yp1+x->pixheight; +} + +#if 1 +static float graph_lastxpix, graph_lastypix; +static void graph_motion(void *z, t_floatarg dx, t_floatarg dy) { + t_canvas *x = (t_canvas *)z; + float newxpix = graph_lastxpix + dx, newypix = graph_lastypix + dy; + t_garray *a = (t_garray *)x->boxes->first(); + int oldx = int(0.5 + canvas_pixelstox(x, graph_lastxpix)); + int newx = int(0.5 + canvas_pixelstox(x, newxpix)); + float oldy = canvas_pixelstoy(x, graph_lastypix); + float newy = canvas_pixelstoy(x, newypix); + graph_lastxpix = newxpix; + graph_lastypix = newypix; + // verify that the array is OK + if (!a || a->_class != garray_class) return; + int nelem; + t_float *vec; + if (!garray_getfloatarray(a, &nelem, &vec)) return; + if (oldx < 0) oldx = 0; else if (oldx >= nelem) oldx = nelem - 1; + if (newx < 0) newx = 0; else if (newx >= nelem) newx = nelem - 1; + if (oldx < newx - 1) {for (int i=oldx+1; i<=newx; i++) vec[i] = newy + (oldy-newy) * float(newx-i)/float(newx - oldx);} + else if (oldx > newx + 1) {for (int i=oldx-1; i>=newx; i--) vec[i] = newy + (oldy-newy) * float(newx-i)/float(newx - oldx);} + else vec[newx] = newy; + garray_redraw(a); +} +#endif + +/* functions to read and write canvases to files: canvas_savetofile() writes a root canvas to a "pd" file. + (Reading "pd" files is done simply by passing the contents to the pd message interpreter.) + Alternatively, the glist_read() and glist_write() functions read and write "data" from and to files + (reading reads into an existing canvas), using a file format as in the dialog window for data. */ +static t_class *declare_class; +void canvas_savedeclarationsto(t_canvas *x, t_binbuf *b); + +/* the following functions 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; + 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; +} +static int canvas_readscalar(t_canvas *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 canvas_readatoms(t_canvas *x, int natoms, t_atom *vec, +int *p_nextmsg, t_symbol *templatesym, t_word *w, int argc, t_atom *argv) { + t_template *t = template_findbyname(templatesym); + if (!t) { + error("%s: no such template", templatesym->name); + *p_nextmsg = natoms; + return; + } + word_restore(w, t, argc, argv); + int n = t->n; + for (int i=0; i<n; i++) { + if (t->vec[i].type == DT_ARRAY) { + t_array *a = w[i].w_array; + int elemsize = a->elemsize, nitems = 0; + t_symbol *arraytemplatesym = t->vec[i].arraytemplate; + t_template *arraytemplate = template_findbyname(arraytemplatesym); + if (!arraytemplate) error("%s: no such template", arraytemplatesym->name); + else while (1) { + int message; + t_word *element; + int nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg); + /* empty line terminates array */ + if (!nline) break; + array_resize(a, nitems + 1); + element = (t_word *)&a->vec[nitems*elemsize]; + canvas_readatoms(x, natoms, vec, p_nextmsg, arraytemplatesym, element, nline, vec + message); + nitems++; + } + } else if (t->vec[i].type == DT_CANVAS) { + while (1) { + if (!canvas_readscalar(w->w_canvas, natoms, vec, p_nextmsg, 0)) break; + } + } + } +} + +static int canvas_readscalar(t_canvas *x, int natoms, t_atom *vec, int *p_nextmsg, int selectit) { + int nextmsg = *p_nextmsg; + //int wasvis = canvas_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; + } + t_symbol *ts = canvas_makebindsym(vec[nextmsg].a_symbol); + *p_nextmsg = nextmsg + 1; + t_template *t = template_findbyname(ts); + if (!t) {error("%s: no such template", ts->name); *p_nextmsg = natoms; return 0;} + t_scalar *sc = scalar_new(x, ts); + if (!sc) {error("couldn't create scalar \"%s\"", ts->name); *p_nextmsg = natoms; return 0;} + //if (wasvis) canvas_getcanvas(x)->mapped = 0; + canvas_add(x,sc); + int message; + int nline = canvas_scanbinbuf(natoms, vec, &message, p_nextmsg); + canvas_readatoms(x, natoms, vec, p_nextmsg, ts, sc->v, nline, vec + message); + //if (wasvis) canvas_getcanvas(x)->mapped = 1; + gobj_changed(sc,0);//is this necessary? + return 1; +} + +static void canvas_readfrombinbuf(t_canvas *x, t_binbuf *b, char *filename, int selectem) { + int message, nextmsg = 0; + int natoms = binbuf_getnatom(b); + t_atom *vec = binbuf_getvec(b); + /* check for file type */ + int nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); + if (nline!=1 && vec[message].a_type != A_SYMBOL && strcmp(vec[message].a_symbol->name, "data")) { + error("%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_atom *templateargs = (t_atom *)getbytes(0); + int ntemplateargs = 0; + 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_symbol->name, "template") || + vec[message+1].a_type != A_SYMBOL) { + canvas_readerror(natoms, vec, message, nline, "bad template header"); + continue; + } + t_symbol *templatesym = canvas_makebindsym(vec[message + 1].a_symbol); + while (1) { + nline = canvas_scanbinbuf(natoms, vec, &message, &nextmsg); + if (nline != 2 && nline != 3) break; + int newnargs = ntemplateargs + nline; + templateargs = (t_atom *)realloc(templateargs, 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); + free(templateargs); + if (!(existtemplate = template_findbyname(templatesym))) { + error("%s: template not found in current patch", templatesym->name); + template_free(newtemplate); + return; + } + if (!template_match(existtemplate, newtemplate)) { + error("%s: template doesn't match current one", templatesym->name); + template_free(newtemplate); + return; + } + template_free(newtemplate); + } + while (nextmsg < natoms) canvas_readscalar(x, natoms, vec, &nextmsg, selectem); +} + +static void canvas_doread(t_canvas *x, t_symbol *filename, t_symbol *format, int clearme) { + t_binbuf *b = binbuf_new(); + t_canvas *canvas = canvas_getcanvas(x); + int wasvis = canvas_isvisible(canvas); + int cr = strcmp(format->name, "cr")==0; + if (!cr && *format->name) error("unknown flag: %s", format->name); + /* flag 2 means eval continuously. this is required to autodetect the syntax */ + if (binbuf_read_via_path(b, filename->name, canvas_getdir(canvas)->name, cr|2)) { + error("read failed"); + binbuf_free(b); + return; + } + if (wasvis) canvas_vis(canvas, 0); + if (clearme) canvas_clear(x); + /* canvas_readfrombinbuf(x, b, filename->name, 0); */ /* what's this for? */ + if (wasvis) canvas_vis(canvas, 1); + binbuf_free(b); +} + +static void canvas_read( t_canvas *x, t_symbol *filename, t_symbol *format) {canvas_doread(x,filename,format,1);} +static void canvas_mergefile(t_canvas *x, t_symbol *filename, t_symbol *format) {canvas_doread(x,filename,format,0);} + +/* read text from a "properties" window, in answer to 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) { +// t_gobj *oldone = 0; +// t_gobj *newone = 0; + x->boxes->remove_by_value(sc); +// if (!newone) {error("couldn't update properties (perhaps a format problem?)"); return;} +// if (!oldone) {bug("data_properties: couldn't find old element"); return;} + canvas_readfrombinbuf(x, b, "properties dialog", 0); +} + +static void canvas_doaddtemplate(t_symbol *templatesym, int *p_ntemplates, t_symbol ***p_templatevec) { + int n = *p_ntemplates; + t_symbol **templatevec = *p_templatevec; + for (int i=0; i < n; i++) if (templatevec[i] == templatesym) return; + templatevec = (t_symbol **)realloc(templatevec, (n+1)*sizeof(*templatevec)); + templatevec[n] = templatesym; + *p_templatevec = templatevec; + *p_ntemplates = n+1; +} + +static void canvas_writelist(t_gobj *y, t_binbuf *b); + +static void canvas_writescalar(t_symbol *templatesym, t_word *w, t_binbuf *b, int amarrayelement) { + t_template *t = template_findbyname(templatesym); + t_atom *a = (t_atom *)getbytes(0); + int n = t->n, natom = 0; + if (!amarrayelement) { + t_atom templatename; + SETSYMBOL(&templatename, gensym(templatesym->name + 3)); + binbuf_add(b, 1, &templatename); + } + if (!t) bug("canvas_writescalar"); + /* write the atoms (floats and symbols) */ + for (int i=0; i<n; i++) { + int ty = t->vec[i].type; + if (ty==DT_FLOAT || ty==DT_SYMBOL) { + a = (t_atom *)realloc(a, (natom+1)*sizeof(*a)); + if (t->vec[i].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); + free(a); + for (int i=0; i<n; i++) { + if (t->vec[i].type == DT_ARRAY) { + t_array *a = w[i].w_array; + int elemsize = a->elemsize, nitems = a->n; + t_symbol *arraytemplatesym = t->vec[i].arraytemplate; + for (int j = 0; j < nitems; j++) + canvas_writescalar(arraytemplatesym, (t_word *)&a->vec[elemsize*j], b, 1); + binbuf_addsemi(b); + } else if (t->vec[i].type == DT_CANVAS) { + canvas_writelist(w->w_canvas->boxes->first(), b); + binbuf_addsemi(b); + } + } +} + +static void canvas_writelist(t_gobj *y, t_binbuf *b) { + for (; y; y = y->next()) if (y->_class==scalar_class) { + t_scalar *z = (t_scalar *)y; + canvas_writescalar(z->t, z->v, b, 0); + } +} + +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_template *t = template_findbyname(templatesym); + canvas_doaddtemplate(templatesym, p_ntemplates, p_templatevec); + if (!t) {bug("canvas_addtemplatesforscalar"); return;} + t_dataslot *ds = t->vec; + for (int i=t->n; i--; ds++, w++) { + if (ds->type == DT_ARRAY) { + t_array *a = w->w_array; + int elemsize = a->elemsize, nitems = a->n; + t_symbol *arraytemplatesym = ds->arraytemplate; + canvas_doaddtemplate(arraytemplatesym, p_ntemplates, p_templatevec); + for (int j=0; j<nitems; j++) + canvas_addtemplatesforscalar(arraytemplatesym, (t_word *)&a->vec[elemsize*j], p_ntemplates, p_templatevec); + } else if (ds->type == DT_CANVAS) + canvas_addtemplatesforlist(w->w_canvas->boxes->first(), p_ntemplates, p_templatevec); + } +} + +static void canvas_addtemplatesforlist(t_gobj *y, int *p_ntemplates, t_symbol ***p_templatevec) { + for (; y; y = y->next()) if (y->_class == scalar_class) { + t_scalar *z = (t_scalar *)y; + canvas_addtemplatesforscalar(z->t, z->v, p_ntemplates, p_templatevec); + } +} + +static t_binbuf *canvas_writetobinbuf(t_canvas *x) { + t_symbol **templatevec = (t_symbol **)getbytes(0); + int ntemplates = 0; + t_binbuf *b = binbuf_new(); + canvas_each(y,x) if (y->_class==scalar_class) { + t_scalar *s = (t_scalar *)y; + canvas_addtemplatesforscalar(s->t, s->v, &ntemplates, &templatevec); + } + binbuf_addv(b,"t;","data"); + for (int i=0; i<ntemplates; i++) { + t_template *t = template_findbyname(templatevec[i]); + int m = t->n; + /* drop "pd-" prefix from template symbol to print it: */ + binbuf_addv(b,"tt;","template",templatevec[i]->name + 3); + for (int j=0; j<m; j++) { + t_symbol *type; + switch (t->vec[j].type) { + case DT_FLOAT: type = &s_float; break; + case DT_SYMBOL: type = &s_symbol; break; + case DT_ARRAY: type = gensym("array"); break; + case DT_CANVAS: type = &s_list; break; + default: type = &s_float; bug("canvas_write"); + } + if (t->vec[j].type == DT_ARRAY) + binbuf_addv(b,"sst;", type, t->vec[j].name, t->vec[j].arraytemplate->name + 3); + else binbuf_addv(b,"ss;", type, t->vec[j].name); + } + binbuf_addsemi(b); + } + binbuf_addsemi(b); + /* now write out the objects themselves */ + canvas_each(y,x) if (y->_class==scalar_class) { + t_scalar *z = (t_scalar *)y; + canvas_writescalar(z->t, z->v, b, 0); + } + return b; +} + +static void canvas_write(t_canvas *x, t_symbol *filename, t_symbol *format) { + t_canvas *canvas = canvas_getcanvas(x); + char *buf = canvas_makefilename(canvas,filename->name,0,0); + int cr = strcmp(format->name, "cr")==0; + if (!cr && *format->name) error("canvas_write: unknown flag: %s", format->name); + t_binbuf *b = canvas_writetobinbuf(x); + if (b) { + if (binbuf_write(b, buf, "", cr)) error("%s: write failed", filename->name); + binbuf_free(b); + } + free(buf); +} + +/* ------ functions 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. */ +void canvas_savecontainerto(t_canvas *x, t_binbuf *b) { + /* have to go to original binbuf to find out how we were named. */ + t_binbuf *bz = binbuf_new(); + t_symbol *patchsym = &s_; + if (x->binbuf) { + binbuf_addbinbuf(bz, x->binbuf); + patchsym = atom_getsymbolarg(1, binbuf_getnatom(bz), binbuf_getvec(bz)); + binbuf_free(bz); + } + int x1=x->screenx1, xs=x->screenx2-x1; + int y1=x->screeny1, ys=x->screeny2-y1; + binbuf_addv(b,"ttiiii","#N","canvas",x1,y1,xs,ys); + if (x->dix->canvas && !x->env) { /* subpatch */ + binbuf_addv(b, "si;", (patchsym != &s_ ? patchsym: gensym("(subpatch)")), x->havewindow); + } else { /* root or abstraction */ + binbuf_addv(b, "i;", (int)x->font); + canvas_savedeclarationsto(x, b); + } +} + +static void canvas_savecoordsto(t_canvas *x, t_binbuf *b) { + /* if everything is the default, skip saving this line */ + if (!x->gop && x->x1==0 && x->y1==0 && x->x2==1 && x->y2==1 && x->pixwidth==0 && x->pixheight==0) return; + /* if we have a graph-on-parent rectangle, we're new style. The format is arranged so + that old versions of Pd can at least do something with it. + otherwise write in 0.38-compatible form. */ + binbuf_addv(b,"ttffffffi","#X","coords", x->x1,x->y1,x->x2,x->y2, (float)x->pixwidth,(float)x->pixheight, x->gop?x->hidetext?2:1:0); + if (x->goprect) binbuf_addv(b, "ff", (float)x->xmargin, (float)x->ymargin); + binbuf_addv(b,";"); +} + +/* get the index of a gobj in a canvas. If y is zero, return the total number of objects. */ +int canvas_oldindex(t_canvas *x, t_gobj *y) { + int i=0; + canvas_each(y2,x) {if (y2==y) break; else i++;} + return i; +} + +static void canvas_saveto(t_canvas *x, t_binbuf *b) { + canvas_savecontainerto(x,b); + canvas_each(y,x) gobj_save(y, b); + canvas_wires_each(oc,t,x) { + int from = canvas_oldindex(x,t.from); + int to = canvas_oldindex(x,t.to); + binbuf_addv(b, "ttiiii;","#X","connect", from, t.outlet, to, t.inlet); + appendix_save(oc,b); + } + canvas_savecoordsto(x,b); +} + +/* 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) { + canvas_each(y,x) { + if (y->_class==scalar_class) { + t_scalar *z = (t_scalar *)y; + canvas_addtemplatesforscalar(z->t, z->v, ntemplatesp, templatevecp); + } else if (y->_class==canvas_class) { + canvas_collecttemplatesfor((t_canvas *)y, ntemplatesp, templatevecp); + } + } +} + +/* save the templates needed by a canvas to a binbuf. */ +static void canvas_savetemplatesto(t_canvas *x, t_binbuf *b) { + t_symbol **templatevec = (t_symbol **)getbytes(0); + int ntemplates = 0; + canvas_collecttemplatesfor(x, &ntemplates, &templatevec); + for (int i=0; i < ntemplates; i++) { + t_template *t = template_findbyname(templatevec[i]); + if (!t) { + bug("canvas_savetemplatesto"); + continue; + } + /* drop "pd-" prefix from template symbol to print */ + binbuf_addv(b,"ttt","#N","struct",templatevec[i]->name+3); + for (int j=0; j<t->n; j++) { + t_symbol *type; + switch (t->vec[j].type) { + case DT_FLOAT: type = &s_float; break; + case DT_SYMBOL: type = &s_symbol; break; + case DT_ARRAY: type = gensym("array"); break; + case DT_CANVAS: type = &s_list; break; + default: type = &s_float; bug("canvas_write"); + } + binbuf_addv(b,"ss",type,t->vec[j].name); + if (t->vec[j].type == DT_ARRAY) binbuf_addv(b, "t", t->vec[j].arraytemplate->name + 3); + } + 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(); + int dsp_status = canvas_suspend_dsp(); + canvas_savetemplatesto(x, b); + canvas_saveto(x, b); + if (!binbuf_write(b, filename->name, dir->name, 0)) { + /* if not an abstraction, reset title bar and directory */ + if (!x->dix->canvas) canvas_rename(x, filename, dir); + post("saved to: %s/%s", dir->name, filename->name); + canvas_reload(filename,dir,x); + } + binbuf_free(b); + canvas_resume_dsp(dsp_status); +} + +/////////////////////////////////////////////////////////////////////////// +// from g_io.c + +/* graphical inlets and outlets, both for control and signals. */ +/* iohannes added multiple samplerates support in vinlet/voutlet */ + +extern "C" void signal_setborrowed(t_signal *sig, t_signal *sig2); +extern "C" void signal_makereusable(t_signal *sig); +extern "C" void inlet_sethelp(t_inlet* i,t_symbol* s); + +/* ------------------------- vinlet -------------------------- */ +t_class *vinlet_class; + +struct t_vinlet : t_object { + t_canvas *canvas; + t_inlet *inlet; + int bufsize; + t_float *buf; /* signal buffer; zero if not a signal */ + t_float *endbuf; + t_float *fill; + t_float *read; + int hop; + /* if not reblocking, the next slot communicates the parent's inlet signal from the prolog to the DSP routine: */ + t_signal *directsignal; + t_resample updown; /* IOhannes */ +}; + +static void *vinlet_new(t_symbol *s) { + t_vinlet *x = (t_vinlet *)pd_new(vinlet_class); + x->canvas = canvas_getcurrent(); + x->inlet = canvas_addinlet(x->canvas,x,0,s); + x->bufsize = 0; + x->buf = 0; + outlet_new(x, 0); + return x; +} + +static void vinlet_bang(t_vinlet *x) {outlet_bang(x->outlet);} +static void vinlet_pointer(t_vinlet *x, t_gpointer *gp) {outlet_pointer(x->outlet, gp);} +static void vinlet_float(t_vinlet *x, t_float f) {outlet_float(x->outlet, f);} +static void vinlet_symbol(t_vinlet *x, t_symbol *s) {outlet_symbol(x->outlet, s);} +static void vinlet_list(t_vinlet *x, t_symbol *s, int argc, t_atom *argv) {outlet_list(x->outlet, s, argc, argv);} +static void vinlet_anything(t_vinlet *x, t_symbol *s, int argc, t_atom *argv) {outlet_anything(x->outlet, s, argc, argv);} + +static void vinlet_free(t_vinlet *x) { + canvas_rminlet(x->canvas, x->inlet); + resample_free(&x->updown); +} + +t_inlet *vinlet_getit(t_pd *x) { + if (pd_class(x) != vinlet_class) bug("vinlet_getit"); + return ((t_vinlet *)x)->inlet; +} + +/* ------------------------- signal inlet -------------------------- */ +int vinlet_issignal(t_vinlet *x) {return x->buf!=0;} + +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->read; + while (n--) *out++ = *in++; + if (in == x->endbuf) in = x->buf; + x->read = in; + return w+4; +} + +/* tb: vectorized */ +t_int *vinlet_perf8(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->read; + for (; n; n -= 8, in += 8, out += 8) { + out[0] = in[0]; out[1] = in[1]; out[2] = in[2]; out[3] = in[3]; + out[4] = in[4]; out[5] = in[5]; out[6] = in[6]; out[7] = in[7]; + } + if (in == x->endbuf) in = x->buf; + x->read = in; + return w+4; +} + +/* T.Grill: SIMD version */ +t_int *vinlet_perfsimd(t_int *w) { + t_vinlet *x = (t_vinlet *)(w[1]); + t_float *in = x->read; + copyvec_simd((t_float *)w[2],in,w[3]); + if (in == x->endbuf) in = x->buf; + x->read = in; + return w+4; +} + +static void vinlet_dsp(t_vinlet *x, t_signal **sp) { + if (!x->buf) return; /* no buffer means we're not a signal inlet */ + t_signal *outsig = sp[0]; + if (x->directsignal) signal_setborrowed(sp[0], x->directsignal); + else { + const int vecsize = outsig->vecsize; + /* if the outsig->v is aligned the x->read will also be... */ + if(vecsize&7) dsp_add(vinlet_perform, 3, x, outsig->v,vecsize); + else if(SIMD_CHECK1(outsig->n,outsig->v)) + dsp_add(vinlet_perfsimd, 3, x, outsig->v,vecsize); + else dsp_add(vinlet_perf8, 3, x, outsig->v,vecsize); + x->read = 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->fill; + if (out == x->endbuf) { + t_float *f1 = x->buf, *f2 = x->buf + x->hop; + int nshift = x->bufsize - x->hop; + out -= x->hop; + while (nshift--) *f1++ = *f2++; + } + while (n--) *out++ = *in++; + x->fill = out; + return w+4; +} + +extern "C" int inlet_getsignalindex(t_inlet *x); + +/* set up prolog DSP code */ +extern "C" void vinlet_dspprolog(t_vinlet *x, t_signal **parentsigs, int myvecsize, int calcsize, int phase, int period, +int frequency, int downsample, int upsample, int reblock, int switched) { + t_signal *insig; + x->updown.downsample = downsample; + 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->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 "fill" phase is in sync with the "read" phase. */ + prologphase = (phase - 1) & (period - 1); + if (parentsigs) { + insig = parentsigs[inlet_getsignalindex(x->inlet)]; + parentvecsize = insig->vecsize; + re_parentvecsize = parentvecsize * upsample / downsample; + } else { + insig = 0; + parentvecsize = 1; + re_parentvecsize = 1; + } + bufsize = max(re_parentvecsize,myvecsize); + oldbufsize = x->bufsize; + if (bufsize != oldbufsize) { + t_float *buf = x->buf; + buf = (t_float *)resizealignedbytes(buf,oldbufsize * sizeof(*buf), bufsize * sizeof(*buf)); + memset((char *)buf, 0, bufsize * sizeof(*buf)); + x->bufsize = bufsize; + x->endbuf = buf + bufsize; + x->buf = buf; + } + if (parentsigs) { + /* IOhannes { */ + x->hop = period * re_parentvecsize; + x->fill = x->endbuf - (x->hop - prologphase * re_parentvecsize); + if (upsample * downsample == 1) + dsp_add(vinlet_doprolog, 3, x, insig->v, re_parentvecsize); + else { + resamplefrom_dsp(&x->updown, insig->v, parentvecsize, re_parentvecsize, x->updown.method); + dsp_add(vinlet_doprolog, 3, x, x->updown.v, 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->refcount) signal_makereusable(insig); + } else memset((char *)x->buf, 0, bufsize * sizeof(*x->buf)); + 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->directsignal = parentsigs[inlet_getsignalindex(x->inlet)]; + } +} + +static void *vinlet_newsig(t_symbol *s) { + t_vinlet *x = (t_vinlet *)pd_new(vinlet_class); + x->canvas = canvas_getcurrent(); + x->inlet = canvas_addinlet(x->canvas,x,&s_signal,s); + x->endbuf = x->buf = (t_float *)getalignedbytes(0); + x->bufsize = 0; + x->directsignal = 0; + outlet_new(x, &s_signal); + resample_init(&x->updown); + /* this should be thought over: it might prove hard to provide consistency between labeled up- & downsampling methods + maybe indices would be better... + up till now we provide several upsampling methods and 1 single downsampling method (no filtering !) */ + if (s) { + char c=*s->name; + switch(c) { + case'h':case'H':x->updown.method=RESAMPLE_HOLD; break; /* up: sample and hold */ + case'l':case'L':x->updown.method=RESAMPLE_LINEAR; break; /* up: linear interpolation */ + case'b':case'B':x->updown.method=RESAMPLE_BLOCK; break; /* down: ignore the 2nd half of the block */ + default: x->updown.method=RESAMPLE_ZERO; /* up: zero-padding */ + } + } + return x; +} + +static void vinlet_setup() { + t_class *c = vinlet_class = class_new2("inlet",vinlet_new,vinlet_free,sizeof(t_vinlet),CLASS_NOINLET,"S"); + class_addcreator2("inlet~",vinlet_newsig,"S"); + class_addbang( c, vinlet_bang); + class_addpointer( c, vinlet_pointer); + class_addfloat( c, vinlet_float); + class_addsymbol( c, vinlet_symbol); + class_addlist( c, vinlet_list); + class_addanything(c, vinlet_anything); + class_addmethod2( c, vinlet_dsp,"dsp",""); + class_sethelpsymbol(c, gensym("pd")); +} + +/* ------------------------- voutlet -------------------------- */ + +t_class *voutlet_class; + +struct t_voutlet : t_object { + t_canvas *canvas; + t_outlet *parentoutlet; + int bufsize; + t_float *buf; /* signal buffer; zero if not a signal */ + t_float *endbuf; + t_float *empty; /* next to read out of buffer in epilog code */ + t_float *write; /* next to write in to buffer */ + int 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 functions. */ + t_signal *directsignal; + /* and here's a flag indicating that we aren't blocked but have to do a copy (because we're switched). */ + char justcopyout; + t_resample updown; /* IOhannes */ +}; + +static void *voutlet_new(t_symbol *s) { + t_voutlet *x = (t_voutlet *)pd_new(voutlet_class); + x->canvas = canvas_getcurrent(); + x->parentoutlet = canvas_addoutlet(x->canvas,x,0); + inlet_new(x,x,0,0); + x->bufsize = 0; + x->buf = 0; + return x; +} + +static void voutlet_bang(t_voutlet *x) +{outlet_bang(x->parentoutlet);} +static void voutlet_pointer(t_voutlet *x, t_gpointer *gp) +{outlet_pointer(x->parentoutlet, gp);} +static void voutlet_float(t_voutlet *x, t_float f) +{outlet_float(x->parentoutlet, f);} +static void voutlet_symbol(t_voutlet *x, t_symbol *s) +{outlet_symbol(x->parentoutlet, s);} +static void voutlet_list(t_voutlet *x, t_symbol *s, int argc, t_atom *argv) +{outlet_list(x->parentoutlet, s, argc, argv);} +static void voutlet_anything(t_voutlet *x, t_symbol *s, int argc, t_atom *argv) +{outlet_anything(x->parentoutlet, s, argc, argv);} + +static void voutlet_free(t_voutlet *x) { + canvas_rmoutlet(x->canvas, x->parentoutlet); + resample_free(&x->updown); +} + +t_outlet *voutlet_getit(t_pd *x) { + if (pd_class(x) != voutlet_class) bug("voutlet_getit"); + return ((t_voutlet *)x)->parentoutlet; +} + +/* ------------------------- signal outlet -------------------------- */ + +int voutlet_issignal(t_voutlet *x) {return 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->write, *outwas = out, *end = x->endbuf; + while (n--) { + *out++ += *in++; + if (out == end) out = x->buf; + } + outwas += x->hop; + if (outwas >= end) outwas = x->buf; + 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 */ + t_float *in = x->empty; + if (x->updown.downsample != x->updown.upsample) out = x->updown.v; /* IOhannes */ + for (int n = (int)(w[3]); n--; in++) *out++ = *in, *in = 0; + if (in == x->endbuf) in = x->buf; + 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 *in = x->empty; + t_float *out = x->updown.v; /* IOhannes */ + for (int n = (int)(w[2]); n--; in++) *out++ = *in, *in = 0; + if (in == x->endbuf) in = x->buf; + x->empty = in; + return w+3; +} +/* } IOhannes */ +extern "C" 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. */ +extern "C" void voutlet_dspprolog(t_voutlet *x, t_signal **parentsigs, int myvecsize, int calcsize, int phase, int period, +int frequency, int downsample, int upsample, int reblock, int switched) { + x->updown.downsample=downsample; x->updown.upsample=upsample; /* IOhannes */ + x->justcopyout = (switched && !reblock); + if (reblock) { + x->directsignal = 0; + } else { + if (!parentsigs) bug("voutlet_dspprolog"); + x->directsignal = parentsigs[outlet_getsignalindex(x->parentoutlet)]; + } +} + +static void voutlet_dsp(t_voutlet *x, t_signal **sp) { + if (!x->buf) return; + t_signal *insig = sp[0]; + if (x->justcopyout) dsp_add_copy(insig->v, x->directsignal->v, insig->n); + else if (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]->refcount++; */ + signal_setborrowed(x->directsignal, sp[0]); + } else dsp_add(voutlet_perform, 3, x, insig->v, insig->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. */ +extern "C" void voutlet_dspepilog(t_voutlet *x, t_signal **parentsigs, +int myvecsize, int calcsize, int phase, int period, int frequency, int downsample, int upsample, int reblock, int switched) { + if (!x->buf) return; /* this shouldn't be necesssary... */ + x->updown.downsample=downsample; + x->updown.upsample=upsample; /* IOhannes */ + if (reblock) { + t_signal *outsig; + int parentvecsize, bufsize, oldbufsize; + int re_parentvecsize; /* IOhannes */ + int bigperiod, epilogphase, blockphase; + if (parentsigs) { + outsig = parentsigs[outlet_getsignalindex(x->parentoutlet)]; + parentvecsize = outsig->vecsize; + re_parentvecsize = parentvecsize * upsample / downsample; + } else { + outsig = 0; + parentvecsize = 1; + re_parentvecsize = 1; + } + bigperiod = myvecsize/re_parentvecsize; /* IOhannes */ + if (!bigperiod) bigperiod = 1; + epilogphase = phase & (bigperiod - 1); + blockphase = (phase + period - 1) & (bigperiod - 1) & (- period); + bufsize = re_parentvecsize; /* IOhannes */ + if (bufsize < myvecsize) bufsize = myvecsize; + if (bufsize != (oldbufsize = x->bufsize)) { + t_float *buf = x->buf; + buf = (t_float *)resizealignedbytes(buf,oldbufsize * sizeof(*buf),bufsize * sizeof(*buf)); + memset((char *)buf, 0, bufsize * sizeof(*buf)); + x->bufsize = bufsize; + x->endbuf = buf + bufsize; + x->buf = buf; + } + /* IOhannes: { */ + if (re_parentvecsize * period > bufsize) bug("voutlet_dspepilog"); + x->write = x->buf + re_parentvecsize * blockphase; + if (x->write == x->endbuf) x->write = x->buf; + if (period == 1 && frequency > 1) x->hop = re_parentvecsize / frequency; + else x->hop = period * re_parentvecsize; + /* } IOhannes */ + if (parentsigs) { + /* set epilog pointer and schedule it */ + /* IOhannes { */ + x->empty = x->buf + re_parentvecsize * epilogphase; + if (upsample*downsample==1) + dsp_add(voutlet_doepilog, 3, x, outsig->v, re_parentvecsize); + else { + dsp_add(voutlet_doepilog_resampling, 2, x, re_parentvecsize); + resampleto_dsp(&x->updown, outsig->v, re_parentvecsize, parentvecsize, 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->parentoutlet)]; + dsp_add_zero(outsig->v, outsig->n); + } + } +} + +static void *voutlet_newsig(t_symbol *s) { + t_voutlet *x = (t_voutlet *)pd_new(voutlet_class); + x->canvas = canvas_getcurrent(); + x->parentoutlet = canvas_addoutlet(x->canvas,x,&s_signal); + inlet_new(x,x,&s_signal,&s_signal); + x->endbuf = x->buf = (t_float *)getalignedbytes(0); + x->bufsize = 0; + resample_init(&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) { + char c=*s->name; + switch(c) { + case 'h': case 'H': x->updown.method=RESAMPLE_HOLD; break; /* up: sample and hold */ + case 'l': case 'L': x->updown.method=RESAMPLE_LINEAR; break; /* up: linear interpolation */ + case 'b': case 'B': x->updown.method=RESAMPLE_BLOCK; break; /* down: ignore the 2nd half of the block */ + default: x->updown.method=RESAMPLE_ZERO; /* up: zero-padding */ + } + } + return x; +} + +static void voutlet_setup() { + t_class *c = voutlet_class = class_new2("outlet",voutlet_new,voutlet_free,sizeof(t_voutlet),CLASS_NOINLET,"S"); + class_addcreator2("outlet~",voutlet_newsig,"S"); + class_addbang( c, voutlet_bang); + class_addpointer( c, voutlet_pointer); + class_addfloat( c, (t_method)voutlet_float); + class_addsymbol( c, voutlet_symbol); + class_addlist( c, voutlet_list); + class_addanything(c, voutlet_anything); + class_addmethod2( c, voutlet_dsp, "dsp", ""); + class_sethelpsymbol(c, gensym("pd")); +} + +/* 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. + IOhannes changed the canvas_restore, so that it might accept $args as well (like "pd $0_test") + so you can make multiple & distinguishable templates; added Krzysztof Czajas fix to avoid crashing... */ +t_class *scalar_class; + +void word_init(t_word *wp, t_template *t, t_gpointer *gp) { + t_dataslot *datatypes = t->vec; + for (int i=0; i < t->n; i++, datatypes++, wp++) { + int type = datatypes->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->arraytemplate, gp); + else if (type == DT_CANVAS) { + /* LATER test this and get it to work */ + wp->w_canvas = canvas_new(0,0,0,0); + } + } +} + +void word_restore(t_word *wp, t_template *t, int argc, t_atom *argv) { + t_dataslot *datatypes = t->vec; + for (int i=0; i<t->n; i++, datatypes++, wp++) { + int type = datatypes->type; + if (type == DT_FLOAT) { + float f=0; + if (argc) {f = atom_getfloat(argv); argv++; argc--;} + wp->w_float = f; + } else if (type == DT_SYMBOL) { + t_symbol *s=&s_; + if (argc) {s = atom_getsymbol(argv); argv++; argc--;} + wp->w_symbol = s; + } + } + if (argc) post("warning: word_restore: extra arguments"); +} + +void word_free(t_word *wp, t_template *t) { + t_dataslot *dt = t->vec; + for (int i=0; i<t->n; i++, dt++) { + if (dt->type == DT_ARRAY) pd_free(wp[i].w_array); + else if (dt->type == DT_CANVAS) pd_free(wp[i].w_canvas); + } +} + +static void gpointer_setcanvas(t_gpointer *gp, t_canvas *canvas, t_scalar *x); +static void gpointer_setarray(t_gpointer *gp, t_array *array, t_word *w); +static t_word *gpointer_word(t_gpointer *gp) {return gp->o->_class == array_class ? gp->w : gp->scalar->v;} +static t_canvas *gpointer_getcanvas(t_gpointer *gp) { + if (gp->o->_class != array_class) return gp->canvas; + return 0; /* FIXME */ +} +static t_scalar *gpointer_getscalar(t_gpointer *gp) { + if (gp->o->_class != array_class) return gp->scalar; + return 0; +} + +/* make a new scalar and add to the canvas. 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_canvas *owner, t_symbol *templatesym) { + t_gpointer gp; + gpointer_init(&gp); + t_template *t = template_findbyname(templatesym); + TEMPLATE_CHECK(templatesym,0) + t_scalar *x = (t_scalar *)getbytes(sizeof(t_scalar) + (t->n - 1) * sizeof(*x->v)); + x->_class = scalar_class; + x->t = templatesym; + gpointer_setcanvas(&gp, owner, x); + word_init(x->v, t, &gp); + return x; +} + +/* Pd method to create a new scalar, add it to a canvas, and initialize it from the message arguments. */ +int canvas_readscalar(t_canvas *x, int natoms, t_atom *vec, int *p_nextmsg, int selectit); +static void canvas_scalar(t_canvas *canvas, t_symbol *classname, t_int argc, t_atom *argv) { + t_symbol *templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (!template_findbyname(templatesym)) {error("%s: no such template", atom_getsymbolarg(0, argc, argv)->name); return;} + int nextmsg; + t_binbuf *b = binbuf_new(); + binbuf_restore(b, argc, argv); + canvas_readscalar(canvas, binbuf_getnatom(b), binbuf_getvec(b), &nextmsg, 0); + binbuf_free(b); +} + +static void scalar_getbasexy(t_scalar *x, float *basex, float *basey) { + t_template *t = template_findbyname(x->t); + *basex = template_getfloat(t,&s_x,x->v,0); + *basey = template_getfloat(t,&s_y,x->v,0); +} + +/* +static void scalar_displace(t_gobj *z, t_canvas *canvas, int dx, int dy) { + t_scalar *x = (t_scalar *)z; + t_symbol *templatesym = x->t; + t_template *t = template_findbyname(templatesym); + t_symbol *zz; + int xonset, yonset, xtype, ytype, gotx, goty; + TEMPLATE_CHECK(templatesym,) + gotx = template_find_field(t,&s_x,&xonset,&xtype,&zz); + if (gotx && (xtype != DT_FLOAT)) gotx = 0; + goty = template_find_field(t,&s_y,&yonset,&ytype,&zz); + if (goty && (ytype != DT_FLOAT)) goty = 0; + if (gotx) *(t_float *)(((char *)(x->v)) + xonset) += dx * (canvas_pixelstox(canvas, 1) - canvas_pixelstox(canvas, 0)); + if (goty) *(t_float *)(((char *)(x->v)) + yonset) += dy * (canvas_pixelstoy(canvas, 1) - canvas_pixelstoy(canvas, 0)); + scalar_redraw(x, canvas); +}*/ + +static void scalar_vis(t_gobj *z, t_canvas *owner, int vis) { + t_scalar *x = (t_scalar *)z; + t_template *t = template_findbyname(x->t); + t_canvas *templatecanvas = template_findcanvas(t); + float basex, basey; + scalar_getbasexy(x, &basex, &basey); + /* if we don't know how to draw it, make a small rectangle */ + if (!templatecanvas) { + if (vis) { + int x1 = canvas_xtopixels(owner, basex); + int y1 = canvas_ytopixels(owner, basey); + sys_vgui(".x%lx.c create rectangle %d %d %d %d -tags scalar%lx\n", + (long)canvas_getcanvas(owner), x1-1, y1-1, x1+1, y1+1, (long)x); + } else sys_vgui(".x%lx.c delete scalar%lx\n", (long)canvas_getcanvas(owner), (long)x); + return; + } + //canvas_each(y,templatecanvas) pd_getparentwidget(y)->w_parentvisfn(y,owner,x->v,t,basex,basey,vis); + //sys_unqueuegui(x); +} + +static void scalar_doredraw(t_gobj *client, t_canvas *canvas) { + scalar_vis(client, canvas, 0); + scalar_vis(client, canvas, 1); +} + +void scalar_redraw(t_scalar *x, t_canvas *canvas) { + //if (canvas_isvisible(canvas)) sys_queuegui(x, canvas, scalar_doredraw); +} + +#if 0 +int scalar_doclick(t_word *data, t_template *t, t_scalar *sc, t_array *ap, t_canvas *owner, +float xloc, float yloc, int xpix, int ypix, int shift, int alt, int dbl, int doit) { + t_canvas *templatecanvas = template_findcanvas(t); + float basex = template_getfloat(t,&s_x,data,0); + float basey = template_getfloat(t,&s_y,data,0); + canvas_each(y,templatecanvas) { + int hit = pd_getparentwidget(y)->w_parentclickfn(y, owner, data, t, sc, ap, basex+xloc, basey+yloc, + xpix, ypix, shift, alt, dbl, doit); + if (hit) return hit; + }*/ + return 0; +} +#endif + +static int scalar_click(t_gobj *z, t_canvas *owner, int xpix, int ypix, int shift, int alt, int dbl, int doit) { + t_scalar *x = (t_scalar *)z; + t_template *t = template_findbyname(x->t); + return scalar_doclick(x->v, t, x, 0, owner, 0, 0, xpix, ypix, shift, alt, dbl, doit); +} + +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(); + canvas_writescalar(x->t, x->v, b2, 0); + binbuf_addv(b,"tt","#X","scalar"); + binbuf_addbinbuf(b, b2); + binbuf_addsemi(b); + binbuf_free(b2); +} + +/* +static void scalar_properties(t_gobj *z, t_canvas *owner) { + t_scalar *x = (t_scalar *)z; + char *buf, buf2[80]; + int bufsize; + t_binbuf *b; + b = canvas_writetobinbuf(owner, 0); + binbuf_gettext(b, &buf, &bufsize); + binbuf_free(b); + buf = (char *)realloc(buf, bufsize+1); + buf[bufsize] = 0; + sprintf(buf2, "pdtk_data_dialog %%s {"); + sys_gui(buf); + sys_gui("}\n"); + free(buf); +} +*/ + +static void scalar_free(t_scalar *x) { + t_template *t = template_findbyname(x->t); + TEMPLATE_CHECK(x->t,) + word_free(x->v, t); + /* the "size" field in the class is zero, so Pd doesn't try to free us automatically (see pd_free()) */ + free(x); +} + +static void g_scalar_setup() { + scalar_class = class_new2("scalar",0,scalar_free,0,CLASS_GOBJ,""); + class_setsavefn(scalar_class, scalar_save); +} + +void array_redraw(t_array *a, t_canvas *canvas); + +/* +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 t_gtemplate : t_object { + t_template *t; + t_canvas *owner; + t_symbol *sym; + t_gtemplate *next; + int argc; + t_atom *argv; +}; + +static void template_conformarray(t_template *tfrom, t_template *tto, int *conformaction, t_array *a); +static void template_conformcanvas(t_template *tfrom, t_template *tto, int *conformaction, t_canvas *canvas); +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"??? */ + +/* return true if two dataslot definitions match */ +static int dataslot_matches(t_dataslot *ds1, t_dataslot *ds2, int nametoo) { + return (!nametoo || ds1->name == ds2->name) && ds1->type == ds2->type && + (ds1->type != DT_ARRAY || ds1->arraytemplate == ds2->arraytemplate); +} + +/* -- templates, the active ingredient in gtemplates defined below. ------- */ + +static t_template *template_new(t_symbol *templatesym, int argc, t_atom *argv) { + t_template *x = (t_template *)pd_new(template_class); + x->n = 0; + x->vec = (t_dataslot *)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_symbol; + newname = argv[1].a_symbol; + if (newtypesym == &s_float) newtype = DT_FLOAT; + else if (newtypesym == &s_symbol) newtype = DT_SYMBOL; + else if (newtypesym == &s_list) newtype = DT_CANVAS; + else if (newtypesym == gensym("array")) { + if (argc < 3 || argv[2].a_type != A_SYMBOL) {error("array lacks element template or name"); goto bad;} + newarraytemplate = canvas_makebindsym(argv[2].a_symbol); + newtype = DT_ARRAY; + argc--; + argv++; + } else {error("%s: no such type", newtypesym->name); goto bad;} + newn = (oldn = x->n) + 1; + x->vec = (t_dataslot *)realloc(x->vec, newn*sizeof(*x->vec)); + x->n = newn; + x->vec[oldn].type = newtype; + x->vec[oldn].name = newname; + x->vec[oldn].arraytemplate = newarraytemplate; + bad: + argc -= 2; argv += 2; + } + x->sym = templatesym; + if (templatesym->name) pd_bind(x,x->sym); + return x; +} + +int template_size(t_template *x) {return x->n * sizeof(t_word);} + +int template_find_field(t_template *x, t_symbol *name, int *p_onset, int *p_type, t_symbol **p_arraytype) { + if (!x) {bug("template_find_field"); return 0;} + for (int i = 0; i<x->n; i++) if (x->vec[i].name == name) { + *p_onset = i*sizeof(t_word); + *p_type = x->vec[i].type; + *p_arraytype = x->vec[i].arraytemplate; + return 1; + } + return 0; +} + +#define ERR(msg,ret) do {\ + if (loud) error("%s.%s: "msg, x->sym->name, fieldname->name);\ + return ret;} while(0); +static t_float template_getfloat(t_template *x, t_symbol *fieldname, t_word *wp, int loud) { + int onset, type; t_symbol *arraytype; + if (!template_find_field(x, fieldname, &onset, &type, &arraytype)) ERR("no such field",0); + if (type != DT_FLOAT) ERR("not a number",0); + return *(t_float *)(((char *)wp) + onset); +} +static t_symbol *template_getsymbol(t_template *x, t_symbol *fieldname, t_word *wp, int loud) { + int onset, type; t_symbol *arraytype; + if (!template_find_field(x, fieldname, &onset, &type, &arraytype)) ERR("no such field",&s_); + if (type != DT_SYMBOL) ERR("not a symbol",&s_); + return *(t_symbol **)(((char *)wp) + onset); +} +static 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)) ERR("no such field",); + if (type != DT_FLOAT) ERR("not a number",); + *(t_float *)(((char *)wp) + onset) = f; +} +static 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)) ERR("no such field",); + if (type != DT_SYMBOL) ERR("not a symbol",); + *(t_symbol **)(((char *)wp) + onset) = s; +} +#undef ERR + +/* 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". */ +static int template_match(t_template *x1, t_template *x2) { + if (x1->n < x2->n) return 0; + for (int i=x2->n; i < x1->n; i++) + if (x1->vec[i].type == DT_ARRAY || x1->vec[i].type == DT_CANVAS) return 0; + if (x2->n > x1->n) post("add elements..."); + for (int i=0; i < x2->n; i++) if (!dataslot_matches(&x1->vec[i], &x2->vec[i], 1)) return 0; + return 1; +} + +/* --------------- CONFORMING TO CHANGES IN A TEMPLATE ------------ */ + +/* the following functions 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) { + for (int i=0; i<tto->n; 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_canvas *canvas, t_scalar *scfrom) { + t_scalar *x; + t_template *scalartemplate; + /* possibly replace the scalar */ + if (scfrom->t == tfrom->sym) { + t_gpointer gp; + /* see scalar_new() for comment about the gpointer. */ + gpointer_init(&gp); + x = (t_scalar *)getbytes(sizeof(t_scalar) + (tto->n - 1) * sizeof(*x->v)); + x->_class = scalar_class; + x->t = tfrom->sym; + gpointer_setcanvas(&gp, canvas, x); + /* Here we initialize to the new template, but array and list elements will still belong to old template. */ + word_init(x->v, tto, &gp); + template_conformwords(tfrom, tto, conformaction, scfrom->v, x->v); + /* replace the old one with the new one in the list */ + canvas->boxes->remove_by_value(scfrom); + canvas->boxes->add(x); + pd_free(scfrom); + scalartemplate = tto; + } else { + x = scfrom; + scalartemplate = template_findbyname(x->t); + } + /* convert all array elements and sublists */ + for (int i=0; i < scalartemplate->n; i++) { + t_dataslot *ds = scalartemplate->vec + i; + if (ds->type == DT_CANVAS) template_conformcanvas(tfrom, tto, conformaction, x->v[i].w_canvas); + if (ds->type == DT_ARRAY) template_conformarray( tfrom, tto, conformaction, x->v[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) { + t_template *scalartemplate = 0; + if (a->templatesym == tfrom->sym) { + /* the array elements must all be conformed */ + int oldelemsize = sizeof(t_word) * tfrom->n; + int newelemsize = sizeof(t_word) * tto->n; + char *newarray = (char *)getbytes(newelemsize * a->n); + char *oldarray = a->vec; + if (a->elemsize != oldelemsize) bug("template_conformarray"); + for (int i=0; i<a->n; i++) { + t_word *wp = (t_word *)(newarray + newelemsize*i); + word_init(wp, tto, &a->gp); + template_conformwords(tfrom, tto, conformaction, (t_word *)(oldarray + oldelemsize*i), wp); + word_free((t_word *)(oldarray + oldelemsize*i), tfrom); + } + scalartemplate = tto; + a->vec = newarray; + free(oldarray); + } else scalartemplate = template_findbyname(a->templatesym); + /* convert all arrays and sublist fields in each element of the array */ + for (int i=0; i<a->n; i++) { + t_word *wp = (t_word *)(a->vec + sizeof(t_word) * a->n * i); + for (int j=0; j < scalartemplate->n; j++) { + t_dataslot *ds = scalartemplate->vec + j; + if (ds->type == DT_CANVAS) template_conformcanvas(tfrom, tto, conformaction, wp[j].w_canvas); + if (ds->type == DT_ARRAY) template_conformarray( tfrom, tto, conformaction, wp[j].w_array); + } + } +} + +/* this routine searches for every scalar in the canvas that belongs + to the "from" template and makes it belong to the "to" template. Descend canvases recursively. + We don't handle redrawing here; this is to be filled in LATER... */ +t_array *garray_getarray(t_garray *x); +static void template_conformcanvas(t_template *tfrom, t_template *tto, int *conformaction, t_canvas *canvas) { + canvas_each(g,canvas) { + t_class *c = g->_class; + /* what's the purpose of the assignment here?... consult original code */ + if (c==scalar_class) g = template_conformscalar(tfrom, tto, conformaction, canvas, (t_scalar *)g); + else if (c==canvas_class) template_conformcanvas(tfrom, tto, conformaction, (t_canvas *)g); + else if (c==garray_class) template_conformarray(tfrom, tto, conformaction, garray_getarray((t_garray *)g)); + } +} + +/* globally conform all scalars from one template to another */ +void template_conform(t_template *tfrom, t_template *tto) { + int nto = tto->n, nfrom = tfrom->n, doit = 0; + int *conformaction = (int *)getbytes(sizeof(int) * nto); + int *conformedfrom = (int *)getbytes(sizeof(int) * nfrom); + for (int i=0; i< nto; i++) conformaction[i] = -1; + for (int i=0; i<nfrom; i++) conformedfrom[i] = 0; + for (int i=0; i < nto; i++) { + t_dataslot *dataslot = &tto->vec[i]; + for (int j=0; j<nfrom; j++) { + t_dataslot *dataslot2 = &tfrom->vec[j]; + if (dataslot_matches(dataslot, dataslot2, 1)) { + conformaction[i] = j; + conformedfrom[j] = 1; + } + } + } + for (int i=0; i<nto; i++) if (conformaction[i] < 0) { + t_dataslot *dataslot = &tto->vec[i]; + for (int j=0; j<nfrom; j++) + if (!conformedfrom[j] && dataslot_matches(dataslot, &tfrom->vec[j], 0)) { + conformaction[i] = j; + conformedfrom[j] = 1; + } + } + if (nto != nfrom) doit = 1; + else for (int i=0; i<nto; i++) if (conformaction[i] != i) doit = 1; + if (doit) { + post("conforming template '%s' to new structure", tfrom->sym->name); + for (int i=0; i<nto; i++) post("... %d", conformaction[i]); + foreach(gl,windowed_canvases) template_conformcanvas(tfrom, tto, conformaction, gl->first); + } + free(conformaction); + free(conformedfrom); +} + +t_template *template_findbyname(t_symbol *s) {return (t_template *)pd_findbyclass(s, template_class);} + +t_canvas *template_findcanvas(t_template *t) { + if (!t) bug("template_findcanvas"); + t_gtemplate *gt = t->list; + if (!gt) return 0; + return gt->owner; + /* return ((t_canvas *)pd_findbyclass(t->sym, canvas_class)); */ +} + +void template_notify(t_template *t, t_symbol *s, int argc, t_atom *argv) { + if (t->list) outlet_anything(t->list->outlet, s, argc, argv); +} + +/* bash the first of (argv) with a pointer to a scalar, and send on + to template as a notification message */ +static void template_notifyforscalar(t_template *t, t_canvas *owner, t_scalar *sc, t_symbol *s, int argc, t_atom *argv) { + t_gpointer gp; + gpointer_init(&gp); + gpointer_setcanvas(&gp, owner, sc); + SETPOINTER(argv, &gp); + template_notify(t, s, argc, argv); + gpointer_unset(&gp); +} + +/* 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_symbol *templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + t_template *x = (t_template *)pd_findbyclass(templatesym, template_class); + if (!argc) return 0; + argc--; argv++; + if (x) { + 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->list) { + error("%s: template mismatch", templatesym->name); + } else { + template_conform(x, y); + pd_free(x); + t_template *y2 = template_new(templatesym, argc, argv); + y2->list = 0; + } + } + pd_free(y); + } 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->sym->name) pd_unbind(x,x->sym); + free(x->vec); +} + +/* ---------------- gtemplates. One per canvas. ----------- */ + +/* "Struct": an 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 +"struct" 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); + x->owner = canvas_getcurrent(); + x->next = 0; + x->sym = sym; + x->argc = argc; + x->argv = (t_atom *)getbytes(argc * sizeof(t_atom)); + for (int i=0; i<argc; i++) x->argv[i] = argv[i]; + /* already have a template by this name? */ + if (t) { + x->t = t; + /* if it's already got a "struct" object we + just tack this one to the end of the list and leave it there. */ + if (t->list) { + t_gtemplate *x2, *x3; + for (x2 = x->t->list; (x3 = x2->next); x2 = x3) {} + x2->next = x; + post("template %s: warning: already exists.", sym->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); + //canvas_redrawallfortemplate(t, 2); + /* 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 = template_new(sym, argc, argv); + } + pd_free(y); + t->list = x; + //canvas_redrawallfortemplate(t, 1); + } + } else { + /* otherwise make a new one and we're the only struct on it. */ + x->t = t = template_new(sym, argc, argv); + t->list = x; + } + outlet_new(x,0); + return x; +} + +static void *gtemplate_new(t_symbol *s, int argc, t_atom *argv) { + 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_symbol *sym = canvas_makebindsym(canvas_getcurrent()->name); + static int warned; + if (!warned) { + post("warning -- 'template' (%s) is obsolete; replace with 'struct'", sym->name); + warned = 1; + } + return gtemplate_donew(sym, argc, argv); +} + +t_template *gtemplate_get(t_gtemplate *x) {return x->t;} + +static void gtemplate_free(t_gtemplate *x) { + /* get off the template's list */ + t_template *t = x->t; + if (x == t->list) { + //canvas_redrawallfortemplate(t, 2); + if (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 the existing template with it. */ + t_template *z = template_new(&s_, x->next->argc, x->next->argv); + template_conform(t, z); + pd_free(t); + pd_free(z); + z = template_new(x->sym, x->next->argc, x->next->argv); + z->list = x->next; + for (t_gtemplate *y=z->list; y ; y=y->next) y->t=z; + } else t->list = 0; + //canvas_redrawallfortemplate(t, 1); + } else { + t_gtemplate *x2, *x3; + for (x2=t->list; (x3=x2->next); x2=x3) if (x==x3) {x2->next=x3->next; break;} + } + free(x->argv); +} + +/* --------------- FIELD DESCRIPTORS (NOW CALLED SLOT) ---------------------- */ +/* a field descriptor can hold a constant or a variable's name; in the latter case, + 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. +*/ +/* note: there is also t_dataslot which plays a similar role. could they be merged someday? */ + +struct _slot { + char type; /* LATER consider removing this? */ + char var; + union { + t_float f; /* the field is a constant float */ + t_symbol *s; /* the field is a constant symbol */ + t_symbol *varsym; /* the field is variable and this is the name */ + }; + float min,max; + float scrmin,scrmax; /* min and max screen values */ + float quantum; /* quantization in value */ +}; + +static void slot_setfloat_const( t_slot *fd, float f) { + fd->type = A_FLOAT; fd->var = 0; fd->f = f; fd->min = fd->max = fd->scrmin = fd->scrmax = fd->quantum = 0;} +static void slot_setsymbol_const(t_slot *fd, t_symbol *s) { + fd->type = A_SYMBOL; fd->var = 0; fd->s = s; fd->min = fd->max = fd->scrmin = fd->scrmax = fd->quantum = 0;} + +static void slot_setfloat_var(t_slot *fd, t_symbol *s) { + char *s1, *s2, *s3; + fd->type = A_FLOAT; + fd->var = 1; + if (!(s1 = strchr(s->name, '(')) || !(s2 = strchr(s->name, ')')) || s1>s2) { + fd->varsym = s; + fd->min = fd->max = fd->scrmin = fd->scrmax = fd->quantum = 0; + } else { + fd->varsym = symprintf("%.*s",s1-s->name,s->name); + t_int got = sscanf(s1, "(%f:%f)(%f:%f)(%f)", &fd->min, &fd->max, &fd->scrmin, &fd->scrmax, &fd->quantum); + if (got < 2) goto fail; + if (got == 3 || (got < 4 && strchr(s2, '('))) goto fail; + if (got < 5 && (s3 = strchr(s2, '(')) && strchr(s3+1, '(')) goto fail; + if (got == 4) fd->quantum = 0; + else if (got == 2) { + fd->quantum = 0; + fd->scrmin = fd->min; + fd->scrmax = fd->max; + } + return; + fail: + error("parse error: %s", s->name); + fd->min = fd->scrmin = fd->max = fd->scrmax = fd->quantum = 0; + } +} + +#define CLOSED 1 +#define BEZ 2 +#define NOMOUSE 4 +#define A_ARRAY 55 /* LATER decide whether to enshrine this in m_pd.h */ + +static void slot_setfloatarg(t_slot *fd, int argc, t_atom *argv) { + if (argc <= 0) slot_setfloat_const(fd, 0); + else if (argv->a_type == A_SYMBOL) slot_setfloat_var( fd, argv->a_symbol); + else slot_setfloat_const(fd, argv->a_float); +} +static void slot_setsymbolarg(t_slot *fd, int argc, t_atom *argv) { + if (argc <= 0) slot_setsymbol_const(fd, &s_); + else if (argv->a_type == A_SYMBOL) { + fd->type = A_SYMBOL; + fd->var = 1; + fd->varsym = argv->a_symbol; + fd->min = fd->max = fd->scrmin = fd->scrmax = fd->quantum = 0; + } else slot_setsymbol_const(fd, &s_); +} +static void slot_setarrayarg(t_slot *fd, int argc, t_atom *argv) { + if (argc <= 0) slot_setfloat_const(fd, 0); + else if (argv->a_type == A_SYMBOL) { + fd->type = A_ARRAY; + fd->var = 1; + fd->varsym = argv->a_symbol; + } else slot_setfloat_const(fd, argv->a_float); +} + +/* getting and setting values via slots -- note confusing names; the above are setting up the slot itself. */ + +/* convert a variable's value to a screen coordinate via its slot */ +static t_float slot_cvttocoord(t_slot *f, float val) { + float coord, extreme, div; + if (f->max == f->min) return val; + div = (f->scrmax - f->scrmin)/(f->max - f->min); + coord = f->scrmin + (val - f->min) * div; + extreme = f->scrmin<f->scrmax ? f->scrmin : f->scrmax; if (coord<extreme) coord = extreme; + extreme = f->scrmin>f->scrmax ? f->scrmin : f->scrmax; if (coord>extreme) coord = extreme; + return coord; +} + +/* read a variable via slot and convert to screen coordinate */ +static t_float slot_getcoord(t_slot *f, t_template *t, t_word *wp, int loud) { + if (f->type!=A_FLOAT) {if (loud) error("symbolic data field used as number"); return 0;} + if (f->var) return slot_cvttocoord(f, template_getfloat(t, f->varsym, wp, loud)); + return f->f; +} +static t_float slot_getfloat(t_slot *f, t_template *t, t_word *wp, int loud) { + if (f->type!=A_FLOAT) {if (loud) error("symbolic data field used as number"); return 0;} + if (f->var) return template_getfloat(t, f->varsym, wp, loud); + return f->f; +} +static t_symbol *slot_getsymbol(t_slot *f, t_template *t, t_word *wp, int loud) { + if (f->type!=A_SYMBOL) {if (loud) error("numeric data field used as symbol"); return &s_;} + if (f->var) return template_getsymbol(t, f->varsym, wp, loud); + return f->s; +} + +/* convert from a screen coordinate to a variable value */ +static float slot_cvtfromcoord(t_slot *f, float coord) { + if (f->scrmax == f->scrmin) return coord; + else { + float div = (f->max - f->min)/(f->scrmax - f->scrmin); + float extreme; + float val = f->min + (coord - f->scrmin) * div; + if (f->quantum != 0) val = ((int)((val/f->quantum) + 0.5)) * f->quantum; + extreme = f->min<f->max ? f->min : f->max; if (val<extreme) val=extreme; + extreme = f->min>f->max ? f->min : f->max; if (val>extreme) val=extreme; + return val; + } + } + +static void slot_setcoord(t_slot *f, t_template *t, t_word *wp, float coord, int loud) { + if (f->type == A_FLOAT && f->var) { + float val = slot_cvtfromcoord(f, coord); + template_setfloat(t, f->varsym, wp, val, loud); + } else { + if (loud) error("attempt to set constant or symbolic data field to a number"); + } +} + +#define FIELDSET(T,F,D) if (argc) slot_set##T##arg(&x->F,argc--,argv++); \ + else slot_setfloat_const(&x->F,D); + +/* 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; + +/* includes polygons too */ +struct t_curve : t_object { + int flags; /* CLOSED and/or BEZ and/or NOMOUSE */ + t_slot fillcolor, outlinecolor, width, vis; + int npoints; + t_slot *vec; + t_canvas *canvas; +}; + +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->name; + int flags = 0; + x->canvas = canvas_getcurrent(); + if (classname[0] == 'f') {classname += 6; flags |= CLOSED;} else classname += 4; + slot_setfloat_const(&x->vis, 1); + if (classname[0] == 'c') flags |= BEZ; + while (1) { + t_symbol *firstarg = atom_getsymbolarg(0, argc, argv); + if (!strcmp(firstarg->name,"-v") && argc > 1) { + slot_setfloatarg(&x->vis, 1, argv+1); + argc -= 2; argv += 2; + } else + if (!strcmp(firstarg->name,"-x")) { + flags |= NOMOUSE; + argc -= 1; argv += 1; + } else break; + } + x->flags = flags; + if (flags&CLOSED&&argc) slot_setfloatarg( &x->fillcolor, argc--,argv++); + else slot_setfloat_const(&x->fillcolor, 0); + FIELDSET(float,outlinecolor,0); + FIELDSET(float,width,1); + if (argc < 0) argc = 0; + int nxy = argc + (argc&1); + x->npoints = nxy>>1; + x->vec = (t_slot *)getbytes(nxy * sizeof(t_slot)); + t_slot *fd = x->vec; + for (int i=0; i<argc; i++, fd++, argv++) slot_setfloatarg(fd, 1, argv); + if (argc & 1) slot_setfloat_const(fd, 0); + return x; +} + +static void curve_float(t_curve *x, t_floatarg f) { + if (x->vis.type != A_FLOAT || x->vis.var) { + error("global vis/invis for a template with variable visibility"); + return; + } + int viswas = x->vis.f!=0; + if ((f!=0 && viswas) || (f==0 && !viswas)) return; + canvas_redrawallfortemplatecanvas(x->canvas, 2); + slot_setfloat_const(&x->vis, f!=0); + canvas_redrawallfortemplatecanvas(x->canvas, 1); +} + +static int rangecolor(int n) {return n*9/255;} + +static void numbertocolor(int n, char *s) { + n = (int)max(n,0); + sprintf(s, "#%2.2x%2.2x%2.2x", rangecolor(n/100), rangecolor((n/10)%10), rangecolor(n%10)); +} + +static void curve_vis(t_gobj *z, t_canvas *canvas, t_word *data, t_template *t, float basex, float basey, int vis) { + t_curve *x = (t_curve *)z; + int n = x->npoints; + t_slot *f = x->vec; + if (!slot_getfloat(&x->vis, t, data, 0)) return; + if (vis) { + if (n > 1) { + int flags = x->flags; + char outline[20], fill[20]; + int pix[200]; + if (n > 100) n = 100; + /* calculate the pixel values before we start printing out the TK message so that + "error" printout won't be interspersed with it. Only show up to 100 points so we don't + have to allocate memory here. */ + for (int i=0; i<n; i++, f += 2) { + pix[2*i ] = canvas_xtopixels(canvas, basex + slot_getcoord(f+0, t, data, 1)); + pix[2*i+1] = canvas_ytopixels(canvas, basey + slot_getcoord(f+1, t, data, 1)); + } + numbertocolor((int)slot_getfloat(&x->outlinecolor, t, data, 1), outline); + if (flags & CLOSED) { + numbertocolor((int)slot_getfloat(&x->fillcolor, t, data, 1), fill); + //sys_vgui(".x%lx.c create polygon\\\n", (long)canvas_getcanvas(canvas)); + } else sys_vgui(".x%lx.c create line\\\n", (long)canvas_getcanvas(canvas)); + for (int i=0; i<n; i++) sys_vgui("%d %d\\\n", pix[2*i], pix[2*i+1]); + sys_vgui("-width %f\\\n", max(slot_getfloat(&x->width, t, data, 1),1.0f)); + 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%lx\n", (long)data); + } else post("warning: curves need at least two points to be graphed"); + } else { + if (n > 1) sys_vgui(".x%lx.c delete curve%lx\n", (long)canvas_getcanvas(canvas), (long)data); + } +} + +static struct { + int field; + float xcumulative, xbase, xper; + float ycumulative, ybase, yper; + t_canvas *canvas; + t_scalar *scalar; + t_array *array; + t_word *wp; + t_template *t; + t_gpointer gpointer; +} cm; + +/* LATER protect against the template changing or the scalar disappearing probably by attaching a gpointer here ... */ +#if 0 +static void curve_motion(void *z, t_floatarg dx, t_floatarg dy) { + t_curve *x = (t_curve *)z; + t_slot *f = x->vec + cm.field; + t_atom at; + if (!gpointer_check(&curve_motion_gpointer, 0)) {post("curve_motion: scalar disappeared"); return;} + cm.xcumulative += dx; + cm.ycumulative += dy; + if (f[0].var && dx!=0) slot_setcoord(f, cm.t, cm.wp, cm.xbase + cm.xcumulative*cm.xper, 1); + if (f[1].var && dy!=0) slot_setcoord(f+1, cm.t, cm.wp, cm.ybase + cm.ycumulative*cm.yper, 1); + /* LATER figure out what to do to notify for an array? */ + if (cm.scalar) template_notifyforscalar(cm.t, cm.canvas, cm.scalar, gensym("change"), 1, &at); + if (cm.scalar) gobj_changed(cm.scalar,0); else gobj_changed(cm.array,0); +} +#endif + +int iabs(int a) {return a<0?-a:a;} + +static int curve_click(t_gobj *z, t_canvas *canvas, t_word *data, t_template *t, t_scalar *sc, +t_array *ap, float basex, float basey, int xpix, int ypix, int shift, int alt, int dbl, int doit) { + t_curve *x = (t_curve *)z; + int bestn = -1; + int besterror = 0x7fffffff; + t_slot *f = x->vec; + if (!slot_getfloat(&x->vis, t, data, 0)) return 0; + for (int i=0; i<x->npoints; i++, f += 2) { + int xval = (int)slot_getcoord(f , t, data, 0), xloc = canvas_xtopixels(canvas, basex + xval); + int yval = (int)slot_getcoord(f+1, t, data, 0), yloc = canvas_ytopixels(canvas, basey + yval); + int xerr = iabs(xloc-xpix); + int yerr = iabs(yloc-ypix); + if (!f->var && !(f+1)->var) continue; + if (yerr > xerr) xerr = yerr; + if (xerr < besterror) { + cm.xbase = xval; + cm.ybase = yval; + besterror = xerr; + bestn = i; + } + } + if (besterror > 10) return 0; + if (doit) { + cm.xper = canvas_pixelstox(canvas, 1) - canvas_pixelstox(canvas, 0); + cm.yper = canvas_pixelstoy(canvas, 1) - canvas_pixelstoy(canvas, 0); + cm.xcumulative = cm.ycumulative = 0; + cm.canvas = canvas; + cm.scalar = sc; + cm.array = ap; + cm.wp = data; + cm.field = 2*bestn; + cm.t = t; + if (cm.scalar) gpointer_setcanvas(&cm.gpointer, cm.canvas, cm.scalar); + else gpointer_setarray(&cm.gpointer, cm.array, cm.wp); + /* canvas_grab(canvas, z, curve_motion, 0, xpix, ypix); */ + } + return 1; +} + +t_class *plot_class; + +struct t_plot : t_object { + t_canvas *canvas; + t_slot outlinecolor, width, xloc, yloc, xinc, style; + t_slot data, xpoints, ypoints, wpoints; + t_slot vis; + t_slot scalarvis; /* true if drawing the scalar at each point */ +}; + +static void *plot_new(t_symbol *classsym, t_int argc, t_atom *argv) { + t_plot *x = (t_plot *)pd_new(plot_class); + int defstyle = PLOTSTYLE_POLY; + x->canvas = canvas_getcurrent(); + slot_setfloat_var(&x->xpoints,&s_x); + slot_setfloat_var(&x->ypoints,&s_y); + slot_setfloat_var(&x->wpoints, gensym("w")); + slot_setfloat_const(&x->vis, 1); + slot_setfloat_const(&x->scalarvis, 1); + while (1) { + const char *f = atom_getsymbolarg(0, argc, argv)->name; + argc--; argv++; + if (!strcmp(f, "curve") || !strcmp(f, "-c")) defstyle = PLOTSTYLE_BEZ; + else if (!strcmp(f,"-v") &&argc>0) {slot_setfloatarg(&x->vis, 1,argv+1);argc--;argv++;} + else if (!strcmp(f,"-vs")&&argc>0) {slot_setfloatarg(&x->scalarvis,1,argv+1);argc--;argv++;} + else if (!strcmp(f,"-x") &&argc>0) {slot_setfloatarg(&x->xpoints, 1,argv+1);argc--;argv++;} + else if (!strcmp(f,"-y") &&argc>0) {slot_setfloatarg(&x->ypoints, 1,argv+1);argc--;argv++;} + else if (!strcmp(f,"-w") &&argc>0) {slot_setfloatarg(&x->wpoints, 1,argv+1);argc--;argv++;} + else break; + } + FIELDSET(array,data,1); + FIELDSET(float,outlinecolor,0); + FIELDSET(float,width,1); + FIELDSET(float,xloc,1); + FIELDSET(float,yloc,1); + FIELDSET(float,xinc,1); + FIELDSET(float,style,defstyle); + return x; +} + +void plot_float(t_plot *x, t_floatarg f) { + int viswas; + if (x->vis.type != A_FLOAT || x->vis.var) {error("global vis/invis for a template with variable visibility"); return;} + viswas = x->vis.f!=0; + if ((f!=0 && viswas) || (f==0 && !viswas)) return; + canvas_redrawallfortemplatecanvas(x->canvas, 2); + slot_setfloat_const(&x->vis, f!=0); + canvas_redrawallfortemplatecanvas(x->canvas, 1); +} + +/* 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, +float *stylep, float *visp, float *scalarvisp) { + int arrayonset, type; + t_symbol *elemtemplatesym; + t_array *array; + if (x->data.type != A_ARRAY || !x->data.var) {error("needs an array field"); return -1;} + if (!template_find_field(ownertemplate, x->data.varsym, &arrayonset, &type, &elemtemplatesym)) { + error("%s: no such field", x->data.varsym->name); + return -1; + } + if (type != DT_ARRAY) {error("%s: not an array", x->data.varsym->name); return -1;} + array = *(t_array **)(((char *)data) + arrayonset); + *linewidthp = slot_getfloat(&x->width, ownertemplate, data, 1); + *xlocp = slot_getfloat(&x->xloc, ownertemplate, data, 1); + *xincp = slot_getfloat(&x->xinc, ownertemplate, data, 1); + *ylocp = slot_getfloat(&x->yloc, ownertemplate, data, 1); + *stylep = slot_getfloat(&x->style, ownertemplate, data, 1); + *visp = slot_getfloat(&x->vis, ownertemplate, data, 1); + *scalarvisp = slot_getfloat(&x->scalarvis, 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" */ +static int array_getfields(t_symbol *elemtemplatesym, t_canvas **elemtemplatecanvasp, +t_template **elemtemplatep, int *elemsizep, t_slot *xslot, t_slot *yslot, t_slot *wslot, +int *xonsetp, int *yonsetp, int *wonsetp) { + int type; + t_symbol *dummy, *varname; + 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. */ + t_template *elemtemplate = template_findbyname(elemtemplatesym); + if (!elemtemplate) {error("%s: no such template", elemtemplatesym->name); return -1;} + if (!(elemtemplatesym==&s_float || (elemtemplatecanvas = template_findcanvas(elemtemplate)))) { + error("%s: no canvas for this template", elemtemplatesym->name); + return -1; + } + *elemtemplatecanvasp = elemtemplatecanvas; + *elemtemplatep = elemtemplate; + *elemsizep = elemtemplate->n * sizeof(t_word); +#define FOO(f,name,onset) \ + varname = f && f->var ? f->varsym : gensym(name); \ + if (!template_find_field(elemtemplate,varname,&onset,&type,&dummy) || type!=DT_FLOAT) onset=-1; + FOO(yslot,"y",*yonsetp) + FOO(xslot,"x",*xonsetp) + FOO(wslot,"w",*wonsetp) +#undef FOO + return 0; +} + +static void plot_vis(t_gobj *z, t_canvas *canvas, t_word *data, t_template *t, float basex, float basey, int tovis) { + 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, style, xsum, yval, vis, scalarvis; + t_array *array; + if (plot_readownertemplate(x, data, t, &elemtemplatesym, &array, &linewidth, &xloc, &xinc, &yloc, &style, &vis, &scalarvis)) return; + t_slot *xslot = &x->xpoints, *yslot = &x->ypoints, *wslot = &x->wpoints; + if (!vis) return; + if (array_getfields(elemtemplatesym, &elemtemplatecanvas, &elemtemplate, &elemsize, + xslot, yslot, wslot, &xonset, &yonset, &wonset)) return; + int nelem = array->n; + char *elem = (char *)array->vec; + if (tovis) { + if (style == PLOTSTYLE_POINTS) { + float minyval = 1e20, maxyval = -1e20; + int ndrawn = 0; + xsum = basex + xloc; + for (int i=0; i<nelem; i++) { + float yval; + int ixpix, inextx; + if (xonset >= 0) { + float usexloc = basex + xloc + *(float *)((elem + elemsize*i) + xonset); + ixpix = canvas_xtopixels(canvas, slot_cvttocoord(xslot, usexloc)); + inextx = ixpix + 2; + } else { + ixpix = canvas_xtopixels(canvas, slot_cvttocoord(xslot, xsum)); xsum += xinc; + inextx = canvas_xtopixels(canvas, slot_cvttocoord(xslot, xsum)); + } + yval = yonset>=0 ? yloc + *(float *)((elem + elemsize*i) + yonset) : 0; + if (yval > maxyval) maxyval = yval; + if (yval < minyval) minyval = yval; + if (i == nelem-1 || inextx != ixpix) { + sys_vgui(".x%lx.c create rectangle %d %d %d %d -fill black -width 0 -tags plot%lx\n", + (long)canvas_getcanvas(canvas), ixpix, + (int) canvas_ytopixels(canvas, basey + slot_cvttocoord(yslot, minyval)), inextx, + (int)(canvas_ytopixels(canvas, basey + slot_cvttocoord(yslot, maxyval))+linewidth), + (long)data); + ndrawn++; + minyval = 1e20; + maxyval = -1e20; + } + if (ndrawn > 2000 || ixpix >= 3000) break; + } + } else { + char outline[20]; + int lastpixel = -1, ndrawn = 0; + float yval = 0, wval = 0; + int ixpix = 0; + /* draw the trace */ + numbertocolor((int)slot_getfloat(&x->outlinecolor, t, data, 1), outline); + if (wonset >= 0) { + /* found "w" field which controls linewidth. The trace is a filled polygon with 2n points. */ + //sys_vgui(".x%lx.c create polygon \\\n", (long)canvas_getcanvas(canvas)); + xsum = xloc; + for (int i=0; i<nelem; i++) { + float usexloc = xonset>=0 ? xloc+*(float *)(elem+elemsize*i+xonset) : (xsum+=xinc); + float yval = yonset>=0 ? *(float *)(elem+elemsize*i+yonset) : 0; + wval = *(float *)(elem+elemsize*i+wonset); + float xpix = canvas_xtopixels(canvas, basex + slot_cvttocoord(xslot, usexloc)); + ixpix = (int)roundf(xpix); + if (xonset >= 0 || ixpix != lastpixel) { + sys_vgui("%d %f \\\n", ixpix, canvas_ytopixels(canvas, + basey + slot_cvttocoord(yslot,yval+yloc) + - slot_cvttocoord(wslot,wval))); + ndrawn++; + } + lastpixel = ixpix; + if (ndrawn >= 1000) goto ouch; + } + lastpixel = -1; + for (int i=nelem-1; i>=0; i--) { + float usexloc = xonset>=0 ? xloc+*(float *)(elem+elemsize*i+xonset) : (xsum-=xinc); + float yval = yonset>=0 ? *(float *)(elem+elemsize*i+yonset) : 0; + wval = *(float *)((elem + elemsize*i) + wonset); + float xpix = canvas_xtopixels(canvas, basex + slot_cvttocoord(xslot, usexloc)); + ixpix = (int)roundf(xpix); + if (xonset >= 0 || ixpix != lastpixel) { + sys_vgui("%d %f \\\n", ixpix, canvas_ytopixels(canvas, + basey + yloc + slot_cvttocoord(yslot, yval) + slot_cvttocoord(wslot, 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) { + int y = int(slot_cvttocoord(yslot, yval)); + int w = int(slot_cvttocoord(wslot, wval)); + sys_vgui("%d %f %d %f\\\n", + ixpix + 10, canvas_ytopixels(canvas, basey + yloc + y + w), + ixpix + 10, canvas_ytopixels(canvas, basey + yloc + y - w)); + } + ouch: + sys_vgui(" -width 1 -fill %s -outline %s\\\n", outline, outline); + if (style == PLOTSTYLE_BEZ) sys_vgui("-smooth 1\\\n"); + sys_vgui("-tags plot%lx\n", (long)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%lx.c create line \\\n", (long)canvas_getcanvas(canvas)); + xsum = xloc; + for (int i=0; i<nelem; i++) { + float usexloc = xonset>=0 ? xloc+*(float *)(elem+elemsize*i+xonset) : xsum; if (xonset>=0) xsum+=(int)xinc; + float yval = yonset>=0 ? *(float *)(elem+elemsize*i+yonset) : 0; + float xpix = canvas_xtopixels(canvas, basex + slot_cvttocoord(xslot, usexloc)); + ixpix = (int)roundf(xpix); + if (xonset >= 0 || ixpix != lastpixel) { + sys_vgui("%d %f \\\n", ixpix, canvas_ytopixels(canvas, basey + yloc + slot_cvttocoord(yslot, 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, + canvas_ytopixels(canvas, basey + yloc + slot_cvttocoord(yslot, yval))); + sys_vgui("-width %f -fill %s -smooth %d -tags plot%lx",linewidth,outline,style==PLOTSTYLE_BEZ,(long)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. */ + if (scalarvis != 0) { + int xsum = (int)xloc; + for (int i=0; i<nelem; i++) { + //float usexloc = xonset>=0 ? basex + xloc + *(float *)(elem+elemsize*i+xonset) : basex+xsum; + if (xonset>=0) xsum+=int(xinc); + yval = yonset>=0 ? *(float *)(elem+elemsize*i+yonset) : 0; + //float useyloc = basey + yloc + slot_cvttocoord(yslot, yval); + /*canvas_each(y,elemtemplatecanvas) pd_getparentwidget(y)->w_parentvisfn(y, canvas, + (t_word *)(elem+elemsize*i), elemtemplate, usexloc, useyloc, tovis);*/ + } + } + } else { + /* un-draw the individual points */ + /* if (scalarvis != 0) + for (int i=0; i<nelem; i++) + canvas_each(y,elemtemplatecanvas) + pd_getparentwidget(y)->w_parentvisfn(y, canvas, (t_word *)(elem+elemsize*i), elemtemplate,0,0,0);*/ + /* and then the trace */ + sys_vgui(".x%lx.c delete plot%lx\n", (long)canvas_getcanvas(canvas), (long)data); + } +} + +static int plot_click(t_gobj *z, t_canvas *canvas, t_word *data, t_template *t, t_scalar *sc, +t_array *ap, 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, style, vis, scalarvis; + t_array *array; + if (plot_readownertemplate(x, data, t, &elemtemplatesym, &array, &linewidth, &xloc, &xinc, + &yloc, &style, &vis, &scalarvis)) return 0; + if (!vis) return 0; + return array_doclick(array, canvas, sc, ap, elemtemplatesym, linewidth, basex + xloc, xinc, + basey + yloc, scalarvis, &x->xpoints, &x->ypoints, &x->wpoints, xpix, ypix, shift, alt, dbl, doit); +} + +/* ---------------- drawnumber: draw a number (or symbol) ---------------- */ +/* drawnumbers draw numeric fields at controllable locations, with controllable color and label. + invocation: (drawnumber|drawsymbol) [-v <visible>] variable x y color label */ + +t_class *drawnumber_class; + +#define DRAW_SYMBOL 1 + +struct t_drawnumber : t_object { + t_slot value, xloc, yloc, color, vis; + t_symbol *label; + int flags; + t_canvas *canvas; +}; + +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->name; + int flags = 0; + if (classname[4] == 's') flags |= DRAW_SYMBOL; + x->flags = flags; + slot_setfloat_const(&x->vis, 1); + x->canvas = canvas_getcurrent(); + while (1) { + t_symbol *firstarg = atom_getsymbolarg(0, argc, argv); + if (!strcmp(firstarg->name,"-v") && argc > 1) { + slot_setfloatarg(&x->vis, 1, argv+1); + argc -= 2; argv += 2; + } else break; + } + if (flags & DRAW_SYMBOL) { + if (argc) slot_setsymbolarg( &x->value,argc--,argv++); + else slot_setsymbol_const(&x->value,&s_); + } else FIELDSET(float,value, 0); + FIELDSET(float,xloc,0); + FIELDSET(float,yloc,0); + FIELDSET(float,color,1); + if (argc) x->label = atom_getsymbolarg(0, argc, argv); else x->label = &s_; + return x; +} + +void drawnumber_float(t_drawnumber *x, t_floatarg f) { + if (x->vis.type != A_FLOAT || x->vis.var) {error("global vis/invis for a template with variable visibility"); return;} + int viswas = x->vis.f!=0; + if ((f != 0 && viswas) || (f == 0 && !viswas)) return; + canvas_redrawallfortemplatecanvas(x->canvas, 2); + slot_setfloat_const(&x->vis, f!=0); + canvas_redrawallfortemplatecanvas(x->canvas, 1); +} + +static void drawnumber_vis(t_gobj *z, t_canvas *canvas, t_word *data, t_template *t, float basex, float basey, int vis) { + t_drawnumber *x = (t_drawnumber *)z; + if (!slot_getfloat(&x->vis, t, data, 0)) return; + if (vis) { + t_atom at; + int xloc = canvas_xtopixels(canvas, basex + slot_getcoord(&x->xloc, t, data, 0)); + int yloc = canvas_ytopixels(canvas, basey + slot_getcoord(&x->yloc, t, data, 0)); + char colorstring[20]; + numbertocolor((int)slot_getfloat(&x->color, t, data, 1), colorstring); + if (x->flags & DRAW_SYMBOL) SETSYMBOL(&at, slot_getsymbol(&x->value, t, data, 0)); + else SETFLOAT( &at, slot_getfloat( &x->value, t, data, 0)); + std::ostringstream buf; + buf << x->label->name; + atom_ostream(&at,buf); + sys_vgui(".x%lx.c create text %d %d -anchor nw -fill %s -text {%s}", + (long)canvas_getcanvas(canvas), xloc, yloc, colorstring, buf.str().data()); + sys_vgui(" -font {Courier 42} -tags drawnumber%lx\n", (long)data); /*sys_hostfontsize(canvas_getfont(canvas))*/ + } else sys_vgui(".x%lx.c delete drawnumber%lx\n", (long)canvas_getcanvas(canvas), (long)data); +} + +static struct { + float ycumulative; + t_canvas *canvas; + t_scalar *scalar; + t_array *array; + t_word *wp; + t_template *t; + t_gpointer gpointer; + int symbol; + int firstkey; +} dn; + +/* LATER protect against the template changing or the scalar disappearing probably by attaching a gpointer here ... */ +#if 0 +static void drawnumber_motion(void *z, t_floatarg dx, t_floatarg dy) { + t_drawnumber *x = (t_drawnumber *)z; + t_slot *f = &x->value; + t_atom at; + if (!gpointer_check(&dn.gpointer, 0)) {post("drawnumber_motion: scalar disappeared"); return;} + if (dn.symbol) {post("drawnumber_motion: symbol"); return;} + dn.ycumulative -= dy; + template_setfloat(dn.t, f->varsym, dn.wp, dn.ycumulative, 1); + if (dn.scalar) gobj_changed(dn.scalar); else gobj_changed(dn.array); +} +#endif + +static void drawnumber_key(void *z, t_floatarg fkey) { + //t_drawnumber *x = (t_drawnumber *)z; + int key = (int)fkey; + if (!gpointer_check(&dn.gpointer, 0)) { + post("drawnumber_motion: scalar disappeared"); + return; + } + if (key == 0) return; + if (dn.symbol) { + /* key entry for a symbol field... has to be rewritten in Tcl similarly to TextBox for edition of [drawsymbol] */ + // template_getsymbol(dn.t, f->varsym, dn.wp, 1)->name; + } else { + /* key entry for a numeric field... same here... [drawnumber] */ + //t_slot *f = &x->value; + //float newf; + //if (sscanf(sbuf, "%g", &newf) < 1) newf = 0; + //template_setfloat(dn.t, f->varsym, dn.wp, newf, 1); + //t_atom at; + //if (dn.scalar) template_notifyforscalar(dn.t, dn.canvas, dn.scalar, gensym("change"), 1, &at); + //if (dn.scalar) gobj_changed(dn.scalar,0); else gobj_changed(dn.array,0); + } +} + +#if 0 +static int drawnumber_click(t_gobj *z, t_canvas *canvas, t_word *data, t_template *t, t_scalar *sc, t_array *ap, 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; + drawnumber_getrect(z, canvas, data, t, basex, basey, &x1, &y1, &x2, &y2); + if (xpix >= x1 && xpix <= x2 && ypix >= y1 && ypix <= y2 + && x->value.var && slot_getfloat(&x->vis, t, data, 0)) { + if (doit) { + dn.canvas = canvas; + dn.wp = data; + dn.t = t; + dn.scalar = sc; + dn.array = ap; + dn.firstkey = 1; + dn.ycumulative = slot_getfloat(&x->value, t, data, 0); + dn.symbol = (x->flags & DRAW_SYMBOL)!=0; + if (dn.scalar) + gpointer_setcanvas(&dn.gpointer, dn.canvas, dn.scalar); + else gpointer_setarray(&dn.gpointer, dn.array, dn.wp); + /* canvas_grab(glist, z, drawnumber_motion, drawnumber_key, xpix, ypix); */ + } + return 1; + } else return 0; +} +#endif + +static void drawnumber_free(t_drawnumber *x) {} + +static void g_template_setup() { + template_class = class_new2("template",0,template_free,sizeof(t_template),CLASS_PD,""); + class_addmethod2(pd_canvasmaker._class, template_usetemplate, "struct", "*"); + gtemplate_class = class_new2("struct",gtemplate_new,gtemplate_free,sizeof(t_gtemplate),CLASS_NOINLET,"*"); + class_addcreator2("template",gtemplate_new_old,"*"); + + curve_class = class_new2("drawpolygon",curve_new,0,sizeof(t_curve),0,"*"); + class_setdrawcommand(curve_class); + class_addcreator2("drawcurve", curve_new,"*"); + class_addcreator2("filledpolygon",curve_new,"*"); + class_addcreator2("filledcurve", curve_new,"*"); + class_addfloat(curve_class, curve_float); + plot_class = class_new2("plot",plot_new,0,sizeof(t_plot),0,"*"); + class_setdrawcommand(plot_class); + class_addfloat(plot_class, plot_float); + drawnumber_class = class_new2("drawnumber",drawnumber_new,drawnumber_free,sizeof(t_drawnumber),0,"*"); + class_setdrawcommand(drawnumber_class); + class_addfloat(drawnumber_class, drawnumber_float); + class_addcreator2("drawsymbol",drawnumber_new,"*"); +} + +/* ------------- gpointers - safe pointing --------------- */ + +/* 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) { + if (!gp->o) return 0; + if (gp->o->_class == array_class) return 1; + return headok || 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) { + if (gp->o->_class == array_class) return gp->array->templatesym; + t_scalar *sc = gp->scalar; + return sc ? sc->t : 0; +} + +/* copy a pointer to another, assuming the second one hasn't yet been initialized. New gpointers should be initialized + either by this routine or by gpointer_init below. */ +void gpointer_copy(const t_gpointer *gpfrom, t_gpointer *gpto) { + *gpto = *gpfrom; + if (gpto && gpto->o) gpto->o->refcount++; else bug("gpointer_copy"); +} + +void gpointer_unset(t_gpointer *gp) { + if (gp->scalar) { + if (!--gp->o->refcount) pd_free(gp->o); + gp->o=0; + gp->scalar=0; + } +} + +static void gpointer_setcanvas(t_gpointer *gp, t_canvas *o, t_scalar *x) { + gpointer_unset(gp); + gp->o=o; gp->scalar = x; gp->o->refcount++; +} +static void gpointer_setarray(t_gpointer *gp, t_array *o, t_word *w) { + gpointer_unset(gp); + gp->o=o; gp->w = w; gp->o->refcount++; +} + +void gpointer_init(t_gpointer *gp) { + gp->o = 0; + gp->scalar = 0; +} + +/* ---------------------- pointers ----------------------------- */ + +static t_class *ptrobj_class; + +struct t_typedout { + t_symbol *type; + t_outlet *outlet; +}; + +struct t_ptrobj : t_object { + t_gpointer gp; + t_typedout *typedout; + int ntypedout; + t_outlet *otherout; + t_outlet *bangout; +}; + +static void *ptrobj_new(t_symbol *classname, int argc, t_atom *argv) { + t_ptrobj *x = (t_ptrobj *)pd_new(ptrobj_class); + t_typedout *to; + gpointer_init(&x->gp); + x->typedout = to = (t_typedout *)getbytes(argc * sizeof (*to)); + x->ntypedout = argc; + for (int n=argc; n--; to++) { + to->outlet = outlet_new(x,&s_pointer); + to->type = canvas_makebindsym(atom_getsymbol(argv++)); + } + x->otherout = outlet_new(x,&s_pointer); + x->bangout = outlet_new(x,&s_bang); + pointerinlet_new(x,&x->gp); + return x; +} + +static void ptrobj_traverse(t_ptrobj *x, t_symbol *s) { + t_canvas *canvas = (t_canvas *)pd_findbyclass(s, canvas_class); + if (canvas) gpointer_setcanvas(&x->gp, canvas, 0); + else error("list '%s' not found", s->name); +} + +static void ptrobj_vnext(t_ptrobj *x, float f) { + t_gpointer *gp = &x->gp; + int wantselected = f!=0; + if (!gp->o) {error("next: no current pointer"); return;} + if (gp->o->_class == array_class) {error("next: lists only, not arrays"); return;} + t_canvas *canvas = gp->canvas; + if (wantselected) {error("next: next-selected unsupported in desiredata"); return;} + /* if (wantselected && !canvas_isvisible(canvas)) {error("next: next-selected only works for a visible window"); return;} */ + t_gobj *gobj = gp->scalar; + if (!gobj) gobj = canvas->boxes->first(); + else gobj = gobj->next(); + while (gobj && (gobj->_class != scalar_class || wantselected)) gobj = gobj->next(); + if (gobj) { + t_typedout *to = x->typedout; + t_scalar *sc = (t_scalar *)gobj; + gp->scalar = sc; + for (int n = x->ntypedout; n--; to++) + if (to->type == sc->t) {outlet_pointer(to->outlet, &x->gp); return;} + outlet_pointer(x->otherout, &x->gp); + } else { + gpointer_unset(gp); + outlet_bang(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) { + if (!gpointer_check(&x->gp, 1)) {error("bang: empty pointer"); return;} + t_canvas *canvas = gpointer_getcanvas(&x->gp); + if (argc && argv->a_type == A_SYMBOL) pd_typedmess(canvas_getcanvas(canvas), argv->a_symbol, argc-1, argv+1); + else error("send-window: no message?"); +} + +static void ptrobj_bang(t_ptrobj *x) { + t_typedout *to = x->typedout; + if (!gpointer_check(&x->gp, 1)) {error("bang: empty pointer"); return;} + t_symbol *templatesym = gpointer_gettemplatesym(&x->gp); + for (int n=x->ntypedout; n--; to++) + if (to->type == templatesym) {outlet_pointer(to->outlet, &x->gp); return;} + outlet_pointer(x->otherout, &x->gp); +} + +static void ptrobj_pointer(t_ptrobj *x, t_gpointer *gp) { + gpointer_unset(&x->gp); + gpointer_copy(gp, &x->gp); + ptrobj_bang(x); +} + +static void ptrobj_rewind(t_ptrobj *x) { + if (!gpointer_check(&x->gp, 1)) {error("rewind: empty pointer"); return;} + if (x->gp.o->_class == array_class) {error("rewind: sorry, unavailable for arrays"); return;} + gpointer_setcanvas(&x->gp, x->gp.canvas, 0); + ptrobj_bang(x); +} + +static void ptrobj_free(t_ptrobj *x) { + free(x->typedout); + gpointer_unset(&x->gp); +} + +/* ---------------------- get ----------------------------- */ + +static t_class *get_class; +struct t_getvariable { + t_symbol *sym; + t_outlet *outlet; +}; +struct t_get : t_object { + t_symbol *templatesym; + int nout; + t_getvariable *variables; +}; +static void *get_new(t_symbol *why, int argc, t_atom *argv) { + t_get *x = (t_get *)pd_new(get_class); + x->templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (argc) argc--, argv++; + t_getvariable *sp = x->variables = (t_getvariable *)getbytes(argc * sizeof (*x->variables)); + x->nout = argc; + for (int i=0; i < argc; i++, sp++) { + sp->sym = atom_getsymbolarg(i, argc, argv); + sp->outlet = outlet_new(x,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->nout; + t_template *t = template_findbyname(x->templatesym); + t_getvariable *vp; + TEMPLATE_CHECK(x->templatesym,) + if (!gpointer_check(gp, 0)) {error("stale or empty pointer"); return;} + t_word *vec = gpointer_word(gp); + vp = x->variables + nitems-1; + for (int i=nitems-1; i>=0; i--, vp--) { + int onset, type; + t_symbol *arraytype; + if (template_find_field(t, vp->sym, &onset, &type, &arraytype)) { + if (type == DT_FLOAT) outlet_float(vp->outlet, *(t_float *)(((char *)vec) + onset)); + else if (type == DT_SYMBOL) outlet_symbol(vp->outlet, *(t_symbol **)(((char *)vec) + onset)); + else error("%s.%s is not a number or symbol", t->sym->name, vp->sym->name); + } else error("%s.%s: no such field", t->sym->name, vp->sym->name); + } +} + +static void get_free(t_get *x) {free(x->variables);} + +/* ---------------------- set ----------------------------- */ + +static t_class *set_class; + +struct t_setvariable { + t_symbol *sym; + union word w; +}; + +struct t_set : t_object { + t_gpointer gp; + t_symbol *templatesym; + int nin; + int issymbol; + t_setvariable *variables; +}; + +static void *set_new(t_symbol *why, int argc, t_atom *argv) { + t_set *x = (t_set *)pd_new(set_class); + if (argc && (argv[0].a_type == A_SYMBOL) && !strcmp(argv[0].a_symbol->name,"-symbol")) { + x->issymbol = 1; + argc--; + argv++; + } + else x->issymbol = 0; + x->templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (argc) argc--, argv++; + t_setvariable *sp = x->variables = (t_setvariable *)getbytes(argc * sizeof (*x->variables)); + x->nin = argc; + if (argc) { + for (int i=0; i<argc; i++, sp++) { + sp->sym = atom_getsymbolarg(i, argc, argv); + if (x->issymbol) sp->w.w_symbol = &s_; + else sp->w.w_float = 0; + if (i) { + if (x->issymbol) symbolinlet_new(x,&sp->w.w_symbol); + else floatinlet_new(x, &sp->w.w_float); + } + } + } + pointerinlet_new(x,&x->gp); + gpointer_init(&x->gp); + return x; +} + +static void set_bang(t_set *x) { + int nitems = x->nin; + t_template *t = template_findbyname(x->templatesym); + t_gpointer *gp = &x->gp; + TEMPLATE_CHECK(x->templatesym,) + if (!gpointer_check(gp, 0)) {error("empty pointer"); return;} + if (gpointer_gettemplatesym(gp) != x->templatesym) { + error("%s: got wrong template (%s)",x->templatesym->name,gpointer_gettemplatesym(gp)->name); + return; + } + if (!nitems) return; + t_word *vec = gpointer_word(gp); + t_setvariable *vp=x->variables; + if (x->issymbol) for (int i=0; i<nitems; i++,vp++) template_setsymbol(t, vp->sym, vec, vp->w.w_symbol, 1); + else for (int i=0; i<nitems; i++,vp++) template_setfloat(t, vp->sym, vec, vp->w.w_float, 1); + scalar_redraw(gp->scalar, gpointer_getcanvas(gp)); /* but ought to use owner_array->gp.scalar */ +} + +static void set_float(t_set *x, t_float f) { + if (x->nin && !x->issymbol) {x->variables[0].w.w_float = f; set_bang(x);} + else error("type mismatch or no field specified"); +} +static void set_symbol(t_set *x, t_symbol *s) { + if (x->nin && x->issymbol) {x->variables[0].w.w_symbol = s; set_bang(x);} + else error("type mismatch or no field specified"); +} + +static void set_free(t_set *x) { + free(x->variables); + gpointer_unset(&x->gp); +} + +/* ---------------------- elem ----------------------------- */ + +static t_class *elem_class; + +struct t_elem : t_object { + t_symbol *templatesym; + t_symbol *fieldsym; + t_gpointer gp; + t_gpointer gparent; +}; + +static void *elem_new(t_symbol *templatesym, t_symbol *fieldsym) { + t_elem *x = (t_elem *)pd_new(elem_class); + x->templatesym = canvas_makebindsym(templatesym); + x->fieldsym = fieldsym; + gpointer_init(&x->gp); + gpointer_init(&x->gparent); + pointerinlet_new(x,&x->gparent); + outlet_new(x,&s_pointer); + return x; +} + +static void elem_float(t_elem *x, t_float f) { + int indx = (int)f, nitems, onset; + t_symbol *fieldsym = x->fieldsym, *elemtemplatesym; + t_template *t = template_findbyname(x->templatesym); + t_template *elemtemplate; + t_gpointer *gparent = &x->gparent; + t_array *array; + int elemsize, type; + if (!gpointer_check(gparent, 0)) {error("empty pointer"); return;} + if (gpointer_gettemplatesym(gparent) != x->templatesym) { + error("%s: got wrong template (%s)", x->templatesym->name, gpointer_gettemplatesym(gparent)->name); + return; + } + t_word *w = gpointer_word(gparent); + TEMPLATE_CHECK(x->templatesym,) + if (!template_find_field(t, fieldsym, &onset, &type, &elemtemplatesym)) { + error("couldn't find array field %s", fieldsym->name); + return; + } + if (type != DT_ARRAY) {error("element: field %s not of type array", fieldsym->name); return;} + if (!(elemtemplate = template_findbyname(elemtemplatesym))) { + error("couldn't find field template %s", elemtemplatesym->name); + return; + } + elemsize = elemtemplate->n * sizeof(t_word); + array = *(t_array **)(((char *)w) + onset); + nitems = array->n; + if (indx < 0) indx = 0; + if (indx >= nitems) indx = nitems-1; + gpointer_setarray(&x->gp, array, (t_word *)&array->vec[indx*elemsize]); + outlet_pointer(x->outlet, &x->gp); +} + +static void elem_free(t_elem *x, t_gpointer *gp) { + gpointer_unset(&x->gp); + gpointer_unset(&x->gparent); +} + +/* ---------------------- getsize ----------------------------- */ + +static t_class *getsize_class; + +struct t_getsize : t_object { + t_symbol *templatesym; + t_symbol *fieldsym; +}; + +static void *getsize_new(t_symbol *templatesym, t_symbol *fieldsym) { + t_getsize *x = (t_getsize *)pd_new(getsize_class); + x->templatesym = canvas_makebindsym(templatesym); + x->fieldsym = fieldsym; + outlet_new(x,&s_float); + return x; +} + +static void getsize_pointer(t_getsize *x, t_gpointer *gp) { + int onset, type; + t_symbol *fieldsym = x->fieldsym, *elemtemplatesym; + t_template *t = template_findbyname(x->templatesym); + TEMPLATE_CHECK(x->templatesym,) + if (!template_find_field(t, fieldsym, &onset, &type, &elemtemplatesym)) { + error("couldn't find array field %s", fieldsym->name); + return; + } + if (type != DT_ARRAY) {error("field %s not of type array", fieldsym->name); return;} + if (!gpointer_check(gp, 0)) {error("stale or empty pointer"); return;} + if (gpointer_gettemplatesym(gp) != x->templatesym) { + error("%s: got wrong template (%s)", x->templatesym->name, gpointer_gettemplatesym(gp)->name); + return; + } + t_word *w = gpointer_word(gp); + t_array *array = *(t_array **)(((char *)w) + onset); + outlet_float(x->outlet, (float)(array->n)); +} + +/* ---------------------- setsize ----------------------------- */ + +static t_class *setsize_class; + +struct t_setsize : t_object { + t_symbol *templatesym; + t_symbol *fieldsym; + t_gpointer gp; +}; + +static void *setsize_new(t_symbol *templatesym, t_symbol *fieldsym, t_floatarg newsize) { + t_setsize *x = (t_setsize *)pd_new(setsize_class); + x->templatesym = canvas_makebindsym(templatesym); + x->fieldsym = fieldsym; + gpointer_init(&x->gp); + pointerinlet_new(x,&x->gp); + return x; +} + +static void setsize_float(t_setsize *x, t_float f) { + int onset, type; + t_template *t = template_findbyname(x->templatesym); + int newsize = (int)f; + t_gpointer *gp = &x->gp; + if (!gpointer_check(&x->gp, 0)) {error("empty pointer"); return;} + if (gpointer_gettemplatesym(&x->gp) != x->templatesym) { + error("%s: got wrong template (%s)", x->templatesym->name, gpointer_gettemplatesym(&x->gp)->name); + return; + } + t_word *w = gpointer_word(gp); + TEMPLATE_CHECK(x->templatesym,) + t_symbol *elemtemplatesym; + if (!template_find_field(t, x->fieldsym, &onset, &type, &elemtemplatesym)) { + error("couldn't find array field %s", x->fieldsym->name); + return; + } + if (type != DT_ARRAY) {error("field %s not of type array", x->fieldsym->name); return;} + t_template *elemtemplate = template_findbyname(elemtemplatesym); + if (!elemtemplate) { + error("couldn't find field template %s", elemtemplatesym->name); + return; + } + int elemsize = elemtemplate->n * sizeof(t_word); + t_array *array = *(t_array **)(((char *)w) + onset); + if (elemsize != array->elemsize) bug("setsize_gpointer"); + int nitems = array->n; + if (newsize < 1) newsize = 1; + if (newsize == nitems) return; + + /* here there was something to erase the array before resizing it */ + + /* now do the resizing and, if growing, initialize new scalars */ + array->vec = (char *)resizealignedbytes(array->vec, elemsize * nitems, elemsize * newsize); + array->n = newsize; + if (newsize > nitems) { + char *newelem = &array->vec[nitems*elemsize]; + int nnew = newsize - nitems; + while (nnew--) { + word_init((t_word *)newelem, elemtemplate, gp); + newelem += elemsize; + } + } + gobj_changed(gpointer_getscalar(gp),0); +} + +static void setsize_free(t_setsize *x) {gpointer_unset(&x->gp);} + +/* ---------------------- append ----------------------------- */ + +static t_class *append_class; + +struct t_appendvariable { + t_symbol *sym; + t_float f; +}; + +struct t_append : t_object { + t_gpointer gp; + t_symbol *templatesym; + int nin; + t_appendvariable *variables; +}; + +static void *append_new(t_symbol *why, int argc, t_atom *argv) { + t_append *x = (t_append *)pd_new(append_class); + x->templatesym = canvas_makebindsym(atom_getsymbolarg(0, argc, argv)); + if (argc) argc--, argv++; + x->variables = (t_appendvariable *)getbytes(argc * sizeof (*x->variables)); + x->nin = argc; + if (argc) { + t_appendvariable *sp = x->variables; + for (int i=0; i<argc; i++, sp++) { + sp->sym = atom_getsymbolarg(i, argc, argv); + sp->f = 0; + if (i) floatinlet_new(x,&sp->f); + } + } + pointerinlet_new(x,&x->gp); + outlet_new(x,&s_pointer); + gpointer_init(&x->gp); + return x; +} + +static void append_float(t_append *x, t_float f) { + int nitems = x->nin; + t_template *t = template_findbyname(x->templatesym); + t_gpointer *gp = &x->gp; + TEMPLATE_CHECK(x->templatesym,) + if (!gp->o) {error("no current pointer"); return;} + if (gp->o->_class == array_class) {error("lists only, not arrays"); return;} + t_canvas *canvas = gp->canvas; + if (!nitems) return; + x->variables[0].f = f; + t_scalar *sc = scalar_new(canvas,x->templatesym); + if (!sc) {error("%s: couldn't create scalar", x->templatesym->name); return;} + canvas->boxes->add(sc); + gobj_changed(sc,0); + gp->scalar = sc; + t_word *vec = sc->v; + t_appendvariable *vp=x->variables; + for (int i=0; i<nitems; i++,vp++) template_setfloat(t, vp->sym, vec, vp->f, 1); + scalar_redraw(sc, canvas); + outlet_pointer(x->outlet, gp); +} + +static void append_free(t_append *x) { + free(x->variables); + gpointer_unset(&x->gp); +} + +/* ---------------------- sublist ----------------------------- */ + +static t_class *sublist_class; + +struct t_sublist : t_object { + t_symbol *templatesym; + t_symbol *fieldsym; + t_gpointer gp; +}; + +static void *sublist_new(t_symbol *templatesym, t_symbol *fieldsym) { + t_sublist *x = (t_sublist *)pd_new(sublist_class); + x->templatesym = canvas_makebindsym(templatesym); + x->fieldsym = fieldsym; + gpointer_init(&x->gp); + outlet_new(x,&s_pointer); + return x; +} + +static void sublist_pointer(t_sublist *x, t_gpointer *gp) { + t_symbol *dummy; + t_template *t = template_findbyname(x->templatesym); + int onset, type; + TEMPLATE_CHECK(x->templatesym,) + if (!gpointer_check(gp, 0)) {error("stale or empty pointer"); return;} + if (!template_find_field(t, x->fieldsym, &onset, &type, &dummy)) { + error("couldn't find field %s", x->fieldsym->name); + return; + } + if (type != DT_CANVAS) {error("field %s not of type list", x->fieldsym->name); return;} + t_word *w = gpointer_word(gp); + gpointer_setcanvas(&x->gp, *(t_canvas **)(((char *)w) + onset), 0); + outlet_pointer(x->outlet, &x->gp); +} + +static void sublist_free(t_sublist *x, t_gpointer *gp) {gpointer_unset(&x->gp);} + +static void g_traversal_setup() { + t_class *c = ptrobj_class = class_new2("pointer",ptrobj_new,ptrobj_free,sizeof(t_ptrobj),0,"*"); + class_addmethod2(c, ptrobj_traverse,"traverse", "s"); + class_addmethod2(c, ptrobj_next,"next",""); + class_addmethod2(c, ptrobj_vnext,"vnext","F"); + class_addmethod2(c, ptrobj_sendwindow,"send-window","*"); + class_addmethod2(c, ptrobj_rewind, "rewind",""); + class_addpointer(c, ptrobj_pointer); + class_addbang(c, ptrobj_bang); + get_class = class_new2("get",get_new,get_free,sizeof(t_get),0,"*"); + class_addpointer(get_class, get_pointer); + set_class = class_new2("set",set_new,set_free,sizeof(t_set),0,"*"); + class_addfloat(set_class, set_float); + class_addsymbol(set_class, set_symbol); + class_addbang(set_class, set_bang); + elem_class = class_new2("element",elem_new,elem_free,sizeof(t_elem),0,"SS"); + class_addfloat(elem_class, elem_float); + getsize_class = class_new2("getsize",getsize_new,0,sizeof(t_getsize),0,"SS"); + class_addpointer(getsize_class, getsize_pointer); + setsize_class = class_new2("setsize",setsize_new,setsize_free,sizeof(t_setsize),0,"SSFF"); + class_addfloat(setsize_class, setsize_float); + append_class = class_new2("append",append_new,append_free,sizeof(t_append),0,"*"); + class_addfloat(append_class, append_float); + sublist_class = class_new2("sublist",sublist_new,sublist_free,sizeof(t_sublist),0,"SS"); + class_addpointer(sublist_class, sublist_pointer); +} + +/*EXTERN*/ void canvas_savecontainerto(t_canvas *x, t_binbuf *b); + +struct t_iemgui : t_object { + t_canvas *canvas; + int h,w; + int ldx,ldy; + int isa; /* bit 0: loadinit; bit 20: scale */ + int font_style, fontsize; + int fcol,bcol,lcol; /* foreground, background, label colors */ + t_symbol *snd,*rcv,*lab; /* send, receive, label symbols */ +}; + +struct t_bng : t_iemgui { + int count; + int ftbreak; /* flash time break (ms) */ + int fthold; /* flash time hold (ms) */ +}; + +struct t_slider : t_iemgui { + t_float val; + t_float min,max; + int steady; + int is_log; + int orient; +}; + +struct t_radio : t_iemgui { + int on, on_old; + int change; + int number; + t_atom at[2]; + int orient; + int oldstyle; +}; + +struct t_toggle : t_iemgui { + float on; + float nonzero; +}; + +struct t_cnv : t_iemgui { + t_atom at[3]; + int vis_w, vis_h; +}; + +struct t_dropper : t_iemgui { + t_symbol *s; + t_symbol *ds; +}; + +struct t_vu : t_iemgui { + int led_size; + int peak,rms; + float fp,fr; + int scale; +}; + +struct t_nbx : t_iemgui { + double val; + double min,max; + double k; + char buf[32]; + int log_height; + int change; + int is_log; +}; + +struct t_foo { int argc; t_atom *argv; t_binbuf *b; }; +static int pd_pickle(t_foo *foo, char *fmt, ...); +static int pd_savehead(t_binbuf *b, t_iemgui *x, char *name); + +static t_class *radio_class, *slider_class; +static t_symbol *sym_hdl, *sym_hradio, *sym_vdl, *sym_vradio, *sym_vsl, *sym_vslider; + +t_class *dummy_class; +/*static*/ t_class *text_class; +static t_class *mresp_class; +static t_class *message_class; +static t_class *gatom_class; + +void canvas_text(t_canvas *gl, t_symbol *s, int argc, t_atom *argv) { + t_text *x = (t_text *)pd_new(text_class); + x->binbuf = binbuf_new(); + x->x = atom_getintarg(0, argc, argv); + x->y = atom_getintarg(1, argc, argv); + if (argc > 2) binbuf_restore(x->binbuf, argc-2, argv+2); + canvas_add(gl,x); + pd_set_newest(x); +} + +void canvas_getargs(int *argcp, t_atom **argvp) { + t_canvasenvironment *e = canvas_getenv(canvas_getcurrent()); + *argcp = e->argc; + *argvp = e->argv; +} + +static void canvas_objtext(t_canvas *gl, int xpix, int ypix, t_binbuf *b, int index=-1) { + t_text *x=0; + int argc, n; + t_atom *argv; + char *s; + newest = 0; + binbuf_gettext(b,&s,&n); + pd_pushsym(gl); + canvas_getargs(&argc, &argv); + binbuf_eval(b, &pd_objectmaker, argc, argv); + if (binbuf_getnatom(b)) { + if (!newest) { + char *s = binbuf_gettext2(b); + error("couldn't create %s",s); + free(s); + } else if (!(x = pd_checkobject(newest))) { + char *s = binbuf_gettext2(b); + error("didn't return a patchable object: %s",s); + free(s); + } + } + /* make a "broken object", that is, one that should appear with a dashed contour. */ + if (!x) { + x = (t_text *)pd_new(dummy_class); + pd_set_newest(x); + } + x->binbuf = b; + x->x = xpix; + x->y = ypix; + canvas_add(gl,x,index); + if (x->_class== vinlet_class) canvas_resortinlets(canvas_getcanvas(gl)); + if (x->_class==voutlet_class) canvas_resortoutlets(canvas_getcanvas(gl)); + pd_popsym(gl); +} + +void canvas_obj(t_canvas *gl, t_symbol *s, int argc, t_atom *argv) { + t_binbuf *b = binbuf_new(); + if (argc >= 2) { + binbuf_restore(b, argc-2, argv+2); + canvas_objtext(gl, atom_getintarg(0, argc, argv), atom_getintarg(1, argc, argv), b); + } else canvas_objtext(gl,0,0,b); +} + +void canvas_objfor(t_canvas *gl, t_text *x, int argc, t_atom *argv) { + x->binbuf = binbuf_new(); + x->x = atom_getintarg(0, argc, argv); + x->y = atom_getintarg(1, argc, argv); + if (argc > 2) binbuf_restore(x->binbuf, argc-2, argv+2); + canvas_add(gl,x); +} + +struct t_mresp : t_pd { + t_outlet *outlet; +}; +struct t_message : t_text { + t_mresp mresp; + t_canvas *canvas; +}; + +static void mresp_bang(t_mresp *x) {outlet_bang(x->outlet);} +static void mresp_float(t_mresp *x, t_float f) {outlet_float(x->outlet, f);} +static void mresp_symbol(t_mresp *x, t_symbol *s) {outlet_symbol(x->outlet, s);} +static void mresp_list(t_mresp *x, t_symbol *s, int argc, t_atom *argv) + {outlet_list(x->outlet, s, argc, argv);} +static void mresp_anything(t_mresp *x, t_symbol *s, int argc, t_atom *argv) + {outlet_anything(x->outlet, s, argc, argv);} + +static void message_bang(t_message *x) +{binbuf_eval(x->binbuf,&x->mresp, 0, 0);} +static void message_float(t_message *x, t_float f) +{t_atom at; SETFLOAT(&at, f); binbuf_eval(x->binbuf, &x->mresp, 1, &at);} +static void message_symbol(t_message *x, t_symbol *s) +{t_atom at; SETSYMBOL(&at, s); binbuf_eval(x->binbuf, &x->mresp, 1, &at);} +static void message_list(t_message *x, t_symbol *s, int argc, t_atom *argv) +{binbuf_eval(x->binbuf, &x->mresp, argc, argv);} +static void message_add2(t_message *x, t_symbol *s, int argc, t_atom *argv) +{binbuf_add(x->binbuf, argc, argv); gobj_changed(x,"binbuf");} +static void message_set(t_message *x, t_symbol *s, int argc, t_atom *argv) +{binbuf_clear(x->binbuf); message_add2(x,s,argc,argv);} +static void message_add(t_message *x, t_symbol *s, int argc, t_atom *argv) +{binbuf_add(x->binbuf, argc, argv); binbuf_addsemi(x->binbuf); gobj_changed(x,"binbuf");} +static void message_addcomma(t_message *x) +{t_atom a; SETCOMMA(&a); binbuf_add(x->binbuf, 1, &a); gobj_changed(x,"binbuf");} +static void message_adddollar(t_message *x, t_floatarg f) +{t_atom a; SETDOLLAR(&a, f<0?0:(int)f); binbuf_add(x->binbuf, 1, &a); gobj_changed(x,"binbuf");} +//static void message_adddollsym(t_message *x, t_symbol *s) +//{t_atom a; SETDOLLSYM(&a, s); binbuf_add(x->binbuf, 1, &a); gobj_changed(x,"binbuf");} + +static void message_adddollsym(t_message *x, t_symbol *s) { + t_atom a; + SETDOLLSYM(&a, symprintf("$%s",s->name)); + binbuf_add(x->binbuf, 1, &a); + gobj_changed(x,"binbuf"); +} + +static void message_addsemi(t_message *x) {message_add(x,0,0,0);} + +void canvas_msg(t_canvas *gl, t_symbol *s, int argc, t_atom *argv) { + t_message *x = (t_message *)pd_new(message_class); + x->mresp._class = mresp_class; + x->mresp.outlet = outlet_new(x,&s_float); + x->binbuf = binbuf_new(); + x->canvas = gl; + pd_set_newest(x); + if (argc > 1) { + x->x = atom_getintarg(0, argc, argv); + x->y = atom_getintarg(1, argc, argv); + if (argc > 2) binbuf_restore(x->binbuf, argc-2, argv+2); + } else { + x->x = 0; + x->y = 0; + } + canvas_add(gl,x); +} + +struct t_gatom : t_text { + t_atom atom; /* this holds the value and the type */ + t_canvas *canvas; /* owning canvas */ + t_float max,min; + t_symbol *label; /* symbol to show as label next to box */ + t_symbol *rcv; + t_symbol *snd; + char wherelabel; /* 0-3 for left, right, up, down */ + t_symbol *expanded_to; /* snd after $0, $1, ... expansion */ + short width; +}; + +/* prepend "-" as necessary to avoid empty strings, so we can use them in Pd messages. + A more complete solution would be to introduce some quoting mechanism; + but then we'd be much more complicated. */ +static t_symbol *gatom_escapit(t_symbol *s) { + if (!s || !*s->name) return gensym("-"); + if (*s->name == '-') { + char shmo[1000]; + snprintf(shmo,1000,"-%s",s->name); + shmo[999] = 0; + return gensym(shmo); + } + else return s; +} + +/* undo previous operation: strip leading "-" if found. */ +static t_symbol *gatom_unescapit(t_symbol *s) { + if (*s->name == '-') return gensym(s->name+1); + return s; +} + +static void gatom_set(t_gatom *x, t_symbol *s, int argc, t_atom *argv) { + t_atom oldatom = x->atom; + if (!argc) return; + if (x->atom.a_type == A_FLOAT) { + SETFLOAT( &x->atom,atom_getfloat(argv)); if (x->atom.a_float !=oldatom.a_float ) gobj_changed(x,"atom"); + } else if (x->atom.a_type == A_SYMBOL) { + SETSYMBOL(&x->atom,atom_getsymbol(argv)); if (x->atom.a_symbol!=oldatom.a_symbol) gobj_changed(x,"atom"); + } +} + +static void gatom_bang(t_gatom *x) { + t_symbol *s = x->expanded_to; + t_outlet *o = x->outlet; + if (x->atom.a_type == A_FLOAT) { + if (o) outlet_float(o, x->atom.a_float); + if (*s->name && s->thing) { + if (x->snd == x->rcv) goto err; + pd_float(s->thing, x->atom.a_float); + } + } else if (x->atom.a_type == A_SYMBOL) { + if (o) outlet_symbol(o, x->atom.a_symbol); + if (*s->name && s->thing) { + if (x->snd == x->rcv) goto err; + pd_symbol(s->thing, x->atom.a_symbol); + } + } + return; +err: + error("%s: atom with same send/receive name (infinite loop)", x->snd->name); +} + +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_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_reload(t_gatom *x, t_symbol *sel, int argc, t_atom *argv) { + int width; t_float wherelabel; + t_symbol *rcv,*snd; + if (!pd_scanargs(argc,argv,"ifffaaa",&width,&x->min,&x->max,&wherelabel,&x->label,&rcv,&snd)) return; + gobj_changed(x,0); + SET(label,gatom_unescapit(x->label)); + if (x->min>=x->max) {SET(min,0); SET(max,0);} + CLAMP(width,1,80); + SET(width,width); + SET(wherelabel,(int)wherelabel&3); + if (x->rcv) pd_unbind(x, canvas_realizedollar(x->canvas, x->rcv)); + SET(rcv,gatom_unescapit(rcv)); + if (x->rcv) pd_bind( x, canvas_realizedollar(x->canvas, x->rcv)); + SET(snd,gatom_unescapit(snd)); + SET(expanded_to,canvas_realizedollar(x->canvas, x->snd)); +} + +/* We need a list method because, since there's both an "inlet" and a + "nofirstin" flag, the standard list behavior gets confused. */ +static void gatom_list(t_gatom *x, t_symbol *s, int argc, t_atom *argv) { + if (!argc) gatom_bang(x); + else if (argv->a_type == A_FLOAT) gatom_float(x, argv->a_w.w_float); + else if (argv->a_type == A_SYMBOL) gatom_symbol(x, argv->a_w.w_symbol); + else error("gatom_list: need float or symbol"); +} + +void canvas_atom(t_canvas *gl, t_atomtype type, int argc, t_atom *argv) { + t_gatom *x = (t_gatom *)pd_new(gatom_class); + if (type == A_FLOAT) {SET(width, 5); SETFLOAT(&x->atom, 0);} + else {SET(width,10); SETSYMBOL(&x->atom, &s_symbol);} + x->canvas = gl; + SET(min,0); + SET(max,0); + SET(wherelabel,0); + SET(label,0); + SET(rcv,0); + SET(snd,0); + x->expanded_to = &s_; //??? + x->binbuf = binbuf_new(); + binbuf_add(x->binbuf, 1, &x->atom); + SET(x,atom_getintarg(0, argc, argv)); + SET(y,atom_getintarg(1, argc, argv)); + inlet_new(x,x,0,0); + outlet_new(x, type == A_FLOAT ? &s_float: &s_symbol); + if (argc>2) gatom_reload(x,&s_,argc-2,argv+2); + canvas_add(gl,x); + pd_set_newest(x); +} + +void canvas_floatatom( t_canvas *gl, t_symbol *s, int argc, t_atom *argv) {canvas_atom(gl, A_FLOAT, argc, argv);} +void canvas_symbolatom(t_canvas *gl, t_symbol *s, int argc, t_atom *argv) {canvas_atom(gl, A_SYMBOL, argc, argv);} +static void gatom_free(t_gatom *x) {if (x->rcv) pd_unbind(x, canvas_realizedollar(x->canvas, x->rcv));} + +extern "C" void text_save(t_gobj *z, t_binbuf *b) { + t_text *x = (t_text *)z; + t_canvas *c = (t_canvas *)z; /* in case it is */ + if (x->_class == message_class) { + binbuf_addv(b,"ttii","#X","msg", (t_int)x->x, (t_int)x->y); + binbuf_addbinbuf(b, x->binbuf); + } else if (x->_class == gatom_class) { + t_gatom *g = (t_gatom *)x; + t_symbol *sel = g->atom.a_type==A_SYMBOL? gensym("symbolatom") : gensym("floatatom"); + binbuf_addv(b,"tsii","#X", sel, (t_int)x->x, (t_int)x->y); + binbuf_addv(b,"iffi", (t_int)g->width, g->min, g->max, (t_int)g->wherelabel); + binbuf_addv(b,"sss", gatom_escapit(g->label), gatom_escapit(g->rcv), gatom_escapit(g->snd)); + } else if (x->_class == text_class) { + binbuf_addv(b, "ttii","#X","text", (t_int)x->x, (t_int)x->y); + binbuf_addbinbuf(b, x->binbuf); + } else { + if (zgetfn(x,gensym("saveto")) && + !(x->_class==canvas_class && (canvas_isabstraction(c) || canvas_istable(c)))) { + mess1(x,gensym("saveto"),b); + binbuf_addv(b,"ttii","#X","restore", (t_int)x->x, (t_int)x->y); + } else { + binbuf_addv(b,"ttii","#X","obj", (t_int)x->x, (t_int)x->y); + } + if (x->binbuf) { + binbuf_addbinbuf(b, x->binbuf); + } else { + /*bug("binbuf missing at #X restore !!!");*/ + } + } + binbuf_addv(b, ";"); +} + +static t_binbuf *canvas_cut_wires(t_canvas *x, t_gobj *o) { + t_binbuf *buf = binbuf_new(); + canvas_wires_each(oc,t,x) { + if ((o==t.from) != (o==t.to)) + binbuf_addv(buf,"ttiiii;","#X","connect", t.from->dix->index, t.outlet, t.to->dix->index, t.inlet); + } + return buf; +} +static void canvas_paste_wires(t_canvas *x, t_binbuf *buf) { + pd_bind(x,gensym("#X")); + binbuf_eval(buf,0,0,0); + pd_unbind(x,gensym("#X")); +} + +static void text_setto(t_text *x, t_canvas *canvas, char *buf, int bufsize) { + if (x->_class == message_class || x->_class == gatom_class || x->_class == text_class) { + binbuf_text(x->binbuf, buf, bufsize); + gobj_changed(x,"binbuf"); + pd_set_newest(x); + return; + } + t_binbuf *b = binbuf_new(); + binbuf_text(b, buf, bufsize); + int natom1 = binbuf_getnatom(x->binbuf); t_atom *vec1 = binbuf_getvec(x->binbuf); + int natom2 = binbuf_getnatom(b); t_atom *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 && vec1[0].a_symbol == s_pd && + vec2[0].a_type == A_SYMBOL && vec2[0].a_symbol == s_pd) { + typedmess(x,gensym("rename"),natom2-1,vec2+1); + binbuf_free(x->binbuf); + x->binbuf = b; + pd_set_newest(x); /* fake object creation, for simplicity of client */ + } else { + int xwas=x->x, ywas=x->y; + int backupi = x->dix->index; + t_binbuf *buf = canvas_cut_wires(canvas_getcanvas(canvas),x); + canvas_delete(canvas,x); + canvas_objtext(canvas,xwas,ywas,b,backupi); + t_pd *backup = newest; + post("backupi=%d newest->index=%d",backupi,((t_object *)newest)->dix->index); + if (newest && pd_class(newest) == canvas_class) canvas_loadbang((t_canvas *)newest); + canvas_paste_wires(canvas_getcanvas(canvas), buf); + newest = backup; + } +} + +t_object *symbol2opointer(t_symbol *s) { + t_text *o; + if (sscanf(s->name,"x%lx",(long*)&o)<1) {error("expected object-id"); return 0;} + if (!object_table->exists(o)) {error("%s target is not a currently valid pointer",s->name); return 0;} + if ((object_table->get(o)&1)==0) { + error("%s target is zombie? object_table says '%ld'",s->name,object_table->get(o)); + return 0; + } + if (!o->_class->patchable) {error("%s target not a patchable object"); return 0;} + return o; +} + +t_object *atom2opointer(t_atom *a) {return symbol2opointer(atom_getsymbol(a));} + +static void canvas_text_setto(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { + t_text *o = atom2opointer(&argv[0]); if (!o) return; + char str[4096]; + for (int i=0; i<argc-1; i++) str[i] = atom_getint(argv+i+1); + str[argc-1]=0; + text_setto(o,x,str,argc-1); +} + +static void canvas_object_moveto(t_canvas *x, t_symbol *name, t_floatarg px, t_floatarg py) { + t_text *o = symbol2opointer(name); if (!o) return; + o->x=(int)px; gobj_changed(o,"x"); + o->y=(int)py; gobj_changed(o,"y"); +} +static void canvas_object_delete(t_canvas *x, t_symbol *name) { + t_text *o = symbol2opointer(name); if (!o) return; + fprintf(stderr,"canvas_object_delete %p\n",o); + canvas_delete(x,o); +} + +static void canvas_object_get_tips(t_canvas *x, t_symbol *name) { + t_text *o = symbol2opointer(name); if (!o) return; + char foo[666]; + if (o->_class->firstin) strcpy(foo,o->_class->firsttip->name); else strcpy(foo,""); + int n = obj_ninlets(x); + //char *foop = foo; + for (int i=!!o->_class->firstin; i<n; i++) { + strcat(foo," "); + strcat(foo,inlet_tip(o->inlet,i)); + } + sys_mgui(o,"tips=","S",foo); +} + +extern "C" void open_via_helppath(const char *name, const char *dir); +static void canvas_object_help(t_canvas *x, t_symbol *name) { + t_text *o = symbol2opointer(name); if (!o) return; + const char *hn = class_gethelpname(o->_class); + bool suffixed = strcmp(hn+strlen(hn)-3, ".pd")==0; + char *buf; + asprintf(&buf,"%s%s",hn,suffixed?"":".pd"); + open_via_helppath(buf, o->_class->externdir->name); + free(buf); +} + +static long canvas_children_count(t_canvas *x) { + long n=0; + canvas_each(y,x) n++; + return n; +} + +static void canvas_reorder_last(t_canvas *x, int dest) { + int n = canvas_children_count(x); + t_gobj *foo = canvas_remove_nth(x,n-1); + fprintf(stderr,"canvas_reorder_last(x=%p,dest=%d) n=%d\n",x,dest,n); + fprintf(stderr,"foo=%p\n",foo); + if (!foo) {bug("canvas_remove_nth returned NULL"); return;} + canvas_insert_nth(x,dest,foo); +} + +/* this supposes that $2=#X */ +#if 0 +static void canvas_object_insert(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { + //t_text *o; + //t_binbuf *b; + if (argc<1) {error("not enough args"); return;} + if (argv[0].a_type != A_FLOAT) {error("$1 must be float"); return;} + int i = atom_getint(argv); + if (argv[2].a_type != A_SYMBOL) {error("$2 must be symbol"); return;} + s = argv[2].a_symbol; + x->next_add = i; + if (s == gensym("obj")) { + /* b = binbuf_new(); binbuf_restore(b, argc-5, argv+5); + canvas_objtext(x,atom_getintarg(3,argc,argv),atom_getintarg(4,argc,argv),0,b); */ + canvas_obj(x,s,argc-3,argv+3); + } else if (s == gensym("restore")) { canvas_restore(x,s,argc-3,argv+3); + } else if (s == gensym("floatatom")) { canvas_floatatom(x,s,argc-3,argv+3); + } else if (s == gensym("symbolatom")) { canvas_floatatom(x,s,argc-3,argv+3); + } else if (s == gensym("text")) { canvas_text(x,gensym("text"),argc-3,argv+3); + } else post("UNSUPPORTED object_insert: %s",s->name); + /* canvas_reorder_last(x,i); */ + x->next_add = -1; +/*err: pd_popsym(x);*/ +} +#endif + +static void g_text_setup() { + t_class *c; + text_class = class_new2("text", 0,0,sizeof(t_text),CLASS_NOINLET|CLASS_PATCHABLE,0); + dummy_class = class_new2("dummy",0,0,sizeof(t_text),CLASS_NOINLET|CLASS_PATCHABLE,0); + + c = mresp_class = class_new2("messresponder",0,0,sizeof(t_text),CLASS_PD,""); + class_addbang( c, mresp_bang); + class_addfloat( c, (t_method) mresp_float); + class_addsymbol( c, mresp_symbol); + class_addlist( c, mresp_list); + class_addanything(c, mresp_anything); + + c = message_class = class_new2("message",0,0,sizeof(t_message),CLASS_PATCHABLE,""); + class_addbang(c, message_bang); + class_addfloat(c, message_float); + class_addsymbol(c, message_symbol); + class_addlist(c, message_list); + class_addanything(c, message_list); + class_addmethod2(c, message_set, "set","*"); + class_addmethod2(c, message_add, "add","*"); + class_addmethod2(c, message_add2,"add2","*"); + class_addmethod2(c, message_addcomma, "addcomma", ""); + class_addmethod2(c, message_addsemi, "addsemi", ""); + class_addmethod2(c, message_adddollar, "adddollar", "f"); + class_addmethod2(c, message_adddollsym, "adddollsym","s"); + + c = gatom_class = class_new2("gatom",0,gatom_free,sizeof(t_gatom),CLASS_NOINLET|CLASS_PATCHABLE,""); + class_addbang(c, gatom_bang); + class_addfloat(c, gatom_float); + class_addsymbol(c, gatom_symbol); + class_addlist(c, gatom_list); + class_addmethod2(c, gatom_set, "set","*"); + class_addmethod2(c, gatom_reload, "reload","*"); +} + +static int iemgui_color_hex[]= { + 0xfcfcfc, 0xa0a0a0, 0x404040, 0xfce0e0, 0xfce0c0, + 0xfcfcc8, 0xd8fcd8, 0xd8fcfc, 0xdce4fc, 0xf8d8fc, + 0xe0e0e0, 0x7c7c7c, 0x202020, 0xfc2828, 0xfcac44, + 0xe8e828, 0x14e814, 0x28f4f4, 0x3c50fc, 0xf430f0, + 0xbcbcbc, 0x606060, 0x000000, 0x8c0808, 0x583000, + 0x782814, 0x285014, 0x004450, 0x001488, 0x580050 +}; + +static int iemgui_clip_size(int size) {return max(8,size);} + +int convert_color2(int x) { + return ~ (((0xfc0000&x)>>6) | ((0xfc00&x)>>4) | ((0xfc&x)>>2)); +} + +static int convert_color(int x) { + if (x>=0) return iemgui_color_hex[x%30]; + x=~x; + return ((x&0x3f000)<<6) | ((x&0xfc0)<<4) | ((x&0x3f)<<2); +} + +static void iemgui_send(t_iemgui *x, t_symbol *s) { + SET(snd,canvas_realizedollar(x->canvas, s)); + if (x->snd==s_empty) SET(snd,0); +} + +static void iemgui_receive(t_iemgui *x, t_symbol *s) { + t_symbol *rcv = canvas_realizedollar(x->canvas, s); + if (rcv==s_empty) rcv=0; + if (rcv==x->rcv) return; + if(x->rcv) pd_unbind(x,x->rcv); + SET(rcv,rcv); + if(x->rcv) pd_bind(x,x->rcv); +} + +static void iemgui_label(t_iemgui *x, t_symbol *s) { + SET(lab,s==s_empty?0:s); +} +static void iemgui_label_pos(t_iemgui *x, t_float ldx, t_float ldy) { + SET(ldx,(int)ldx); + SET(ldy,(int)ldy); +} +static void iemgui_label_font(t_iemgui *x, t_symbol *s, int ac, t_atom *av) { + SET(fontsize,max(4,(int)atom_getintarg(1, ac, av))); + SET(font_style,atom_getintarg(0, ac, av)); +} +static void iemgui_delta(t_iemgui *x, t_symbol *s, int ac, t_atom *av) { + SET(x,x->x+(int)atom_getintarg(0, ac, av)); + SET(y,x->y+(int)atom_getintarg(1, ac, av)); +} +static void iemgui_pos(t_iemgui *x, t_symbol *s, int ac, t_atom *av) { + SET(x,x->x+(int)atom_getintarg(0, ac, av)); + SET(y,x->y+(int)atom_getintarg(1, ac, av)); +} + +static int iemgui_compatible_col(int i) {return i>=0 ? iemgui_color_hex[i%30] : (~i)&0xffffff;} + +static void iemgui_color(t_iemgui *x, t_symbol *s, int ac, t_atom *av) { + int i=0; + SET(bcol,iemgui_compatible_col(atom_getintarg(i++, ac, av))); + if(ac > 2) SET(fcol,iemgui_compatible_col(atom_getintarg(i++, ac, av))); + SET(lcol,iemgui_compatible_col(atom_getintarg(i++, ac, av))); +} + +#define NEXT p=va_arg(val,void*); /*printf("p=%p\n",p);*/ +int pd_vscanargs(int argc, t_atom *argv, char *fmt, va_list val) { + int optional=0; + int i,j=0; + for (i=0; fmt[i]; i++) { + switch (fmt[i]) { + case 0: error("too many args"); return 0; + case '*': goto break1; /* rest is any type */ + case 'F': case 'f': case 'd': case 'i': case 'c': case 'b': + if (!IS_A_FLOAT(argv,j)) {error("expected float in $%d",i+1); return 0;} + j++; break; + case 'S': case 's': + if (!IS_A_SYMBOL(argv,j)) {error("expected symbol in $%d",i+1); return 0;} + j++; break; + case '?': break; + case 'a': + if (!IS_A_FLOAT(argv,j) && !IS_A_SYMBOL(argv,j)) {error("expected float or symbol in $%d",i+1); return 0;} + j++; break; + case ';': optional=1; break; + default: error("bad format string"); return 0; + } + } + if (j<argc && !optional) {error("not enough args"); return 0;} +break1: + i=0; + for (int j=0; fmt[j] || i<argc; j++) { + void *p; + switch (fmt[j]) { + case ';': continue; /*ignore*/ + case '*': goto break2; + case '?': case 'F': case 'S': break; /* skip */ /* what are those for, again? */ + case 'd': NEXT; *(double*)p = atom_getfloatarg(i,argc,argv); break; + case 'f': NEXT; *(float*)p = atom_getfloatarg(i,argc,argv); break; + case 'i': NEXT; *(int*)p = atom_getintarg(i,argc,argv); break; + case 'b': NEXT; *(int*)p = !!atom_getintarg(i,argc,argv); break; /* 0 or 1 */ + case 'c': NEXT; *(int*)p = convert_color(atom_getintarg(i,argc,argv)); break; /* IEM-style 8:8:8 colour */ + case 's': NEXT; *(t_symbol**)p=atom_getsymbolarg(i,argc,argv); break; + case 'a': NEXT; /* send-symbol, receive-symbol, or IEM-style label */ + if (IS_A_SYMBOL(argv,i)) + *(t_symbol**)p = atom_getsymbolarg(i,argc,argv); + if (*(t_symbol**)p == s_empty) *(t_symbol**)p = 0; + else if (IS_A_FLOAT(argv,i)) { + char str[80]; + sprintf(str, "%d", (int)atom_getintarg(i,argc,argv)); + *(t_symbol**)p = gensym(str); + } + break; + default: post("WARNING: bug using pd_scanargs()"); return 0; /* hmm? */ + } + i++; + } +break2: + return 1; +} + +/* exceptionally we're using pointers for each of the args even though + we are saving. this is so we can copy+paste pd_scanargs lines almost + directly. in the future, this could be merged with pd_scanargs and + made declarative, by storing a list of &(0->blah) relative offsets + into each struct... +*/ +int pd_vsaveargs(t_binbuf *b, char *fmt, va_list val) { + t_atom a; + int i; + for (i=0; ; i++) { + switch (fmt[i]) { + case 0: goto break2; + case ';': continue; /* skip */ + case '?': case 'F': case 'S': break; /* skip */ + case 'd': SETFLOAT(&a,*(va_arg(val,double*))); break; + case 'f': SETFLOAT(&a,*(va_arg(val,float *))); break; + case 'i': SETFLOAT(&a,*(va_arg(val, int *))); break; + case 'b': SETFLOAT(&a,!!*(va_arg(val,int *))); break; + case 'c': /* colour, from IEM format to RGB 8:8:8 format */ + SETFLOAT(&a,convert_color2(*(va_arg(val, int *)))); break; + case 'a': + case 's': { t_symbol *s = *(va_arg(val,t_symbol**)); + SETSYMBOL(&a,s?s:s_empty); } break; + default: post("WARNING: bug using pd_saveargs()"); goto err; /* WHAT? */ + } + binbuf_add(b,1,&a); + } +break2: + binbuf_addv(b, ";"); + return 1; +err: + post("WARNING: pd_saveargs failed; fmt=%s, i=%d",fmt,i); + return 0; +} + +int pd_scanargs(int argc, t_atom *argv, char *fmt, ...) { + int i; + va_list val; + va_start(val,fmt); + i=pd_vscanargs(argc,argv,fmt,val); + va_end(val); + return i; +} + +int pd_saveargs(t_binbuf *b, char *fmt, ...) { + int i; + va_list val; + va_start(val,fmt); + i=pd_vsaveargs(b,fmt,val); + va_end(val); + return i; +} + +int pd_pickle(t_foo *foo, char *fmt, ...) { + va_list val; + va_start(val,fmt); + int r = foo->b ? + pd_vsaveargs(foo->b,fmt,val) : + pd_vscanargs(foo->argc,foo->argv,fmt,val); + va_end(val); + return r; +} + +static int pd_savehead(t_binbuf *b, t_iemgui *x, char *name) { + binbuf_addv(b, "ttiit","#X","obj", (t_int)x->x, (t_int)x->y, name); + return 1; +} + +void pd_upload(t_gobj *self) { + long alive = (long)object_table->get(self) & 1; + if (!alive) { + sys_mgui(self,"delete",""); + pd_free_zombie(self); + return; + } + t_binbuf *b = binbuf_new(); + t_class *c = self->_class; + t_text *x = (t_text *)self; + if (c==canvas_class) { + /* just the "#N canvas" line, not the contents */ + canvas_savecontainerto((t_canvas *)self,b); + canvas_savecoordsto((t_canvas *)self,b); /* this may be too early */ + binbuf_addv(b, "ttii", "#X","restore", (t_int)x->x, (t_int)x->y); + if (x->binbuf) { + //pd_print(x,"pd_upload"); + binbuf_addbinbuf(b, x->binbuf); + } else { + /*bug("binbuf missing at #X restore !!!");*/ + } + binbuf_addv(b, ";"); + } else { /* this was outside of the "else" for a while. why? I don't remember */ + c->savefn(self,b); + } + int n; + char *s; + appendix_save(self,b); + binbuf_gettext(b,&s,&n); + if (s[n-1]=='\n') n--; + if (c->patchable) { + sys_vgui("change x%lx x%lx %d {%.*s} %d %d %d\n",(long)self,(long)self->dix->canvas,self->dix->index,n,s, + obj_ninlets((t_text *)self), obj_noutlets((t_text *)self), x->_class!=dummy_class); + } else { + sys_vgui("change x%lx x%lx %d {%.*s}\n",(long)self,(long)self->dix->canvas,self->dix->index,n,s); + } + binbuf_free(b); + free(s); + if (c==canvas_class) { + t_canvas *can = (t_canvas *)self; + sys_mgui(self,"name=","s",can->name); + sys_mgui(self,"folder=","s",canvas_getenv(can)->dir); + sys_mgui(self,"havewindow=","i",can->havewindow); + } + if (c==gatom_class) { + t_gatom *g = (t_gatom *)x; + if (g->atom.a_type==A_SYMBOL) sys_mgui(g,"set","s",g->atom.a_symbol); + else sys_mgui(g,"set","f",g->atom.a_float); + } + if (object_table->exists(self)) { + object_table->set(self,object_table->get(self)|2); /* has been uploaded */ + } else post("object_table is broken"); +} + +void sys_mgui(void *self_, const char *sel, const char *fmt, ...) { + t_gobj *self = (t_gobj *)self_; + char buf[4096]; + int i=0, n=sizeof(buf); + va_list val; + va_start(val,fmt); + i+=snprintf(buf+i,n-i,"x%lx %s", (long)self, sel); + if (i>=n) goto over; + while (*fmt) { + switch (*fmt) { + case 'f': case 'd': i+=snprintf(buf+i,n-i," %f",va_arg(val,double)); break; + case 'i': i+=snprintf(buf+i,n-i," %d",va_arg(val,int)); break; + case 'p': i+=snprintf(buf+i,n-i," x%lx",(long)va_arg(val,void*)); break; + /* + case 's': i+=snprintf(buf+i,n-i," \"%s\"",va_arg(val,t_symbol *)->name); break; + case 'S': i+=snprintf(buf+i,n-i," \"%s\"",va_arg(val,const char *)); break; + */ + case 's': i+=snprintf(buf+i,n-i," {%s}",va_arg(val,t_symbol *)->name); break; + case 'S': i+=snprintf(buf+i,n-i," {%s}",va_arg(val,const char *)); break; + } + if (i>=n) goto over; + fmt++; + } + va_end(val); + i+=snprintf(buf+i,n-i,"\n"); + if (i>=n) goto over; + sys_gui(buf); + return; +over: + post("sys_mgui: can't send: buffer overflow"); + abort(); +} + +static void iemgui_subclass (t_class *c) { + class_addmethod2(c, iemgui_delta, "delta","*"); + class_addmethod2(c, iemgui_pos, "pos","*"); + class_addmethod2(c, iemgui_color, "color","*"); + class_addmethod2(c, iemgui_send, "send","S"); + class_addmethod2(c, iemgui_receive, "receive","S"); + class_addmethod2(c, iemgui_label, "label","S"); + class_addmethod2(c, iemgui_label_pos, "label_pos","ff"); + class_addmethod2(c, iemgui_label_font, "label_font","*"); +} + +t_symbol *pd_makebindsym(t_pd *x) {return symprintf(".x%lx",(long)x);} + +t_iemgui *iemgui_new(t_class *qlass) { + t_iemgui *x = (t_iemgui *)pd_new(qlass); + x->canvas = canvas_getcurrent(); + x->w = x->h = 15; + x->ldx=0; + x->ldy=-6; + x->isa=0; + x->font_style = 0; + x->fontsize = 8; + x->snd = 0; + x->rcv = 0; + x->lab = s_empty; + x->bcol = 0xffffff; + x->fcol = 0x000000; + x->lcol = 0x000000; + pd_bind(x,pd_makebindsym(x)); + return x; +} + +static void iemgui_constrain(t_iemgui *x) { + SET(fontsize,max(x->fontsize,4)); + SET(h,iemgui_clip_size(x->h)); + SET(w,iemgui_clip_size(x->w)); +} + +void iemgui_init(t_iemgui *x, t_floatarg f) {SET(isa,(x->isa&~1)|!!f);} + +void binbuf_update(t_iemgui *x, t_symbol *qlass, int argc, t_atom *argv) { + t_binbuf *buf = x->binbuf; + if (!buf) return; + binbuf_clear(buf); + t_atom foo; + SETSYMBOL(&foo,qlass); + binbuf_add(buf,1,&foo); + binbuf_add(buf,argc,argv); +} + +static /*bool*/ int iemgui_loadbang (t_iemgui *self) { + return !sys_noloadbang && self->isa&1; +} + +static /*bool*/ int iemgui_forward (t_iemgui *self) { + return !self->snd || !self->rcv || self->snd != self->rcv; +} + +static t_class *bng_class; + +static void bng_check_minmax(t_bng *x) { + if(x->ftbreak > x->fthold) { + int h = x->ftbreak; + SET(ftbreak,x->fthold); + SET(fthold,h); + } + SET(ftbreak,max(x->ftbreak,10)); + SET(fthold ,max(x->fthold, 50)); +} + +static void bng_set(t_bng *x) { + SET(count,x->count+1); + sys_mgui(x,"bang","i",x->count); +} + +static void bng_bout2(t_bng *x) { + outlet_bang(x->outlet); + if(x->snd && x->snd->thing) pd_bang(x->snd->thing); +} + +static void bng_bang(t_bng *x) { + bng_set(x); + outlet_bang(x->outlet); + if(x->snd && x->snd->thing && iemgui_forward(x)) pd_bang(x->snd->thing); +} +static void bng_bang2 (t_bng *x) { {bng_set(x); bng_bout2(x);}} +static void bng_loadbang(t_bng *x) {if(iemgui_loadbang(x)) {bng_set(x); bng_bout2(x);}} + +static void bng_size(t_bng *x, t_symbol *s, int ac, t_atom *av) { + SET(w,iemgui_clip_size((int)atom_getintarg(0, ac, av))); + SET(h,x->w); +} + +static void bng_flashtime(t_bng *x, t_symbol *s, int ac, t_atom *av) { + SET(ftbreak,atom_getintarg(0, ac, av)); + SET(fthold ,atom_getintarg(1, ac, av)); + bng_check_minmax(x); +} + +static int bng_pickle(t_bng *x, t_foo *foo) { + return pd_pickle(foo,"iiiiaaaiiiiccc",&x->w,&x->fthold,&x->ftbreak,&x->isa,&x->snd,&x->rcv,&x->lab, + &x->ldx,&x->ldy,&x->font_style,&x->fontsize,&x->bcol,&x->fcol,&x->lcol); +} + +static void bng_savefn(t_bng *x, t_binbuf *b) { + t_foo foo = {0,0,b}; if (!b) return; + pd_savehead(b,x,"bng"); bng_pickle(x,&foo); +} + +static void bng_reload(t_bng *x, t_symbol *s, int argc, t_atom *argv) { + t_foo foo = { argc, argv, 0 }; + binbuf_update(x,gensym("bng"),argc,argv); + if (!bng_pickle(x,&foo)) return; + SET(h,x->w); + bng_check_minmax(x); + iemgui_constrain(x); + if (x->rcv) pd_bind(x,x->rcv); +} + +static void *bng_new(t_symbol *s, int argc, t_atom *argv) { + t_bng *x = (t_bng *)iemgui_new(bng_class); + SET(ftbreak,250); + SET(fthold,50); + SET(count,0); + bng_check_minmax(x); + outlet_new(x, &s_bang); + if (argc) bng_reload(x,0,argc,argv); + return x; +} + +static void iemgui_free(t_iemgui *x) { + if(x->rcv) pd_unbind(x,x->rcv); +} + +static t_class *toggle_class; + +static void toggle_action(t_toggle *x) { + outlet_float(x->outlet, x->on); + if(x->snd && x->snd->thing) pd_float(x->snd->thing, x->on); +} + +static void toggle_bang(t_toggle *x) {SET(on,x->on?0.0:x->nonzero); toggle_action(x);} +static void toggle_set(t_toggle *x, t_floatarg f) {SET(on,f); if(f) SET(nonzero,f);} +static void toggle_float(t_toggle *x, t_floatarg f) {toggle_set(x,f);if(iemgui_forward(x)) toggle_action(x);} +static void toggle_fout (t_toggle *x, t_floatarg f) {toggle_set(x,f); toggle_action(x);} +static void toggle_loadbang(t_toggle *x) {if(iemgui_loadbang(x)) toggle_fout(x, (float)x->on);} +static void toggle_nonzero(t_toggle *x, t_floatarg f) {if (f) SET(nonzero,f);} + +static void toggle_size(t_toggle *x, t_symbol *s, int ac, t_atom *av) { + SET(w,iemgui_clip_size((int)atom_getintarg(0, ac, av))); + SET(h,x->w); +} + +static int toggle_pickle(t_toggle *x, t_foo *foo) { + return pd_pickle(foo,"iiaaaiiiicccf;f",&x->w,&x->isa,&x->snd,&x->rcv,&x->lab, + &x->ldx,&x->ldy,&x->font_style,&x->fontsize,&x->bcol,&x->fcol,&x->lcol,&x->on,&x->nonzero); +} + +static void toggle_savefn(t_toggle *x, t_binbuf *b) { + t_foo foo = {0,0,b}; if (!b) return; + pd_savehead(b,x,"tgl"); toggle_pickle(x,&foo); +} + +static void toggle_reload(t_toggle *x, t_symbol *s, int argc, t_atom *argv) { + t_foo foo = { argc, argv, 0 }; + binbuf_update(x,gensym("tgl"),argc,argv); + if (!toggle_pickle(x,&foo)) return; + SET(h,x->w); + SET(on,x->isa&1 && x->on ? x->nonzero : 0.0); + SET(nonzero,argc==14 && IS_A_FLOAT(argv,13) ? atom_getfloatarg(13, argc, argv) : 1.0); + if (!x->nonzero) SET(nonzero,1.0); + iemgui_constrain(x); + if (x->rcv) pd_bind(x,x->rcv); +} + +static void *toggle_new(t_symbol *s, int argc, t_atom *argv) { + t_toggle *x = (t_toggle *)iemgui_new(toggle_class); + SET(on,0.0); + SET(nonzero,1.0); + outlet_new(x, &s_float); + if (argc) toggle_reload(x,0,argc,argv); + return x; +} + +static void radio_set(t_radio *x, t_floatarg f) { + int i=(int)f; + int old=x->on_old; + CLAMP(i,0,x->number-1); + if(x->on!=old) SET(on_old,x->on); + SET(on,i); + if(x->on!=old) SET(on_old,old); +} + +static void radio_send2(t_radio *x, float a, float b) { + SETFLOAT(x->at,a); + SETFLOAT(x->at+1,b); + outlet_list(x->outlet, &s_list, 2, x->at); + if(x->snd && x->snd->thing) pd_list(x->snd->thing, &s_list, 2, x->at); +} + +static void radio_send(t_radio *x, float a) { + outlet_float(x->outlet,a); + if(x->snd && x->snd->thing) pd_float(x->snd->thing,a); +} + +static void radio_bang(t_radio *x) { + if (x->oldstyle) { + if(x->change && x->on!=x->on_old) radio_send2(x,x->on_old,0.0); + SET(on_old,x->on); + radio_send2(x,x->on,1.0); + } else { + radio_send(x,x->on); + } +} + +static void radio_fout2(t_radio *x, t_floatarg f, int forwardonly) { + int i=(int)f; + CLAMP(i,0,x->number-1); + if (x->oldstyle) { + /* compatibility with earlier "hdial" behavior */ + if(x->change && i!=x->on_old && (!forwardonly || iemgui_forward(x))) radio_send2(x,x->on_old,0.0); + SET(on_old,x->on); + SET(on,i); + SET(on_old,x->on); + radio_send2(x,x->on,1.0); + if (!forwardonly || iemgui_forward(x)) radio_send2(x,x->on,1.0); + } else { + SET(on,i); + if (!forwardonly || iemgui_forward(x)) radio_send(x,x->on); + SET(on_old,x->on); + } +} + +static void radio_fout (t_radio *x, t_floatarg f) {radio_fout2(x,f,0);} +static void radio_float(t_radio *x, t_floatarg f) {radio_fout2(x,f,1);} +static void radio_loadbang(t_radio *x) {if(iemgui_loadbang(x)) radio_bang(x);} +static void radio_orient(t_radio *x,t_floatarg v) {SET(orient,!!v); +post("v=%f, !!v=%d, orient=%d",v,!!v,x->orient);} + +static void radio_number(t_radio *x, t_floatarg num) { + int n=(int)num; + CLAMP(n,1,128); + if (n != x->number) { + SET(number,n); + CLAMP(x->on,0,x->number-1); gobj_changed(x,"on"); + SET(on_old,x->on); + } +} + +static void radio_size(t_radio *x, t_float size) { + SET(w,iemgui_clip_size((int)size)); + SET(h,x->w); +} + +static void radio_double_change(t_radio *x) {SET(change,1);} +static void radio_single_change(t_radio *x) {SET(change,0);} + +static int radio_pickle(t_radio *x, t_foo *foo) { + return pd_pickle(foo, "ibiiaaaiiiiccci",&x->w,&x->change,&x->isa,&x->number,&x->snd,&x->rcv,&x->lab, + &x->ldx,&x->ldy,&x->font_style,&x->fontsize,&x->bcol,&x->fcol,&x->lcol,&x->on); +} + +static t_symbol *radio_flavor(t_radio *x) { + return x->orient?x->oldstyle?sym_vdl:sym_vradio:x->oldstyle?sym_hdl:sym_hradio; +} + +static void radio_savefn(t_radio *x, t_binbuf *b) { + t_foo foo = {0,0,b}; if (!b) return; + pd_savehead(b,x,radio_flavor(x)->name); + radio_pickle(x,&foo); +} + +static void radio_reload(t_radio *x, t_symbol *s, int argc, t_atom *argv) { + t_foo foo = { argc, argv, 0 }; + binbuf_update(x,radio_flavor(x),argc,argv); + if (!radio_pickle(x,&foo)) return; + iemgui_constrain(x); + if (x->rcv) pd_bind(x,x->rcv); + gobj_changed(x,0); +} + +static void *radio_new(t_symbol *s, int argc, t_atom *argv) { + t_radio *x = (t_radio *)iemgui_new(radio_class); + SET(on_old,0); + SET(on,0); + SET(number,8); + SET(change,1); + if (s==sym_hdl) {SET(orient,0); SET(oldstyle,1);} else + if (s==sym_vdl) {SET(orient,1); SET(oldstyle,1);} else + if (s==sym_hradio) {SET(orient,0); SET(oldstyle,0);} else + if (s==sym_vradio) {SET(orient,1); SET(oldstyle,0);} + SET(on,x->isa&1 ? x->on : 0); + SET(on_old,x->on); + outlet_new(x, &s_list); + if (argc) radio_reload(x,0,argc,argv); + return x; +} + +#define IEM_SL_DEFAULTSIZE 128 +#define IEM_SL_MINSIZE 2 + +static void slider_check_width(t_slider *x, int w) { + double l = (double)(x->orient ? x->h : x->w)-1; + int m = (int)(l*100); + if(w < IEM_SL_MINSIZE) w = IEM_SL_MINSIZE; + if (x->orient) SET(h,w); else SET(w,w); + if(x->val > m) SET(val,m); +} + +static void slider_check_minmax(t_slider *x) { + double min=x->min, max=x->max; + if(x->is_log) { + 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; } + } + SET(min,min); + SET(max,max); +} + +// the value/centipixel ratio +static double slider_ratio (t_slider *x) { + double diff = x->is_log ? log(x->max/x->min) : (x->max-x->min); + return diff / (double)(x->orient ? (x->h-1) : (x->w-1)); +} + +static void slider_set(t_slider *x, t_floatarg f) { + if(x->min > x->max) CLAMP(f,x->max,x->min); + else CLAMP(f,x->min,x->max); + SET(val,floor(100.0 * (x->is_log ? log(f/x->min) : (f-x->min)) / slider_ratio(x) + 0.5)); +} + +static void slider_bang(t_slider *x) { + double t = (double)x->val * slider_ratio(x) * 0.01; + double out = x->is_log ? x->min*exp(t) : x->min+t; + if (fabs(out) < 1.0e-10) out = 0.0; + outlet_float(x->outlet, out); + if(x->snd && x->snd->thing) pd_float(x->snd->thing, out); +} + +static void slider_size(t_slider *x, t_symbol *s, int ac, t_atom *av) { + int a = atom_getintarg(0,ac,av); + int b = ac>1 ? atom_getintarg(1,ac,av) : 0; + if (x->orient) { + SET(w,iemgui_clip_size(a)); + if(ac>1) slider_check_width(x,b); + } else { + slider_check_width(x,a); + if(ac>1) SET(h,iemgui_clip_size(b)); + } +} + +static void slider_range(t_slider *x, t_float min, t_float max) +{SET(min,min); SET(max,max); slider_check_minmax(x);} +static void slider_lin(t_slider *x) {SET(is_log,0); slider_check_minmax(x);} +static void slider_log(t_slider *x) {SET(is_log,1); slider_check_minmax(x);} +static void slider_steady(t_slider *x, t_floatarg f) {SET(steady,!!f);} +static void slider_float(t_slider *x, t_floatarg f) {slider_set(x,f);if(iemgui_forward(x))slider_bang(x);} +static void slider_loadbang(t_slider *x) {if(iemgui_loadbang(x)) slider_bang(x);} +static void slider_orient(t_slider *x,t_floatarg v) {SET(orient,!!v);} + +static int slider_pickle(t_slider *x, t_foo *foo) { + return pd_pickle(foo, "iiffbiaaaiiiicccf;b", + &x->w,&x->h,&x->min,&x->max,&x->is_log,&x->isa,&x->snd,&x->rcv,&x->lab, + &x->ldx,&x->ldy,&x->font_style,&x->fontsize,&x->bcol,&x->fcol,&x->lcol,&x->val,&x->steady); +} + +static void slider_savefn(t_slider *x, t_binbuf *b) { + t_foo foo = {0,0,b}; if (!b) return; + pd_savehead(b,x,(char *)(x->orient?"vsl":"hsl")); slider_pickle(x,&foo); +} + +static void slider_reload(t_slider *x, t_symbol *s, int argc, t_atom *argv) { + t_foo foo = { argc, argv, 0 }; + binbuf_update(x,gensym((char *)(x->orient?"vsl":"hsl")),argc,argv); + if (!slider_pickle(x,&foo)) return; +//this is wrong because it should happen when loading a file but not when loading from properties: + SET(val,x->isa&1 ? x->val : 0); +//end wrong. + iemgui_constrain(x); + slider_check_minmax(x); + slider_check_width(x, x->orient ? x->h : x->w); + if(x->rcv) pd_bind(x,x->rcv); + gobj_changed(x,0); +} + +static void *slider_new(t_symbol *s, int argc, t_atom *argv) { + t_slider *x = (t_slider *)iemgui_new(slider_class); + SET(orient,s==sym_vslider||s==sym_vsl); + SET(is_log,0); + SET(min,0.0); + SET(steady,1); + SET(max,(double)(IEM_SL_DEFAULTSIZE-1)); + if (x->orient) SET(h,IEM_SL_DEFAULTSIZE); else SET(w,IEM_SL_DEFAULTSIZE); + outlet_new(x, &s_float); + if (argc) slider_reload(x,0,argc,argv); + return x; +} + +static t_class *nbx_class; + +static void nbx_clip(t_nbx *x) {CLAMP(x->val,x->min,x->max);} + +static int nbx_check_minmax(t_nbx *x) { + double min=x->min, max=x->max; + int val=(int)x->val; + if(x->is_log) { + if(min==0.0 && max==0.0) max = 1.0; + if(max>0.0 && min<=0.0) min = 0.01*max; + if(max<=0.0 && min>0.0) max = 0.01*min; + } else { + if(min>max) swap(min,max); + } + SET(min,min); + SET(max,max); + CLAMP(x->val,x->min,x->max); + SET(k,x->is_log ? exp(log(x->max/x->min)/(double)(x->log_height)) : 1.0); + return x->val!=val; +} + +static void nbx_bang(t_nbx *x) { + outlet_float(x->outlet, x->val); + if(x->snd && x->snd->thing) pd_float(x->snd->thing, x->val); +} + +static void nbx_set(t_nbx *x, t_floatarg f) {SET(val,f); nbx_clip(x);} +static void nbx_float(t_nbx *x, t_floatarg f) {nbx_set(x, f); if(iemgui_forward(x)) nbx_bang(x);} + +static void nbx_log_height(t_nbx *x, t_floatarg lh) { + SET(log_height,max(10,(int)lh)); + SET(k,x->is_log ? exp(log(x->max/x->min)/(double)(x->log_height)) : 1.0); +} + +static void nbx_size(t_nbx *x, t_symbol *s, int ac, t_atom *av) { + SET(w,max(1,(int)atom_getintarg(0, ac, av))); + if(ac > 1) SET(h,max(8,(int)atom_getintarg(1, ac, av))); +} + +static void nbx_range(t_nbx *x, t_float min, t_float max) +{SET(min,min); SET(max,max); nbx_check_minmax(x);} + +static void nbx_lin(t_nbx *x) {SET(is_log,0); } +static void nbx_log(t_nbx *x) {SET(is_log,1); nbx_check_minmax(x);} +static void nbx_loadbang(t_nbx *x) {if(iemgui_loadbang(x)) nbx_bang(x);} + +static void nbx_list(t_nbx *x, t_symbol *s, int ac, t_atom *av) { + if (!IS_A_FLOAT(av,0)) return; + nbx_set(x, atom_getfloatarg(0, ac, av)); + nbx_bang(x); +} + +static int nbx_pickle(t_nbx *x, t_foo *foo) { + return pd_pickle(foo,"iiddbiaaaiiiicccd;i", + &x->w,&x->h,&x->min,&x->max,&x->is_log,&x->isa,&x->snd,&x->rcv,&x->lab, + &x->ldx,&x->ldy,&x->font_style,&x->fontsize,&x->bcol,&x->fcol,&x->lcol,&x->val,&x->log_height); +} + +static void nbx_savefn(t_nbx *x, t_binbuf *b) { + t_foo foo = {0,0,b}; + if (!b) return; + pd_savehead(b,x,"nbx"); + nbx_pickle(x,&foo); +} + +static void nbx_reload(t_nbx *x, t_symbol *s, int argc, t_atom *argv) { + t_foo foo = { argc, argv, 0 }; + binbuf_update(x,gensym("nbx"),argc,argv); + if (!nbx_pickle(x,&foo)) return; + if (!x->isa&1) SET(val,0.0); + iemgui_constrain(x); + SET(w,max(x->w,1)); + nbx_check_minmax(x); + SET(w,max(x->w,1)); + if (x->rcv) pd_bind(x,x->rcv); + gobj_changed(x,0); +} + +static void *nbx_new(t_symbol *s, int argc, t_atom *argv) { + t_nbx *x = (t_nbx *)iemgui_new(nbx_class); + SET(log_height,256); + SET(is_log,0); + SET(w,5); + SET(h,14); + SET(min,-1.0e+37); + SET(max,1.0e+37); + x->buf[0]=0; + SET(change,0); + outlet_new(x, &s_float); + if (argc) nbx_reload(x,0,argc,argv); + return x; +} + +#define IEM_VU_STEPS 40 + +static char 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 +}; + +static void vu_check_height(t_vu *x, int h) { + int n=max(h/IEM_VU_STEPS,2); + SET(led_size,n-1); + SET(h,IEM_VU_STEPS * n); +} + +static void vu_scale(t_vu *x, t_floatarg fscale) {SET(scale,!!fscale);} + +static void vu_size(t_vu *x, t_symbol *s, int ac, t_atom *av) { + SET(w, iemgui_clip_size((int)atom_getintarg(0, ac, av))); + if(ac>1) vu_check_height(x, (int)atom_getintarg(1, ac, av)); +} + +static int vuify(t_vu *x, float v) { + return v<=-99.9 ? 0 : + v>=12.0 ? IEM_VU_STEPS : + vu_db2i[(int)(2.0*(v+100.0))]; +} + +static float vu_round(float v) {return 0.01*(int)(100.0*v+0.5);} + +static void vu_float0(t_vu *x, t_floatarg v) { + SET(rms, vuify(x,v)); SET(fr,vu_round(v)); outlet_float(x->out(0), x->fr); + sys_mgui(x,"rms=","i",x->rms);} +static void vu_float1(t_vu *x, t_floatarg v) { + SET(peak,vuify(x,v)); SET(fp,vu_round(v)); outlet_float(x->out(1),x->fp); + sys_mgui(x,"peak=","i",x->peak);} + +static void vu_bang(t_vu *x) { + outlet_float(x->out(1), x->fp); + outlet_float(x->out(0), x->fr); +} + +static int vu_pickle(t_vu *x, t_foo *foo) { + return pd_pickle(foo,"iiaaiiiiccb;i",&x->w,&x->h,&x->rcv,&x->lab,&x->ldx,&x->ldy,&x->font_style, + &x->fontsize,&x->bcol,&x->lcol,&x->scale,&x->isa); +} + +static void vu_savefn(t_vu *x, t_binbuf *b) { + t_foo foo = {0,0,b}; if (!b) return; + pd_savehead(b,x,"vu"); vu_pickle(x,&foo); +} + +static void vu_reload(t_vu *x, t_symbol *s, int argc, t_atom *argv) { + t_foo foo = { argc, argv, 0 }; + binbuf_update(x,gensym("vu"),argc,argv); + if (!vu_pickle(x,&foo)) return; + iemgui_constrain(x); + if(x->rcv) pd_bind(x,x->rcv); + gobj_changed(x,0); +} + +static t_class *vu_class; + +static void *vu_new(t_symbol *s, int argc, t_atom *argv) { + t_vu *x = (t_vu *)iemgui_new(vu_class); + outlet_new(x, &s_float); + outlet_new(x, &s_float); + SET(bcol,0x000000); + SET(h,IEM_VU_STEPS*3); + SET(scale,1); + SET(rms,0); /* ??? */ + SET(peak,0); + SET(fp,-101.0); + SET(fr,-101.0); + vu_check_height(x,x->h); + inlet_new(x,x,&s_float,gensym("ft1")); + if (argc) vu_reload(x,0,argc,argv); + return x; +} + +static t_class *cnv_class; + +static void cnv_get_pos(t_cnv *x) { + error("unimplemented (TODO)"); +// if(x->snd && x->snd->thing) {x->at[0].a_float = x; x->at[1].a_float = y; pd_list(x->snd->thing, &s_list, 2, x->at);} +} + +static void cnv_size(t_cnv *x, t_symbol *s, int ac, t_atom *av) { + SET(h,max(1,(int)atom_getintarg(0, ac, av))); + SET(w,x->h); +} + +static void cnv_vis_size(t_cnv *x, t_symbol *s, int ac, t_atom *av) { + SET(vis_w,max(1,(int)atom_getintarg(0, ac, av))); + SET(vis_h,x->w); + if(ac > 1) SET(vis_h,max(1,(int)atom_getintarg(1, ac, av))); + gobj_changed(x,0); +} + +static int cnv_pickle(t_cnv *x, t_foo *foo) { + return pd_pickle(foo,"iiiaaaiiiicc;i",&x->w,&x->vis_w,&x->vis_h, + &x->snd,&x->rcv,&x->lab,&x->ldx,&x->ldy,&x->font_style,&x->fontsize,&x->bcol,&x->lcol,&x->isa); +} + +static void cnv_savefn(t_cnv *x, t_binbuf *b) { + t_foo foo = {0,0,b}; if (!b) return; + pd_savehead(b,x,"cnv"); cnv_pickle(x,&foo); +} + +static void cnv_reload(t_cnv *x, t_symbol *s, int argc, t_atom *argv) { + t_foo foo = { argc, argv, 0 }; + binbuf_update(x,gensym("cnv"),argc,argv); + if (!cnv_pickle(x,&foo)) return; + SET(w,max(x->w,1)); + SET(h,x->w); + SET(vis_w,max(x->vis_w,1)); + SET(vis_h,max(x->vis_h,1)); + x->at[0].a_type = x->at[1].a_type = A_FLOAT; //??? + iemgui_constrain(x); + if (x->rcv) pd_bind(x,x->rcv); + gobj_changed(x,0); +} +#undef FOO + +static void *cnv_new(t_symbol *s, int argc, t_atom *argv) { + t_cnv *x = (t_cnv *) iemgui_new(cnv_class); + SET(bcol,0xe0e0e0); + SET(fcol,0x000000); + SET(lcol,0x404040); + SET(w,15); + SET(vis_w,100); + SET(vis_h,60); + if (argc) cnv_reload(x,0,argc,argv); + return x; +} + +void canvas_notice(t_gobj *x, t_gobj *origin, int argc, t_atom *argv) { + t_canvas *self = (t_canvas *)x; + gobj_changed3(self,origin,argc,argv); +} + +void gobj_onsubscribe(t_gobj *x, t_gobj *observer) {gobj_changed(x,0);} + +void canvas_onsubscribe(t_gobj *x, t_gobj *observer) { + t_canvas *self = (t_canvas *)x; + gobj_onsubscribe(x,observer); + canvas_each( y,self) y->_class->onsubscribe( y,observer); + canvas_wires_each(oc,t,self) oc->_class->onsubscribe(oc,observer); +} + +/* [declare] and canvas_open come from 0.40 */ +/* ------------------------------- declare ------------------------ */ + +/* put "declare" objects in a patch to tell it about the environment in +which objects should be created in this canvas. This includes directories to +search ("-path", "-stdpath") and object libraries to load +("-lib" and "-stdlib"). These must be set before the patch containing +the "declare" object is filled in with its contents; so when the patch is +saved, we throw early messages to the canvas to set the environment +before any objects are created in it. */ + +struct t_declare : t_object { + int useme; +}; + +static void *declare_new(t_symbol *s, int argc, t_atom *argv) { + t_declare *x = (t_declare *)pd_new(declare_class); + x->useme = 1; + /* LATER update environment and/or load libraries */ + return x; +} + +static void declare_free(t_declare *x) { + x->useme = 0; + /* LATER update environment */ +} + +void canvas_savedeclarationsto(t_canvas *x, t_binbuf *b) { + canvas_each(y,x) { + if (pd_class(y) == declare_class) { + binbuf_addv(b,"t","#X"); + binbuf_addbinbuf(b, ((t_declare *)y)->binbuf); + binbuf_addv(b, ";"); + } else if (pd_class(y) == canvas_class) canvas_savedeclarationsto((t_canvas *)y, b); + } +} + +static void canvas_declare(t_canvas *x, t_symbol *s, int argc, t_atom *argv) { + t_canvasenvironment *e = canvas_getenv(x); +#if 0 + startpost("declare:: %s", s->name); + postatom(argc, argv); + endpost(); +#endif + for (int i=0; i<argc; i++) { + char *buf; + char *flag = atom_getsymbolarg(i, argc, argv)->name; + if ((argc > i+1) && !strcmp(flag, "-path")) { + e->path = namelist_append(e->path, atom_getsymbolarg(i+1, argc, argv)->name, 0); + i++; + } else if (argc>i+1 && !strcmp(flag, "-stdpath")) { + asprintf(&buf, "%s/%s", sys_libdir->name, atom_getsymbolarg(i+1, argc, argv)->name); + e->path = namelist_append(e->path,buf,0); + i++; + } else if (argc>i+1 && !strcmp(flag, "-lib")) { + sys_load_lib(x, atom_getsymbolarg(i+1, argc, argv)->name); + i++; + } else if (argc>i+1 && !strcmp(flag, "-stdlib")) { + asprintf(&buf, "%s/%s", sys_libdir->name, atom_getsymbolarg(i+1, argc, argv)->name); + sys_load_lib(0,buf); + i++; + } else post("declare: %s: unknown declaration", flag); + } +} + +/* utility function to read a file, looking first down the canvas's search path (set with "declare" + objects in the patch and recursively in calling patches), then down the system one. The filename + is the concatenation of "name" and "ext". "Name" may be absolute, or may be relative with slashes. + If anything can be opened, the true directory is put in the buffer dirresult (provided by caller), + which should be "size" bytes. The "nameresult" pointer will be set somewhere in the interior of + "dirresult" and will give the file basename (with slashes trimmed). If "bin" is set a 'binary' + open is attempted, otherwise ASCII (this only matters on Microsoft.) If "x" is zero, the file is + sought in the directory "." or in the global path.*/ +int canvas_open2(t_canvas *x, const char *name, const char *ext, char **dirresult, char **nameresult, int bin) { + int fd = -1; + /* first check if "name" is absolute (and if so, try to open) */ + if (sys_open_absolute(name, ext, dirresult, nameresult, bin, &fd)) return fd; + /* otherwise "name" is relative; start trying in directories named in this and parent environments */ + for (t_canvas *y=x; y; y = y->dix->canvas) if (y->env) { + t_canvas *x2 = x; + while (x2 && x2->dix->canvas) x2 = x2->dix->canvas; + const char *dir = x2 ? canvas_getdir(x2)->name : "."; + for (t_namelist *nl = y->env->path; nl; nl = nl->nl_next) { + char *realname; + asprintf(&realname, "%s/%s", dir, nl->nl_string); + if ((fd = sys_trytoopenone(realname, name, ext, dirresult, nameresult, bin)) >= 0) return fd; + } + } + return open_via_path2((x ? canvas_getdir(x)->name : "."), name, ext, dirresult, nameresult, bin); +} +/* end miller 0.40 */ + +int canvas_open(t_canvas *x, const char *name, const char *ext, char *dirresult, char **nameresult, unsigned int size, int bin) { + char *dirr; + int r = canvas_open2(x,name,ext,&dirr,nameresult,bin); + if (dirr) {strncpy(dirresult,dirr,size); dirresult[size-1]=0; free(dirr);} + return r; +} + +static void canvas_with_reply (t_pd *x, t_symbol *s, int argc, t_atom *argv) { + if (!( argc>=2 && IS_A_FLOAT(argv,0) && IS_A_SYMBOL(argv,1) )) return; + pd_typedmess(x,atom_getsymbol(&argv[1]),argc-2,argv+2); + queue_put(manager->q,reply_new((short)atom_getfloat(&argv[0]),newest)); +} + +static void canvas_get_elapsed (t_canvas *x) { + canvas_each(y,x) { + sys_mgui(y,"elapsed","f",y->dix->elapsed / 800000000.0); + } +} + +static void g_canvas_setup() { + reply_class = class_new2("reply",0,reply_free,sizeof(t_reply),CLASS_GOBJ,"!"); +// class_setsavefn(reply_class, (t_savefn)reply_savefn); + declare_class = class_new2("declare",declare_new,declare_free,sizeof(t_declare),CLASS_NOINLET,"*"); + t_class *c = canvas_class = class_new2("canvas",0,canvas_free,sizeof(t_canvas),CLASS_NOINLET,""); + /* here is the real creator function, invoked in patch files + by sending the "canvas" message to #N, which is bound to pd_canvasmaker. */ + class_addmethod2(pd_canvasmaker._class,canvas_new,"canvas","*"); + class_addmethod2(c,canvas_restore,"restore","*"); + class_addmethod2(c,canvas_coords,"coords","*"); + class_addmethod2(c,canvas_setbounds,"bounds","ffff"); + class_addmethod2(c,canvas_obj,"obj","*"); + class_addmethod2(c,canvas_msg,"msg","*"); + class_addmethod2(c,canvas_floatatom,"floatatom","*"); + class_addmethod2(c,canvas_symbolatom,"symbolatom","*"); + class_addmethod2(c,canvas_text,"text","*"); + class_addmethod2(c,canvas_canvas,"graph","*"); + class_addmethod2(c,canvas_scalar,"scalar","*"); + class_addmethod2(c,canvas_declare,"declare","*"); + class_addmethod2(c,canvas_push,"push",""); + class_addmethod2(c,canvas_pop,"pop","F"); + class_addmethod2(c,canvas_loadbang,"loadbang",""); + class_addmethod2(c,canvas_relocate,"relocate","ss"); + class_addmethod2(c,canvas_vis,"vis","f"); + class_addmethod2(c,canvas_menu_open,"menu-open",""); + class_addmethod2(c,canvas_clear,"clear",""); + class_addcreator2("pd",subcanvas_new,"S"); + class_addcreator2("page",subcanvas_new,"S"); + class_addmethod2(c,canvas_dsp,"dsp",""); + class_addmethod2(c,canvas_rename_method,"rename","*"); + class_addcreator2("table",table_new,"SF"); + class_addmethod2(c,canvas_close,"close","F"); + class_addmethod2(c,canvas_redraw,"redraw",""); + class_addmethod2(c,canvas_find_parent,"findparent",""); + class_addmethod2(c,canvas_arraydialog,"arraydialog","sfff"); + class_addmethod2(c,canvas_connect,"connect","ffff"); + class_addmethod2(c,canvas_disconnect,"disconnect","ffff"); + class_addmethod2(c,canvas_write,"write","sS"); + class_addmethod2(c,canvas_read, "read","sS"); + class_addmethod2(c,canvas_mergefile, "mergefile","sS"); + class_addmethod2(c,canvas_savetofile,"savetofile","ss"); + class_addmethod2(c,canvas_saveto, "saveto","!"); + class_addmethod2(c,graph_bounds,"bounds","ffff"); + class_addmethod2(c,graph_xticks,"xticks","fff"); + class_addmethod2(c,graph_xlabel,"xlabel","*"); + class_addmethod2(c,graph_yticks,"yticks","fff"); + class_addmethod2(c,graph_ylabel,"ylabel","*"); + class_addmethod2(c,graph_array,"array","sfsF"); + //class_addmethod2(c,canvas_sort,"sort",""); +// dd-specific + class_addmethod2(c,canvas_object_moveto,"object_moveto","sff"); + class_addmethod2(c,canvas_object_delete,"object_delete","s"); + //class_addmethod2(c,canvas_object_insert,"object_insert","*"); + class_addmethod2(c,canvas_object_get_tips,"object_get_tips","s"); + class_addmethod2(c,canvas_object_help,"object_help","s"); + class_addmethod2(c,canvas_text_setto,"text_setto","*"); + class_addmethod2(c,canvas_with_reply,"with_reply","*"); + class_addmethod2(pd_canvasmaker._class,canvas_with_reply,"with_reply","*"); + class_addmethod2(c,canvas_get_elapsed,"get_elapsed",""); + class_setnotice(c, canvas_notice); + class_setonsubscribe(c, canvas_onsubscribe); +} + +t_class *visualloader_class; + +static t_pd *visualloader_new(t_symbol *s, int argc, t_atom *argv) {return pd_new(visualloader_class);} +static void visualloader_free(t_pd *self) {free(self);} +static void copy_atoms(int argc, t_atom *argvdest, t_atom *argvsrc) {memcpy(argvdest,argvsrc,argc*sizeof(t_atom));} +static void visualloader_anything(t_gobj *self, t_symbol *s, int argc, t_atom *argv) { + int i=0,j=0; + //printf("visualloader_anything start newest=%p\n",newest); + while (j<argc) { + i=j; + while (j<argc && atom_getsymbolarg(j,argc,argv)!=gensym(",")) j++; + if (i==j) {j++; continue;} + t_arglist *al = (t_arglist *) malloc(sizeof(t_arglist) + (j-i)*sizeof(t_atom)); + al->c=j-i; + copy_atoms(al->c,al->v,&argv[i]); + //printf("#V reading '%s':\n",s->name); + if (!newest) {error("#V: there is no newest object\n"); return;} + t_visual *h = ((t_gobj *)newest)->dix->visual; + if (h->exists(s)) { + //printf("'%s' exists, deleting\n",s->name); + free(h->get(s)); + } + h->set(s,al); + //fprintf(stderr,"visualloader... %p %d\n",newest,hash_size(h)); + j++; + if (j<argc) {s=atom_getsymbolarg(j,argc,argv);j++;} + } + //printf("visualloader_anything end\n"); + gobj_changed(self,0); +} + +extern "C" void glob_update_path (); + +void glob_help(t_pd *bogus, t_symbol *s) { + t_class *c = class_find(s); + if (!c) { + //post("help: no such class '%s'",s->name); return; + t_binbuf *b = binbuf_new(); + binbuf_addv(b,"s",s); + newest = 0; + binbuf_eval(b,&pd_objectmaker,0,0); + if (!newest) {post("help: no such class '%s'",s->name); return;} + c = newest->_class; + pd_free(newest); + } + const char *hn = class_gethelpname(c); + char *buf; + bool suffixed = strcmp(hn+strlen(hn)-3, ".pd")==0; + asprintf(&buf,"%s%s",hn,suffixed?"":".pd"); + open_via_helppath(buf, c->externdir->name); + free(buf); +} + +extern "C" void glob_update_class_list (t_pd *self, t_symbol *cb_recv, t_symbol *cb_sel) { + t_symbol *k; t_class *v; + sys_gui("global class_list; set class_list {"); + hash_foreach(k,v,class_table) sys_vgui("%s ", ((t_symbol *)k)->name); + sys_gui("}\n"); + sys_vgui("%s %s\n",cb_recv->name, cb_sel->name); +} + +EXTERN t_class *glob_pdobject; + +t_pd *pd_new2(int argc, t_atom *argv) { + if (argv[0].a_type != A_SYMBOL) {error("pd_new2: start with symbol please"); return 0;} + pd_typedmess(&pd_objectmaker,argv[0].a_symbol,argc-1,argv+1); + return newest; +} +t_pd *pd_new3(const char *s) { + t_binbuf *b = binbuf_new(); + binbuf_text(b,(char *)s,strlen(s)); + t_pd *self = pd_new2(binbuf_getnatom(b),binbuf_getvec(b)); + binbuf_free(b); + return self; +} + +extern "C" void boxes_init() { + t_class *c; + c = boxes_class = class_new2("__boxes" ,0/*boxes_new*/ , boxes_free,sizeof(t_boxes),CLASS_GOBJ,""); + class_setnotice(c,t_notice(boxes_notice)); + c = gop_filtre_class = class_new2("__gop_filtre",0/*gop_filtre_new*/,gop_filtre_free,sizeof(t_boxes),CLASS_GOBJ,""); + class_setnotice(c,t_notice(gop_filtre_notice)); +} + +static void desire_setup() { + t_class *c; + s_empty = gensym("empty"); + s_Pd = gensym("Pd"); + s_pd = gensym("pd"); + manager_class = class_new2("__manager",manager_new,manager_free,sizeof(t_manager),0,"*"); + class_addanything(manager_class,manager_anything); + class_setnotice(manager_class,manager_notice); + manager = manager_new(0,0,0); +#define S(x) x##_setup(); + S(vinlet) S(voutlet) S(g_array) S(g_canvas) S(g_scalar) S(g_template) S(g_traversal) S(g_text) +#undef S + + c = bng_class = class_new2("bng",bng_new,iemgui_free,sizeof(t_bng),0,"*"); + iemgui_subclass(c); + class_addbang (c, bng_bang); + class_addfloat (c, bng_bang2); + class_addsymbol (c, bng_bang2); + class_addpointer (c, bng_bang2); + class_addlist (c, bng_bang2); + class_addanything(c, bng_bang2); + class_addmethod2(c,bng_reload,"reload","*"); + class_addmethod2(c,bng_loadbang,"loadbang",""); + class_addmethod2(c,bng_size,"size","*"); + class_addmethod2(c,bng_flashtime,"flashtime","*"); + class_addmethod2(c,iemgui_init,"init","f"); + class_setsavefn(c, (t_savefn)bng_savefn); + class_sethelpsymbol(c, gensym("bng")); + class_setfieldnames(c, "foo bar x1 y1 class w hold break isa snd rcv lab ldx ldy fstyle fs bcol fcol lcol"); + + c = toggle_class = class_new2("tgl",toggle_new,iemgui_free,sizeof(t_toggle),0,"*"); + class_addcreator2("toggle",toggle_new,"*"); + iemgui_subclass(c); + class_addbang(c, toggle_bang); + class_addfloat(c, toggle_float); + class_addmethod2(c,toggle_reload,"reload","*"); + class_addmethod2(c,toggle_loadbang,"loadbang",""); + class_addmethod2(c,toggle_set,"set","f"); + class_addmethod2(c,toggle_size,"size","*"); + class_addmethod2(c,iemgui_init,"init","f"); + class_addmethod2(c,toggle_nonzero,"nonzero","f"); + class_setsavefn(c, (t_savefn)toggle_savefn); + class_sethelpsymbol(c, gensym("toggle")); + + c = radio_class = class_new2("radio",radio_new,iemgui_free,sizeof(t_radio),0,"*"); + iemgui_subclass(c); + class_addbang(c, radio_bang); + class_addfloat(c, radio_float); + class_addmethod2(c,radio_reload, "reload","*"); + class_addmethod2(c,radio_loadbang, "loadbang",""); + class_addmethod2(c,radio_set, "set","f"); + class_addmethod2(c,radio_size, "size","f"); + class_addmethod2(c,iemgui_init, "init","f"); + class_addmethod2(c,radio_fout, "fout","f"); + class_addmethod2(c,radio_number, "number","f"); + class_addmethod2(c,radio_orient,"orient","f"); + class_addmethod2(c,radio_single_change, "single_change",""); + class_addmethod2(c,radio_double_change, "double_change",""); + sym_hdl = gensym("hdl"); sym_hradio = gensym("hradio"); + sym_vdl = gensym("vdl"); sym_vradio = gensym("vradio"); + class_setsavefn(c,(t_savefn)radio_savefn); + class_sethelpsymbol(c, gensym("hradio")); + class_addcreator2("hradio",radio_new,"*"); + class_addcreator2("vradio",radio_new,"*"); + class_addcreator2("hdl",radio_new,"*"); + class_addcreator2("vdl",radio_new,"*"); + class_addcreator2("rdb",radio_new,"*"); + class_addcreator2("radiobut",radio_new,"*"); + class_addcreator2("radiobutton",radio_new,"*"); + + c = slider_class = class_new2("slider",slider_new,iemgui_free,sizeof(t_slider),0,"*"); + class_addcreator2("hslider",slider_new,"*"); + class_addcreator2("vslider",slider_new,"*"); + class_addcreator2("hsl" ,slider_new,"*"); + class_addcreator2("vsl" ,slider_new,"*"); + + iemgui_subclass(c); + class_addbang(c,slider_bang); + class_addfloat(c,slider_float); + class_addmethod2(c,slider_reload,"reload","*"); + class_addmethod2(c,slider_loadbang,"loadbang",""); + class_addmethod2(c,slider_set,"set","f"); + class_addmethod2(c,slider_size,"size","*"); + class_addmethod2(c,slider_range,"range","ff"); + class_addmethod2(c,slider_log,"log",""); + class_addmethod2(c,slider_lin,"lin",""); + class_addmethod2(c,iemgui_init,"init","f"); + class_addmethod2(c,slider_steady,"steady","f"); + class_addmethod2(c,slider_orient,"orient","f"); + sym_vsl = gensym("vsl"); + sym_vslider = gensym("vslider"); + class_setsavefn(c,(t_savefn)slider_savefn); + class_sethelpsymbol(c, gensym("hslider")); + + c = nbx_class = class_new2("nbx",nbx_new,iemgui_free,sizeof(t_nbx),0,"*"); + iemgui_subclass(c); + class_addbang(c,nbx_bang); + class_addfloat(c,nbx_float); + class_addlist(c, nbx_list); + class_addmethod2(c,nbx_reload,"reload","*"); + class_addmethod2(c,nbx_loadbang,"loadbang",""); + class_addmethod2(c,nbx_set,"set","f"); + class_addmethod2(c,nbx_size,"size","*"); + class_addmethod2(c,nbx_range,"range","*"); + class_addmethod2(c,nbx_log,"log",""); + class_addmethod2(c,nbx_lin,"lin",""); + class_addmethod2(c,iemgui_init,"init","f"); + class_addmethod2(c,nbx_log_height,"log_height","f"); + class_setsavefn(c,(t_savefn)nbx_savefn); + class_sethelpsymbol(c, gensym("numbox2")); + + c = cnv_class = class_new2("cnv",cnv_new,iemgui_free,sizeof(t_cnv),CLASS_NOINLET,"*"); + class_addmethod2(c,cnv_reload,"reload","*"); + class_addmethod2(c,cnv_size,"size","*"); + class_addmethod2(c,cnv_vis_size,"vis_size","*"); + class_addmethod2(c,cnv_get_pos,"get_pos",""); + iemgui_subclass(c); + class_setsavefn(c,(t_savefn)cnv_savefn); + class_sethelpsymbol(c, gensym("my_canvas")); + + c = vu_class = class_new2("vu",vu_new,iemgui_free,sizeof(t_vu),0,"*"); + iemgui_subclass(c); + class_addbang(c,vu_bang); + class_addfloat(c,vu_float0); + class_addmethod2(c,vu_float1,"ft1","f"); + class_addmethod2(c,vu_reload,"reload","*"); + class_addmethod2(c,vu_size,"size","*"); + class_addmethod2(c,vu_scale,"scale","F"); + class_setsavefn(c,(t_savefn)vu_savefn); + class_sethelpsymbol(c, gensym("vu")); + + visualloader_class = class_new2("#V",visualloader_new,visualloader_free,sizeof(t_object),CLASS_GOBJ,"*"); + class_addanything(visualloader_class,visualloader_anything); + pd_bind(pd_new(visualloader_class),gensym("#V")); +} + +/* ---------------------------------------------------------------- */ +/* formerly m_glob.c */ + +t_class *glob_pdobject; +static t_class *maxclass; + +#define IGN(sym) if (s==gensym(sym)) return; +void max_default(t_pd *x, t_symbol *s, int argc, t_atom *argv) { + IGN("audioindev"); + IGN("audiooutdev"); + IGN("audioininfo"); + IGN("audiooutinfo"); + IGN("testaudiosettingresult"); + IGN("audiodevice"); + IGN("xrun"); + IGN("audio_started"); + IGN("sys_lock_timeout"); + IGN("midiindev"); + IGN("midioutdev"); + IGN("midicurrentindev"); + IGN("midicurrentoutdev"); + IGN("audiocurrentininfo"); + IGN("audiocurrentoutinfo"); + IGN("asiolatency"); + startpost("%s: unknown message %s ", class_getname(pd_class(x)), s->name); + std::ostringstream buf; + for (int i = 0; i < argc; i++) {buf << " "; atom_ostream(argv+i,buf);} + post("%s",buf.str().data()); + endpost(); +} + +static void openit(const char *dirname, const char *filename) { + char *dirbuf; + char *nameptr; + int fd = open_via_path2(dirname,filename,"",&dirbuf,&nameptr,0); + if (!fd) {error("%s: can't open", filename); return;} + close(fd); + glob_evalfile(0, gensym(nameptr), gensym(dirbuf)); + free(dirbuf); +} + +extern "C" t_socketreceiver *netreceive_newest_receiver(t_text *x); + +/* this should be rethought for multi-client */ +void glob_initfromgui(void *dummy, t_symbol *s) { + char buf[256], buf2[256]; + char cwd[666]; + getcwd(cwd,665); + sys_socketreceiver=netreceive_newest_receiver(sys_netreceive); + sys_vgui("%s",lost_posts.str().data()); + /* load dynamic libraries specified with "-lib" args */ + for (t_namelist *nl=sys_externlist; nl; nl = nl->nl_next) + if (!sys_load_lib(0, nl->nl_string)) + post("%s: can't load library", nl->nl_string); + /* open patches specified with "-open" args */ + for (t_namelist *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 (t_namelist *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; + sys_get_audio_apis(buf); + sys_get_midi_apis(buf2); + sys_vgui("pd_startup {%s} %s %s\n", pd_version, buf, buf2); +/* + fprintf(stdout,"This line was printed on stdout\n"); + fprintf(stderr,"This line was printed on stderr\n"); +*/ +} + +void glob_meters(void *dummy, t_floatarg f); +void glob_audiostatus(void *dummy); +void glob_audio_properties(t_pd *dummy, t_floatarg flongform); +void glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_audio_setapi(t_pd *dummy, t_floatarg f); +void glob_midi_properties(t_pd *dummy, t_floatarg flongform); +void glob_midi_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_midi_setapi(t_pd *dummy, t_floatarg f); +void glob_start_path_dialog(t_pd *dummy, t_floatarg flongform); +void glob_path_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_start_startup_dialog(t_pd *dummy, t_floatarg flongform); +void glob_startup_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv); +void glob_ping(t_pd *dummy); +extern "C" { +void glob_finderror(t_pd *dummy); +}; +/* tb: message-based audio configuration { */ +void glob_audio_testaudiosetting(t_pd * dummy, t_symbol *s, int ac, t_atom *av); +void glob_audio_getaudioindevices(t_pd * dummy, t_symbol *s, int ac, t_atom *av); +void glob_audio_getaudiooutdevices(t_pd * dummy, t_symbol *s, int ac, t_atom *av); +void glob_audio_getaudioininfo(t_pd * dummy, t_float f); +void glob_audio_getaudiooutinfo(t_pd * dummy, t_float f); +//void glob_audio_samplerate(t_pd * dummy, t_float f); +//void glob_audio_delay(t_pd * dummy, t_float f); +//void glob_audio_dacblocksize(t_pd * dummy, t_float f); +//void glob_audio_scheduler(t_pd * dummy, t_float f); +void glob_audio_device(t_pd * dummy, t_symbol *s, int argc, t_atom *argv); +//void glob_audio_device_in(t_pd * dummy, t_symbol *s, int argc, t_atom *argv); +//void glob_audio_device_out(t_pd * dummy, t_symbol *s, int argc, t_atom *argv); +void glob_audio_getcurrent_devices (); +void glob_audio_asio_latencies(t_pd * dummy, t_float f); +void glob_midi_getindevs( t_pd *dummy, t_symbol *s, int ac, t_atom *av); +void glob_midi_getoutdevs(t_pd *dummy, t_symbol *s, int ac, t_atom *av); +void glob_midi_getcurrentindevs(t_pd *dummy); +void glob_midi_getcurrentoutdevs(t_pd *dummy); +/* tb } */ + +static void glob_object_table() { + t_symbol *s_inlet = gensym("inlet"); + t_symbol *s___list = gensym("__list"); + size_t inlets=0, lists=0, zombies=0; + t_pd *k; long v; + post("object_table = {"); + hash_foreach(k,v,object_table) { + t_pd *x = (t_pd *)k; + if (!(long)v&1) {zombies++; continue;} /* skip zombies */ + //post(" %p %ld %s",k,(long)v,x->_class->name->name); + if (x->_class->name == s_inlet) {inlets++; continue;} + if (x->_class->name == s___list) {lists++; continue;} + int nobs = x->_class->gobj ? ((t_gobj *)x)->dix->nobs : 0; + // this has been duplicated as the ostream operator of t_pd * (see above). + t_binbuf *b; + if (x->_class->patchable && (b = ((t_text *)x)->binbuf)) { + char *buf; int bufn; + binbuf_gettext(b,&buf,&bufn); + post(" %p %ld (%dobs) %s [%.*s]",k,(long)v,nobs,x->_class->name->name,bufn,buf); + } else post(" %p %ld (%dobs) %s",k,(long)v,nobs,x->_class->name->name); + } + post("} (%ld non-omitted objects, plus %ld [inlet], plus %ld [__list], plus %ld zombies)", + object_table->size()-inlets-lists-zombies,inlets,lists,zombies); +} + +extern t_class *glob_pdobject; +extern "C" void glob_init () { + /* can this one really be called "max"? isn't that a name conflict? */ + maxclass = class_new2("max",0,0,sizeof(t_pd),CLASS_DEFAULT,""); + class_addanything(maxclass, max_default); + pd_bind((t_pd *)&maxclass, gensym("max")); + + /* this smells bad... a conflict with [pd] subpatches */ + t_class *c = glob_pdobject = class_new2("pd",0,0,sizeof(t_pd),CLASS_DEFAULT,""); + class_addmethod2(c,glob_initfromgui, "init", "*"); + class_addmethod2(c,glob_setfilename, "filename", "ss"); + class_addmethod2(c,glob_evalfile, "open", "ss"); + class_addmethod2(c,glob_quit, "quit", ""); + class_addmethod2(c,glob_dsp, "dsp", "*"); + class_addmethod2(c,glob_meters, "meters", "f"); + class_addmethod2(c,glob_audiostatus, "audiostatus", ""); + class_addmethod2(c,glob_finderror, "finderror", ""); + class_addmethod2(c,glob_audio_properties, "audio-properties", "F"); + class_addmethod2(c,glob_audio_dialog, "audio-dialog", "*"); + class_addmethod2(c,glob_audio_setapi, "audio-setapi", "f"); + class_addmethod2(c,glob_midi_setapi, "midi-setapi", "f"); + class_addmethod2(c,glob_midi_properties, "midi-properties", "F"); + class_addmethod2(c,glob_midi_dialog, "midi-dialog", "*"); + class_addmethod2(c,glob_ping, "ping",""); + /* tb: message-based audio configuration { */ +// class_addmethod2(c,glob_audio_samplerate, "audio-samplerate", "F"); +// class_addmethod2(c,glob_audio_delay, "audio-delay", "F"); +// class_addmethod2(c,glob_audio_dacblocksize,"audio-dacblocksize", "F"); +// class_addmethod2(c,glob_audio_scheduler, "audio-scheduler", "F"); + class_addmethod2(c,glob_audio_device, "audio-device", "*"); +// class_addmethod2(c,glob_audio_device_in, "audio-device-in", "*"); +// class_addmethod2(c,glob_audio_device_out, "audio-device-out", "*"); + class_addmethod2(c,glob_audio_getaudioindevices, "getaudioindev", "*"); + class_addmethod2(c,glob_audio_getaudiooutdevices,"getaudiooutdev", "*"); + class_addmethod2(c,glob_audio_getaudioininfo, "getaudioininfo", "f"); + class_addmethod2(c,glob_audio_getaudiooutinfo, "getaudiooutinfo", "f"); + class_addmethod2(c,glob_audio_testaudiosetting, "testaudiosetting", "*"); + class_addmethod2(c,glob_audio_getcurrent_devices,"getaudiodevice", ""); + class_addmethod2(c,glob_audio_asio_latencies, "getasiolatencies", "F"); + class_addmethod2(c,glob_midi_getoutdevs,"getmidioutdev", "*"); + class_addmethod2(c,glob_midi_getindevs, "getmidiindev", "*"); + class_addmethod2(c,glob_midi_getcurrentoutdevs, "getmidicurrentoutdev", ""); + class_addmethod2(c,glob_midi_getcurrentindevs, "getmidicurrentindev", ""); + /* tb } */ +#ifdef UNIX + class_addmethod2(c,glob_watchdog, "watchdog", ""); +#endif + class_addmethod2(c,glob_update_class_list, "update-class-list", "ss"); + class_addmethod2(c,glob_update_class_info, "update-class-info", "sss"); + class_addmethod2(c,glob_update_path, "update-path", ""); + class_addmethod2(c,glob_help, "help", "s"); + class_addmethod2(c,glob_object_table,"object_table",""); + class_addanything(c, max_default); + pd_bind((t_pd *)&glob_pdobject, gensym("pd")); +} + +/* ---------------------------------------------------------------- */ +/* formerly s_print.c */ + +t_printhook sys_printhook; +int sys_printtostderr; + +static void dopost(const char *s) { + if (sys_printhook) sys_printhook(s); + else if (sys_printtostderr) fprintf(stderr, "%s", s); + else { + std::ostringstream t; + for(int i=0; s[i]; i++) { + if (strchr("\\\"[]$\n",s[i])) t << '\\'; + t << char(s[i]=='\n' ? 'n' : s[i]); + } + sys_vgui("pdtk_post \"%s\"\n",t.str().data()); + } +} + +void post(const char *fmt, ...) { + char *buf; va_list ap; va_start(ap, fmt); + size_t n = vasprintf(&buf, fmt, ap); va_end(ap); + buf=(char*)realloc(buf,n+2); strcpy(buf+n,"\n"); + dopost(buf); free(buf); +} +void startpost(const char *fmt, ...) { + char *buf; va_list ap; va_start(ap, fmt); + vasprintf(&buf, fmt, ap); va_end(ap); + dopost(buf); free(buf); +} + +void poststring(const char *s) {dopost(" "); dopost(s);} + +void postatom(int argc, t_atom *argv) { + std::ostringstream buf; + for (int i=0; i<argc; i++) {buf << " "; atom_ostream(argv+i,buf);} + dopost(buf.str().data()); +} + +/* what's the point? */ +void postfloat(float f) {t_atom a; SETFLOAT(&a, f); postatom(1, &a);} +void endpost () {dopost("\n");} + +static t_pd *error_object; +static char *error_string; +void canvas_finderror(void *object); + +void verror(const char *fmt, va_list ap) { + if (error_string) free(error_string); + dopost("error: "); + vasprintf(&error_string,fmt,ap); + dopost(error_string); + dopost("\n"); + //post("at stack level %ld\n",pd_stackn); + error_object = pd_stackn>=0 ? pd_stack[pd_stackn-1].self : 0; +} +void error( const char *fmt, ...) {va_list ap; va_start(ap,fmt); verror(fmt,ap); va_end(ap);} +void pd_error(void *moot, const char *fmt, ...) {va_list ap; va_start(ap,fmt); verror(fmt,ap); va_end(ap);} + +void verbose(int level, const char *fmt, ...) { + char *buf; + va_list ap; + if (level>sys_verbose) return; + dopost("verbose("); + postfloat((float)level); + dopost("):"); + va_start(ap, fmt); + vasprintf(&buf,fmt,ap); + va_end(ap); + dopost(buf); + dopost("\n"); +} + +extern "C" void glob_finderror(t_pd *dummy) { + if (!error_object) {post("no findable error yet."); return;} + post("last trackable error was for object x%lx: %s", error_object, error_string); + sys_mgui(error_object,"show_error","S",error_string); + canvas_finderror(error_object); +} + +void bug(const char *fmt, ...) { + char *buf; + va_list ap; + dopost("bug: "); + va_start(ap, fmt); + vasprintf(&buf,fmt,ap); + va_end(ap); + dopost(buf); + free(buf); + dopost("\n"); +} + +static const char *errobject; +static const char *errstring; + +void sys_logerror (const char *object, const char *s){errobject=object; errstring = s;} +void sys_unixerror(const char *object) {errobject=object; errstring = strerror(errno);} + +void sys_ouch () { + if (*errobject) error("%s: %s", errobject, errstring); else error("%s", errstring); +} + +/* properly close all open root canvases */ +extern "C" void glob_closeall(void *dummy, t_floatarg fforce) { + foreach(x,windowed_canvases) canvas_close(x->first); +} + +/* ---------------------------------------------------------------- */ +/* formerly m_conf.c */ + +void builtins_setup (); +void builtins_dsp_setup (); +void desire_setup (); +void d_soundfile_setup (); +void d_ugen_setup (); + +extern "C" void conf_init () { + builtins_setup(); + builtins_dsp_setup(); + desire_setup(); + d_soundfile_setup(); + d_ugen_setup(); +} + +// and just to make some externs happy: +#define BYE error("%s unimplemented in desiredata!", __PRETTY_FUNCTION__); +extern "C" { + void glist_grab () {BYE} + void glist_xtopixels () {BYE} + void glist_ytopixels () {BYE} + void *glist_findrtext () {BYE return 0;} + void canvas_fixlinesfor () {BYE} + void class_setpropertiesfn () {BYE} + void glist_eraseiofor () {BYE} + void glist_getcanvas () {BYE} + void rtext_gettag () {BYE} + void class_setwidget () {BYE} + void glist_isvisible () {BYE} + void gobj_vis () {BYE} + void gfxstub_deleteforkey () {BYE} + void gfxstub_new () {BYE} + + //redundantwards-compatibility + void canvas_setcurrent (t_canvas *x) {pd_pushsym(x);} + void canvas_unsetcurrent(t_canvas *x) {pd_popsym(x);} +}; diff --git a/desiredata/src/desire.h b/desiredata/src/desire.h new file mode 100644 index 00000000..f3cefe02 --- /dev/null +++ b/desiredata/src/desire.h @@ -0,0 +1,353 @@ +/* + This file is part of PureData + Copyright 2004-2006 by Mathieu Bouchard + Copyright 2000-2001 IEM KUG Graz Austria (Thomas Musil) + 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 declares the C interface for the second half of DesireData. + this is where most of the differences between DesireData and PureData lie. + + SYNONYMS: + 1. glist = graph = canvas = patcher; those were not always synonymous. + however, graph sometimes means a canvas that is displaying an array. + 2. outconnect = connection = patchcord = wire + + canvases have a few flags that determine their appearance: + gl_havewindow: should open its own window (only if mapped? or...) + gl_isgraph: the GOP flag: show as graph-on-parent, vs TextBox. + gl_owner==0: it's a root canvas, should be in canvas_list. + In this case "gl_havewindow" is always set. + + canvas_list is a list of root windows only, which can be traversed using canvases_each. + + If a canvas 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 canvas that's just a text object on its parent is always "toplevel." An + embedded canvas 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 canvas shows up as a graph on its parent, the graph is blanked while the + canvas has its own window, even if miniaturized. +*/ + +#ifndef DESIRE +#define DESIRE +#endif +#ifndef __DESIRE_H +#define __DESIRE_H + +#include "m_pd.h" +#include "s_stuff.h" + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +//#include <map> +extern "C" { +#endif + +/* ----------------------- m_imp.h ---------------------------------------------------*/ + +typedef struct _methodentry { + t_symbol *me_name; + t_gotfn me_fun; + t_atomtype me_arg[MAXPDARG+1]; +} t_methodentry; + +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_stringmethod) (t_pd *x, const char *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); + +t_pd *pd_new2(int argc, t_atom *argv); +t_pd *pd_new3(const char *s); + +struct _class { + t_symbol *name; /* name (mostly for error reporting) */ + t_symbol *helpname; /* name of help file */ + t_symbol *externdir; /* directory extern was loaded from */ + size_t size; /* size of an instance */ + t_methodentry *methods; /* methods other than bang, etc below */ + int nmethod; /* number of methods */ + t_method freemethod; /* function to call before freeing */ + t_bangmethod bangmethod; /* common methods */ + t_pointermethod pointermethod; + t_floatmethod floatmethod; + t_symbolmethod symbolmethod; /* or t_stringmethod, but only C++ has anonymous unions, so... */ + t_listmethod listmethod; + t_anymethod anymethod; + t_savefn savefn; /* function to call when saving */ + int floatsignalin; /* onset to float for signal input */ + unsigned gobj:1; /* true if is a gobj */ + unsigned patchable:1; /* true if we have a t_object header */ + unsigned firstin:1; /* if patchable, true if draw first inlet */ + unsigned drawcommand:1; /* a drawing command for a template */ + unsigned newatoms:1; /* can handle refcounting of atoms (future use) */ + unsigned use_stringmethod:1; /* the symbolmethod slot holds a stringmethod instead */ + t_symbol *firsttip; + t_symbol **fields; /* names of fields aka attributes, and I don't mean the #V attributes. */ + int nfields; /* ... and how many of them */ + t_notice notice; /* observer method */ + t_onsubscribe onsubscribe; /* observable method */ +}; + +//#define c_methods methods /* for Cyclone */ +//#define c_nmethod nmethod /* for Cyclone */ +//#define c_externdir externdir /* for PDDP */ +//#define c_name name /* for Cyclone,Creb,Pidip */ +//#define c_size size /* for Cyclone,Flext */ +//#define me_name name /* for Cyclone */ +//#define me_fun fun /* for Cyclone */ +//#define me_arg arg /* for Cyclone */ + +/* m_obj.c */ +EXTERN int obj_noutlets(t_object *x); +EXTERN int obj_ninlets(t_object *x); +EXTERN t_outconnect *obj_starttraverseoutlet(t_object *x, t_outlet **op, int nout); +EXTERN t_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect, t_object **destp, t_inlet **inletp, int *whichp); +EXTERN t_outconnect *obj_connect(t_object *source, int outno, t_object *sink, int inno); +EXTERN void obj_disconnect(t_object *source, int outno, t_object *sink, int inno); +EXTERN void outlet_setstacklim(void); +EXTERN int obj_issignalinlet(t_object *x, int m); +EXTERN int obj_issignaloutlet(t_object *x, int m); +EXTERN int obj_nsiginlets(t_object *x); +EXTERN int obj_nsigoutlets(t_object *x); +EXTERN int obj_siginletindex(t_object *x, int m); +EXTERN int obj_sigoutletindex(t_object *x, int m); + +/* misc */ +EXTERN void glob_evalfile(t_pd *ignore, t_symbol *name, t_symbol *dir); +EXTERN void glob_initfromgui(void *dummy, t_symbol *s); +EXTERN void glob_quit(void *dummy); + +/* ----------------------- g_canvas.h ------------------------------------------------*/ + +/* i don't know whether this is currently used at all in DesireData. -- matju 2006.09 */ +#ifdef GARRAY_THREAD_LOCK +#include <pthread.h> /* TB: for t_garray */ +#endif + +typedef struct t_gtemplate t_gtemplate; +typedef struct _canvasenvironment t_canvasenvironment; +typedef struct _slot t_slot; + +/* the t_tick structure describes where to draw x and y "ticks" for a canvas */ +typedef struct _tick { /* where to put ticks on x or y axes */ + float point; /* one point to draw a big tick at */ + float inc; /* x or y increment per little tick */ + int lperb; /* little ticks per big; 0 if no ticks to draw */ +} t_tick; + +typedef struct t_boxes t_boxes; + +/* the t_canvas structure, which describes a list of elements that live on an area of a window.*/ +#ifdef PD_PLUSPLUS_FACE +struct _glist : t_object { +#else +struct _glist { + t_object gl_obj; /* header in case we're a [pd] or abstraction */ +#endif + int pixwidth, pixheight; /* width in pixels (on parent, if a graph) */ + float x1,y1,x2,y2; /* bounding rectangle in our own coordinates */ + int screenx1, screeny1, screenx2, screeny2; /* screen coordinates when toplevel */ + int xmargin, ymargin; /* origin for GOP rectangle */ + /* ticks and tick labels */ + t_tick xtick; int nxlabels; t_symbol **xlabel; float xlabely; + t_tick ytick; int nylabels; t_symbol **ylabel; float ylabelx; + t_symbol *name; /* symbol bound here */ + int font; /* nominal font size in points, e.g., 10 */ + t_canvasenvironment *env; /* one of these per $0; env=0 for subpatches */ + unsigned int havewindow:1; /* this indicates whether we publish to the manager */ + unsigned int gop:1; + unsigned int goprect:1; /* gop version >= 0.39 */ + unsigned int hidetext:1; /* hide object-name + args when doing graph on parent */ + long next_o_index; /* next object index. to be incremented on each use */ + long next_w_index; /* next wire index. to be incremented on each use */ + t_boxes *boxes; +}; + +/* LATER consider adding font size to this struct (see canvas_getfont()) */ +struct _canvasenvironment { + t_symbol *dir; /* directory patch lives in */ + int argc; /* number of "$" arguments */ + t_atom *argv; /* array of "$" arguments */ + long dollarzero; /* value of "$0" */ + t_namelist *path;/* search path (0.40) */ +}; + +/* a data structure to describe a field in a pure datum */ +#define DT_FLOAT 0 +#define DT_SYMBOL 1 +#define DT_CANVAS 2 +#define DT_ARRAY 3 + +typedef struct t_dataslot { + int type; + t_symbol *name; + t_symbol *arraytemplate; /* filled in for arrays only */ +} t_dataslot; + +#ifdef PD_PLUSPLUS_FACE +typedef struct t_template : t_pd { +#else +typedef struct t_template { + t_pd t_pdobj; /* header */ +#endif + t_gtemplate *list; /* list of "struct"/gtemplate objects */ + t_symbol *sym; /* name */ + int n; /* number of dataslots (fields) */ + t_dataslot *vec; /* array of dataslots */ +} t_template; + +/* this is not really a t_object, but it needs to be observable and have a refcount, so... */ +#ifdef PD_PLUSPLUS_FACE +struct _array : t_object { +#else +struct _array { + t_gobj gl_obj; +#endif + int n; /* number of elements */ + int elemsize; /* size in bytes; LATER get this from template */ + char *vec; /* array of elements */ + t_symbol *templatesym; /* template for elements */ + t_gpointer gp; /* pointer to scalar or array element we're in */ +}; + +#ifdef PD_PLUSPLUS_FACE +struct _garray : t_gobj { +#else +struct _garray { + t_gobj x_gobj; +#endif + t_scalar *scalar; /* scalar "containing" the array */ + t_canvas *canvas; /* containing canvas */ + t_symbol *name; /* unexpanded name (possibly with leading '$') */ + t_symbol *realname; /* expanded name (symbol we're bound to) */ + unsigned int usedindsp:1; /* true if some DSP routine is using this */ + unsigned int saveit:1; /* true if we should save this with parent */ + unsigned int listviewing:1; /* true if list view window is open */ + unsigned int hidename:1; /* don't print name above graph */ +}; + +t_array *garray_getarray(t_garray *x); + +/* structure for traversing all the connections in a canvas */ +typedef struct t_linetraverser { + t_canvas *canvas; + t_object *from; int nout; int outlet; t_outlet *outletp; + t_object *to; int nin; int inlet; t_inlet *inletp; + t_outconnect *next; + int nextoutno; +#ifdef __cplusplus + t_linetraverser() {} + t_linetraverser(t_canvas *canvas); +#endif +} t_linetraverser; + +extern t_canvas *canvas_list; /* list of all root canvases */ +extern t_class *vinlet_class, *voutlet_class, *canvas_class; +extern int canvas_valid; /* incremented when pointers might be stale */ +EXTERN int sys_noloadbang; +EXTERN t_symbol *s_empty; + +#define PLOTSTYLE_POINTS 0 /* plotting styles for arrays */ +#define PLOTSTYLE_POLY 1 +#define PLOTSTYLE_BEZ 2 + +/* from kernel.c */ +EXTERN void gobj_save(t_gobj *x, t_binbuf *b); +EXTERN void pd_eval_text(char *t, size_t size); +EXTERN int sys_syntax; + +/* from desire.c */ +EXTERN int pd_scanargs(int argc, t_atom *argv, char *fmt, ...); +EXTERN int pd_saveargs(t_binbuf *b, char *fmt, ...); +EXTERN void pd_upload(t_gobj *self); +EXTERN void sys_mgui(void *self, const char *sel, const char *fmt, ...); +EXTERN void canvas_add(t_canvas *x, t_gobj *g, int index=-1); +EXTERN void canvas_delete(t_canvas *x, t_gobj *y); +EXTERN void canvas_deletelinesfor(t_canvas *x, t_text *text); +EXTERN void canvas_deletelinesforio(t_canvas *x, t_text *text, t_inlet *inp, t_outlet *outp); +EXTERN int canvas_isvisible(t_canvas *x); +EXTERN int canvas_istoplevel(t_canvas *x); +EXTERN int canvas_istable(t_canvas *x); +EXTERN int canvas_isabstraction(t_canvas *x); +EXTERN t_canvas *canvas_getcanvas(t_canvas *x); +EXTERN t_canvas *canvas_getrootfor(t_canvas *x); +EXTERN t_canvas *canvas_getcurrent(void); +EXTERN t_canvasenvironment *canvas_getenv(t_canvas *x); +EXTERN int canvas_getdollarzero(void); +EXTERN t_symbol *canvas_realizedollar(t_canvas *x, t_symbol *s); +EXTERN t_symbol *canvas_makebindsym(t_symbol *s); + +EXTERN void linetraverser_start(t_linetraverser *t, t_canvas *x); +EXTERN t_outconnect *linetraverser_next(t_linetraverser *t); + +/* -------------------- TO BE SORTED OUT --------------------- */ +EXTERN void canvas_redrawallfortemplatecanvas(t_canvas *x, int action); +EXTERN void array_resize(t_array *x, int n); +EXTERN void word_init(t_word *wp, t_template *tmpl, t_gpointer *gp); +EXTERN void word_restore(t_word *wp, t_template *tmpl, int argc, t_atom *argv); +EXTERN t_scalar *scalar_new(t_canvas *owner, t_symbol *templatesym); +EXTERN void word_free(t_word *wp, t_template *tmpl); +EXTERN void scalar_redraw(t_scalar *x, t_canvas *canvas); +//EXTERN int pd_pickle(t_foo *foo, char *fmt, ...); +EXTERN void pd_set_newest (t_pd *x); +char* inlet_tip(t_inlet* i,int num); + +extern t_hash<t_pd *,long> *object_table; +extern t_hash<t_symbol *,t_class *> *class_table; + +/* some kernel.c stuff that wasn't in any header, when shifting to C++. */ +void obj_moveinletfirst(t_object *x, t_inlet *i); +void obj_moveoutletfirst(t_object *x, t_outlet *o); +int inlet_getsignalindex(t_inlet *x); +int outlet_getsignalindex(t_outlet *x); +void text_save(t_gobj *z, t_binbuf *b); +t_sample *obj_findsignalscalar(t_object *x, int m); +void class_set_extern_dir(t_symbol *s); +void glob_update_class_info (t_pd *bogus, t_symbol *s, t_symbol *cb_recv, t_symbol *cb_sel); +void pd_free_zombie(t_pd *x); + +/* some other stuff that wasn't in any header */ +void glob_watchdog(t_pd *dummy); + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +} +#endif + +#ifdef __cplusplus +#include<iostream> +template <class T> static T min(T a, T b) {return a<b?a:b;} +template <class T> static T max(T a, T b) {return a>b?a:b;} +template <class T> T clip(T a, T b, T c) {return min(max(a,b),c);} +void oprintf(std::ostream &buf, const char *s, ...); +void voprintf(std::ostream &buf, const char *s, va_list args); +EXTERN std::ostringstream lost_posts; +#endif + +#define L post("%s:%d in %s",__FILE__,__LINE__,__PRETTY_FUNCTION__); +#define LS(self) post("%s:%d in %s (self=%lx class=%s)",__FILE__,__LINE__,__PRETTY_FUNCTION__,(long)self, \ + ((t_gobj *)self)->_class->name->name); + +#define STACKSIZE 1024 +struct t_call { + t_pd *self; /* receiver */ + t_symbol *s; /* selector */ + /* insert temporary profiling variables here */ +}; + +EXTERN t_call pd_stack[STACKSIZE]; +EXTERN int pd_stackn; + +EXTERN int gstack_empty(); /* that's a completely different stack: see pd_pushsym,pd_popsym */ + +#endif /* __DESIRE_H */ diff --git a/desiredata/src/desire.tk b/desiredata/src/desire.tk new file mode 100644 index 00000000..70f3d052 --- /dev/null +++ b/desiredata/src/desire.tk @@ -0,0 +1,9019 @@ +#!/usr/bin/env wish +set cvsid {$Id: desire.tk,v 1.1.2.600.2.419 2007-10-27 00:22:27 matju Exp $} +#-----------------------------------------------------------------------------------# +# +# DesireData +# Copyright (c) 2004 by Mathieu Bouchard +# Copyright (c) 2005,2006,2007 by Mathieu Bouchard and Chun Lee +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. + +# See file ../COPYING.desire-client.txt for further informations on licensing terms. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Note that this is not under the same license as the rest of PureData. +# Even the DesireData server-side modifications stay on the same license +# as the rest of PureData. +# +#-----------------------------------------------------------------------------------# + +# this command rebuilds the package index: echo pkg_mkIndex . | tclsh + +set debug 0 ;# DON'T TOUCH THIS, make yourself a debug.tcl instead! + +if {[catch {winfo children .}]} {set tk 0} {set tk 1} + +set argh0 [file normalize [file join [pwd] $argv0]] +set auto_path [concat . \ + [list [file join [file dirname [file dirname $argh0]] lib/pd/bin]] \ + /usr/lib/tcllib1.7 \ + /usr/lib/tclx8.4 \ + $auto_path] + +package require poe +if {$tk} {package require bgerror} + +catch {package require Tclx} +#if {[catch {source /home/matju/src/pd-desiredata/pd/src/profile_dd.tcl}]} {error_dump} +if {[file exists debug.tcl]} {source debug.tcl} + +proc which {file} { + global env + foreach dir [split $::env(PATH) ":"] { + if {[file exists $dir/$file]} {return $dir/$file} + } + return "" +} + +#-----------------------------------------------------------------------------------# +# some list processing functions and some math too +# these could become another library like objective.tcl +# if they become substantial enough + +# min finds the smallest of two values +# max finds the biggest of two values +# [clip $v $min $max] does [max $min [min $max $v]] +proc min {x y} {expr {$x<$y?$x:$y}} +proc max {x y} {expr {$x>$y?$x:$y}} +proc clip {x min max} { + if {$x<$min} {return $min} + if {$x>$max} {return $max} + return $x +} + +# set several variables from elements of a list +# WARNING: for @-variables, use [list @a @b @c] instead of {@a @b @c} +proc mset {vars list} { + uplevel 1 "foreach {$vars} {$list} {break}" +} + +# add or subtract two lists +proc l+ { al bl} {set r {}; foreach a $al b $bl {lappend r [expr {$a+$b}]}; return $r} +proc l- { al bl} {set r {}; foreach a $al b $bl {lappend r [expr {$a-$b}]}; return $r} +# halve a list +proc l/2 { al } {set r {}; foreach a $al {lappend r [expr {$a/2 }]}; return $r} + +# like l+ or l- but for any infix supported by expr +proc lzip {op al bl} { + set r {} + set e "\$a $op \$b" + foreach a $al b $bl {lappend r [expr $e]} + return $r +} + +# do an operation between all elements of a list and a second argument +proc lmap {op al b } { + set r {} + set e "\$a $op \$b" + foreach a $al {lappend r [expr $e]} + return $r +} + +# sum and product of a list, like math's capital Sigma and capital Pi. +proc lsum {al} {set r 0; foreach a $al {set r [expr {$r+$a}]}; return $r} +proc lprod {al} {set r 1; foreach a $al {set r [expr {$r*$a}]}; return $r} + +# all elements from end to beginning +proc lreverse {list} { + set r {} + for {set i [expr {[llength $list]-1}]} {$i>=0} {incr i -1} {lappend r [lindex $list $i]} + return $r +} + +# list substraction is like set substraction but order-preserving +# this is the same algorithm as Ruby's - operation on Arrays +proc lwithout {a b} { + set r {} + foreach x $b {set c($x) {}} + foreach x $a {if {![info exists c($x)]} {lappend r $x}} + return $r +} + +proc lintersection {a b} { + set r {} + foreach x $b {set c($x) {}} + foreach x $a {if {[info exists c($x)]} {lappend r $x}} + return $r +} + +# removes duplicates from a list, but it must be already sorted. +proc luniq {a} { + set last [lindex $a 0] + set r [list $last] + set i 0 + foreach x $a { + if {$i && [string compare $last $x]} {lappend r $x} + incr i; set last $x + } + return $r +} + +# one-dimensional intervals (left-closed, right-open); not much in use at the moment, not that they wouldn't deserve to! +proc inside {x x0 x1} {return [expr $x>=$x0 && $x<$x1]} +proc overlap {y0 y1 x0 x1} {return [expr [inside $y0 $x0 $x1] || [inside $y1 $x0 $x1]]} + +proc distance {point1 point2} { + set off [l- $point1 $point2] + return [expr {sqrt([lsum [lzip * $off $off]])}] +} + +proc rect_centre {rect} { + mset {x1 y1 x2 y2} $rect + return [list [expr {($x1+$x2)/2}] [expr {($y1+$y2)/2}]] +} + +proc lmake {start end} {for {set i $start} {$i<=$end} {incr i} {lappend l $i}; return $l} +#-----------------------------------------------------------------------------------# +set callback_list {} + +proc append_callback {mode when def} { + global callback_list + dict set callback_list $mode $when $def +} + +proc remove_callback {mode} { + global callback_list + set callback_list [dict remove $callback_list $mode] +} + +proc modes_callback {self def {args}} { + global callback_list + set i 0 + dict for {mode callbacks} $callback_list { + foreach {when call} $callbacks { + if {$def == $when} {eval $self $call $args; incr i} + } + } + if {!$i} {return 0} else {return 1} +} + +#-----------------------------------------------------------------------------------# +# Observer pattern +# there's no class for "observer". +# it's anything that has def $myclass notice {args} {...} in which args indicate +# attributes that have changed, or is an empty list if an unspecified number of +# attributes (maybe all) have changed. + +class_new Observable {} +def Observable init {args} { + eval [concat [list super] $args] + set @subscribers {} +} +def Observable subscribe {observer} { + set i [lsearch $@subscribers $observer] + if {$i<0} {lappend @subscribers $observer} +} +def Observable unsubscribe {observer} { + set i [lsearch $@subscribers $observer] + if {$i>=0} {set @subscribers [lreplace $@subscribers $i $i]} +} + +if {$have_expand} { + #def Observable changed {args} { + # puts "Observable changed $self called from [info level [expr [info level]-2]]" + # foreach x $@subscribers {$x notice $self {expand}$args]} + #} + def Observable changed {args} {foreach x $@subscribers {$x notice $self {expand}$args}} + def Observable child_changed {origin args} {foreach x $@subscribers {$x notice $origin {expand}$args}} +} else { + def Observable changed {args} {foreach x $@subscribers {eval [concat [list $x notice $self] $args]}} + def Observable child_changed {origin args} {foreach x $@subscribers {eval [concat [list $x notice $origin] $args]}} +} +def Observable subscribers {} {return $@subscribers} + +#-----------------------------------------------------------------------------------# +set poolset(foo) bar +array unset poolset foo + +class_new Manager {Thing} + +def Manager init {} { + set @q {} + $self call +} + +def Manager call {} { + global poolset + #if {[llength $@q]} {post "client queue %d" [llength $@q]} + + for {set i 0} {$i < [llength $@q]} {incr i} { + set o [lindex $@q $i] + unset poolset($o) + if {[info exists _($o:_class)]} { + if {[catch {$o draw_maybe}]} {puts [error_dump]} + } else { + puts " tries to draw ZOMBIE $o" + } + if {$i == [expr [llength $@q] - 1]} {set @q {}} + } + after 50 "$self call" +} + +def Manager notice {origin args} { + global poolset + if {[info exists poolset($origin)]} { + # post %s "def Manager notice: double dirty" + # nothing for now + } { + set poolset($origin) {-1} + lappend @q $origin + } + #post "Manager notice: queue length is now %d" [llength $@q] +} + +set serial 0 +proc serial {n obj} { + if {$n >= $::serial} {error "object creation serial number is in the future"} + eval [concat $::replyset($n) [list $obj]] + array unset ::replyset $n +} + +proc philtre {atoms} { + set r {} + foreach atom $atoms {lappend r [regsub -all {([;,\\ ])} $atom {\\\1}]} + return [join $r] +} + +# you pass the 2nd argument if and only if the message creates an object (or pretends to). +# this happens with #N canvas, and those methods of #X: +# obj, msg, floatatom, symbolatom, text, connect, text_setto, array. +# this does NOT happen with #X coords/restore/pop. +proc netsend {message {callback ""}} { + #if {$message == ""} {error "empty message... surely a mistake"} + if {$::sock == ""} {error "connection to server needed for doing this"} + if {$callback != ""} { + set ::replyset($::serial) $callback + set message [concat [lrange $message 0 0] [list with_reply $::serial] [lrange $message 1 end]] + incr ::serial + } + set text "[philtre $message];" + if {$::debug} {puts "[VTcyan]<- $text[VTgrey]"} + puts $::sock $text +} + +#-----------------------------------------------------------------------------------# +# This is not a real Hash, just the same interface as a Ruby/Python/Perl Hash... or quite like Tcl arrays themselves +class_new Hash {Thing} + +def Hash init {args} { super; foreach {k v} $args {$self set $k $v}} +def Hash reinit {args} {$self clear; foreach {k v} $args {$self set $k $v}} +def Hash set {k v} {set ::hash($self:$k) $v} +def Hash exists {k} {info exists ::hash($self:$k)} +def Hash get {k} {set ::hash($self:$k)} +def Hash size {} {llength [$self keys]} +def Hash unset {k} {unset ::hash($self:$k)} +def Hash list {} {set r {}; foreach k [$self keys] {lappend r $k [$self get $k]}; return $r} +def Hash keys {} { + set r {} + set n [string length $self:] + foreach k [array names ::hash $self:*] {lappend r [string range $k $n end]} + return $r +} +def Hash values {} { + set r {} + foreach k [array names ::hash $self:*] {lappend r $::hash($k)} + return $r +} +def Hash clear {} {foreach k [$self keys] {$self unset $k}} +def Hash delete {} {$self clear; super} + +def Hash search {v} { + foreach k [$self keys] {if {[$self get $k] == $v} {return $k}} + return -1 ;# this is not correct as -1 could be a Hash key, though not in its current context of use... +} + +if 0 { + set h [Hash new foo bar 1 2 3 4] + $h set hello world + puts keys=[$h keys] + puts values=[$h values] + puts list=[$h list] + $h unset foo + puts list=[$h list] + $h clear + puts list=[$h list] + foreach i {1 2 3 4} {puts "exists $i : [$h exists $i]"} +} + +class_new Selection {Hash} +def Selection set {k v} {super $k $v; $v selected?= 1} +def Selection unset {k} { + #set v [$self get $k]; puts "$v ::: [$v class]" + if {[$self exists $k]} {[$self get $k] selected?= 0} + super $k +} +#-----------------------------------------------------------------------------------# +# abstract class: subclass must def {value value= <<} +class_new Clipboard {Observable Thing} +def Clipboard init {{value ""}} {super; $self value= $value; set @copy_count 0} + +# uses system clipboard +class_new Clipboard1 {Clipboard} +def Clipboard1 value= {value} {clipboard clear; clipboard append $value; $self changed} +def Clipboard1 << {value} { clipboard append $value; $self changed} +def Clipboard1 value {} {clipboard get} + +# uses string buffer (not system clipboard) +class_new Clipboard2 {Clipboard} +def Clipboard2 value= {value} {set @value $value; $self changed} +def Clipboard2 << {value} {append @value $value; $self changed} +def Clipboard2 value {} {return $@value} + +if {$tk} { + set clipboard [Clipboard1 new] +} else { + set clipboard [Clipboard2 new] +} + +#-----------------------------------------------------------------------------------# +class_new EventHistory {Observable Thing} + +def EventHistory init {} {super; set @list {}} +def EventHistory add {e} {lappend @list $e; $self changed add $e} +def EventHistory list {{formatted 1}} { + if {!$formatted} {return $@list} + set r {} + foreach event $@list { + mset {type W x y mod K k} $event + lappend r [format "%-13s %9s %4d %4d %4d %4d %s" $type $K $k $x $y $mod $W] + } + return $r +} +set ::event_history [EventHistory new] + +#-----------------------------------------------------------------------------------# +class_new CommandHistory {Observable Thing} + +def CommandHistory init {} { + super + set @undo_stack {} + set @redo_stack {} +} + +def CommandHistory can_undo? {} {return [expr [llength @undo_stack] > 0]} +def CommandHistory can_redo? {} {return [expr [llength @redo_stack] > 0]} +def CommandHistory next_undo_name {} {return stuff} +def CommandHistory next_redo_name {} {return stuff} +def CommandHistory undo_stack {} {return $@undo_stack} +def CommandHistory redo_stack {} {return $@redo_stack} + +# overload this if you want to control how many levels +# of undo may be kept. +# keep in mind that undo information is kept hierarchically. +def CommandHistory add {message} { + lappend @undo_stack [list do $message [lrange [info level -3] 1 end]] + set @redo_stack {} + $self changed +} + +def CommandHistory can't {} { + lappend @undo_stack [list can't {} [lrange [info level -3] 1 end]] + set @redo_stack {} + $self changed +} + +# runs the restore procedure for the last item in the root undo queue. +def CommandHistory undo {} { + global errorInfo + if {![$self can_perform? [lindex $@undo_stack end]]} {error "Can't undo this!"} + set backup $@undo_stack + set @undo_stack $@redo_stack + set @redo_stack {} + #set err [catch {$self perform [lindex $backup end]}]; if {$err} {set err $errorInfo} + $self perform [lindex $backup end] + set @redo_stack $@undo_stack + set @undo_stack [lrange $backup 0 end-1] + $self changed + #if {$err} {post %s $err; error "undo: $err"} +} + +def CommandHistory redo {} { + global errorInfo + if {![$self can_perform? [lindex $@undo_stack end]]} {error "Can't redo this!"} + set backup $@redo_stack + set @redo_stack {} + set err [catch {$self perform [lindex $backup end]}]; if {$err} {set err $errorInfo} + $self perform [lindex $backup end] + set @redo_stack [lrange $backup 0 end-1] + $self changed + #if {$err} {post %s $err; error "redo: $err"} +} + +def CommandHistory can_perform? {action} { + switch -- [lindex $action 0] { + do {return 1} + can't {return 0} + default { + foreach x [lrange $action 1 end] { + if {![$self can_perform? $x]} {return 0} + } + return 1 + } + } +} + +def CommandHistory perform {action} { + switch -- [lindex $action 0] { + do {eval [lindex $action 1]} + can't {error "can't undo this!"} + default {foreach x [lindex $action 1] {$self perform $x}} + } +} + +def CommandHistory atomically {what code} { + global errorInfo + set ubackup @undo_stack; set @undo_stack {} + set rbackup @redo_stack; set @redo_stack {} + uplevel 2 $code + set atom $@undo_stack + set @undo_stack $ubackup + set @redo_stack $rbackup + lappend @undo_stack [list $what $atom [lrange [info level -3] 1 end]] + $self changed +} + +def CommandHistory list {} { + set r {} + set hist [concat [$self undo_stack] [list "You Are Here"] [lreverse [$self redo_stack]]] + set i 0 + foreach e $hist {lappend r "$i: $e"; incr i} + return $r +} + +set command_history [CommandHistory new] + +#-----------------------------------------------------------------------------------# +class_new History {Thing} + +def History init {size} { + set @size $size + set @hist {{}} + set @histi -1 +} + +def History histi= {val} {set @histi $val} +def History histi {} {return $@histi} + +def History set_hist {idx stuff} {set @hist [lreplace $@hist $idx $idx $stuff]} + +def History prepend {stuff} { + set @hist [linsert $@hist 1 $stuff] + if {[llength $@hist] >= $@size} {set @hist [lrange $@hist 0 [expr $@size-1]]} +} + +def History traverse {incr} { + set @histi [expr $@histi + $incr] + set mod [expr ([llength $@hist]<[expr $@size+1]) ?[llength $@hist]:[expr $@size+1]] + if {$@histi >=$mod} {set @histi [expr $@histi%$mod]} + if {$@histi < 0} {set @histi [expr ($@histi+$mod)%$mod]} + return [lindex $@hist $@histi] +} + +History new_as obj_hist 5 +#-----------------------------------------------------------------------------------# +# this is the beginning of the more application-dependent part. + +switch $tcl_platform(os) { + Darwin {set OS osx} + default {set OS $tcl_platform(platform)} +} + +if {$tk} { + option add *foreground #000000 + option add *font {Helvetica -12} + foreach tkclass {Menu Button Checkbutton Radiobutton Entry Text Spinbox Scrollbar Canvas} { + option add *$tkclass*borderWidth 1 + option add *$tkclass*activeBorderWidth 1 + } + foreach tkclass {CheckButton RadioButton} { + option add *$tkclass*selectColor #dd3000 + } + foreach tkclass {Entry Text} { + option add *$tkclass*background #b0c4d8 + option add *$tkclass*selectBackground #6088b0 + } + option add *__tk__messagebox*Canvas*borderWidth 0 + foreach tkclass {Listbox} { + option add *$tkclass*background #c4d8b0 + option add *$tkclass*selectBackground #88b060 + } + foreach tkclass {Label} { + #option add *$tkclass*background #909090 + } + # very small icons: + foreach {name w h values} { + icon_empty 7 7 "0,0,0,0,0,0,0" + icon_plus 7 7 "8,8,8,127,8,8,8" + icon_minus 7 7 "0,0,0,127,0,0,0" + icon_close 7 7 "99,119,62,28,62,119,99" + icon_wedge_up 7 5 "8,28,62,127,0" + icon_wedge_down 7 5 "0,127,62,28,8" + icon_up 7 7 "8,28,62,127,28,28,28" + icon_down 7 7 "28,28,28,127,62,28,8" + icon_right 7 7 "8,24,63,127,63,24,8" + icon_left 7 7 "8,12,126,127,126,12,8" + } { + image create bitmap $name -data "#define z_width $w\n#define z_height $h + static unsigned char z_bits[] = { $values };" + } + # 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 <Key-Tab> "" + #bind all <Key-Shift-Tab> "" + bind all <<PrevWindow>> "" + bind Text <Control-t> {} + bind Text <Control-s> {} + set mods {{} 0 Shift- 1 Control- 4 Shift-Control- 5 Alt- 8 Shift-Alt- 9 Control-Alt- 12 Shift-Control-Alt- 13} + foreach type {KeyPress KeyRelease} { + foreach {subtype mod} $mods { + bind all <$subtype$type> "$::event_history add \[list $type %W %x %y $mod %K %k\]" + } + } + foreach type {ButtonPress ButtonRelease} { + foreach {subtype mod} $mods { + bind all <$subtype$type> "$::event_history add \[list $type %W %x %y $mod %b %b\]" + } + } +} + +proc modekey {k mode} { + set s "" + if {$mode&1} {append s Shift-} + if {$mode&4} {append s Control-} + if {$mode&8} {append s Alt-} + if {[regexp {[0-9]} $k]} {set k Key-$k} + return $s$k +} + +proc modeclick {k mode event} { + set s "" + if {$mode&1} {append s Shift-} + if {$mode&4} {append s Control-} + if {$mode&8} {append s Alt-} + if {[regexp {[0-9]} $k]} {set k $event-$k} + return $s$k +} + + +# there are two palettes of 30 colours used in Pd +# when placed in a 3*10 grid, the difference is that +# the left corner of 3*3 (the greys) are transposed (matrixwise) +# here is the one used in the swatch color selector: +set preset_colors { + fcfcfc e0e0e0 bcbcbc fce0e0 fce0c0 fcfcc8 d8fcd8 d8fcfc dce4fc f8d8fc + a0a0a0 7c7c7c 606060 fc2828 fcac44 e8e828 14e814 28f4f4 3c50fc f430f0 + 404040 202020 000000 8c0808 583000 782814 285014 004450 001488 580050 +} + +set preset_colors2 { + fcfcfc a0a0a0 404040 fce0e0 fce0c0 fcfcc8 d8fcd8 d8fcfc dce4fc f8d8fc + e0e0e0 7c7c7c 202020 fc2828 fcac44 e8e828 14e814 28f4f4 3c50fc f430f0 + bcbcbc 606060 000000 8c0808 583000 782814 285014 004450 001488 580050 +} + +switch $::OS { + osx {set pd_tearoff 0} + default {set pd_tearoff 1} +} + +proc guess_lang {} { + set lang C + if {[info exist ::env(LC_ALL)]} {set lang $::env(LC_ALL)} + if {[info exist ::env(LANG)]} {set lang $::env(LANG)} + set lang [lindex [split $lang {[_.]}] 0] + return $lang +} + +#temporary +set leet 0 + +proc say {k args} { + global text + if {[llength $args]} { + set text($k) [lindex $args 0] + } else { + if {[info exist text($k)]} { + if {$::leet} { + return [string map -nocase {a 4 e 3 t 7 s 5 i 1 o 0 g 9} $text($k)] + } else { + return $text($k) + } + } else {return "{{$k}}"} + } +} + +proc can_say {k args} { + return [info exist ::text($k)] +} + +proc say_namespace {k code} {uplevel 1 $code} +proc say_category {text} {} + +switch -- [lindex [file split $argh0] end] { + desire.tk {set cmdline(server) [file join [file dirname $argh0] pd]} + default {set cmdline(server) [file join [file dirname [file dirname $argh0]] bin/pd]} +} + +set cmdline(rcfilename) ~/.pdrc +set cmdline(ddrcfilename) ~/.ddrc +set cmdline(console) 1000 +if {[file exists ../icons/mode_edit.gif]} { + set cmdline(icons) ../icons +} else { + set cmdline(icons) [file join [file dirname [file dirname $argh0]] lib/pd/icons] +} + +#-----------------------------------------------------------------------------------# +set accels {} +proc read_client_prefs_from {filename} { + global cmdline look key accels + set ::accels {} + puts "reading from $filename" + set fd [open $filename] + set contents [read $fd] + close $fd + foreach {category category_data} $contents { + foreach {class class_data} $category_data { + foreach {var val} $class_data {set ${category}($class:$var) $val} + } + } + foreach k [array names key] { + if {[llength $key($k)]} { + if {![dict exists $accels $key($k)]} { + dict set accels $key($k) $k + } else { + dict lappend accels $key($k) $k + } + } + } +} +proc read_ddrc {} { ;# load defaults then load .ddrc + if {[file exists "defaults.ddrc"]} { + read_client_prefs_from "defaults.ddrc" + } else { + read_client_prefs_from [file join [file dirname [file dirname $::argh0]] "lib/pd/bin/defaults.ddrc"] + } + if {[file exists $::cmdline(ddrcfilename)]} { + read_client_prefs_from $::cmdline(ddrcfilename) + } +} +read_ddrc + +#-----------------------------------------------------------------------------------# + +set cmdline(port) 0 +set cmdline(gdb) 0 +set cmdline(gdbconsole) 1 +set cmdline(valgrind) 0 +if {$look(View:language) eq "auto"} { + set language [guess_lang] +} else { + set language $look(View:language) +} + +set files_to_open {} + +proc cmdline_help {} { + puts "DesireData commandline options: + -serverargs (for future use) + -server select the executable for the pd server + -gdb run pd server through gdb + -manualgdb run gdb in the terminal + -valgrind run pd server through valgrind + -novalgrind ... or don't + -safemode run desiredata with all default settings + -dzinc use zinc emulation" +} + +for {set i 0} {$i < $argc} {incr i} { + global cmdline files_to_open + set o [lindex $argv $i] + switch -regexp -- $o { + ^-port\$ {incr i; set cmdline(port) [lindex $argv $i]} + ^-serverargs\$ {error "not supported yet"} + ^-server\$ {incr i; set cmdline(server) [lindex $argv $i]} + ^-gdb\$ {set cmdline(gdb) 1} + ^-manualgdb\$ {set cmdline(gdbconsole) 0} + ^-valgrind\$ {set cmdline(valgrind) 1} + ^-novalgrind\$ {set cmdline(valgrind) 0} + ^-safemode\$ {set cmdline(safemode) 1} + ^-dzinc\$ {set cmdline(dzinc) 1} + ^(-h|-help|--help)\$ {cmdline_help; exit 1} + ^- {puts "ERROR: command line argument: unknown $o"} + default {lappend files_to_open [lindex $argv $i]} + } +} + +#set cmdline(server) \"$cmdline(server)\" +set encoding "" +set langoptions { + english francais deutsch catala espanol portugues italiano bokmal + euskara polski dansk chinese nihongo brasiliano turkce nederlands + russkij + +} +#lappend langoptions {chinese} +#lappend langoptions {esperanto} +set langfile locale/[switch -regexp -- $language { + ^(en|english|C)$ {list english} + ^(fr|francais)$ {list francais} + ^(de|deutsch)$ {list deutsch} + ^(ca|catala)$ {list catala} + ^(es|espanol)$ {list espanol} + ^(pt|portugues)$ {list portugues} + ^(it|italiano)$ {list italiano} + ^(nb|norsk|bokmal)$ {list bokmal} + ^(ch|chinese)$ {set encoding utf-8; list chinese} + ^(eu|euskara)$ {list euskara} + ^(eo|esperanto)$ {set encoding utf-8; list esperanto} + ^(pl|polski)$ {set encoding utf-8; list polski} + ^(dk|dansk)$ {list dansk} + ^(ja|japanese|nihongo)$ {list nihongo} + ^(br|brasiliano)$ {list brasiliano} + ^(tr|turkce)$ {set encoding utf-8; list turkce} + ^(nl|nederlands)$ {list nederlands} + ^(ru|russkij)$ {set encoding utf-8; list russkij} + default {error "huh??? unknown language (locale)"} +}].tcl + +proc localedir {x} {file join [file dirname [file dirname $::argh0]] lib/pd/bin/$x} +if {[regexp {desire\.tk$} $argh0]} { + source locale/index.tcl + if {$encoding != ""} {source -encoding $encoding $langfile} else {source $langfile} +} else { + source [localedir locale/index.tcl] + if {$encoding != ""} {source -encoding $encoding [localedir $langfile]} else {source [localedir $langfile]} +} + +if {[info exists ::cmdline(safemode)]} {read_client_prefs_from "defaults.ddrc"} +if {[info exists ::cmdline(dzinc)]} {package require dzinc} + +#-----------------------------------------------------------------------------------# + +#!@#$ is this still valid? +set look(Box:extrapix) [switch $::OS { + osx {concat 2} + default {concat 1}}] + +#font is defined as Thing for now, as the completion needs to get to these ones. + +#!@#$ View->??? +#set look(View:tooltip) 1 + +#!@#$ View->TextBox ? +#set look(View:minobjwidth) 21 + +#!@#$ View->ObjectBox +#set look(View:fg) #000000 +#set look(View:bg) #ffffff +#set look(View:frame1) #99cccc +#set look(View:frame2) #668888 +#set look(View:frame3) #000000 + +#!@#$ this is supposed to be BlueBox! +#set look(Slider:bg) #ccebff + +set zoom(canned) [list 25 33 50 75 100 125 150 200 250 300 400] +set scale_amount 1.1 +################## set up main window ######################### + +class_new Console {View} + +def Console init {c} { + set @c $c + frame $c + text $c.1 -width 72 -height 20 -yscrollcommand "$c.2 set" -font [$self look font] + scrollbar $c.2 -command "$c.1 yview" + pack $c.1 -side left -fill both -expand yes + pack $c.2 -side left -fill y -expand no + pack $c -fill both -expand yes + $c.2 set 0.0 1.0 + switch $::OS { osx { + bind $c.1 <MouseWheel> {$c.1 yview scroll [expr -2-abs(%D)/%D] units} + }} + set @lines 0 +} + +def Console widget {} {return $@c} + +def Console post_string {x} { + set oldpos [lindex [$@c.2 get] 1] + $@c.1 insert end $x + regsub -all "\n" $x "" y + set n [expr [string length $x]-[string length $y]] + incr @lines $n + while {$@lines >= $::cmdline(console)} { + $@c.1 delete 1.0 2.0 + incr @lines -1 + } + if {$oldpos > 0.9999} {$@c.1 see end} +} + +#class_new Client {Menuable View} +class_new Client {Menuable Thing} + +set ctrls_audio_on 0 +set ctrls_meter_on 0 + +def Client window {} {return .} + +def Client init_binds {} { + bind . <Control-Key> {$main ctrlkey %x %y %K %A 0} + bind . <Control-Shift-Key> {$main ctrlkey %x %y %K %A 1} + switch $::OS { + osx { + bind . <Mod1-Key> {$main ctrlkey %x %y %K %A 0} + bind . <Mod1-Shift-Key> {$main ctrlkey %x %y %K %A 1} + } + } +# bind . <Motion> {.debug.1 configure -text "widget = %W"} +} + +# miller uses this nowadays (matju fished it in pd-cvs for 0.40). we don't use it for now. +# remember to fix all quoting problems, which in the end may or may not involve the following proc. +proc pdtk_unspace {x} { + set y [string map {" " "_" ";" "" "," "" "{" "" "}" "" "\\" ""} $x] + if {$y == ""} {set y "empty"} + concat $y +} + +proc pdtk_pd_meters {indb outdb inclip outclip} { + foreach {z clip db} [list in $inclip $indb out $outclip $outdb] { + .controls.$z.1.mtr coords m 0 0 $db 0 + .controls.$z.1.clip configure -background [if {$clip==1} {concat red} {concat black}] + } +} + +proc pd_startup {version apilist midiapilist args} { + set ::pd_version $version + set ::pd_apilist $apilist + set ::pd_midiapilist $midiapilist + foreach api $apilist { + lappend ::pd_apilist2 "-[string tolower [lindex $api 0]]" + } + set version [regsub "^DesireData " $::pd_version ""] + post "DesireData server version $version" +} + +def Client init_controls {} { + menu .mbar + pack [frame .controls] -side top -fill x + foreach t {file window help} { + .mbar add cascade -label [say $t] -menu [menu .mbar.$t -tearoff $::pd_tearoff] + } + .mbar.window configure -postcommand "$self fix_window_menu" + foreach {z fill} {in #0060ff out #00ff60} { + set f .controls.$z + frame $f + frame $f.1 -borderwidth 2 -relief groove + canvas $f.1.mtr -width 100 -height 10 -bg #222222 + $f.1.mtr create line [list 0 0 0 0] -width 24 -fill $fill -tags m + canvas $f.1.clip -width 5 -height 10 -bg #222222 + pack $f.1.mtr $f.1.clip -side left + pack [label $f.2 -text [say $z]:] $f.1 -side left + pack $f -side left -pady 0 -padx 0 + } + foreach {w x y z} { + audiobutton audio ctrls_audio_on {netsend [list pd dsp $ctrls_audio_on]} + meterbutton meters ctrls_meter_on {netsend [list pd meters $ctrls_meter_on]} + } { + pack [checkbutton .controls.$w -text [say $x] -variable $y -anchor w -command $z] -side left + } + button .controls.clear -text [say console_clear] -command {.log.1 delete 0.0 end} -padx 2 -pady 0 + button .controls.dio -text [say io_errors] -command {netsend [list pd audiostatus]} -padx 2 -pady 0 + pack .controls.clear .controls.dio -side right + if {$::debug} { + frame .debug + pack [label .debug.1 -anchor w -text ""] -side left + pack [entry .debug.3 -textvariable ::serial -width 5] -side right + pack [label .debug.2 -text "obj.serial: " -justify right] -side right + pack .debug -side bottom -fill x + } + if {$::cmdline(console)} {set ::console [Console new .log]} + . configure -menu .mbar + wm title . "DesireData" + catch {wm iconphoto . icon_pd} + regexp {\d\d\d\d/\d\d/\d\d} $::cvsid version + regsub -all "/" $version "." version + set ::pd_version_client $version + post "DesireData client version $version with Tcl %s and Tk %s" $::tcl_patchLevel $::tk_patchLevel +} + +proc pdtk_pd_dsp {value} { + global ctrls_audio_on + set ctrls_audio_on $value +} + +proc pdtk_pd_dio {red} { + .controls.dio configure -background red -activebackground [if {$red==1} {list red} {list lightgrey}] +} + +############### set up global variables ################################ + +set pd_opendir [pwd] + +############### set up socket ########################################## +set sock {} +set sock_lobby {} + +proc poll_sock {} { + global sock sock_lobby cmdline + if {[llength $sock]==0} {return} + while {1} { + set cmd [gets $sock] + if {[eof $sock]} { + if {!$cmdline(gdb)} { + tk_messageBox -message "connection ended by server.\n(crash? try: desire -gdb)" -type ok + } + set sock {} + return + } + if {[fblocked $sock]} {break} + if {$::debug} {if {[string first pdtk_post $cmd]!=0} {puts "[VTmagenta]-> $cmd[VTgrey]"}} + append sock_lobby "\n$cmd" + if {[catch {eval $sock_lobby}]} { + global errorCode errorInfo + switch -regexp -- $errorInfo { "^missing close-brace" { + #puts "waiting for the end of: [string range $sock_lobby 0 40]" + continue + }} + error_dump + } + set sock_lobby {} + } + flush $sock + after 50 poll_sock +} + +set server_pid 0 +proc poll_gdb {} { + global gdb + while {1} { + set line [gets $gdb] + if {$line=="" || [fblocked $gdb]} {break} ;# which way should i check for no input? + if {[eof $gdb]} {return} + regsub {^\(gdb\) ?} $line {} line + if {[regexp {^\[Thread debug} $line]} {continue} + if {[regexp {^\[New Thread.*LWP (\d+)} $line dummy pid]} { + if {!$::server_pid} { + set ::server_pid $pid + post "server pid=$pid" + continue + } + } + if {[regexp {^Reading symbols from} $line]} {continue} + if {[regexp {^Using host libthread_db} $line]} {continue} + if {[regexp {^Starting program:} $line]} {continue} + if {[regexp {^Program received signal (\w+), (.*)\.} $line bogus sig1 sig2]} { + set where "" + # can anyone figure out why a long backtrace won't be slurped in this case? + set timeout [expr [clock seconds]+2] + #fconfigure $gdb -blocking 1 -buffering none + while {![eof $gdb] && [clock seconds] < $timeout} { + set line [gets $gdb] + if {$line eq ""} {continue} ;# busy-wait + regsub {^\(gdb\) ?} $line {} line + append where "$line\n" + #puts "where size = [string length $where]" + } + OopsDialog new $sig1 $sig2 $where + } + post "\[gdb\] %s" $line + } + after 100 poll_gdb +} + +proc pd_connect {} { + global sock + if {[catch {set sock [socket 127.0.0.1 13666]}]} { + post "can't connect... wait a second" + after 1000 pd_connect + return + } + post "Connected to server" + fconfigure $sock -blocking 0 -buffering line + netsend [list pd init] + poll_sock + foreach f $::files_to_open { + set ff [file split [file normalize $f]] + set ffl [llength $ff] + set file [lindex $ff [expr $ffl-1]] + set dir [join [lrange $ff 0 [expr $ffl-2]] [file separator]] + netsend [join [list pd open $file $dir]] + } +} + +set server_port 13666 +after 1000 pd_connect + +after 0 { + if {$cmdline(port)} { + set server_port $cmdline(port) + # and then do nothing... + } elseif {$cmdline(valgrind)} { + #exec valgrind --tool=memcheck $cmdline(server) -guiport $server_port & + exec valgrind --tool=memcheck --gen-suppressions=all --suppressions=valgrind3.supp $cmdline(server) -guiport $server_port & + } else { + if {$cmdline(gdb)} { + if {$cmdline(console) && $cmdline(gdbconsole)} { + set gdb [open "| gdb --quiet 2&>1" w+] + fconfigure $gdb -blocking 0 -buffering none + puts $gdb "file \"$cmdline(server)\"" ;# bad quoting, sorry + puts $gdb "run -guiport $server_port" + puts $gdb "where" + puts $gdb "quit" + flush $gdb + after 0 poll_gdb + } else { + exec gdb --args $cmdline(server) -guiport $server_port & + #exec gdb --tui --args $cmdline(server) -guiport $server_port & + } + } else { + exec $cmdline(server) -guiport $server_port & + } + } +} + +################ utility functions ######################### + +proc enquote {x} { + set foo [string map {"," "" ";" "" "\"" ""} $x] + return [string map {" " "\\ " "{" "" "}" ""} $foo] +} + +proc pdtk_watchdog {} {netsend [list pd ping]; after 2000 {pdtk_watchdog}} + +proc accel_munge {acc} { + switch $::OS { + osx { + set tmp [string toupper [string map {Ctrl Meta} $acc] end] + if [string is upper [string index $acc end]] { + return Shift+$tmp + } else { + return $tmp + } + } + default {return $acc} + } +} + +# a menuable must be a View +# and it must have a window so that the Menuable methods work +# it could be renamed to Windowed +class_new Menuable {} +def Menuable init {args} { + eval [concat [list super] $args] + set @accel {} + set @menubar .$self.m +} + +# this doesn't have to do with menus, only with toplevel windows. +def Menuable raise {} { + set w [$self window] + set w $w.c + raise $w + focus -force $w +} + +set untitled_number 1 +set untitled_folder [pwd] + +# just a dummy proc +proc none {args} {} + +def Client new_file {} { + global untitled_number untitled_folder + netsend [list pd filename Untitled-$untitled_number $untitled_folder] + netsend [list #N canvas] + netsend [list #X pop 1] + incr untitled_number +} + + +set patch_filetypes { + {"pd files" ".pd"} + {"max files" ".pat"} + {"all files" "*"} +} + +set image_filetypes { + {"image files" ".gif .png"} + {"gif files" ".gif"} + {"png files" ".png"} + {"all files" "*"} +} + +#only works with tcltk 8.5 +catch {tk_getOpenFile -load-once} +if {$tcl_version>=8.5} { + set ::tk::dialog::file::showHiddenBtn 1 + set ::tk::dialog::file::showHiddenVar 0 +} + +def Client open_file {} { + global pd_opendir patch_filetypes + set filename [tk_getOpenFile -defaultextension .pd -filetypes $patch_filetypes -initialdir $pd_opendir] + if {$filename != ""} {$self open_file_really $filename} +} + +def Client open_file_really {filename} { + set i [string last / $filename] + set folder [string range $filename 0 [expr $i-1]] + set ::pd_opendir $folder + set basename [string range $filename [expr $i+1] end] + if {[string last .pd $filename] >= 0} { + netsend [list pd open [enquote $basename] [enquote $folder]] + } +} + +def Client send_message {} { + toplevel .sendpanel + set e .sendpanel.entry + pack [entry $e -textvariable send_textvar] -side bottom -fill both -ipadx 100 + $e select from 0 + $e select adjust end + bind $e <KeyPress-Return> {netsend $send_textvar; after 50 {destroy .sendpanel}} + focus $e +} + +def Client quit {} { + set answer [tk_messageBox -message "Do you really wish to quit?" -type yesno -icon question] + switch -- $answer {yes {netsend [list pd quit]; exit}} +} + +def Client abort_server {} { + set answer [tk_messageBox -message "Do you really wish to abort?" -type yesno -icon question] + switch -- $answer {yes {exec kill -ABRT $::server_pid}} +} + +def Client server_prefs {} {ServerPrefsDialog new_as pdrc} +def Client client_prefs {} {ClientPrefsDialog new_as ddrc} + +proc menu_pop_pd {} {raise .} + +def Menuable populate_menu {menu list} { + global key + if {[string index $menu 0] != "."} {set menu $@menubar.$menu} + foreach name $list { + if {$name == ""} {$menu add separator; continue} + set k "" + if {[info exists key($@_class:$name)]} { + if {[string length $key($@_class:$name)]} {set k $key($@_class:$name)} + } + $menu add command -label [say $name] -command "$self $name" -accelerator [accel_munge $k] + } +} + +def Client init_menus {} { + #removed paths after send_message + $self populate_menu file { + new_file open_file {} + server_prefs client_prefs send_message {} + audio_on audio_off {} + abort_server quit} + $self populate_menu help { + about documentation class_browser do_what_i_mean {} + test_audio_and_midi load_meter latency_meter {} + clipboard_view command_history_view event_history_view keyboard_view client_class_tree} +} + +def Client init {} { + super + set @menubar .mbar + $self init_controls + $self init_binds + $self init_menus + # it's necessary to raise the window on OSX + switch $::OS { osx {raise .; wm iconify .; after 100 {wm deiconify .}}} + after 0 { + Listener new .tcl [say "tcl_console"] tcl_eval + Listener new .pd [say "pd_console"] pd_eval + } + #wm geometry .[$self keyboard_view] -0+0 + #wm geometry .[$self event_history_view] -0-0 +} + +proc post {args} { + set s "[eval [linsert $args 0 format]]\n" + # set s "[info level -1]: $s" + if {$::cmdline(console)} {$::console post_string $s} else {puts stderr $s} +} +proc pdtk_post {s} { + if {$::cmdline(console)} {$::console post_string $s} else {puts stderr $s} +} + +def Menuable eval% {code} { + regsub -all %W $code $self code + uplevel [info level] $code +} + +def Menuable getkey {k} { + global accels + if {[dict exists $accels $k]} { + set vars [dict get $accels $k] + foreach var $vars { + #mset {class key} [split [dict get $accels $k] ":"] + mset {class key} [split $var ":"] + #if {$class != $@_class} {return ""} else {return $key} + if {$class == $@_class} {return $key} + } + } +} + +def Menuable ctrlkey {x y key iso shift} { + set key [if {$shift} {string toupper $key} {string tolower $key}] + set key "Ctrl+$key" + set cmd [$self getkey $key] + if {![string length $cmd]} { + switch [$self class]:$key { + #explicitly listed here to do nothing + Client:Ctrl+c {} + Client:Ctrl+v {} + default {post "unknown key $key"} + } + } else {$self eval% "%W $cmd"} +} + +def Menuable altkey {x y key iso shift} { + set key [if {$shift} {string toupper $key} {string tolower $key}] + set key "Alt+$key" + set cmd [$self getkey $key] + if {[string length $cmd]} {$self eval% "%W $cmd"} else {post "unknown key $key"} +} + +#-----------------------------------------------------------------------------------# +set pd_apilist "{ALSA 1}" +set pd_apilist2 "default" + +#-----------------------------------------------------------------------------------# +#fixme: actually, is it ok that View<Menuable ? --matju +class_new View {Menuable Observable Thing} + +# normally ninlets/noutlets should be in class "Box", but server-side isn't smart enough +def View pdclass= {v} {set @pdclass $v} +def View pdclass {} {return $@pdclass} +def View ninlets= {v} {set @ninlets $v} +def View ninlets {} {return $@ninlets} +def View noutlets= {v} {set @noutlets $v} +def View noutlets {} {return $@noutlets} +def View click {x y f target} {} +def View unclick {x y f target} {} +def View motion {x y f target} {} + +def View subpatch {} {if {[info exists @subpatch]} {return 1} else {return 0}} + +def View look_cache {k} { + global look look_cache + foreach super [$@_class ancestors] { + if {[info exists look($super:$k)]} { + set look_cache($@_class:$k) $super + return $super + } + } +} +def View look {k} { + if {![info exists ::look_cache($@_class:$k)]} {$self look_cache $k} + return $::look($::look_cache($@_class:$k):$k) +} + +def View init {} { + super + set @selected? 0 + set @index 0xDEADBEEF + set @ninlets 1 ;# should be in Box init + set @noutlets 0 ;# should be in Box init + set @canvas "" +# set @inside_box 42 ;# temporary fix + set @ioselect {} +} + +def View classtags {} {return {foo}} + +set item { + set canvas [$self get_canvas] + if {$canvas == ""} {return} + set c [$self cwidget] + set zoom [$canvas zoom] + set coords [lmap * $coords $zoom] + set find [lsearch $args "-width"] + if {$find >= 0} { + incr find + set w [lindex $args $find] + lset args $find [format %.0f [expr {$w*$zoom}]] + } + set find [lsearch $args "-font"] + if {$find >= 0} { + incr find + set fs [lindex [lindex $args $find] 1] + lset args $find 1 [format %.0f [expr {$fs*$zoom}]] + } + set tags {} + foreach s $suffixes {lappend tags "$self$s"} + set ss [lindex $tags 0] + lappend tags $self + set tags [concat $tags [$self classtags]] +} +if {$have_expand} { + append item { + if {![llength [$c gettags $ss]]} { + $c create $type $coords -tags $tags {expand}$args + } { + $c itemconfigure $ss {expand}$args + $c coords $ss {expand}$coords + } + } +} else { + append item { + if {![llength [$c gettags $ss]]} { + eval [concat [list $c create $type $coords -tags $tags] $args] + } { + eval [concat [list $c itemconfigure $ss] $args] + eval [concat [list $c coords $ss] $coords] + } + } +} +def View item {suffixes type coords args} $item + +def View item_delete {{suffix all}} { + if {$@canvas == ""} {return} + set c [$@canvas widget] + if {![winfo exists $c]} { + set canvas [$@canvas get_canvas] + if {$canvas == ""} {return} + set c [[$@canvas get_canvas] widget] + if {![winfo exists $c]} {return} + } + switch -- $suffix { + all {$c delete $self} + default {$c delete $self$suffix}} +} + +def View draw {} {} + +def View delete {} {$self erase; super} +def View erase {} {$self item_delete} +def View selected? {} {return $@selected?} +def View selected?= {x} {set @selected? $x; $self changed} ;# this is for use by the Selection class only +def View edit? {} {if {[info exists @edit]} {return $@edit} else {return 0}} +def View select {state} { + set ostate [$self selected?] + set @selected? $state + if {$state!=$ostate} {$self changed} +} + +# give topleft point of an object in the canvas it's rendered in. +# this includes GOP and excludes zoom. +# inheritance problem: this doesn't work for Wire, which doesn't store its positions +def View xy {} { + if {$@canvas == ""} {return [list $@x1 $@y1]} + set type [$@canvas type] + if {$type == "gopabs" || $type == "gopsub"} { + mset {xmargin ymargin} [$@canvas margin] + mset {x y} [$@canvas xy] + set x1 [expr {($@x1-$xmargin)+$x}] + set y1 [expr {($@y1-$ymargin)+$y}] + #if $self's gop is opened + if {[regexp {^.x[0-9a-f]{6,8}.c} [focus] f]} { + if {$f == ".$@canvas.c"} {set x1 $@x1; set y1 $@y1} + } + #if {[focus] != "." && [focus] == ".$@canvas.c"} {set x1 $@x1; set y1 $@y1} + return [list $x1 $y1] + } + return [list $@x1 $@y1] +} + +def View canvas {} {return $@canvas} +def View canvas= {c} { + set @canvas $c + # should "subscribe" call "changed"? (or pretend to?) + $self subscribe $c + $self changed + $self outside_of_the_box +} + +def View visible {} {if {[info exists @inside_box]} {return $@inside_box} {return -1}} + +# this returns the canvas actually exists/drawn +# see also def Canvas get_canvas +def View get_canvas {} { + set canvas $@canvas + if {$canvas == ""} {return ""} + #while {![$canvas havewindow]} {set canvas [$canvas canvas]} + while {![winfo exists [$canvas widget]]} {set canvas [$canvas canvas]} + return $canvas +} +# this will return the top level gop if gop is nested +def View get_parent_gop {canvas} { + set obj $self + while {$canvas != [$obj canvas]} {set obj [$obj canvas]} + return $obj +} + +def View outside_of_the_box {} { + if {$@canvas eq ""} {return} + # always hide these things + if {[$self class] == "Wire"} {set @inside_box 0; return} + if {[$self class] == "ObjectBox"} {set @inside_box 0; return} + if {[$self class] == "MessageBox"} {set @inside_box 0; return} + if {[$self class] == "Comment"} {set @inside_box 0; return} + if {[$self class] == "Array"} {$@canvas visibles+= $self; set @inside_box 1; return} + set x1 $@x1; set y1 $@y1 + if {[$@canvas gop]} { + set mess [$@canvas get_mess] + set pixwidth [lindex $mess 4] + set pixheight [lindex $mess 5] + if {[llength $mess] == 6} { + set xmargin 0; set ymargin 0 + } else { + set xmargin [lindex $mess 6]; set ymargin [lindex $mess 7] + } + if {$x1 < $pixwidth +$xmargin && $x1 > $xmargin && \ + $y1 < $pixheight+$ymargin && $y1 > $ymargin} { + set @inside_box 1 + $@canvas visibles+= $self + } else { + set @inside_box 0 + $@canvas visibles-= $self + } + } else {set @inside_box 1} +} + +def View draw_maybe {} { + if {$@canvas == "" && [winfo exists .$self.c]} {$self draw; return} + if {[$self class] == "Canvas" && $@canvas == ""} {return} + if {$@inside_box} { + #if {[$@canvas mapped] && ![$@canvas abs] && [$self gop_check]} {$self draw} + if {[$@canvas mapped] && [$self gop_check]} {$self draw} + } else { + # for drawing opened gop + if {[winfo exists .$@canvas.c]} {$self draw} + } +} +#this checks if $self can be seen, ie nested gop. +def View gop_check {} { + set canvases $@canvas + while {[lindex $canvases end] != ""} {lappend canvases [[lindex $canvases end] canvas]} + set canvases [lreplace $canvases end-1 end]; # all canvases + foreach canvas $canvases { + # if a canvas is not a gop and its window does not exists + if {![$canvas gop] && ![winfo exists [$canvas widget]]} {return 0; break} + } + return 1 +} + +#-----------------------------------------------------------------------------------# + +class_new Canvas {Menuable ObjectBox} + +def Canvas close {} { + after cancel $@motion_after_id + if {$@subpatch} { + #can't wait till @mapped get updated thru proc change + if {$@gop} {foreach x [$@objects values] {$x outside_of_the_box}} + $self save_geometry + $self copy_times= 0 + netsend [list .$self vis 0] + #netsend [list .$self close] + return + } + if {$@gop} { + foreach x [$@objects values] {$x outside_of_the_box} + netsend [list .$self close] + return + } + switch [tk_messageBox -message [say save_changes?] -icon question -type yesnocancel -default cancel] { + yes {$self save; netsend [list .$self close]} + no { netsend [list .$self close]} + cancel {} + } +} + +def Canvas save_geometry {} { + set geometry [wm geometry .$self] + set cw [winfo width [$self widget]]; set ch [winfo height [$self widget]] + foreach {size x y} [split $geometry "+"] {mset {w h} [split $size "x"]; set x1 $x; set y1 $y} + set x2 [expr $x1+$cw]; set y2 [expr $y1+$ch] + netsend [list .$self bounds $x1 $y1 $x2 $y2] +} + +def Canvas save {} { + if {$@subpatch} {return [$@canvas save]} + $self checkgeometry + set c [$self widget] + if {![regexp {^Untitled-[0-9]} $@name]} { + $self save_geometry + netsend [list .$self savetofile $@name $@folder] + } else { + $self save_as + } +} + +def Canvas save_as {} { + $self checkgeometry + set filename [tk_getSaveFile -filetypes $::patch_filetypes] + if {$filename != ""} { + set @file [string range $filename [expr [string last / $filename]+1] end] + set @folder [string range $filename 0 [expr [string last / $filename]-1]] + $self save_geometry + puts "save $@file dir to $@folder" + netsend [list .$self savetofile $@file $@folder] + } +} + +def Canvas print {} { + set filename [tk_getSaveFile -initialfile pd.ps -defaultextension .ps -filetypes { {{postscript} {.ps}} }] + if {$filename != ""} {[$self widget] postscript -file $filename} +} +def Canvas quit {} {$::main quit} +def Canvas abort_server {} {$::main abort_server} + +proc wonder {} {tk_messageBox -message [say ask_cool] -type yesno -icon question} + +def Canvas eval% {code} { + mset {x y} $@curpos + regsub -all %X $code $x code + regsub -all %Y $code $y code + super $code +} + +def Client documentation {} { + set filename [tk_getOpenFile -defaultextension .pd -filetypes { {{documentation} {.pd .txt .htm}} } -initialdir $::docdir] + if {$filename != ""} { + if {[string first .txt $filename] >= 0} { + menu_opentext $filename + } elseif {[string first .htm $filename] >= 0} { + menu_openhtml $filename + } else { + set i [string last / $filename] + set help_directory [string range $filename 0 [expr $i-1]] + set basename [string range $filename [expr $i+1] end] + netsend [list pd open [enquote $basename] [enquote $help_directory]] + } + } +} + +def Canvas new_file {} {$::main new_file} +def Canvas open_file {} {$::main open_file} +def Canvas send_message {} {$::main send_message} +def Client test_audio_and_midi {} {menu_doc_open doc/7.stuff/tools testtone.pd } +def Client load_meter {} {menu_doc_open doc/7.stuff/tools load-meter.pd} +def Client latency_meter {} {menu_doc_open doc/7.stuff/tools latency.pd } +def Client about {} {AboutDialog new} +def Client class_browser {} {Browser new_as browser browser 0 0 ""} +def Client audio_on {} {netsend [list pd dsp 1]} +def Client audio_off {} {netsend [list pd dsp 0]} +def Client keyboard_view {} { KeyboardDialog new $::event_history} +def Client clipboard_view {} { ClipboardDialog new $::clipboard} +def Client command_history_view {} { ListDialog new $::command_history [say command_history_view]} +def Client event_history_view {} {EventHistoryDialog new $::event_history} +def Client do_what_i_mean {} {wonder} + +set pd_prefix [file dirname [file dirname [which pd]]] +set pd_guidir ${pd_prefix}/lib/pd +set doc_number 1 +set updir [file dirname [file dirname $argh0]] +if {[file exists $updir/lib/pd/doc]} { + set docdir $updir/lib/pd/doc +} else { + set docdir $updir/doc +} + +proc menu_opentext {filename} { + set w [format ".help%d" $::doc_number] + toplevel $w + wm title $w $filename + frame $w.1 + frame $w.2 + pack [text $w.1.text -relief raised -bd 2 -yscrollcommand "$w.1.scroll set"] -side left -fill both -expand 1 + pack [scrollbar $w.1.scroll -command "$w.1.text yview"] -side right -fill y + pack [button $w.2.close -text [say close] -command "destroy $w"] -side right + pack $w.2 -side bottom -fill x -expand 0 + pack $w.1 -side bottom -fill both -expand 1 + set f [open $filename] + while {![eof $f]} { + set bigstring [read $f 1000] + regsub -all PD_BASEDIR $bigstring $::pd_guidir bigstring + regsub -all PD_VERSION $bigstring $::pd_version bigstring + $w.1.text insert end $bigstring + } + $w.1.text configure -state disabled + close $f + incr ::doc_number + return $w +} + +proc menu_doc_open {subdir basename} { + set dirname $::pd_guidir/$subdir + if {[string first .txt $basename] >= 0} { + return [menu_opentext $dirname/$basename] + } else { + netsend [list pd open $basename $dirname] + } +} + +#-----------------------------------------------------------------------------------# +def Canvas editmode {} {return $@editmode} +def Canvas editmode= {mode} { + if {$mode == $@editmode} {return} + if {!$mode} {$self deselect_all} + $self redraw ;# why this??? + set @editmode $mode; $self changed editmode +# catch {.$self.bbar.edit configure -image icon_mode_$mode} + if {$@mapped} { + if {$mode} {set im icon_mode_edit} else {set im icon_mode_run} + [$self window].bbar.edit configure -image $im + if {[$self look hairstate] && !$@editmode} {$@crosshair erase} + if {[$self look gridstate]} { + if {$@editmode} {$@grid draw} else {$@grid erase} + } + } + # comment's look depends on the value of @editmode + foreach child [$@objects values] {if {[[$child class] <= Comment]} {$child changed}} + #!@#$ should update the checkbox in the editmenu +} + +def Canvas editmodeswitch {} {$self editmode= [expr !$@editmode]} + +def Canvas window {} { + #if {$@gop && $@canvas != ""} {return [$@canvas window]} + return .$self +} +def Canvas widget {} {return .$self.c} +def View cwidget {} {return .[$self get_canvas].c} + +#-----------------------------------------------------------------------------------# + +def Canvas atomically {proc} {$@history atomically $proc} +def Canvas undo {} {$@history undo} +def Canvas redo {} {$@history redo} + +def Canvas init {mess} { + set @mapped 0 + set @gop 0 + set @goprect "" + set @abs 0 + set @name "" + set @folder "???" + set @file "" + super {#X obj 666 666 pd} ;# bogus + $self reinit $mess + set @zoom 1.0 ;# must be a float, not int + set @action none + set @objects [Hash new]; set @objectsel [Selection new]; set @visibles {} + set @wires [Hash new]; set @wiresel [Selection new] + + set @focus "" + set @curpos {30 30} + set @bbox {0 0 100 100} + set @dehighlight {} +# if {$@mapped} {$self init_window} ;#!@#$ @mapped can't possibly be 1 at this point + set @history $::command_history + $self subscribe $::manager + $self changed + #$self canvas= $self ;#!@#$ EEVIL + set @coords 0 + set @jump 0 + set @keynav_iocount 0 ;# the io select count + set @keynav_port 0 ;# which in/outlet is selected + set @keynav 0 ;# the list of objects that has io selected + set @keynav_iosel 0 ;# last object that is io selected + set @keynav_iosel_o {} ;# list of objects that has outlet selected + set @keynav_iosel_i {} ;# list of objects that has inlet selected + set @iosel_deselect 0 ;# if selected should be deselected by clicking at empty space + set @keynav_current 0 + set @keynav_last_obj 0 + set @keynav_last_wire 0 + set @keynav_tab_sel "wire" + set @keynav_shift 0 + set @copy_count 0 + set @findbar "" + set @find_string "" + set @iohilite {-1 0 0 0 0} + set @keyprefix 0 + set @coords {0 0 1 1} ;# default #X coords line + set @pixsize {0 0} + set @margin {0 0} + set @macro_q {} + set @macro_delay 200 + set @blinky "" + set @editmode 0 + set @show_id 0 + set @motion_queue {} +} + +def Canvas reinit {mess} { + switch -- [lindex $mess 0] { + "#N" { + # those four are not to be confused with other @variables of similar names. + set @canvas_pos [lrange $mess 2 3] + set @canvas_size [lrange $mess 4 5] + set args [lrange $mess 6 end] + switch [llength $args] { + 1 { + set @subpatch 0 + mset [list @fontsize] $args + set @name "" + set @mapped 1 + } + 2 { + set @subpatch 1 + mset [list @name @mapped] $args + set @fontsize "what?" + } + default {error "wrong number of arguments (expecting 5 or 6, got [expr 4+[llength $args]])"} + } + } + "#X" { + switch -- [lindex $mess 1] { + obj {} + restore { + set @x1 [lindex $mess 2] + set @y1 [lindex $mess 3] + set args [lrange $mess 4 end] + $self text= [lrange $mess 4 end] + if {!$@subpatch && [llength $args] != 0} {set @abs 1} + if {$@mapped && !$@gop} { + #if {!$@subpatch && $@text != ""} {set @abs 1; return} + #if {![winfo exists .$self.c]} {$self init_window} + } + } + coords { + set @coords [lrange $mess 2 5] + set @pixsize [lrange $mess 6 7] + switch [llength $mess] { + 8 {set @gop 0} + 9 {set @gop [lindex $mess 8]} + 11 { + set @gop [lindex $mess 8] + set @margin [lrange $mess 9 10] + } + default {error "what???"} + } + if {$@gop} {set @mapped 1} + } + } + } + "" {return} + default {error "huh? mess=$mess"} + } +} + +# doesn't this look like Canvas deconstruct ? +def Canvas get_mess {} { + return [concat $@coords $@pixsize $@margin] +} + + +def Canvas margin {} {return $@margin} +def Canvas gop {} {return $@gop} +def Canvas hidtext {} {return $@hidetext} +def Canvas abs {} {return $@abs} +#def Canvas abs {} {if {!$@subpatch} {return 1} else {return 0}} +def Canvas subpatch {} {return $@subpatch} +def Canvas get_dimen {} {return $@canvas_size} + +def Canvas gop_rect {} { + mset {pxs pys} $@pixsize + mset {mx my} $@margin + set rect [list $mx $my [expr $mx+$pxs] [expr $my+$pys]] + if {$@goprect == ""} { + set @goprect [GopRect new $self $rect] + } elseif {!$@editmode} {$@goprect delete; set @goprect ""; return} + $@goprect draw + +} + +# should be called once and only from init +def Canvas init_window {} { + lappend ::window_list $self + set win .$self + set c [$self widget] + if {$::tcl_platform(platform) == "macintosh"} { + toplevel $win -menu $win.m + } else { + if {[$self look menubar]} {toplevel $win -menu $win.m} else {toplevel $win -menu ""} + + } + catch {wm iconphoto $win icon_pd} + set @menubar $win.m + $self init_menus + # turn buttonbar on/off + set @buttonbar [ButtonBar new $self] + if {[$self look buttonbar]} {pack [$@buttonbar widget] -side top -fill x -expand no} + set @statusbar [StatusBar new $self] + # turn statusbar on/off + if {[$self look statusbar]} {pack [$@statusbar widget] -side bottom -fill x} + set w [expr [lindex $@canvas_size 0]-4];# dd canvas is 4 pixel out with pd canvas? + set h [expr [lindex $@canvas_size 1]-4] + pack [canvas $c -width $w -height $h -background white] -side left -expand 1 -fill both + set @yscroll $win.yscroll; set @xscroll $win.xscroll + $self init_scrollbars + wm minsize $win 1 1 + wm geometry $win +[lindex $@canvas_pos 0]+[lindex $@canvas_pos 1] + wm protocol $win WM_DELETE_WINDOW "$self close" + focus $c + $self new_binds + $self update_title + $self motion_update + set @runcommand [Runcommand new .$self "command" canvas_eval] + set @crosshair [Crosshair new $self] + set @active [Active new $self] + set @sense [Sense new $self] + set @grid [Grid new $self] +} + +def Canvas activate_menubar= {val} {if {$val} {.$self configure -menu $@menubar} {.$self configure -menu ""}} + +def Canvas activate_buttonbar= {val} { + if {$val} { + pack [$@buttonbar widget] -side top -fill x -expand no -before [$self widget] + } else {pack forget [$@buttonbar widget]} +} + +def Canvas activate_statusbar= {val} { + if {$val} { + if {[winfo exists $@yscroll]} {set w .$self.yscroll} else {set w .$self.c} + pack [$@statusbar widget] -side bottom -fill x -before $w + } else {pack forget [$@statusbar widget]} +} + +def Canvas activate_scrollbars= {val} {if {!$val} {$self init_scrollbars} {$self remove_scrollbars}} + +def Canvas activate_grid= {val} {if {$val} {$@grid draw} {$@grid erase}} + +def Canvas init_scrollbars {} { + set win .$self + set c [$self widget] + if {[winfo exists $win.yscroll]} {return} + set size [$c bbox foo] + mset {xs ys} $@canvas_size + pack [scrollbar $win.yscroll -command "$c yview" ] -side right -fill y -before $c + pack [scrollbar $win.xscroll -command "$c xview" -orient horizontal] -side bottom -fill x -before $c + set xw $win.xscroll; set yw $win.yscroll + $c configure -yscrollcommand "$self scroll_set $yw" -xscrollcommand "$self scroll_set $xw" + after 0 [list $self adjust_scrollbars] +} + +def Canvas remove_scrollbars {} { + set win .$self + set c [$self widget] + if {![winfo exists $win.yscroll]} {return} + #use destroy instead of pack forget so that it can be tested with winfo exists + destroy $win.yscroll + destroy $win.xscroll + $c configure -yscrollcommand "" -xscrollcommand "" -scrollregion "" +} + +def Canvas adjust_scrollbars {} { + set c [$self widget] + set size [$c bbox foo] + if {[$self look scrollbar]} {$self auto_scrollbars} + if {$size != ""} { + mset {xmin ymin xmax ymax} {0 0 100 100} + mset {x1 y1 x2 y2} $size + if {$x2 > 100} {set xmax $x2} + if {$y2 > 100} {set ymax $y2} + set bbox [list $xmin $ymin $xmax $ymax] + set oldbbox [$c cget -scrollregion] + # it is very inefficient to call "configure" here + if {"$oldbbox" != "$bbox"} {$c configure -scrollregion $bbox} + set @bbox $bbox + } +} + +def Canvas auto_scrollbars {} { + set c [$self widget] + if {[$c bbox foo] != ""} { + mset {cx1 cy1 cx2 cy2} [$c bbox foo] + } else { + set cx2 [lindex $@canvas_size 0]; set cy2 [lindex $@canvas_size 1] + } + set x2 [$c canvasx [winfo width $c]] + set y2 [$c canvasy [winfo height $c]] + if {$x2 == 1} {set x2 $cx2; set y2 $cy2} + if {$cx2 <= $x2 && $cy2 <= $y2} {$self remove_scrollbars} {$self init_scrollbars} +} + +def Canvas delete_window {} { + set wl {} + foreach w $::window_list {if {$w != $self} {lappend wl $w}} + set ::window_list $wl + destroy .$self +} + +def Canvas delete {} { + $self delete_window + super +} + +def Canvas focus {} {return $@focus} +def Canvas focus= {o} {set @focus $o} +def Canvas history {} {return $@history} + +#-----------------------------------------------------------------------------------# +def Canvas find {} { + if {[info exists ::_(findmodel:_class)]} { + focus .$self.find.find + findmodel reinit + } else { + FindModel new_as findmodel $self + FindView new $self + focus .$self.find.find + } +} +def Canvas find_again {} { + if {[info exists ::_(findmodel:_class)]} {findmodel remove_info;findmodel search_recursive} +} + +def Canvas find_last_error {} {netsend [list pd finderror]} + +def Canvas bind {eventtype selector args} { + set c [$self widget] + #bind $c $eventtype [concat [list $self $selector] $args \; $self statusbar_draw %x %y] + #bind $c $eventtype "puts \[time {[concat [list $self $selector] $args \; $self statusbar_draw %x %y]}\]" + #bind $c $eventtype "puts \[time {[concat [list $self $selector] $args]}\]" + if {[$self look statusbar]} { + bind $c $eventtype [concat [list $self $selector] $args \; $self statusbar_draw %x %y] + } else { + bind $c $eventtype [concat [list $self $selector] $args] + } +} + +def Canvas new_binds {} { + # mouse buttons + $self bind <Button> click_wrap %x %y %b 0 + $self bind <Shift-Button> click_wrap %x %y %b 1 + $self bind <Control-Button> click_wrap %x %y %b 2 + $self bind <Control-Shift-Button> click_wrap %x %y %b 3 + $self bind <Alt-Button> click_wrap %x %y %b 4 + $self bind <Alt-Shift-Button> click_wrap %x %y %b 5 + $self bind <Alt-Control-Button> click_wrap %x %y %b 6 + $self bind <Alt-Control-Shift-Button> click_wrap %x %y %b 7 + switch $::OS { + osx { + $self bind <Button-2> click_wrap %x %y %b 8 + $self bind <Control-Button> click_wrap %x %y 3 8 + } + default { + $self bind <Button-3> click_wrap %x %y %b 8 + } + } + switch $::OS { unix { + $self bind <Button-4> scroll y -1 + $self bind <Button-5> scroll y +1 + $self bind <Shift-Button-4> scroll x -1 + $self bind <Shift-Button-5> scroll x +1 + } default { + $self bind <MouseWheel> scroll y \[expr -abs(%D)/%D\] + $self bind <Shift-MouseWheel> scroll x \[expr -abs(%D)/%D\] + }} + $self bind <ButtonRelease> unclick_wrap %x %y %b 0 + $self bind <Shift-ButtonRelease> unclick_wrap %x %y %b 1 + + # keyboard + $self bind <Control-Key> ctrlkey %x %y %K %A 0 + $self bind <Control-Shift-Key> ctrlkey %x %y %K %A 1 + $self bind <Alt-Key> altkey %x %y %K %A 0 + $self bind <Alt-Shift-Key> altkey %x %y %K %A 1 + switch $::OS { + unix { + $self bind <Mod1-Key> altkey %x %y %K %A 0 + $self bind <Mod1-Shift-Key> altkey %x %y %K %A 1 + $self bind <Mod4-Key> altkey %x %y %K %A 0 + $self bind <Mod4-Shift-Key> altkey %x %y %K %A 1 + } + osx { + $self bind <Mod1-Key> ctrlkey %x %y %K %A 0 + $self bind <Mod1-Shift-Key> ctrlkey %x %y %K %A 1 + } + } + $self bind <Key> key_wrap %x %y %K %A 0 + $self bind <Shift-Key> key_wrap %x %y %K %A 1 + $self bind <KeyRelease> keyup_wrap %x %y %K %A 0 + $self bind <Shift-KeyRelease> keyup_wrap %x %y %K %A 1 + $self bind <Control-KeyRelease> keyup_wrap %x %y %K %A 0 + $self bind <Motion> motion_wrap %x %y 0 + $self bind <Alt-Motion> motion_wrap %x %y 4 + $self bind <Control-Motion> motion_wrap %x %y 9 + #$self bind <Map> map + #$self bind <Unmap> unmap + $self bind <Leave> leave + $self bind <Configure> configure %h %w +} + +def Canvas configure {h w} { + if {[$self look gridstate]} { + $@grid update $h $w + if {[winfo exists .$self.yscroll]} {return}; # scrollbar will update grid already + $@grid draw + } +} + +#def Canvas map {} {} +#def Canvas unmap {} {} +def Canvas leave {} {$@crosshair erase} + +def Canvas scroll {axis diff} { + set c [$self widget] + $c [list $axis]view scroll $diff units + if {[$self look hairstate] && $@editmode} { + # this should be changed so that we don't need to recompute x,y here. + set x [expr [$c canvasx [expr [winfo pointerx $c] - [winfo rootx $c]]]/$@zoom] + set y [expr [$c canvasy [expr [winfo pointery $c] - [winfo rooty $c]]]/$@zoom] + set target [$self identify_target $x $y 0] + $@crosshair data= $x $y $target + $@crosshair draw + } else { + $@crosshair erase + } +} + +# this allows the grid to update when scroll +def Canvas scroll_set {w v1 v2} {if {[$self look gridstate] && $@editmode} {$@grid draw}; $w set $v1 $v2} + +def Canvas reload {} {netsend [list .$self reupload]} +def Canvas redraw {} { + $self changed + foreach x [$@objects values] {$x changed} + foreach x [ $@wires values] {$x changed} +} + +#patch editing commandline shortcuts +def Canvas o {x y {name ""}} { + set c [$self widget] + if {[$self snap_grid]} {set off [expr [$self look grid_size]/2]} {set off 0} + set @curpos [list [expr [$c canvasx $x]+$off] [expr [$c canvasy $y]+$off]] + $self new_object obj $name +} + +def Canvas c {from outlet to inlet} { + set out_objs [$self parse_idx $from] + set in_objs [$self parse_idx $to] + foreach out $out_objs { + foreach in $in_objs { + $self connect [list $out $outlet $in $inlet] + } + } +} + +def Canvas pc {from outlet to inlet} { + set out_objs [$self parse_idx $from] + set in_objs [$self parse_idx $to] + if {[llength $out_objs] != [llength $in_objs]} {return "No can do :("} + for {set i 0} {$i < [llength $out_objs]} {incr i} { + $self connect [list [lindex $out_objs $i] $outlet [lindex $in_objs $i] $inlet] + } +} + +def Canvas s {selection} { + set objs [$self parse_idx $selection] + foreach obj $objs {$self selection+= [$@objects get $obj]} +} + +def Canvas s+ {selection} { + set objs [$self parse_idx $selection]; set ids {} + foreach obj $objs { + set v [$@objects get $obj]; lappend ids $v + $self selection+= $v + } + $self selection_wire= [$self implicit_wires $ids] +} + +def Canvas sw {from outlet to inlet} { + set out_objs [$self parse_idx $from] + set in_objs [$self parse_idx $to] + foreach out $out_objs { + foreach in $in_objs { + set id [$self wire_idx [list $out $outlet $in $inlet]] + if {$id>=0} {$self selection_wire+= [$@wires get $id]} + } + } +} + +def Canvas parse_idx {val} { + set objs {} + foreach obj [split $val ","] { + if {[regexp {\d+-\d+} $obj range]} { + set l [split $range "-"] + set objs [concat $objs [lmake [lindex $l 0] [lindex $l 1]]] + continue + } + lappend objs $obj + } + return $objs +} + +def Canvas xy_snap {x y} { + if {[$self look snap_grid]} { + set grid [$self look grid_size] + set x [expr floor($x/$grid)*$grid] + set y [expr floor($y/$grid)*$grid] + } + return [list $x $y] + +} + +def Canvas new_object {sel args} { + $self editmode= 1 + $self deselect_all + mset {x y} $@curpos + if {[$self look snap_grid]} { + set grid [$self look grid_size] + set x [expr floor($x/$grid)*$grid] + set y [expr floor($y/$grid)*$grid] + } + switch -- $sel { + obj { set goto [list $self new_object_edit]} + msg { set goto [list $self new_object_edit]} + text { set goto [list $self new_object_edit]} + default {set goto [list $self new_object_callback]} + } + netsend [concat [list .$self $sel $x $y] $args] $goto +} + +def Canvas new_wire_callback {wire} {} + +def Canvas new_object_callback {obj} { + $self add_to_obj_history $obj + set @keynav_last_obj $obj + $self selection+= $obj + if {$@keynav} {$self update_Active $obj} +} + +def Canvas new_object_copyselect {obj} { + $self selection+= $obj + #set @action "move" + #$self click_on_object $obj 0 +} + +def Canvas new_wire_select {wire} {$self selection_wire+= $wire} + +def Canvas new_object_edit {obj} { + if {[$obj class] == "NumBox"} {return} + $obj edit +} + +def Canvas add_to_obj_history {obj} { + if {![[$obj class] <= ObjectBox]} {return} + obj_hist prepend [$obj text] +} + + +def Canvas insertxy {} {return [list $@insert_x $@insert_y]} + +def Canvas insert_object {} {$self do_insert_obj "none" "none"} + +def Canvas get_canvas {} { + if {![info exists @gop]} { + if {[info exists @subpatch]} {if {$@subpatch} {return [$self canvas]}} + if {[info exists @abs]} {if {$@abs} {return [$self canvas]}} + if {[winfo exists .$self.c]} {return $self} + } + #if {[info exists @subpatch]} {if {$@subpatch} {return [$self canvas]}} + return [super] +} + +def Canvas get_topcanvas {} { + set canvas $@canvas + if {$@canvas == ""} {return $self} + while {[$canvas canvas] != ""} {set canvas [$canvas canvas]} + return $canvas +} + +def Canvas do_insert_obj {x y} { + if {$x == "none" && $y == "none"} { + if {[$@wiresel size] != 1} {return} + set wire [$@wiresel values] + puts "insert object for wire $wire" + mset {from outlet to inlet} [$wire report] + set c [$self widget] + set iowidth [$self look iowidth] + mset {ox1 oy1 ox2 oy2} [lmap / [$c bbox ${from}o${outlet}] [$self zoom]] + mset {ix1 iy1 ix2 iy2} [lmap / [$c bbox ${to}i${inlet} ] [$self zoom]] + set x1 [expr $ox1 + $iowidth/2]; set y1 [expr ($oy1+$oy2)/2] + set x2 [expr $ix1 + $iowidth/2]; set y2 [expr ($iy1+$iy2)/2] + set x [expr $x1 + ($x2-$x1)/2] + set y [expr $y1 + ($y2-$y1)/2] + } + netsend [list .$self obj $x $y] [list $self new_object_edit] + set @action insert +} + +def Canvas new_object_insert_wire {obj} { + set wire [$self selection_wire] + $self selection_wire-= $wire + mset {from outlet to inlet} [$wire report] + $self disconnect [$wire connects] + set @keynav 0; $@active hide; set @keynav_tab_sel "object" + set from_idx [$@objects search $from] + set to_idx [$@objects search $to] + set obj3_idx [$@objects search $obj] + $self connect [list $from_idx $outlet $obj3_idx 0] [list $self keynav_current=] + $self connect [list $obj3_idx 0 $to_idx $inlet] + $self action= none +} + +def Canvas chain_object {} { + if {[$@objectsel size] != 1} {return} + set o [$@objectsel values] + mset {x1 y1 x2 y2} [$o bbox] + set grid [$self look grid_size] + if {[$self look snap_grid]} {set y [expr floor(($y2+$grid)/$grid)*$grid]} {set y [expr $y2+10]} + netsend [list .$self obj $x1 $y] [list $self new_object_edit] + set @action chain_obj +} +def Canvas new_object_chain_wire {obj} { + obj_hist prepend [$obj text] + set from_idx [$@objects search [lindex [$self selection] 0]] + $self deselect_all + set to_idx [$@objects search $obj] + $self connect [list $from_idx 0 $to_idx 0] + $self action= none + $self selection= $obj +} + +def Canvas objects {} {return $@objects} +#def Canvas wires {} {return $@wires} +def Canvas selection {} {$@objectsel values} +def Canvas selection= {objs} {$@objectsel clear; $self selection+= $objs} +def Canvas selection+= {objs} {foreach obj $objs {$@objectsel set [$obj index] $obj}} +def Canvas selection-= {objs} {foreach obj $objs {set k [$obj index]; if {[$@objectsel exists $k]} {$@objectsel unset $k}}} +def Canvas selection_wire {} {$@wiresel values} +def Canvas selection_wire= {objs} {$@wiresel clear; $self selection_wire+= $objs} +def Canvas selection_wire+= {objs} {foreach obj $objs {$@wiresel set [$obj index] $obj}} +def Canvas selection_wire-= {objs} {foreach obj $objs {set k [$obj index]; if {[ $@wiresel exists $k]} { $@wiresel unset $k}}} + +def Canvas Object {} {$self new_object obj} +def Canvas Message {} {$self new_object msg} +def Canvas Number {} {$self new_object floatatom} +def Canvas Symbol {} {$self new_object symbolatom} +def Canvas Comment {} {$self new_object text} + +#!@#$ these 9 should be harmonised with the class list instead of having special names +def Canvas bng {} {$self new_object obj bng} +def Canvas tgl {} {$self new_object obj tgl} +def Canvas nbx {} {$self new_object obj nbx} +def Canvas vsl {} {$self new_object obj vsl} +def Canvas hsl {} {$self new_object obj hsl} +def Canvas vradio {} {$self new_object obj vradio} +def Canvas hradio {} {$self new_object obj hradio} +def Canvas vu {} {$self new_object obj vu} +def Canvas dropper {} {$self new_object obj dropper} +def Canvas cnv {} {$self new_object obj cnv} + +def Canvas Graph {} {$self editmode= 1; netsend [list .x$self graph ]} +def Canvas Array {} {$self editmode= 1; netsend [list .x$self menuarray]} + +def Canvas init_menus {} { + set name .$self + set m $name.m + menu $m + #removed Paths after send_message + foreach x {file edit find view put window help} {menu $m.$x -tearoff $::pd_tearoff} + $self populate_menu file {new_file open_file {} send_message {} close save save_as print {} abort_server quit} + $self populate_menu edit {undo redo {} cut copy paste duplicate select_all subpatcherize {} tidy_up {}} + $self populate_menu find {find find_again find_last_error} + $m.view add checkbutton -label [say visual_diff] -selectcolor grey0 -command [list $self visual_diff] \ + -accelerator [accel_munge "Ctrl+e"] -indicatoron 1 + $self populate_menu view {get_elapsed {} reload redraw} + $self populate_menu put {Object Message Number Symbol Comment {} bng tgl nbx vsl hsl vradio hradio vu dropper cnv {} Graph Array} + $self populate_menu window {{}} + $m.edit add checkbutton -label [say edit_mode] -selectcolor grey0 -command [list $self editmodeswitch] \ + -accelerator [accel_munge "Ctrl+e"] -indicatoron 1 + $m.edit configure -postcommand "$self fix_edit_menu" + $m.window configure -postcommand "$self fix_window_menu" + foreach x {file edit view find put window help} { + if {$x=="help"} { + # help menu is in the wrong place in patch windows because it's taken from .mbar ??? + $m add cascade -label [say $x] -menu .mbar.$x + } { + $m add cascade -label [say $x] -menu $m.$x + } + } +} + +# corrects edit menu, enabling or disabling undo/redo +# LATER also cut/copy/paste +def Canvas fix_edit_menu {} { + set e .$self.m.edit + switch $::OS {osx {set i 0} default {set i 1}} + set t [say undo] + if {[$@history can_undo?]} { + $e entryconfigure $i -state normal -label "$t [$@history next_undo_name]" + } else { + $e entryconfigure $i -state disabled -label "[say cannot] $t" + } + incr i + set t [say redo] + if {[$@history can_redo?]} { + $e entryconfigure $i -state normal -label "$t [$@history next_redo_name]" + } else { + $e entryconfigure $i -state disabled -label "[say cannot] $t" + } +} + +def Menuable fix_window_menu {} { + set menu $@menubar.window + $menu delete 0 end + foreach w $::window_list {$menu add command -label [wm title [$w window]] -command "$w raise"} +# {"parentwindow" {menu_windowparent} ""} +} + +#-----------------------------------------------------------------------------------# +#this just tells whether an object is part of the selection, that is, what usually makes objects turn blue. +def Canvas selection_include? {member} {expr [$@objectsel search $member]>=0} + +def Canvas type {} { + if {$@subpatch && !$@gop} {return "sub"} + if {$@subpatch && $@gop} {return "gopsub"} + if {$@abs && !$@gop} {return "abs"} + if {$@abs && $@gop} {return "gopabs"} + if {!$@subpatch && !$@abs && !$@gop} {return "toplevel"} +} + +def Canvas draw {} { + if {$@gop} { + if {[$self gop_check]} {$self all_changed} + #if the focus is not in the opened gop + if {[regexp {^.x[0-9a-f]{6,8}.c} [focus] f]} { + if {$@canvas != ""&& $f != [$self widget]} {super;return} + } elseif {$@canvas != "" && [focus] != [$self widget]} {super;return} + } elseif {$@subpatch || $@abs} {super} + if {!$@mapped} {return} else {if {![winfo exists [$self widget]]} {return}} + $self check_findbar + if {$@editmode} {set bg [$self look bgedit]} else {set bg [$self look bgrun]} + [$self widget] configure -background $bg + $self adjust_scrollbars + if {$@gop} {$self gop_rect} +} + +def Canvas popup_properties {} {CanvasPropertiesDialog new $self} + +def Canvas gop_target {id} { + while {[$id canvas] != $self} {set id [$id canvas]}; return $id +} + +#-----------------------------------------------------------------------------------# +class_new Macro_Rect {View} + +def Macro_Rect init {} {super; $self data= 0 0 0 0 blue} + +def Macro_Rect data= {x1 y1 x2 y2 col} { + set @x1 $x1; set @y1 $y1 + set @x2 $x2; set @y2 $y2 + set @col $col +} + +def Macro_Rect flash {x1 y1 x2 y2 col} { + $self data= $x1 $y1 $x2 $y2 $col + $self draw + after 500 $self erase +} + +def Macro_Rect draw {} { + set @canvas [string range [focus] 1 [string first . [focus] 1]-1] + $self item MACRO rect [list $@x1 $@y1 $@x2 $@y2] -outline $@col +} + +class_new Macro {EventHistory} +def Macro init {} { + $::event_history subscribe $self + set @idx 0 + set @state 0 + set @list {} + set @ref_list {} + set @delay 200 + set @offset_x 0 + set @offset_y 0 + set @rect [Macro_Rect new] +} +def Macro state= {val} { + set @state $val + if {$val} { + set @ref_list {} + set @list {} + post %s "start recording macro..." + } else { + post %s "end..." + } +} +def Macro state {} {return $@state} +def Macro dump {} {set i 0; foreach step $@list {post %s "step $i -> $step"; incr i}} +def Macro idx= {val} {set @idx $val} +def Macro idx {} {return $@idx} +def Macro delay {} {return $@delay} +def Macro append_ref {mess} {lappend @ref_list $mess} +def Macro ref_list {} {puts "$@ref_list";return $@ref_list} +def Canvas ref_list {} {$::macro ref_list} + + +def Macro notice {args} { + if {$@state} { + set mess [lindex $args 2] + switch [lindex $args 1] { + add {$self add $mess} + default {} + } + } +} + +def Macro add {mess} { + mset {event widget x y mode k kval} $mess + if {[regexp {^Control_} $k]} {return} + if {[regexp {^Alt_} $k]} {return} + if {[regexp {^Shift_} $k]} {return} + if {$event == "KeyRelease"} {return} + if {$event == "ButtonRelease"} {lappend @list [lreplace $mess 0 0 "Motion"]} + if {$event == "KeyPress"} {lappend @list [lreplace $mess 0 0 "Motion"]} + if {$event == "ButtonPress"} {lappend @list [lreplace $mess 0 0 "Motion"]} + lappend @list $mess +} + +def Macro test_playable {x y} { + mset {macro_width macro_height} [$self calc_size] + set c [string range [focus] 0 [string first . [focus] 1]+1] + set cwidth [winfo width $c]; set cheight [winfo height $c] + set cx1 [$c canvasx 0]; set cy1 [$c canvasy 0] + set cx2 [$c canvasx $cwidth]; set cy2 [$c canvasy $cheight] + set new_x [expr $x+$macro_width]; set new_y [expr $y+$macro_height] + $@rect flash $x $y $new_x $new_y blue + if {$new_x > $cx2 || $new_x < $cx1 || $new_y > $cy2 || $new_y < $cy1} { + puts "Can't playback Macro:: outside of canvas area" + return + } else {$self idx= 0; $self offset $x $y; $self play [$self delay]} + +} + +def Macro offset {new_x new_y} { + mset {event widget x y mode k kval} [lindex $@list 0] + set @offset_x [expr $new_x-$x] + set @offset_y [expr $new_y-$y] +} + +def Macro play {delay} { + if {$@idx == [llength $@list]} {return} + #$self test_canvas_size + set i 0 + set focus [string range [focus] 1 [string first . [focus] 2]-1] + set step [lindex $@list $@idx] + #puts "\t $step" + mset {event widget x y mode k kval} $step + switch $event { + #KeyRelease {set name [modekey $k $mode]} + KeyPress {set name [modekey $k $mode]} + ButtonPress {set name $event-$k} + ButtonRelease {set name $event-$k} + Motion {set name $event} + default {puts "Error: event $event should not have been here.."} + } + if {$@idx < [llength $@list]} { + #after $delay [list $self run $event $name $k [expr $X+$@offset_x] [expr $Y+$@offset_y] \ + # [expr $x+$@offset_x] [expr $y+$@offset_y]] + after $delay [list $self run $event $name $k $x $y] + } +} + +#this don't make sense when multiple window, and should be re implemeneted somehow +def Macro calc_size {} { + set x1 66666; set x2 0 + set y1 66666; set y2 0 + foreach step $@list { + mset {event widget x y mode k kval} $step + set x1 [min $X $x1]; set x2 [max $x2 $X] + set y1 [min $Y $y1]; set y2 [max $y2 $Y] + + } + return [list [expr $x2-$x1] [expr $y2-$y1]] +} + +def Macro test_canvas_size {} { + set c [string range [focus] 0 [string first . [focus] 1]+1] + set cwidth [winfo width $c]; set cheight [winfo height $c] + set cx1 [$c canvasx 0]; set cy1 [$c canvasy 0] + set cx2 [$c canvasx $cwidth]; set cy2 [$c canvasy $cheight] + mset {x1 y1 x2 y2} [$self calc_size] +} + +def Macro run {event name k x y} { + set w [focus] + incr @idx + event generate $w <$name> -x $x -y $y + if {$event=="KeyPress"} {event generate $w <KeyRelease-$k> -x $x -y $y} + $self play $@delay +} + +def Macro copy {} { + clipboard clear + selection clear + set i 0 + foreach step $@list { + if {$i == [expr [llength $@list]-1]} {set comma ""} else {set comma ","} + mset {event widget x y mode k kval} $step + set mess [list $event $x $y $mode $k] + set t ${mess}$comma + switch $event { + KeyPress {clipboard append [lreplace $t 0 0 "key"]} + ButtonPress {clipboard append [lreplace $t 0 0 "click"]} + ButtonRelease {clipboard append [lreplace $t 0 0 "unclick"]} + } + incr i + } + selection handle -selection PRIMARY [focus] "$self getdata" + selection own -command lost -selection PRIMARY +} + +def Macro getdata {offset maxchar} { + puts "clipboard ::: [clipboard get]" + return [string range [format %s [clipboard get]] $offset [expr {$offset+$maxChars}]] +} + +def Macro reset {} {set @idx 0} + +set ::macro [Macro new] +set ::macro_state 0 + +def Canvas macro_toggle {} {if {![$::macro state]} {set ::macro_state 1} {$::macro state= 0; $::macro dump}} +#def Canvas macro_play {} {puts ">> $@click_at <<"; $::macro idx= 0; $::macro play [$::macro delay]} +def Canvas macro_play {} { + mset {x y} $@click_at + $::macro reset + $::macro play [$::macro delay] + #$::macro test_playable $x $y +} +def Canvas keyevent {} { + focus [$self widget] + event generate [$self widget] <Control-Key-1> +} + +def Canvas macro_copy {} {$::macro copy} + +#-----------------------------------------------------------------------------------# +class_new TextBox {Box} + +def TextBox init {mess} { + super $mess + set @edit 0 + set @x1 [lindex $mess 2] + set @y1 [lindex $mess 3] + set @text [$self remove_braces [join [lrange $mess 4 end]]] + set @multi 0 + set @max_width 40 + # @textoffset is for offseting the text item/widget, ie, ObjectBox vs NumBox + switch [$self class] { + NumBox {set @textoffset [list 10 2]} + default {set @textoffset [list 2 2]} + } +} + +def TextBox text= {text} {set @text [$self remove_braces [join $text]]} +def TextBox text {} {return $@text} +def TextBox filter_text {{for_edit 0}} {return $@text} + +def TextBox draw {} { + if {[$self class] == "Canvas"} {if {$@text == "graph"} {$self update_size; super; return}} + # "TEXT" is the text label while "text" is the the input text field tk widget. + # the text should be drawn before, so that update_size works at the right time. + mset {x1 y1} [$self xy] + set z [$@canvas zoom] + if {$@edit} { + $self draw_edit + } else { + set fw [font measure [$self look font] 0] + set text [$self filter_text] + if {$::leet} {set text [string map -nocase {a 4 e 3 t 7 s 5 i 1 o 0 g 9} $text]} + $self item TEXT text [l+ $@textoffset [$self xy]] \ + -font [$self look font] -text $text \ + -fill [$self look fg] -anchor nw -width [expr ($fw*$@max_width)-1] + # set width with -1 because text item seem to be inclusive at wrap point + # where as the text widget is exclusive + } + $self update_size + super +} + +def TextBox edit {} { + if {$@edit} {return}; set @edit 1; $self changed edit + if {[[$self class] <= AtomBox]} {set @clear 1} +} + +def TextBox new_bind {} { + set t [$self cwidget].${self}text + bind $t <Key> "$self key_input %W %x %y %K %A 0" + bind $t <Control-Return> "$self key_input %W %x %y 10 %A 0" + bind $t <Control-v> "$self paste_resize" + bind $t <Return> "$self unedit" + bind $t <Escape> "$self unedit 0" + bind $t <Up> "$self scroll_history +1" + bind $t <Down> "$self scroll_history -1" + bind $t <Control-p> "$self scroll_history +1" + bind $t <Control-n> "$self scroll_history -1" + bind $t <Alt-BackSpace> "$self clear" +} + +def TextBox draw_edit {} { + set c [$self cwidget] + switch $::tcl_platform(os) { + Linux {$c configure -cursor crosshair} + #need to find equivalent names for other os + } + if {[lsearch [$@canvas selection] $self] < 0} {$@canvas selection+= $self} + set t $c.${self}text + if {[winfo exists $t]} {return} + set @edit 1 + set @tab_repeats 0 + obj_hist histi= 0 + set @selected? 1 + set z [$@canvas zoom] + set font_height [font metrics [$self look font] -linespace] + if {[$c bbox ${self}TEXT] != ""} { + mset {ix1 iy1 ix2 iy2} [lmap / [$c bbox ${self}TEXT] $z] + if {($iy2-$iy1)/$z > $font_height} {set @multi 1} + } else { + set ix1 0; set iy1 0 + set ix2 [font measure [$self look font] 0] + set iy2 [font metrics [$self look font] -linespace] + } + $c delete ${self}TEXT + set font_str [$self look font] + set new_size [format %.0f [expr [lindex $font_str 1]*$z]] + set font_str [lreplace $font_str 1 1 $new_size] + foreach char [split $@text ""] {lappend l [scan $char %c]} + mset {width height} [$self get_size [expr $ix2-$ix1] [expr $iy2-$iy1]] + set insertbg [$self look fg]; set fg [$self look fg] + if {[[$self class] <= AtomBox]} {set fg "red"; set insertbg [$self look bgedit]} + text $t -width $width -height $height -relief flat -bg [$self look bgedit] -borderwidth 0 \ + -highlightthickness 0 -font $font_str -fg $fg -insertbackground $insertbg -wrap word + $self new_bind + $@canvas focus= $self + $self item text window [l+ [lmap / $@textoffset $z] [$self xy]] -window $t -anchor nw -tags "${self}text $self text" + $t insert 1.0 $@text + $t configure -pady 0 -padx 0 + $self resize + focus $t +} + +def TextBox resize {} { + if {[[$self class] <= AtomBox]} {return} + set c [$self cwidget] + set t $c.${self}text + #set z [$@canvas zoom] + set pix_height [$t count -update -ypixels 1.0 end] + set pix_width [font measure [$self look font] [$t get 1.0 end]] + mset {width height} [$self get_size $pix_width $pix_height] + $t configure -width [min $width $@max_width] -height $height -wrap word +} + +def TextBox get_size {w h} { + set c [$self cwidget] + set t $c.${self}text + set pix_height $h + set pix_width $w + set char_width [font measure [$self look font] 0] + set line_height [font metrics [$self look font] -linespace] + set round_chars [expr int(ceil($pix_width/$char_width.0))] + if {$round_chars < $@max_width && !$@multi} { + set round_lines 1 + } else { + set @multi 1 + set round_chars $@max_width + set round_lines [expr int(ceil($pix_height/$line_height))] + } + return [list $round_chars $round_lines] +} + +def TextBox key_input {widget x y key iso shift} { + after 0 "$self after_key $widget" + set c [$@canvas widget] + set t $c.${self}text + if {[[$self class] <= AtomBox]} {if {$@clear} {$t delete 1.0 1.end; set @clear 0}} + switch -- $key { + Tab { + if {[$self class] == "ObjectBox"} { + $self propose_completions; $widget configure -state disabled + } + } + 10 {$t configure -height [expr [lindex [$t configure -height] 4] + 1]} + } +} + +def TextBox after_key {widget} { + $widget configure -state normal; # for in case there is completion box + $self resize + $self changed +} + +def TextBox paste_resize {} { + if {[[$self class] <= AtomBox]} {return} + set c [$self cwidget] + set t $c.${self}text + set fixed [font metrics [$self look font] -fixed] + set text [clipboard get] + if {$fixed} { + set width [string length $text] + } else { + set textpix [font measure [$self look font] $text] + set fwidth [font measure [$self look font] 0] + set width [expr (($textpix+$fwidth-1)/$fwidth)+1] + } + set maxwidth 40 + mset {y1 y2} [$t yview] + if {$width < $maxwidth} {set height 1} {set height [expr ceil($width/$maxwidth.0)]} + $t configure -width [min $width $maxwidth] -height $height -wrap word + after 0 [list $t mark set insert 1.end] + $t mark set insert 1.0 + $self update_size + $self changed +} + +def TextBox scroll_history {incr} { + if {[[$self class] <= MessageBox]} {return} + set c [$self cwidget] + set t $c.${self}text + if {![obj_hist histi]} {obj_hist set_hist 0 [$t get 1.0 1.end]} + $t delete 1.0 1.end + set text [obj_hist traverse $incr] + $t insert 1.0 $text + $t configure -width [string length $text] + $self update_size + after 0 $self changed + +} + +def TextBox clear {} { + set c [$self cwidget] + set t $c.${self}text + $t delete 1.0 1.end + +} + +def TextBox text {} {return $@text} + +def TextBox update_size {} { + if {[info exists @gop]} {if {$@gop} {mset [list @xs @ys] $@pixsize; return}} + if {$@canvas == ""} {puts "update_size: this textbox has no canvas, try again later"; return} + set c [$self cwidget] + set t_widget $c.${self}text + set t_item $c.${self}TEXT + set w2 0; set h2 0 + set xpad 2; set ypad 3 + set z [$@canvas zoom] + if {[winfo exists $t_widget]} { + set textwidth [expr ([winfo reqwidth $t_widget]+$xpad)/$z] + set height [expr ([winfo reqheight $t_widget]+$ypad)/$z] + } else { + mset {x1 y1 x2 y2} [[[$self canvas] widget] bbox ${self}TEXT] + set textwidth [expr ($x2-$x1+$xpad)/$z] + set height [expr ($y2-$y1+$ypad)/$z] + } + set iowidth [$self look iowidth] + set topwidth [expr {(2* $@ninlets-1)*$iowidth}] + set bottomwidth [expr {(2*$@noutlets-1)*$iowidth}] + set @xs [max [$self look minobjwidth] [max $bottomwidth [max $topwidth $textwidth]]] + set @ys $height +} + +#----------------------------------------------------------------------------------- + +class_new ObjectBox {TextBox} + +def ObjectBox init {mess} { + super $mess + set @valid 0 ;# only ObjectBox needs a @valid. (removed all others) + set @ninlets 0 + set @noutlets 0 + set @pdclass "" +} + +def ObjectBox valid= {v} {set @valid $v} +def ObjectBox valid {} {return $@valid} +def Canvas objectsel {} {return $@objectsel} +def Canvas wiresel {} {return $@wiresel} + +def ObjectBox draw_box {} { + super + set xya [$self bbox] + mset {x1 y1 x2 y2} $xya + #set xyb [l+ [list $x2 $y1 $x1 $y1 $x1 $y2] [list -1 +1 +1 +1 +1 -1]] + #set xyc [l+ [list $x2 $y1 $x2 $y2 $x1 $y2] [list -1 +1 -1 -1 +1 -1]] + if {[$self selected?]} {set fg [$self look selectframe]} {set fg [$self look frame3]} + if {$@valid} { + $self item BASE rectangle $xya -fill [$self look bg] -outline $fg -width 1 + } else { + $self item BASE rectangle $xya -fill [$self look bg] -outline $fg -width 1 -dash {3 3} + } + #$self item BASE1 line $xyb -fill [$self look frame1] -width 1 + #$self item BASE2 line $xyc -fill [$self look frame2] -width 1 + #[$@canvas widget] lower ${self}BASE ${self}TEXT + [[$self get_canvas] widget] lower ${self}BASE ${self}TEXT + #[[$self get_canvas] widget] lower ${self}BASE +} + +def ObjectBox draw {} {super; $self draw_io} + +# this is called from the GUI; text= is reserved for server. +def TextBox setto {text} { + [$@canvas history] add [list $self setto $@text] + [$@canvas widget] configure -cursor {} + set @text $text + set l {} + foreach char [split $@text ""] {lappend l [scan $char %c]} + $@canvas selection-= [list $self] + switch [$@canvas action] { + insert {set goto [list $@canvas new_object_insert_wire]} + chain_obj {set goto [list $@canvas new_object_chain_wire]} + subpatcherize {set goto [list $@canvas new_object_subpatcherize_redraw]} + default {set goto [list $@canvas new_object_callback]} + } + netsend [concat [list .$@canvas text_setto $self] $l] $goto +} + +def TextBox unedit {{accept 1}} { + if {!$@edit} {return} + set @edit 0 + $self changed edit + set c [[$self get_canvas] widget] + set t $c.${self}text + if {$accept} {$self setto [$t get 1.0 "end - 1 chars"]} + after 1 "destroy $t" + if {[winfo exists .completion]} {$@action cancel} + focus $c + $@canvas focus= "" +} + +#-----------------------------------------------------------------------------------# +def Canvas name {} {return $@name} +def Canvas folder {} {return $@folder} +def Canvas name= {name} {if {!$@mapped} {return}; set @name $name ; $self update_title} +def Canvas folder= {folder} {if {!$@mapped} {return}; set @folder $folder; $self update_title} + +def Canvas make_title {} { + if {!$@mapped} {return} + if {$@subpatch} { + if {$@canvas == "" || 0==[string compare $@canvas $self]} { + set t "(uh)" + } else { + set t [$@canvas make_title] + } + set t "subpatch '$@name' of $t" + } else { + #set t "$@name in $@folder" + set t "$@name" + } + if {[$self modified?]} {append t "(*)"} + return $t +} + +def Canvas update_title {} { + if {[winfo exists .$self]} {wm title .$self [$self make_title]} +} + +# UNIMPLEMENTED: this should indicate whether the patch in pd is different from the last saved patch +def Canvas modified? {} {return 1} + +def Canvas mapped {} {return $@mapped} +def Canvas mapped= {v} {set @mapped $v} + +def Canvas havewindow= {flag} { + set was [winfo exists .$self] + if {$flag && !$was} {$self init_window; $self redraw} + #if {$flag && $was && [$self gop]} {$self redraw} + if {$flag && $was} {$self raise} + if {!$flag && $was} {$self delete_window} +} + +def Canvas visibles+= {child} { + if {[lsearch $@visibles $child] < 0} {lappend @visibles $child; $self changed visibles} +} +def Canvas visibles-= {child} { + if {[lsearch $@visibles $child] >= 0} {set @visibles [lwithout $@visibles $child]; $self changed visibles} +} + +def Canvas visibles {} {return $@visibles} + +def Canvas all_changed {} { + foreach x $@visibles { + if {[$x class] == "Canvas"} { + if {$@gop} {$x all_changed} + } + $x changed + } +} + +# this is a shim between the client's new-style indices and the server's remaining old-style indices +proc dex {h k} {lsearch [lsort -integer [$h keys]] $k} + +# for undo; calls the server +def Canvas ins {i constructor} { + set parts [pd_mess_split $constructor] + set last [lindex $parts end] + set parts [lrange $parts 0 end-1] + foreach part $parts {netsend $part} + netsend [concat [list .$self object_insert [dex $@objects $i]] $last] ;# bork bork bork + $@history add [list $self del $i] +} + +def Canvas del {i} { + set o [$@objects get $i] + #this keynav should be better sorted out + if {$o == $@keynav_current || $o == $@keynav_last_obj} { + set @keynav_current 0 + set @keynav_last_obj 0 + } + # this "if" might not be necessary... try deconstruct_to for everything. + if {[$o class] != "Canvas"} { + $@history add [list $self ins $i [$o deconstruct]] + } else { + set meuh [Clipboard2 new] + $o deconstruct_to $meuh + $@history add [list $self ins $i [$meuh value]] + $meuh delete + } + netsend [list .$self object_delete $o] +} + +def Canvas wires {} {return $@wires} + +def Canvas delete_selection {} { + if {![$@objectsel size] && ![$@wiresel size]} {return} + #this keynav should be better sorted out + if {$@keynav} { + set @keynav 0 + switch [$@keynav_current class] { + Wire {set @keynav_last_wire 0} + default {set @keynav_last_obj 0} + } + set @keynav_current 0 + $@active hide + } + set del_wire {} + foreach obj [$@objectsel values] { + foreach wire [$obj wires2] { + if {[$@wires search $wire] != -1 && [lsearch $del_wire $wire] < 0} { + $self disconnect [$wire connects] + lappend del_wire $wire + } + } + $self del [$@objects search $obj] + } + foreach x [$@wiresel values] { + if {[$@wires search $x] != -1 && [lsearch $del_wire $x] < 0} { + $self disconnect [$x connects] + } + } + $@objectsel clear + $@wiresel clear +} + +def View position= {xy1} {mset [list @x1 @y1] $xy1; $self changed x1 y1} +def View set_orig_xy {x y} {set @orig_x $x; set @orig_y $y} + +def Canvas motion_wrap {x y f} { + set c [$self widget] + set x [expr [$c canvasx $x]/$@zoom] + set y [expr [$c canvasy $y]/$@zoom] + lappend @motion_queue [list $x $y $f] + #$self motion $x $y $f [$self identify_target $x $y $f] +} + +def Canvas motion_update {} { + if {[llength $@motion_queue]} { + mset {x y f} [lindex $@motion_queue end]; set @motion_queue {} + $self motion $x $y $f [$self identify_target $x $y $f] + } + set @motion_after_id [after 50 "$self motion_update"] +} + +def Canvas click_wrap {x y b f} { + set c [$self widget] + set x [expr [$c canvasx $x]/$@zoom] + set y [expr [$c canvasy $y]/$@zoom] + set f [expr 1<<($b+7)|$f] + $self click $x $y $f [$self identify_target $x $y $f] +} +def Canvas unclick_wrap {x y b f} { + set c [$self widget] + set x [expr [$c canvasx $x]/$@zoom] + set y [expr [$c canvasy $y]/$@zoom] + set f [expr 1<<($b+7)|$f] + $self unclick $x $y $f [$self identify_target $x $y $f] +} +def Canvas key_wrap {x y key iso shift} { + set c [$self widget] + $self key [expr [$c canvasx $x]/$@zoom] [expr [$c canvasy $y]/$@zoom] $key $iso $shift +} +def Canvas keyup_wrap {x y key iso shift} { + set c [$self widget] + $self keyup [expr [$c canvasx $x]/$@zoom] [expr [$c canvasy $y]/$@zoom] $key $iso $shift +} + +proc lsearch_minimum {l} { + set i 0 + set j 0 + set min [lindex $l 0] + foreach o $l { + if {$o < $min} {set i $j; set min $o} + incr j + } + return $i +} + +def Canvas quadrant {du dv array} { + if {$@keynav_current == 0} {set @keynav_current [$@objects get [lindex [$@objects keys] 0]]} + set foo {} + set bar {} + set pos [$@keynav_current xy] + foreach o $array { + mset {x y} [l- $pos [$o xy]] + set u [expr $x+$y] + set v [expr $x-$y] + if {$u*$du>0 && $v*$dv>0} {lappend foo $o; lappend bar [distance $pos [$o xy]]} + } + if {![llength $bar]} {return $@keynav_current} + set best [lindex $foo [lsearch_minimum $bar]] + return $best +} + +def Canvas motion {x y f target} { + #modes_callback $self "motion" $x $y $f $target + set c [$self widget] + $self motion_checkhairtip $target $x $y + eval $@dehighlight + set @dehighlight {} + set oldpos $@curpos + set @curpos [list $x $y] + # detects if the focus is not on the canvas itself in run mode, ie. numbox + if {!$@editmode & [$self focus] != $self & [$self focus] != ""} { + [$self focus] motion $x $y $f $target + } + mset {type id detail} $target + switch $@action { + edit {$self motion_edit $x $y $f} + insert {} + chain_obj {} + imove {$self motion_imove $oldpos $x $y; return} + mouse_copy {$self motion_move $oldpos $x $y; return} + move {$self motion_move $oldpos $x $y; return} + none {} + default {$@action motion $x $y $f $target} + } + if {$@editmode} {$self motion_iohilite2 $x $y $f} + if {$id == ""} {return} +} + +proc remainder {val val2} { + if {[expr {abs($val)}]} {return [expr {(abs($val)%$val2)*($val/abs($val))}]} else {return 0} +} + +def Canvas motion_move {oldpos x y} { + mset {ox oy} $oldpos + if {$@keynav} {$@active draw} + foreach obj [$@objectsel values] { + #if {[[$obj class] <= Box]} { + if {[$self look snap_grid]} { + set grid [$self look grid_size] + set ax [expr {(int($x)/$grid)*$grid}] + set ay [expr {(int($y)/$grid)*$grid}] + set oax [expr {(int($ox)/$grid)*$grid}] + set oay [expr {(int($oy)/$grid)*$grid}] + mset {x1 y1} [$obj xy] + if {![expr {($ax-$oax)%$grid}]} { + set xoff [remainder [expr {int($x1-$ax)}] $grid] + } else {set xoff 0} + if {![expr {($ay-$oay)%$grid}]} { + set yoff [remainder [expr {int($y1-$ay)}] $grid] + } else {set yoff 0} + $obj move [expr ($ax-$oax)-$xoff] [expr ($ay-$oay)-$yoff] + } else { + $obj move [expr {$x-$ox}] [expr {$y-$oy}] + } + #} else { + # puts "Canvas motion warning: trying to move non-Box explicitly" + #} + } +} + +def Canvas motion_imove {oldpos x y} { + mset {ox oy} $oldpos + if {$@keynav} {$@active draw} + if {[$@objectsel size] == 1} { + set obj [$@objectsel values] + set in_objs $obj + set out_objs $obj + } else { + if {![llength $@keynav_iosel_i] || ![llength $@keynav_iosel_o]} { + return + } else { + set obj [lindex $@keynav_iosel_i 0] + set in_objs $@keynav_iosel_i + set out_objs $@keynav_iosel_o + } + } + if {[[$obj class] <= Box]} { + if {[$obj class] == "Canvas"} { + if {[$obj gop]} {[$self widget] raise [list $obj [$obj visibles]]} + } else { + [$self widget] raise $obj + } + mset {type id detail} [$self identify_target $x $y 0] + if {$type == "wire"} { + mset {from outlet to inlet} [$id report] + $self disconnect [$id connects] + set from_idx [$@objects search $from] + set to_idx [$@objects search $to] + foreach obj $in_objs { + set obj3_idx [$@objects search $obj] + if {![llength [$obj ioselect]]} {set port 0} else {set port [lindex [$obj ioselect] 0]} + set w1 [list $from_idx $outlet $obj3_idx $port] + if {[$@wires search $w1]<0} {$self connect $w1} + } + foreach obj $out_objs { + set obj3_idx [$@objects search $obj] + if {![llength [$obj ioselect]]} {set port 0} else {set port [lindex [$obj ioselect] 0]} + set w2 [list $obj3_idx $port $to_idx $inlet] + if {[$@wires search $w2]<0} {$self connect $w2} + } + set @action move + } + foreach obj [$@objectsel values] {$obj move [expr $x-$ox] [expr $y-$oy]} + } else { + puts "Canvas motion warning: trying to move non-Box explicitly" + } +} + +def Canvas motion_edit {x y f} { + if {[distance [list $x $y] $@click_at] > 5} { + foreach obj [$@objectsel values] { + if {[[$obj class] <= Box]} { + $obj backupxy= [$obj xy] + } else { + puts "Canvas motion warning: trying to backup coordinates of non-Box" + } + } + if {$f == 9} {set @action imove} else {set @action move; $self motion_move $@click_at $x $y} + mset {ox oy} $@click_at + } +} + +def Canvas motion_checkhairtip {target x y} { + global tooltip + if {[$self look hairstate] && $@editmode} { + $@crosshair data= $x $y $target + $@crosshair draw + } else { + $@crosshair erase + } + if {[$self look tooltip]} { + if {$tooltip ne "" && ![$tooltip iserror] && [expr [distance [$tooltip curpos] [list $x $y]] > 10]} { + $tooltip delete + set tooltip "" + } + } +} + +def Canvas motion_iohilite2 {x y f} { + set c [$self widget] + set io [$self identify_closestio $x $y $f] + if {$io<0} {set @iohilite [list -1 0 0 0 0]; return} + foreach item {i o} { + set type_idx [string first $item $io] + set type [string index $io $type_idx] + set port [string range $io [expr $type_idx+1] end] + set object [string range $io 0 [expr $type_idx-1]] + if {$type_idx >= 0} {break} + } + mset {iox ioy} [lmap / [rect_centre [$c bbox $io]] $@zoom] + set @iohilite [list $object $iox $ioy $type $port] + $object hilite_io $type $iox $ioy + set @dehighlight [list $c delete ${io}b] +} + +def Canvas iohilite {} {return $@iohilite} + +def Canvas motion_iohilite {target x y} { + set c [$self widget] + mset {type id detail} $target + if {[llength $@keynav_iosel_i] || [llength $@keynav_iosel_o]} {return} + if {$@editmode && [$id canvas] == $self} { + switch $type { + inlet {set io i} + outlet {set io o} + default {return} + } + set port [$id hilite_io $io $x $y] + set @dehighlight [list $c delete ${id}$io${port}b] + } +} + +#-----------------------------------------------------------------------------------# +# returns one of those five things: +# object $id : the body of an object +# inlet $id $inlet : an inlet of an object +# outlet $id $outlet : an outlet of an object +# wire $id : a wire +# label $id : a label +# nothing : nothing +def Canvas identify_target {x y f} { + set c [$self widget] + set cx [expr $x*$@zoom] + set cy [expr $y*$@zoom] + set stack [$c find overlapping [expr $cx-2] [expr $cy-2] [expr $cx+2] [expr $cy+2]] + # reversing the stack is necessary for some things + # not reversing the stack is also necessary for some other things + # we have to figure out something. + set stack [lreverse $stack] + set target "" + foreach tag $stack {set target [$self target $x $y $f $tag]; if {[llength $target] > 1} {break}} + if {[llength $target] > 1} {return $target} {return [list "nothing"]} +} + +def Canvas target {x y f tag} { + set c [$self widget] + set cx [expr $x*$@zoom] + set cy [expr $y*$@zoom] + set tags [$c gettags $tag] + if {[regexp {^[xo][0-9a-f]{6,8}} $tags id]} { + # prior to Aug 15th, Wires had lower priority as all objects together + # now it's same priority, so just stacking order (and it's prolly wrong) + if {[$id classtags] == ""} {return [list "nothing"]} + set class [$id class] + if {[$self == $id]} {continue} + if {[$class <= Wire]} {if {$@action != "imove"} {return [list "wire" $id]}} + if {[$class <= Box]} { + if {$@action == "imove"} { + foreach tag $stack { + set tags2 [$c gettags $tag] + if {[regexp {^[xo][0-9a-f]{6,8}} $tags2 id2]} { + set class [$id2 class] + if {[$class == Wire]} {return [list "wire" $id2]} + } + } + } + mset {x1 y1 x2 y2} [$id bbox] + if {[regexp {^x[0-9a-f]{6,8}LABEL} $tags label]} { + if {$x>$x1 && $x<$x2 && $y>$y1 && $y<$y2} { + return [list "object" $id] + } else { + return [list "label" $id] + } + } + set outs [$id noutlets] + set ins [$id ninlets] + if {$y>=$y2-6 && $outs} { + set val [expr int(($x-$x1)*$outs/($x2-$x1))] + if {$val == $outs} {set val [expr $val-1]} + return [list "outlet" $id $val] + } + if {$y< $y1+2 && $ins} { + set val [expr int(($x-$x1)* $ins/($x2-$x1))] + if {$val == $ins} {set val [expr $val-1]} + return [list "inlet" $id $val] + } + return [list "object" $id] + } + #puts "skipped a $class" + } +} + +def Canvas identify_closestio {x y f} { + set c [$self widget] + set cx [expr {$x*$@zoom}] + set cy [expr {$y*$@zoom}] + set sense [$self pointer_sense] + set stack [$c find overlapping [expr {$cx-$sense}] [expr {$cy-$sense}]\ + [expr {$cx+$sense}] [expr {$cy+$sense}]] + set stack [lreverse $stack] + set ios {} + set objs {} + foreach tag $stack { + set tags [$c gettags $tag] + if {[regexp {^[x][0-9a-f]{6,8}[oi][0-9]{1,3}} $tags io]} { + foreach item {i o} { + set type_idx [string first $item $io] + set object [string range $io 0 [expr $type_idx-1]] + if {$type_idx >= 0} {break} + } + if {[$object canvas] == $self} {lappend ios $io} + } + } + if {![llength $ios]} {return -1} + set mindist 66666 + set idx 0; set i 0 + foreach io $ios { + set point2 [rect_centre [$c bbox $io]] + set point1 [list $x $y] + set dist [distance $point2 $point1] + if {$dist < $mindist} {set mindist $dist; set idx $i} + incr i + } + return [lindex $ios $idx] +} +def Canvas pointer_sense {} {return [$self look pointer_sense]} +def Canvas pointer_sense= {sense} { + set ::look(Canvas:pointer_sense) $sense + mset {x y} $@curpos + $@sense flash $x $y $sense "red" +} + +#-----------------------------------------------------------------------------------# +class_new StatusBar {View} ;# no, using View is wrong here. View is for tk canvas item collections. + +def StatusBar widget {} {return .$@canvas.stat} + +def StatusBar addw {a b text args} { + set f [$self widget] + if {$text!=""} { + eval [concat [list pack [label $f.${a}_l -text $text -font {helvetica -10} -pady 0] -side left] $args] + } + label $f.$a -width $b -font {helvetica -10} -background #cccccc -foreground black -anchor w -pady 0 + pack $f.$a -side left +} + +def StatusBar init {canvas} { + super + set @canvas $canvas + set f [$self widget] + frame $f -border 1 -relief ridge + $self addw px 4 "" + $self addw py 4 "" + $self addw what 28 " " -fill x -expand yes + $self addw action 12 " Action: " + $self addw sel 3 " Sel: " + $self addw focus 10 " Focus: " +} + +def Canvas statusbar_draw {x y} {$@statusbar draw $x $y} +def Canvas action {} {return $@action} +def Canvas action= {action} {set @action $action} +def Canvas zoom {} {return $@zoom} + +def StatusBar draw {x y} { + if {$x == "??"} {return} + set c [$@canvas widget] + set f [$self widget] + set zoom [$@canvas zoom] + set x [expr [$c canvasx $x]/$zoom] + set y [expr [$c canvasy $y]/$zoom] + #set tags [$c gettags [lindex [$c find overlapping [expr $x-2] [expr $y-2] [expr $x+2] [expr $y+2]] end]] + set target [$@canvas identify_target $x $y -1] + mset {type id detail} $target + set t $target + switch -- $type { + object { + if {[info exists _($id:pdclass)]} {set class $_($id:pdclass)} {set class unknown} + append t " \[$class\]" + } + } + set action [$@canvas action] + if {[regexp ^o $action]} {set action [$action class]} + if {[string length [$@canvas focus]]} {set t "focus: [$@canvas focus]"} + $f.px configure -text [format "%4d" [expr round($x)]] + $f.py configure -text [format "%4d" [expr round($y)]] + $f.what configure -text $t + $f.action configure -text $action + $f.sel configure -text [llength [$@canvas selection]] + $f.focus configure -text [$@canvas focus] +} + +#-----------------------------------------------------------------------------------# +class_new FindModel {Thing} + +def FindModel init {canvas} { + set @orig_canvas $canvas + set @find_string "" + set @next_canvases $canvas + set @last_canvas 0 + set @result "" + set @results {} + set @views {} + set @recursive 1 + set @info "" +} + +def FindModel reinit {} { + set @next_canvases $@orig_canvas + set @last_canvas 0 + set @result "" + set @results {} + $self remove_info +} +def FindModel remove_info {} { + foreach view $@views { + set f [$view widget] + destroy $f.info_l; destroy $f.info + } +} +def FindModel delete {} {foreach view $@views {destroy [$view widget]}; super} + +def FindModel find_string= {s} {set @find_string $s} +def FindModel find_string {} {return $@find_string} +def FindModel result= {s} { + $@orig_canvas deselect_all + $@orig_canvas selection= $s + set @result $s +} +def FindModel result {} {return $@result} +def FindModel views+ {view} {lappend @views $view} +def FindModel views {} {return $@views} +def FindModel end? {} {return $@end} +def FindModel end= {v} {set @end 0} + +def FindModel search_recursive {} { + if {![llength $@next_canvases]} {$self end; return} + if {[llength $@results]} {$self result= [lindex $@results 0]; set @results [lreplace $@results 0 0];return} + while {$@last_canvas < [llength $@next_canvases]} { + set canvas [lindex $@next_canvases $@last_canvas] + if {[$self cache_results $canvas]} { + if {![winfo exists [$canvas widget]]} {$canvas popup_open} else {focus [$canvas widget]} + $self result= [lindex $@results 0] + set @results [lreplace $@results 0 0] + incr @last_canvas + return + } else { + incr @last_canvas + $self search_recursive + return + } + } + set old_canvases $@next_canvases + set @next_canvases {} + if {$@recursive} { + foreach canvas $old_canvases {foreach x [$canvas get_childcanvas] {lappend @next_canvases $x}} + } else { + set @next_canvases {} + } + set @last_canvas 0 + $self search_recursive +} + +def FindModel end {} { + $self reinit + $@orig_canvas deselect_all + set @info " \"$@find_string\" not found" + foreach view $@views { + $view addw label "info" "info" + } +} + +def FindModel cache_results {canvas} { + set @results {} + foreach child [$@objects values] { + if {[string first $@find_string [$child text] 0] >= 0} { + lappend @results $child + } + } + if {[llength $@results]} {return 1} else {return 0} +} + +class_new FindView {FindModel} ;# no, using View is wrong here. View is for tk canvas item collections. +def FindView widget {} {return .$@canvas.find} +def FindView init {canvas} { + findmodel views+ $self + set @canvas $canvas + set @break 0 + set f [$self widget] + frame $f -border 1 -relief ridge + $self addw button "close" "" + $self addw text "find" "find" + $self addw checkbutton "recursive" "recursive" + if {[winfo exists .$@canvas.yscroll]} {set w .$@canvas.yscroll} else {set w .$@canvas.c} + pack $f -side bottom -fill x -before $w + set string [findmodel find_string] + if {$string != ""} {$f.find insert 0 $string} +} + +def FindView addw {type name label} { + set f [$self widget] + if {$label!=""} { + eval [concat [list pack [label $f.${label}_l -text ${label}: -font {helvetica -10} -pady 0] -side left]] + } + switch $type { + text { + entry $f.$name -width 10 -relief flat -bg white -borderwidth 0 -highlightthickness 0 + bind $f.$name <Escape> "findmodel delete" + bind $f.$name <Return> "$self find" + } + checkbutton { + checkbutton $f.$name + if {$name == "recursive"} {$f.$name configure -variable _(findmodel:recursive)} + } + button { + button $f.$name -border 1 -command "findmodel delete" -image icon_close -width 9 -height 9 + if {$name == "close"} {bind $f.$name <Return> "findmodel delete"} + } + label {label $f.$name -textvariable _(findmodel:info) -font {helvetica -10} -pady 0} + } + pack $f.$name -side left + bind $f.$name <Tab> "$self traversal %K %W forward" +} + +def FindView find {} { + set f [$self widget] + findmodel find_string= [$f.find get] + findmodel search_recursive + focus .$@canvas.c +} + +def FindView traversal {k w direction} { + set f [$self widget] + if {$w == "$f.recursive"} {set next $f.close} else {set next [tk_focusNext $w]} + focus $next +} + +def Canvas check_findbar {} { + if {[info exists ::_(findmodel:_class)] && ![winfo exists .$self.find.find]} {FindView new $self} +} + +def Canvas get_childcanvas {} { + set canvases {} + foreach child [$@objects values] {if {[$child class] == "Canvas"} {lappend canvases $child}} + return $canvases +} + +def Canvas runcommand {} {$@runcommand pack_prompt} + +class_new Runcommand {Listener} +def Runcommand canvas {} {return $@canvas} + +def Runcommand init {serf name command} { + set @history [History new 20] + set @serf ${serf}.run + set @command $command + set @expanded 0 + set @canvas [string trimleft $serf "."] + set @defs {} + set @completions {} + set @comp_i 0 + set @comp_s "666" + set @show_id 0 + $self defs + set f $@serf + frame $f -border 1 -relief ridge + button $f.close -border 1 -command "$self unpack_prompt" -image icon_close -width 9 -height 9 + bind $f.close <Return> "$self unpack_prompt" + pack $f.close -side left + bind $f.close <Tab> "$self traversal %K %W forward" + label $f.cmd_l -text Command: -font {helvetica -10} -pady 0 + pack $f.cmd_l -side left + entry $f.entry -width 30 -relief flat -bg white -borderwidth 0 -highlightthickness 0 + bind $f.entry <Escape> "$self unpack_prompt" + bind $f.entry <Control-g> "$self unpack_prompt" + bind $f.entry <Return> "$self eval" + bind $f.entry <Up> "$self scroll_history +1" + bind $f.entry <Down> "$self scroll_history -1" + bind $f.entry <Control-p> "$self scroll_history +1" + bind $f.entry <Control-n> "$self scroll_history -1" + bind $f.entry <Tab> "$self completion +" + switch $::tcl_platform(os) { + Linux {bind $f.entry <ISO_Left_Tab> "$self completion -"} + default {bind $f.entry <Shift-Tab> "$self completion -"} + } + pack $f.entry -side left -fill x -expand yes + bind $f.entry <Control-Tab> "$self traversal %K %W forward" +} + +def Runcommand pack_prompt {} { + set f $@serf + set @show_id [$@canvas show_id] + if {!$@show_id} {$@canvas show_id= 1} + if {[winfo exists .$@canvas.yscroll]} {set w .$@canvas.yscroll} else {set w .$@canvas.c} + pack $f -side bottom -fill x -before $w + focus $f.entry +} + +def Runcommand unpack_prompt {} { + if {!$@show_id} {$@canvas show_id= 0} + pack forget $@serf + focus [$@canvas widget] +} + +def Runcommand traversal {k w direction} { + set f $@serf + if {$w == "$f.entry"} {set next $f.close} else {set next [tk_focusNext $w]} + focus $next +} + +def Runcommand eval {} { + set f $@serf + if {[winfo exists $f.completion]} { + set l [string range $@comp 0 [expr [string first ":" $@comp 0]-1]] + $self replace $l + destroy $f.completion + return + } + #$self unpack_prompt + super + $self unpack_prompt +} + +def Runcommand defs {} { + set name [$@canvas class] + set len [string length ${name}_] + foreach def [$name methods] {lappend @defs $def} +} + +def Runcommand completion {which} { + set f $@serf + set text [$f.entry get] + if {$text != $@comp_s} { + set @comp_s $text + set @completions {} + set @comp_i 0 + foreach def $@defs {if {[regexp ^$@comp_s $def]} {lappend @completions $def}} + } + if {![llength $@completions]} return + set def [lindex $@completions $@comp_i] + set args [$@canvas args $def] + if {[lindex $args 0] == "self"} { + set args2 [lreplace $args 0 0] + } else { + set args2 $args + } + if {![llength $args2]} {set args2 "none"} + set @comp [join [list $def ":" $args2]] + if {$which == "+"} { + set @comp_i [expr ($@comp_i+1)%[llength $@completions]] + } else { + set @comp_i [expr $@comp_i-1] + if {$@comp_i<0} {set @comp_i [expr [llength $@completions]-1]} + } + if {![winfo exists $f.completion]} { + label $f.completion -textvariable _($self:comp) -pady 0 + pack $f.completion -side right + } +} + +def Thing args {def} { + set class [$self class] + set ancestors [$class ancestors] + set name ${class}_$def + set n 0 + foreach class $ancestors { + set name ${class}_$def + if {[info exists ::__args($name)]} {break} + } + return $::__args($name) +} + +def Canvas visible_rect {} { + set c [$self widget] + set height [winfo height $c] + set width [winfo width $c] + if {$width == 1 && $height == 1} {set height 300; set width 450} + mset {l r} [$c xview] + mset {t b} [$c yview] + if {$l == $r} {set l 0; set r 1} + if {$t == $b} {set t 0; set b 1} + set w [expr $width / ($r - $l)] + set h [expr $height / ($b - $t)] + mset {l2 r2} [lmap * [list $l $r] $w] + mset {t2 b2} [lmap * [list $t $b] $h] + return [list $l2 $t2 $r2 $b2] +} + +def Canvas clipboard_coords {offset} { + set in 0 + foreach mess [pd_mess_split [$::clipboard value]] { + set type [lindex $mess 1] + switch $type { + canvas {set in 1} "" {} connect {} + default { + if {$type == "restore"} {set in 0} + mset {x y} [lmap + [lrange $mess 2 3] $offset] + if {!$in} {lappend xcoords $x; lappend ycoords $y} + } + } + } + return [list $xcoords $ycoords] +} + +def Canvas paste_visible? {x1 y1 x2 y2 offset} { + set in 0 + foreach mess [pd_mess_split [$::clipboard value]] { + set type [lindex $mess 1] + switch $type { + canvas {set in 1} "" {} connect {} + default { + if {$type == "restore"} {set in 0} + mset {x y} [lmap + [lrange $mess 2 3] $offset] + if {!$in} { + if {$x > $x2 || $x < $x1} {return 0} + if {$y > $y2 || $y < $y1} {return 0} + } + } + } + } + return 1 +} + +def Canvas copy_times= {c} {set @copy_count $c} +def Canvas copy_times {} {return $@copy_count} + +def Canvas copy {} { + global clipboard obj_index_sel + if {![$@objectsel size]} {return} + $clipboard value= "" + $self copy_times= 1 + array unset obj_index_sel $self:* + set j 0 + foreach i [lsort -integer [$@objectsel keys]] { + set child [$@objectsel get $i] + #set obj_index_sel($self:$child) [$@objectsel search $child] + set obj_index_sel($self:$child) $j; incr j + $child deconstruct_to $clipboard + } + foreach wire [$@wiresel values] { + if {[array names obj_index_sel $self:[$wire from]] == ""} {continue} + if {[array names obj_index_sel $self:[$wire to ]] == ""} {continue} + $wire deconstruct_to $clipboard $self + } +} + +def Canvas paste {} { + if {[$self look snap_grid]} {set offset [$self look grid_size]} {set offset 15} + $self do_paste [expr [$self copy_times] * $offset] + $self copy_times= [expr [$self copy_times] + 1] +} + +def Canvas do_paste {offset} { + set in 0 + $self deselect_all + netsend [list .$self "push"] + foreach mess [pd_mess_split [$::clipboard value]] { + set type [lindex $mess 1] + if {$type == "restore"} {incr in -1} + if {!$in} {set with [list $self new_object_copyselect]} else {set with ""} + switch $type { + "" {} + canvas {incr in; netsend $mess} + connect { + if {$with != ""} {set with [list $self new_wire_select]} + netsend $mess $with} + default {netsend [$self paste_coords $mess $offset] $with} + } + } + netsend [list #X pop 1] +} + +def Canvas paste_coords {mess offset} { + set x [lindex $mess 2]; set y [lindex $mess 3] + mset {vx1 vy1 vx2 vy2} [$self visible_rect] + mset {xcoords ycoords} [$self clipboard_coords $offset] + set visible [$self paste_visible? $vx1 $vy1 $vx2 $vy2 $offset] + set ref [lsearch $ycoords [lindex [lsort -real -increasing $ycoords] 0]] + if {!$visible} { + set xoff [expr ($vx2 - $vx1) * 0.25] + set yoff [expr ($vy2 - $vy1) * 0.25] + set x2 [expr [lindex $mess 2] - [lindex $xcoords $ref] + $vx1 + $xoff] + set y2 [expr [lindex $mess 3] - [lindex $ycoords $ref] + $vy1 + $yoff] + return [lreplace $mess 2 3 $x2 $y2] + } else { + puts "\t \t [expr $x+$offset] [expr $y+$offset] <<<<<" + return [lreplace $mess 2 3 [expr $x+$offset] [expr $y+$offset]] + } +} + +def Canvas cut {} { + $@history atomically [list cut] { + $self copy + $self delete_selection + } +} + +def Canvas duplicate {} { + if {[$self look snap_grid]} {set off [$self look grid_size]} {set off 15} + $self do_duplicate $off +} + +def Canvas do_duplicate {offset} { + global clipboard + set backup $clipboard + set clipboard [Clipboard2 new] + $self copy + $self do_paste $offset + $clipboard delete + set clipboard $backup +} + +def Canvas mouse_copy {} { + $self do_duplicate 0 +} + +def Canvas select_all {} { + $self editmode= 1 + eval [concat [list $@objectsel reinit] [$@objects list]] + eval [concat [list $@wiresel reinit] [ $@wires list]] +} +def Canvas deselect_all {} { + #$self editmode= 1 + $@objectsel clear + $@wiresel clear +} + +def Canvas popup_help {} {$::main class_browser} + +def Canvas popup_open {} { + #$self init_window + #set @mapped 1 + if {[winfo exists [$self widget]]} {raise .$self; return} + netsend [list .$self vis 1] + #$self init_window + #$self redraw +} + +def Canvas popup {id x y} { + set p .$self.popup + catch {destroy $p} + menu $p -tearoff false + if {$id == $self} { + $self populate_menu $p {popup_properties popup_help} + } elseif {[$id class] == "Wire"} { + $id populate_menu $p {popup_insert} + } else { + $id populate_menu $p {popup_properties popup_open popup_help + popup_clear_wires popup_remove_from_path popup_delete_from_path popup_copy_id} + } + tk_popup $p [expr $x-5] [expr $y-5] +} + +def View popup_copy_id {} { + clipboard clear + clipboard append $self +} + +def Canvas disconnect {wire} { + set @keynav_tab_sel "wire" + set id [$@wires get [$self wire_idx $wire]] + if {$id == $@keynav_current || $id == $@keynav_last_wire} { + set @keynav_current 0 + set @keynav_last_wire 0 + } + mset {from outlet to inlet} $wire + netsend [list .$self disconnect $from $outlet $to $inlet] + $@history add [list $self connect $wire] +} +def Canvas connect {wire {callback ""}} { + mset {from outlet to inlet} $wire + netsend [list .$self connect $from $outlet $to $inlet] $callback + $@history add [list $self disconnect $wire] +} + +def Canvas clear_wires_of {obj} { + if {![llength [$obj ioselect]]} { + set port 0; set type "none" + } else { + set port [lindex [$obj ioselect] 0] + set type [lindex [$obj ioselect] 1] + } + foreach wire [$obj wires2] { + mset {from outlet to inlet} [$wire report] + switch $type { + i {if { $to==$obj && $inlet==$port} {$self disconnect [$wire connects]; if { !$inlet} {lappend @auto_wire_from $from}}} + o {if {$from==$obj && $outlet==$port} {$self disconnect [$wire connects]; if {!$outlet} {lappend @auto_wire_to $to }}} + none { + $self disconnect [$wire connects] + if {$from==$obj && !$outlet} {lappend @auto_wire_to $to } + if { $to==$obj && ! $inlet} {lappend @auto_wire_from $from} + } + } + } +} + +def Canvas clear_wires {} { + set @auto_wire_to {}; set @auto_wire_from {} + if {[$@objectsel size] == 1} { + set objs [$@objectsel values] + } else { + if {![llength $@keynav_iosel_i] && ![llength $@keynav_iosel_o]} {return} + set objs [list $@keynav_iosel_i $@keynav_iosel_o] + } + foreach obj $objs {$self clear_wires_of $obj} +} + +def Canvas reconnect {} { + foreach from $@auto_wire_from { + set idx1 [$@objects search $from] + foreach to $@auto_wire_to { + set idx2 [$@objects search $to] + set wire [list $idx1 0 $idx2 0] + if {[$self wire_idx $wire] < 0} {$self connect $wire} + } + } +} + +def Canvas delete_obj_from_path {} {$self clear_wires; $self reconnect; $self delete_selection} +def Canvas remove_obj_from_path {} {$self clear_wires; $self reconnect} + +def Canvas wire_idx {connects} { + foreach {idx x} [$@wires list] { + if {[string compare [join $connects] [join [$x connects]]] == 0} {return $idx} + } + return -1 +} + +def Canvas reconnect_brkwires {type brk_quads obj} { + set k [$@objects search $obj] + foreach quad $brk_quads { + mset {from outlet to inlet} $quad + set orig_outlet $outlet; set orig_inlet $inlet + switch $type { + i {set orig_obj $to; set to $k; set inlet 0} + o {set orig_obj $from; set from $k; set outlet 0} + } + netsend [list .$self connect $from $outlet $to $inlet] + } + if {$type == "i"} { + netsend [list .$self connect $k 0 $orig_obj $orig_inlet] + } else { + netsend [list .$self connect $orig_obj $orig_outlet $k 0] + } + $self new_object_callback $obj + +} + +def Canvas expand_port {type k port} { + set obj [$@objects get $k] + mset {bx1 by1 bx2 by2} [$obj io_bbox $type $port] + mset {ox1 oy1 ox2 oy2} [$obj bbox] + mset ys [expr $oy2-$oy1] + mset {brk_wires brk_quads} [$self broken_wires $type $k $port $self] + switch $type { + i {mset {nx ny} [$self xy_snap $bx1 [expr $by1-25]]} + o {mset {nx ny} [$self xy_snap $bx1 [expr $by1+$ys+25]]} + } + foreach quad $brk_quads {$self disconnect $quad} + set reply [list $self reconnect_brkwires $type $brk_quads] + netsend [concat [list .$self obj $nx $ny] t a] $reply + +} + +def Canvas outlet_expand {k outlet} {set reconnect [$self broken_wires o $k $inlet]} + +def Canvas implicit_wires {objs} { + set l {}; set h $@objects + foreach obj $objs { + set k [$h search $obj] + for {set i 0} {$i < [$obj ninlets]} {incr i} { + set ws [$self com_wires i $k $i]; if {[llength $ws]} {foreach w $ws {lappend l $w}} + } + for {set o 0} {$o < [$obj noutlets]} {incr o} { + set ws [$self com_wires o $k $o]; if {[llength $ws]} {foreach w $ws {lappend l $w}} + } + } + #return [lsort -integer -unique $l] + return [lsort -unique $l] +} + +def Canvas com_wires {type k port} { + set h $@objectsel; set obj [$@objects get $k]; set wires [$obj wires2]; set l {} + foreach wire $wires { + mset {f2 o2 t2 i2} [$wire connects] + if {$t2==$k && $i2==$port && $type=="i" && [$h exists $f2]} {lappend l $wire} + if {$f2==$k && $o2==$port && $type=="o" && [$h exists $t2]} {lappend l $wire} + } + return $l +} + +def Canvas broken_wires {type k port canvas} { + set shash [$canvas objectsel] + set obj [[$canvas objects] get $k] + set wires [$obj wires2]; set brk_wires {}; set quads {} + foreach wire $wires { + mset {f2 o2 t2 i2} [$wire connects] + if {$t2==$k && $i2==$port && $type=="i" && ![$shash exists $f2]} { + lappend brk_wires $wire; lappend quads [$wire connects] + } + if {$f2==$k && $o2==$port && $type=="o" && ![$shash exists $t2]} { + lappend brk_wires $wire; lappend quads [$wire connects] + } + } + return [list $brk_wires $quads] +} + + +def Canvas selection_center {} { + set x 0; set y 0 + foreach obj [$@objectsel values] { + mset {x1 y1} [$obj xy] + incr x $x1 + incr y $y1 + } + set n [$@objectsel size] + set x [expr $x / $n] + set y [expr $y / $n] + return [list $x $y] +} + +# translate the key/idx in $@objects to key/idx in $@objectsel +def Canvas idx_map {idx} { + set i 0; set obj [$@objects get $idx] + foreach sobj [$@objectsel values] {if {$obj == $sobj} {return $i}; incr i} + return -1 +} + +def Canvas subpatcherize_mkio {center iolist offset} { + mset {x y} $center; set inx 0; set outx 0 + for {set i 0} {$i < [llength $iolist]} {incr i} { + mset {type io port dsp} [lindex $iolist $i] + if {$type == "i"} { + if {$dsp} {set inlet "inlet~"} {set inlet "inlet"} + netsend [list #X obj [expr ($inx+1)*100] 0 $inlet] + netsend [list #X connect $offset 0 $io $port] + incr inx + } else { + if {$dsp} {set outlet "outlet~"} {set outlet "outlet"} + netsend [list #X obj [expr ($outx+1)*100] [expr $y*2] $outlet] + netsend [list #X connect $io $port $offset 0] + incr outx + } + incr offset + } +} + +def Canvas subpatcherize_iopos {orig io} { + set tab {}; set result {} + for {set x 0} {$x < [llength $orig]} {incr x} { + mset {from k1 io1 dsp} [lindex $orig $x]; mset {k2 io2} [lindex $io $x] + set obj1 [$@objects get $k1] + mset {x1 y1 x2 y2} [$obj1 io_bbox $from $io1]; set x1 [expr int($x1)] + lappend pos $x1; lappend tab [list $k1 $x1 [list $k2 $io2 $dsp]] + #lappend pos $x1; lappend tab [list $k1 $x1 [list $k2 $io2]] + } + set tab [lsort -index 1 -real $tab]; set foo "" + foreach item $tab {mset {k1 val foo2} $item; if {$foo2 != $foo} {lappend result $foo2}; set foo $foo2} + return $result +} + +def Canvas subpatcherize {} { + set center [$self selection_center] + set rewire_off [llength [$@objectsel values]] + set ins {}; set outs {}; set toins {}; set fromouts {}; set broken {}; set iolist {} + foreach obj [$@objectsel values] { + for {set i 0} {$i < [$obj ninlets]} {incr i} { + mset {brk_wires brk_quads} [$self broken_wires i [$@objects search $obj] $i $self] + if {[llength $brk_wires]} { + foreach wire $brk_quads { + set out_obj_name [[$@objects get [lindex $wire 0]] text] + if {[regexp {~} $out_obj_name]} {set dsp 1} {set dsp 0} + lappend broken [concat i $wire $dsp] + } + } + } + for {set o 0} {$o < [$obj noutlets]} {incr o} { + mset {brk_wires brk_quads} [$self broken_wires o [$@objects search $obj] $o $self] + if {[llength $brk_wires]} { + foreach wire $brk_quads { + set out_obj_name [[$@objects get [lindex $wire 0]] text] + if {[regexp {~} $out_obj_name]} {set dsp 1} {set dsp 0} + lappend broken [concat o $wire $dsp] + } + } + } + } + # $broken stores totall number of broken connections, i= need [inlet] o = need [outlet] + foreach c $broken { + mset {type f o t i dsp} $c + if {$type == "i"} { + lappend ins [list $t $i];lappend toins [list o $f $o $dsp] + } else { + lappend outs [list $f $o]; lappend fromouts [list i $t $i $dsp] + } + } + # figures out the inlet/outlet positioning and num of in/outlet to create + set ins [$self subpatcherize_iopos $toins $ins] + set outs [$self subpatcherize_iopos $fromouts $outs] + # iolist stores in/outlets to be conected inside the subpatch + foreach in $ins {mset {idx p dsp} $in; lappend iolist [list i [$self idx_map $idx] $p $dsp]} + foreach out $outs {mset {idx p dsp} $out; lappend iolist [list o [$self idx_map $idx] $p $dsp]} + puts "\t \t Cutting..............." + $self cut + puts "\t \t Push.................." + netsend [list .$self "push"] + netsend [list #N canvas 0 0 450 300 sub 0] [list $self subpatcherize_id] + puts "\t \t Push clipboard........" + foreach mess [pd_mess_split [$::clipboard value]] {netsend $mess} + + #creating in/outlets + $self subpatcherize_mkio $center $iolist $rewire_off + #netsend [list [concat #X restore $center pd sub]] + netsend [concat #X restore $center pd sub] + puts "\t \t Pop..................." + netsend [list .$self "pop"] [list $self subpatcherize_rewire $broken $ins $outs] +} + +def Canvas subpatcherize_id {id} {set @subpatcherize_id $id} + +def Canvas subpatcherize_rewire {wires inlist outlist bogus} { + set obj $@subpatcherize_id + foreach wire $wires { + mset {type f o t i dsp} $wire + if {$type == "i"} { + set idx [lsearch $inlist [list $t $i $dsp]] + $self connect [list $f $o [$@objects search $obj] $idx] + } else { + set idx [lsearch $outlist [list $f $o $dsp]] + $self connect [list [$@objects search $obj] $idx $t $i] + } + } +} + +def Canvas end_action {} { + switch -- $@action { + none {post "ending action 'none' makes no sense"} + default {$@action delete; set @action "none"} + } +} + +proc shift? {f} {return [expr $f&1]} +proc ctrl? {f} {return [expr $f&2]} +proc alt? {f} {return [expr $f&4]} +proc button_of {f} { +# set f [expr $f>>8] +# set b 1 +# while {[expr $f&1==0} {set f [expr $f>>1]} +# return $f + return [expr $f>>8] +} + +class_new FutureWire {View} +def FutureWire init {canvas x y f target} { + super + set @canvas $canvas + mset {type from port} $target + switch $type { + outlet { + set @from $from; set @outlet $port + set @to "" ; set @inlet "" + set port_name ${from}o${port} + } + inlet { + set @from "" ; set @outlet "" + set @to $from; set @inlet $port + set port_name ${from}i${port} + } + } + mset {x y} [lmap / [rect_centre [[$@canvas widget] bbox $port_name]] [$@canvas zoom]] + set @x1 $x + set @y1 $y + $self motion $@x1 $@y1 $f $target +} +def FutureWire motion {x y f target} { + set @x2 $x + set @y2 $y + mset [list type foo bar] $target + $self draw +} +def FutureWire unclick {x y f target} { + mset [list type foo bar] $target + mset {obj iox ioy io idx} [$@canvas iohilite] + if {$obj != -1} { + switch $io {i {set type "inlet"} o {set type "outlet"}} + set target [list $type $obj $idx] + } + switch $type { + outlet {mset [list type @from @outlet] $target} + inlet {mset [list type @to @inlet] $target} + default {} + } + set from_idx [[$@canvas objects] search $@from] + set to_idx [[$@canvas objects] search $@to] + if {$from_idx >= 0 && $to_idx >= 0 && $@from != $@to} { + $@canvas connect [list $from_idx $@outlet $to_idx $@inlet] + } + if {![shift? $f]} {$@canvas end_action} +} +def FutureWire draw {} { + $self item WIRE line [xys $@x1 $@y1 $@x2 $@y2] -dash {4 4 4 4} -fill [$self look dash] -smooth yes +} + +class_new GopRect {View} + +def GopRect init {canvas rect} { + set @canvas $canvas + set @rect $rect +} + +def GopRect draw {} { + $self item GOPRECT rectangle $@rect -outline [$self look fg] +} + +class_new SelRect {View} +def SelRect init {canvas x y bf target} { + super + set @x1 $x + set @y1 $y + set @canvas $canvas + $self motion $x $y 0 $target +} +def SelRect motion {x y f target} { + set @x2 $x + set @y2 $y + $self draw +} +def SelRect unclick {x y f target} { + $self motion $x $y 0 $target + set sel {} + set c [$@canvas widget] + mset {x1 y1 x2 y2} [lmap * [list $@x1 $@y1 $@x2 $@y2] [$@canvas zoom]] + set sel [$c find overlapping $x1 $y1 $x2 $y2] + set selrect_index [lsearch $sel [$c find withtag selrect]] + set sel [lreplace $sel $selrect_index $selrect_index] + if {[llength $sel]} { + set objects {} + #set wires {} + foreach tag $sel { + if {[regexp {^[xo]?[0-9a-f]{6,8}} [$c gettags $tag] id]} { + if {[$@canvas == $id]} {continue} + if {[[$id class] <= Box]} {lappend objects $id} + #elseif {[[$id class] <= Wire]} {lappend wires $id} + } + } + set objects [lsort -unique $objects] + #set wires [lsort -unique $wires] + set objects2 {} + #so that objects in gop won't get selected... + foreach obj $objects {if {[$obj canvas] == $@canvas} {lappend objects2 $obj}} + $@canvas selection+= $objects2 + #$@canvas selection_wire+= $wires + } + $@canvas selection_wire+= [$@canvas implicit_wires $objects] + set _($@canvas:keynav_tab_sel) "wire" + $@canvas end_action + +} +def SelRect draw {} { + $self item RECT line [list $@x1 $@y1 $@x2 $@y1 $@x2 $@y2 $@x1 $@y2 $@x1 $@y1] \ + -fill [$self look rect] -dash {3 3 3 3} -dashoffset 3 +} + +def Canvas click {x y f target} { + if {[winfo exists .completion]} { + raise .completion + focus .completion.comp + return + } + mset {type id detail} $target + set c [$self widget] + focus $c + set @click_at [list $x $y] + if {$f&8} {if {$id == ""} {set id $self}; $self right_click $id $x $y; return} + if {!$@editmode} {$self click_runmode $id $x $y $f $target; return} + set in_selection [expr [$@objectsel search $id]>=0] + switch $@action { + mouse_copy {$self move_selection} + } + switch $type { + outlet {} + inlet {} + object {$self click_on_object $id $f} + wire {$self click_on_wire $id $f $x $y} + nothing { + #$self deselect_all + if {[lindex $@iohilite 0] == -1} { + $self click_on_nothing $f $target $x $y + return + } + } + label {$self click_on_object $id $f} + default {error "BORK: $type"} + } + if {$@iohilite != "" && $type != "wire"} { + mset {obj iox ioy io idx} $@iohilite + if {$obj < 0} {return} + switch $io { + i {set type "inlet"} + o {set type "outlet"} + } + if {$@action == "none" && [$obj canvas] == $self} { + set @action [FutureWire new $self $iox $ioy $f [list $type $obj $idx]] + } + return + } +} + +def Canvas click_runmode {id x y f target} { + foreach obj [$self selection] {if {[[$obj class] <= AtomBox]} {$obj unedit}} + $self deselect_all + #if {$@focus != ""} {if {[[$@focus class] <= TextBox]} {$self selection-= $@focus;$@focus unedit}} + if {[llength $id]} { + if {[$id class] != "Canvas"} { + $id click $x $y $f $target + } else { + if {[$id subpatch]} { + if {![$id mapped]} {$id popup_open} else {if {![$id gop]} {raise .$id}} + } + } + } +} + +def Canvas click_on_object {id f} { + set c [$self widget]; set text $c.${id}text + # so that if one clicks on the objectbox when editing the objectname, the focus won't get lost + if {[winfo exists $text]} {focus $text; return} + switch [expr $f&255] { + 0 { + if {[$self action] == "mouse_copy"} {$self action= "none"; return} + # if $id is the content of GOP + if {[$id canvas] != $self} { + set obj [$id get_parent_gop $self] + } else {set obj $id} + if {[$@objectsel search $obj] < 0 || [$@objectsel size] == 0} { + $self deselect_all + $self selection+= $obj + set @action edit + } else {set @action edit} + } + 1 { + if {[$@objectsel search $id] < 0} { + $self selection+= $id + } else { + $self selection-= $id + } + } + 2 { + if {![llength [$id wires2]]} {return} + $self deselect_all + $self selection+= $id + $self remove_obj_from_path + } + 3 { + if {[$id canvas] != $self} { + set obj [$id get_parent_gop $self] + } else {set obj $id} + if {![$@objectsel size]} {$self selection+= $obj} + if {[$@objectsel search $obj] < 0} { + $self deselect_all + $self selection+= $obj + } + $self action= "mouse_copy" + } + } +} +def Canvas click_on_wire {id f x y} { + set obj_selection [$self selection] + if {[llength $obj_selection]} {$self selection-= $obj_selection} + set @keynav_tab_sel "object" + switch [expr $f&255] { + 0 {$self selection_wire= $id} + 1 {$self selection_wire+= $id} + 2 { + set c [$self widget] + set wire [$id connects] + $self disconnect $wire + set from [$@objects get [lindex $wire 0]]; set outlet [lindex $wire 1] + set to [$@objects get [lindex $wire 2]]; set inlet [lindex $wire 3] + set opos [lmap / [rect_centre [$c bbox ${from}o${outlet}]] [$self zoom]] + set ipos [lmap / [rect_centre [$c bbox ${to}i${inlet}]] [$self zoom]] + set pos [list $x $y] + if {[distance $pos $opos] > [distance $pos $ipos]} { + mset {x1 y1} $ipos + set target [list outlet $from $outlet] + } else { + mset {x1 y1} $opos + set target [list inlet $to $inlet] + } + set @action [FutureWire new $self $x1 $y1 $f $target] + } + } +} + +def Canvas click_on_nothing {f target x y} { + # this cget check actually saves a full tk redraw + if {[[$self widget] cget -cursor] != {}} {[$self widget] configure -cursor {}} + if {$@focus != ""} {if {[[$@focus class] <= TextBox]} {$@focus unedit}} + if {$@action == "insert"} {return} + if {![expr $f&255]} { + $self deselect_all + #$self click_deselect_io + } + switch $@action { + edit {} + move {} + none {} + insert {} + chain_obj {} + imove {} + mouse_copy {} + default {$@action unclick $x $y $f $target} + } + set @action [SelRect new $self $x $y $f $target] +} + +def Canvas right_click {id x y} { + set c [$self widget] + set @insert_x $x; set @insert_y $y + if {$id != $self} {set id [$self gop_target $id]} + $self popup $id [winfo pointerx $c] [winfo pointery $c] +} + +def Canvas unclick {x y f target} { + set c [$self widget] + mset {type id detail} $target + if {$@editmode} { + switch $@action { + edit { + if {[$id canvas] != $self} { + set obj [$id get_parent_gop $self] + } else {set obj $id} + if {[$id class] == "Canvas"} {if {[$id text] == "graph"} {set @action none; return}} + set focus [$self focus] + if {$focus != ""} {if {[[$focus class] <= TextBox]} {$focus unedit}} + $obj edit; set @action none; $obj changed action + } + move {$self unclick_move} + none {} + insert {} + chain_obj {} + mouse_copy {$self mouse_copy} + imove {$self unclick_move} + default {$@action unclick $x $y $f $target} + } + } else {$self unclick_runmode $target $f $x $y} + $self adjust_scrollbars + #$self checkgeometry +} + +def Canvas unclick_move {} { + $self move_selection + set @action none +} + +def Canvas move_selection {} { + foreach obj [$@objectsel values] { + if {![[$obj class] <= Box]} { + puts "Canvas unclick warning: trying to move non-Box explicitly" + continue + } + mset {x1 y1} [$obj xy] + switch $@action { + mouse_copy {} + default {$obj position= [$obj backupxy]} + } + $obj moveto $x1 $y1 + if {[$obj edit?]} {focus [[$obj canvas] widget].${obj}text} + } + set objs [$@objectsel values] +} + +def Canvas unclick_runmode {target f x y} { + if {[$self focus] != ""} {[$self focus] unclick $x $y $f $target} + mset {type id detail} $target + if {$id != ""} { + if {[$id class] == "Array"} {$id unclick $x $y $f $target; return} + } +} + +def Canvas get_bbox {} {return $@bbox} + +if {$have_expand} { + def Canvas notice {origin args} {$self child_changed $origin {expand}$args} +} else { + def Canvas notice {origin args} {eval [concat [list $self child_changed $origin] $args]} +} + +def Canvas tab_jump {} { + if {![$@objects size]} {return} + set @keynav 1 + set olength [$@objectsel size] + set wlength [ $@wiresel size] + if {$@keynav_tab_sel == "object"} { + if {[$@wires size]} {set @keynav_tab_sel "wire"} + } else { + set @keynav_tab_sel "object" + } + switch $@keynav_tab_sel { + object {$self tab_jump_object} + wire {$self tab_jump_wire} + } +} + +def Canvas tab_jump_wire {} { + # if no selection done by the mouse + if {![$@wiresel size]} { + # see if the selected object has wire, if yes, use it + # if keynav_current is already a wire, do nothing + if {[$@keynav_current class] != "Wire"} { + #use the last key selected wire as start if there is one + if {$@keynav_last_wire != 0} { + $self deselect_all + set @keynav_current $@keynav_last_wire + } else { + if {[llength [$@keynav_current wires2]]} { + $self deselect_all + set @keynav_current [lindex [$@keynav_current wires2] 0] + set @keynav_last_wire $@keynav_current + } else { + # check if the canvas has wires, if yes, use it + if {[$@wires size]} { + $self deselect_all + set @keynav_current [lindex [$@wires values] 0] + set @keynav_last_wire $@keynav_current + } else {return} + + } + } + } + } + $self selection_wire= $@keynav_current +} + +def Canvas tab_jump_object {} { + set olength [$@objectsel size] + # if there is no selection done by mouse + if {!$olength} { + # if keynav_current is 0, aka the start of key navigation + if {$@keynav_current == 0} { + if {[$@objects size]} {set @keynav_current [lindex [$@objects values] 0]} else {return} + } else { + # if the keynav_current is a wire, set keynav_current to a object + if {[$@keynav_current class] == "Wire"} { + set @keynav_last $@keynav_current + $self deselect_all + # use the last key selected obj as the start if there is one + if {$@keynav_last_obj != 0} { + set @keynav_current $@keynav_last_obj + } else { + set @keynav_current [lindex [$@keynav_current report] 0] + set @keynav_last_obj $@keynav_current + } + } + } + } elseif {$olength == 1} { + set @keynav_current [lindex [$@objectsel values] 0] + $self deselect_all + } else { + $self deselect_all + set @keynav_current [$@objects get 0] + set @keynav_last_obj $@keynav_current + } + $self selection= $@keynav_current +} + +def Canvas key_nav_up {} {$self key_nav +1 -1 0} +def Canvas key_nav_down {} {$self key_nav -1 +1 0} +def Canvas key_nav_right {} {$self key_nav -1 -1 0} +def Canvas key_nav_left {} {$self key_nav +1 +1 0} +def Canvas key_nav_up_shift {} {$self key_nav +1 -1 1} +def Canvas key_nav_down_shift {} {$self key_nav -1 +1 1} +def Canvas key_nav_right_shift {} {$self key_nav -1 -1 1} +def Canvas key_nav_left_shift {} {$self key_nav +1 +1 1} + +def Canvas key_nav {du dv shift} { + if {$@keynav_shift && !$shift} {} + set @keynav_shift $shift + if {[$@objectsel size] > 1} { + if {[llength $@keynav_iosel_i] > 0 || [llength $@keynav_iosel_o] > 0} { + #$self deselect_all + } + } + if {!$@keynav} {$self tab_jump} + switch $@keynav_tab_sel { + object { + set @keynav_next [$self quadrant $du $dv [$@objects values]] + if {!$shift} { + $self selection-= $@keynav_current + $@keynav_current selected?= 0 + set @keynav_last_obj $@keynav_next + } + if {[$@objectsel search $@keynav_next] < 0} {$self selection+= $@keynav_next} + } + wire { + #$@keynav_current selected?= 0 + set @keynav_next [$self quadrant $du $dv [$@wires values]] + if {!$shift} { + $self selection_wire-= $@keynav_current + $@keynav_current selected?= 0 + set @keynav_last_wire $@keynav_next + } + if {[$@wiresel search $@keynav_next] < 0} {$self selection_wire+= $@keynav_next} + } + } + #$self selection+= $@keynav_next + set @keynav_current $@keynav_next +} + +def Canvas victim {} { + if {[$@objectsel size]} {return [$@objectsel values]} + if {[string compare $@keynav_current 0]} {return {}} + return $@keynav_current +} + +def Canvas key_nav_ioselect {} { + if {![$@objectsel size]} {return} + set var [lindex [$@objectsel values] end] + if {$@keynav_iosel != $var} {set @keynav_iocount 0} + if {$@keynav_port != 0 && $@keynav_iosel == $var} { + #set hilitebox $@keynav_port + foreach io $@keynav_port2 {[$self widget] delete ${io}b} + } + set obj [lindex [$@objectsel values] 0] + set ins [$obj ninlets] + set outs [$obj noutlets] + set ports {}; set ports2 {}; set ports3 {} + for {set i 0} {$i < $ins} {incr i} {lappend ports ${obj}i${i}; lappend ports2 "i"; lappend ports3 $i} + for {set i 0} {$i < $outs} {incr i} {lappend ports ${obj}o${i}; lappend ports2 "o"; lappend ports3 $i} + #incr @keynav_iocount + if {$@keynav_iocount >= [llength $ports]} {set @keynav_iocount 0} + set port [lindex $ports3 $@keynav_iocount] + set type [lindex $ports2 $@keynav_iocount] + set @keynav_port ${obj}${type}${port} + set @keynav_port2 {} + # @keynav_port stores the current hilited in/outlets + # @keynav_ports stores the current hilited in/outlets if there are multiple objects + # @keynav_iosel_i stores the selected inlets + # @keynav_iosel_o stores the selected outlets + foreach object [$@objectsel values] { + if {$@keynav_iosel != $var} {set @keynav_iosel $var} + switch $type { + i { + if {[lsearch $@keynav_iosel_i $object] == -1} { + lappend @keynav_iosel_i $object + set find [lsearch $@keynav_iosel_o $object] + if {$find >= 0} {set @keynav_iosel_o [lreplace $@keynav_iosel_o $find $find]} + } + } + o { + if {[lsearch $@keynav_iosel_o $object] == -1} { + lappend @keynav_iosel_o $object + set find [lsearch $@keynav_iosel_i $object] + if {$find >= 0} {set @keynav_iosel_i [lreplace $@keynav_iosel_i $find $find]} + } + } + } + mset {x y x1 y1} [[$self widget] bbox ${object}${type}${port}] + lappend @keynav_port2 ${object}${type}${port} + set _($object:ioselect) [list [lindex $ports3 $@keynav_iocount] [lindex $ports2 $@keynav_iocount]] + #set _($object:ioselect) [lindex $ports3 $@keynav_iocount] + $object hilite_io [lindex $ports2 $@keynav_iocount] [expr $x/$@zoom] [expr $y/$@zoom] + } + incr @keynav_iocount + +} + +def Canvas keyboard_mode {} { + post "loading keyboard-mode...." + if {[file exists kb-mode.tcl]} { + package require kb-mode + $@runcommand defs + if {![info exists @kbcursor]} {set @kbcursor [kb-mode_init $self]} + puts "cursor::: $@kbcursor" + } +} + +def Canvas keyboard_mode_exit {} { + post "exiting keyboard-mode...." + package forget kb-mode + remove_callback kb-mode + $@runcommand defs + $@kbcursor delete + read_ddrc + remove_callback "kb-mode" + unset @kbcursor +} + +def Canvas keyprefix {} { + if {!$@keyprefix} {set @keyprefix 1} else {set @keyprefix 0} +} + +def Canvas click_deselect_io {} { + if {[llength $@keynav_iosel_i] || [llength $@keynav_iosel_o]} { + if {!$@iosel_deselect} {set @iosel_deselect 1} else {$self dehilite_io; set @iosel_deselect 0} + } else { + $self dehilite_io + } +} + +def Canvas dehilite_io {} { + foreach io [concat $@keynav_iosel_i $@keynav_iosel_o] { + puts "$io is a [$io class]" + set box $_($io:ioselect) + set type [lindex $_($io:ioselect) 1] + set port [lindex $_($io:ioselect) 0] + set tag ${io}${type}${port} + [$self widget] delete ${tag}b + set _($io:ioselect) {} + } + set @keynav_iosel_i {} + set @keynav_iosel_o {} + set @keynav_port 0 + set @keynav_iocount 0 +} + +def Canvas incr_scale {} {$self scale out} +def Canvas decr_scale {} {$self scale in} + +def Canvas scale {mode} { + set s $::scale_amount + switch $mode { in { set s [expr 1/$s] }} + set sel [$@objectsel values] + if {![llength $sel]} {set sel [$@objects values]} + foreach child $sel { + mset {x y} [$child xy] + set x1 [expr $x*$s] + set y1 [expr $y*$s] + $child position= [list $x1 $y1] + netsend [list .$self object_moveto $child $x1 $y1] + } + foreach child $sel {$child changed_wires} +} + +def Canvas incr_zoom {} {$self zooming in} +def Canvas decr_zoom {} {$self zooming out} +def Canvas zooming {mode} { + global zoom bar + set spinbox .$self.bbar.scale + set val [string trimright [$spinbox get] %] + set i [lsearch $zoom(canned) $val] + set end [expr [llength $zoom(canned)] - 1] + switch -regexp $mode { + in|up {if {$i<$end} {incr i +1}} + out|down {if {$i>0} {incr i -1}} + } + set per [lindex $zoom(canned) $i] + $spinbox set $per% + set @zoom [expr $per/100.0] ;# @zoom must be float, not int + #$self propagate_zoom $@zoom + $self redraw + if {[$self look gridstate]} {if {$@editmode} {$@grid erase}} + foreach child [$@objects values] {if {[$child class] == "Canvas" && [$child gop]} {$child all_changed}} +} + +def Canvas propagate_zoom {zoom} { + foreach child [$@objects values] { + if {[$child class] == "Canvas"} { + set @zoom $zoom + $child propagate_zoom $zoom + } + } +} + +#-----------------------------------------------------------------------------------# +set lastcanvasconfigured "" +set lastcanvasconfiguration "" + +def Canvas checkgeometry {} { + set topname .$self + set boo [winfo geometry $topname.c] + set boo2 [wm geometry $topname] + global lastcanvasconfigured lastcanvasconfiguration + if {$topname != $lastcanvasconfigured || $boo != $lastcanvasconfiguration} { + set lastcanvasconfigured $topname + set lastcanvasconfiguration $boo + } +} +#-----------------------------------------------------------------------------------# +def Canvas selection_move {dx dy} { + $@history atomically [list move] { + $self selection_move2 $dx $dy + } +} +def Canvas selection_move2 {dx dy} { + if {![$self editmode]} {return} + foreach o [$@objectsel values] { + mset {x1 y1} [$o xy] + if {[[$o class] <= Box]} { + $o moveto [expr $x1+$dx] [expr $y1+$dy] + } else { + puts "selection_move: $o is not a Box, it's a [$o class]" + } + } +} + +def Canvas key {x y key iso shift} { + global tooltip; if {$tooltip ne ""} {$tooltip delete; set tooltip ""} + #if {[modes_callback $self "key" $x $y $key $iso $shift]} {return} + #if {[$self focus] != ""} {[$self focus] key $key $shift} + #if {$iso != ""} {scan $iso %c key} + #set focus [$self focus] + if {!$@editmode && [llength [$self selection]] == 1} { + set obj [$self selection] + if {[[$obj class] <= AtomBox]} { + if {[regexp {^[a-zA-Z0-9~/\._]{1}$} $key]} {$obj text= $key} {return} + $obj edit + $obj clear 0 + return + } + } + if {$shift} { + if {[$self look snap_grid]} {set motion [expr [$self look grid_size]*2]} {set motion 10} + } else { + if {[$self look snap_grid]} {set motion [$self look grid_size]} {set motion 1} + } + set n [$@objectsel size] + switch -regexp -- $key { + BackSpace|Delete|KP_Delete {$self delete_selection} + Up {if {$n} {$self arrow_key 0 -$motion} else {$self scroll y -$motion}} + Down {if {$n} {$self arrow_key 0 +$motion} else {$self scroll y +$motion}} + Left {if {$n} {$self arrow_key -$motion 0} else {$self scroll x -$motion}} + Right {if {$n} {$self arrow_key +$motion 0} else {$self scroll x +$motion}} + Tab {$self tab_jump} + Return {$self return_key $x $y $key $iso $shift} + F1 {$self deselect_all} + F2 {set @keynav 0; $@active hide} + default {} + } +} + +def Canvas return_key {x y key iso f} { + mset {type id detail} [$self identify_target $x $y $f] + if {![llength $@keynav_iosel_i] && ![llength $@keynav_iosel_o]} { + if {[$@objectsel size] == 1} { + mset {bx1 by1 bx2 by2} [[$self selection] bbox] + set x1 [expr ($bx1+$bx2)/2]; set y1 [expr ($by1+$by2)/2] + $self click_wrap $x1 $y1 1 $f + $self unclick_wrap $x1 $y1 1 $f + } + } else { + foreach out_obj $@keynav_iosel_o { + set from [$@objects search $out_obj] + set outlet [lindex $_($out_obj:ioselect) 0] + foreach in_obj $@keynav_iosel_i { + set to [$@objects search $in_obj] + set inlet [lindex $_($in_obj:ioselect) 0] + $self connect [list $from $outlet $to $inlet] + } + } + $self dehilite_io + } + +} + +def Canvas arrow_key {val1 val2} { + if {![$self editmode]} { + if {[$@objectsel size] == 1} { + set o [$@selection values] + if {[[$o class] <= IEMGUI] || [[$o class] == FloatBox]} {$o key_incr $val1 $val2} + } + } else { + $self selection_move $val1 $val2 + } +} + +def Canvas clear_selection {} { + if {[$@objectsel size] > 0 || [$@wiresel size] > 0} { + $self deselect_all + $self dehilite_io + } + if {$@keynav} { + set @keynav 0 + #bogus switch so that keynav remains the same next time.. + if {$@keynav_tab_sel == "object"} {set @keynav_tab_sel "wire"} {set @keynav_tab_sel "object"} + } +} + +#-----------------------------------------------------------------------------------# +def Canvas keynav_current {} {return $@keynav_current} +def Canvas keynav_current= {current} { + set @keynav_current $current + switch [$current class] { + Wire {set @keynav_last_wire $current} + default {set @keynav_last_obj $current} + } +} +def Canvas keynav {} {return $@keynav} +def Canvas keyup {x y key iso shift} { + if {$iso != ""} {scan $iso %c key} + if {$::macro_state} {$::macro state= 1; set ::macro_state 0 } + $@active draw +} + +class_new Active {View} +def Active init {canvas} { + super + set @canvas $canvas + set @length 3 + set @length2 10 +} +def Active draw {} { + set current [$@canvas keynav_current] + if {$current == ""} {return} + if {![string compare $current 0]} {return} + set col [$self look selectframe] + if {[$@canvas keynav]} { + if {[$current class] == "Wire"} { + $self item_delete + set i 0 + foreach side {1.6 -1.6} { + mset {ax1 ay1 ax2 ay2} [$current bbox] + set t [expr atan2($ay2-$ay1, $ax2-$ax1)] + set cx1 [expr $ax1 + (($ax2-$ax1)* 0.15)] + set cy1 [expr $ay1 + (($ay2-$ay1)* 0.15)] + set cx2 [expr ($@length * cos($t+$side)) + $cx1] + set cy2 [expr ($@length * sin($t+$side)) + $cy1] + + for {set n 0} {$n < 2} {incr n} { + set angle [lindex {45 90 -45 -90} [expr $n+$i]] + mset {ax1 ay1 ax2 ay2} [$current bbox] + set t [expr atan2($ay2-$ay1, $ax2-$ax1)] + set bx1 [expr $ax1 + (($ax2-$ax1)* 0.15)] + set by1 [expr $ay1 + (($ay2-$ay1)* 0.15)] + set bx2 [expr ($@length2 * cos($t+$angle)) + $bx1] + set by2 [expr ($@length2 * sin($t+$angle)) + $by1] + set line [list $cx2 $cy2 $bx2 $by2] + $self item LINE$angle line $line -fill $col -width 2 + } + set i [expr $i+2] + } + } else { + $self item_delete + mset {x y} [lmap - [$current xy] 5] + set line1 [list $x $y $x [expr $y + $@length2]] + set line2 [list $x $y [expr $x + $@length2] $y] + $self item LINE1 line $line1 -fill $col -width 2 + $self item LINE2 line $line2 -fill $col -width 2 + + } + } else {$self hide} +} +def Active hide {} {$self item_delete} +def Active bbox {} { + set current [$@canvas keynav_current] + set l [$current xy] + concat $l $l ;# happy now?? +} + +def Canvas update_Active {item} {$self keynav_current= $item} + +#-----------------------------------------------------------------------------------# +class_new Box {View} + +def Box init {{mess {}}} { + super + # @wires2 stores the connections to and from a object + set @wires2 {} + $self reinit $mess +} + +def Box delete {} {if {$@canvas != ""} {[$@canvas objects] unset $@index}; super} + +def Box remove_braces {str} { + # this hack is to remove the "\" in the text + regsub -all {\\} $str "" text + return $text +} + +def Box reinit {mess} { + global classinfo fields + if {[$::macro state] && [llength $mess] > 4} {$::macro append_ref $mess} + if {![llength $mess]} {return} ;# what? + if {[lindex $mess 1] == "obj"} {set i 4} else {set i 1} + set @pdclass [lindex $mess $i] + if {[info exists fields($@pdclass)]} { + set i 0 + foreach f $fields($@pdclass) {set _($self:$f) [lindex $mess $i]; incr i} + } else { + set @text [$self remove_braces [join [lrange $mess 4 end]]] + } + $self outside_of_the_box +} + +def Box update_size {} {} + +# will be so that @wires are updated correctly without breaking encapsulation +def Box connect_out {} {} +def Box connect_in {} {} + +def Box draw {} { + $self draw_box + if {[$@canvas show_id]} {$self draw_id} {$self item_delete ID} + [[$self get_canvas] widget] raise $self + $self update_hilite_io +# if {[$self class] == "Canvas"} {$self restack} + if {[info exists @elapsed]} { + mset {x1 y1 x2 y2} [$self bbox] + $self item ELAPSED text [l+ {10 1} [list $x1 $y2]] -anchor nw -fill "#008800" \ + -text $@elapsed -font {{DejaVu Sans Mono} -8} + } +} + +def Box elapsed {f} { + set @elapsed $f + $self changed +} + +def Canvas get_elapsed {} {netsend [list .$self get_elapsed]} + +def Canvas show_id {} {return $@show_id} +def Canvas show_id= {val} {set @show_id $val; $self redraw} +def Canvas id_toggle {} {if {$@show_id} {set @show_id 0} {set @show_id 1}; $self redraw} + +def Box draw_id {} { + set id [$self index]: + mset {x y} [$self xy] + set fw [font measure [$self look font] 0] + if {[$@canvas editmode]} { + set col [complement [$@canvas look bgedit]] + } else {set col [complement [$@canvas look bgrun]]} + $self item ID text [list [expr $x-([string length $id]*$fw)] [expr $y+2]] \ + -font [$self look font] -text $id \ + -fill $col -anchor nw +} + +def Box draw_box {} {} +def Box edit {} {} +def Box unedit {} {} + +def Box bbox {} { + mset {x y} [$self xy] + set xs $@xs + set ys $@ys + list $x $y [expr {$x+$xs}] [expr {$y+$ys}] +} + +def Box io_bbox {type port} { + mset {x1 y1 x2 y2} [$self bbox] + set xs [expr {$x2-$x1}] + # method calls aren't as fast as we'd want them to be. + #set iowidth [$self look iowidth] + #set fy [$self look iopos] + set iowidth 7 + set fy -1 + switch $type { + o {set n $@noutlets; set y [expr {$y2+$fy }]} + i {set n $@ninlets; set y [expr {$y1-$fy-1}]} + } + set nplus [expr {$n==1 ? 1 : $n-1}] + set onset [expr {$x1+($xs-$iowidth)*$port/$nplus}] + set points [list $onset $y [expr {$onset+$iowidth}] $y] + return $points +} + + +def Box ioselect= {type port} {set @ioselect [list $type $port]} +def Box ioselect {} {return $@ioselect} + +def Box wires2 {} {return $@wires2} +def Box wires2+= {val} {if {[lsearch $@wires2 $val] < 0} {lappend @wires2 $val}} + +def Box changed_wires {} {foreach wire $@wires2 {$wire changed}} + +def Box delete_wire {wire} { + set find [lsearch $@wires2 $wire] + if {$find != -1} {set @wires2 [lreplace $@wires2 $find $find]} +} + +def Box move {dx dy} { + set @x1 [expr {$@x1+$dx}]; set @y1 [expr {$@y1+$dy}] + set zoom [$@canvas zoom] + $self changed ;# until we find a way to avoid rounding errors on [$@canvas widget] move. + $self changed_wires +} + +# temporary hack... only used during the moving of objects. +def Box backupxy= {xy} {set @backupxy $xy} +def Box backupxy {} {return $@backupxy} + +# the only one sending to the server. +# View position= is when getting position from server. +# View xy is virtual (for GOP) +def Box moveto {x1 y1} { + netsend [list .$@canvas object_moveto $self $x1 $y1] + [$@canvas history] add [list $self moveto $@x1 $@y1] + if {[$self class] == "Canvas"} { + if {[$self gop] && ![winfo exists .$self.c]} {foreach x $@visibles {$x changed}} + } + set @x1 $x1 + set @y1 $y1 + $self changed + $self draw_wires +} + +def Box draw_io2 {which n color} { + for {set i 0} {$i<$n} {incr i} { + set points [$self io_bbox $which $i] + $self item [list $which$i $which] rectangle $points -outline $color -fill $color -width 1 + [[$self get_canvas] widget] raise $self$which$i + } +} + +def Box draw_io {} { + $self draw_io2 i $@ninlets [$self look inletfg] + $self draw_io2 o $@noutlets [$self look outletfg] +} + +def Box tips= {tips} {set @tips $tips} + +def Box tip {type port} { + if {[info exists @tips]} { + set tips $@tips + } else { + netsend [list .$@canvas object_get_tips $self] + #after 500 $self tip $type $port + set tips "" + } + switch $type { + i {return "inlet $port: [lindex $tips $port]"} + o {return "outlet $port"} + } +} + +# type is i or o +def Box hilite_io {type x y} { + mset {x1 y1 x2 y2} [$self bbox] + set xs [expr $x2-$x1] + set c [$@canvas widget] + switch $type {i {set ports $@ninlets} o {set ports $@noutlets}} + set port -1 + set iowidth [$self look iowidth] + + for {set n 0} {$n<$ports} {incr n} { + set tag $self$type$n + set area [lmap / [$c bbox $self$type$n] [$@canvas zoom]] + set center [expr ([lindex $area 2] + [lindex $area 0]) / 2 ] + set dist [expr abs($x - $center)] + if {$dist < [expr ($iowidth/2)+5] && $dist > 0} {set port $n} + } + + if {$ports==0 | $port==-1} return + if {$port >= $ports} {set port [expr $ports-1]} + $self hilite_io_2 $type $port + if {[$self look tooltip]} {$@canvas show_tooltip $x $y [$self tip $type $port] $type} + return $port +} + +def Box update_hilite_io {} { + if {![llength $@ioselect]} {return} + set type [lindex $@ioselect 1] + set zoom [$@canvas zoom] + set port [lindex $@ioselect 0] + set p $type$port + $self hilite_io_2 $type $port +} + +def Box hilite_io_2 {type port} { + set outline [switch $type {i {concat [$self look outletfg]} o {concat [$self look inletfg]}}] + set box [l+ [$self io_bbox $type $port] [list -3 -3 +3 +3]] + $self item $type${port}b rectangle $box -outline $outline -width 1 +} + +#def Box popup_help {} {netsend [list pd help $@pdclass]} +def Box popup_help {} {netsend [list .$@canvas object_help $self]} + +def Box show_error {text} { + regsub "\n" $text "" text + #mset {x1 y1 x2 y2} [$self bbox] + #[$self get_canvas] show_tooltip [expr $x2+4] [expr ($y1+$y2)/2] $text object 1 + mset {x1 y1 x2 y2} [$self bbox] + mset {x y} [rect_centre [$self io_bbox i 0]] + [$self get_canvas] show_tooltip $x $y $text i 1 +} + +def Canvas macro_event_append {e obj} { + if {![llength $@macro_q]} {after $@macro_delay [list $self macro_schedule $@macro_delay] $obj} + lappend @macro_q $e +} + +def Canvas get_clipboard {obj} { + puts "get clipboard" + set content [clipboard get] + set l {}; set s ""; set space " "; + set last_newline [string last ";" $content] + #foreach char [split $content ""] {lappend l [scan $char %c]} + set i 0 + foreach char [split $content ""] { + if {$i == $last_newline} {break} + #if {$char == ";"} {set s ${s}${space}list} {set s ${s}$char} + if {$char != ";"} { + if {$char == "\n"} { + set s ${s}${space}$char + } else { + set s ${s}$char + } + } + incr i + } + netsend [concat [list .$obj clipboard_set] $s] +} + + +def Canvas macro_schedule {delay obj} { + if {[llength $@macro_q]} { + set w [focus] + set m [lindex $@macro_q 0] + set fudge 0 + mset {event x y mode k} $m + switch $event { + key {set name [modekey $k $mode]; set fudge 1} + click {set name [modeclick $k $mode ButtonPress]; set fudge 1} + unclick {set name [modeclick $k $mode ButtonRelease]; set fudge 1} + bang { + after $delay [list $self macro_schedule $@macro_delay] $obj + netsend [list .$obj mbang] + set @macro_q [lreplace $@macro_q 0 0] + return + } + default {puts "Error: this event $event should not have been here.."} + } + if {$fudge} {event generate $w <Motion> -x $x -y $y} + event generate $w <$name> -x $x -y $y + #puts "event generate $w <$name> -x $x -y $y" + if {$event=="key"} {event generate $w <KeyRelease-$k> -x $x -y $y} + set @macro_q [lreplace $@macro_q 0 0] + after $delay [list $self macro_schedule $@macro_delay] $obj + } +} + +def Canvas foobar {} {$self macro_schedule 1000} + +def Canvas macro_q {} {puts "$@macro_q"} +#-----------------------------------------------------------------------------------# +class_new Wire {View} + +def Wire canvas= {c} { + super $c + mset {from outlet to inlet} $@connects + set children [$c objects] + set @from [$children get $from] + set @to [$children get $to] + $@from wires2+= $self; $@to wires2+= $self +} + +def Box index= {i} {super $i; if {$@canvas != ""} {[$@canvas objects] set $i $self}} +def Wire index= {i} {super $i; [$@canvas wires] set $i $self } + +def Wire init {mess} { + super + $self reinit $mess +} + +def Wire reinit {mess} { + if {[$::macro state]} {$::macro append_ref $mess} + mset {x msg from outlet to inlet canvas} $mess + set @connects [list $from $outlet $to $inlet] + set @outlet $outlet + set @inlet $inlet + $self outside_of_the_box +} + +def Wire from {} {return $@from} +def Wire outlet {} {return $@outlet} +def Wire to {} {return $@to} +def Wire inlet {} {return $@inlet} +def Wire move {dx dy} {$self changed} + +# DON'T do the former, it's so horribly slow +#def View draw_wires {} {foreach wire $_($@canvas:wires) {$wire changed}} +def View draw_wires {} {foreach wire $@wires2 {$wire changed}} + +def Wire bbox {} { + set from $@from; set outlet $@outlet + set to $@to; set inlet $@inlet + set zoom [$@canvas zoom] + set c [$@canvas widget] + mset {x1 y1} [lmap / [rect_centre [$c bbox ${from}o${outlet}]] $zoom] + mset {x2 y2} [lmap / [rect_centre [$c bbox ${to}i${inlet} ]] $zoom] + list $x1 $y1 $x2 $y2 +} + +def Wire xy {} { + mset {x1 y1 x2 y2} [$self bbox] + list [expr $x1 + (($x2-$x1)*0.05)] [expr $y1 + (($y2-$y1)*0.05)] +} + +def Wire report {} {list $@from $@outlet $@to $@inlet} +def Wire connects {} {return $@connects} +proc xys {x1 y1 x2 y2} { + return [list $x1 $y1 $x2 $y2] ;# just a straight line, no frills + set r {} + lappend r $x1 $y1 + set dx [expr $x2-$x1] + set dy [expr $y2-$y1] + set d [expr sqrt($dx*$dx+$dy*$dy)] + set n [expr 1+$d/10] + for {set i 1} {$i<$n} {incr i} { + set w $i*($n-$i)/(0.0+$n*$n) + lappend r [expr $x1 + $dx*$i/$n + $dy*(rand()-0.5)*$w] + lappend r [expr $y1 + $dy*$i/$n - $dx*(rand()-0.5)*$w] + } + lappend r $x2 $y2 + return $r +} + +def Wire draw {} { + set zoom [$@canvas zoom] + set c [$@canvas widget] + set iowidth [$@from look iowidth] + mset {ox1 oy1 ox2 oy2} [$@from io_bbox o $@outlet] + mset {ix1 iy1 ix2 iy2} [ $@to io_bbox i $@inlet] + set x1 [expr ($ox1+$ox2)/2.0]; set y1 $oy2 + set x2 [expr ($ix1+$ix2)/2.0]; set y2 $iy1 + set xys [xys $x1 $y1 $x2 $y2] + set length [expr sqrt(pow($x2-$x1,2)+pow($y2-$y1,2))] + # how to customise the arrow size/shape? + set arrowsize [expr $length<100 ? $length/10 : 10] + if {$arrowsize < 5} {set arrow none} {set arrow last} + set arrowshape [list $arrowsize [expr $arrowsize*4/5] [expr $arrowsize/3]] + set wire_width [$self look thick] + set wire_color [$self look fg] + if {[$self selected?]} { + set wire_color [$self look fg2] ;# fg2 should be renamed + } else { + if {[info exists _($@from:text)] && [info exists _($@to:text)]} { + if {[regexp -nocase {~$} [lindex $_($@from:text) 0]] && \ + [regexp -nocase {~$} [lindex $_($@to:text) 0]]} { + set wire_width [expr $wire_width*2] + } + } + } + set options {} + if {[$self look wirearrow]} {lappend options -arrow $arrow -arrowshape $arrowshape} + eval [concat [list $self item WIRE line $xys -width $wire_width -smooth yes -fill $wire_color] $options] +} + +def Wire delete {} { + $self unsubscribe $@canvas + $@from delete_wire $self + $@to delete_wire $self + [$@canvas wires] unset $@index + super +} + +def Wire popup_insert {} { + if {![llength [$@canvas selection_wire]]} {$@canvas selection_wire= $self} + mset {x y} [$@canvas insertxy] + $@canvas do_insert_obj $x $y +} + +#-----------------------------------------------------------------------------------# +############ colouring + +proc color_* {c1 c2} { + scan $c1 #%02x%02x%02x r g b + scan $c2 #%02x%02x%02x R G B + return [format #%02x%02x%02x [expr ($r*$R)>>8] [expr ($g*$G)>>8] [expr ($b*$B)>>8]] +} + +# equivalent to color* #c0c0c0 $c +proc darker {c} { + scan $c #%02x%02x%02x r g b + set r [expr $r*3/4] + set g [expr $g*3/4] + set b [expr $b*3/4] + return [format #%02x%02x%02x $r $g $b] +} + +proc brighter {c} { + scan $c #%02x%02x%02x r g b + set r [min 255 [expr $r*4/3]] + set g [min 255 [expr $g*4/3]] + set b [min 255 [expr $b*4/3]] + return [format #%02x%02x%02x $r $g $b] +} + + +proc parse_color {c} { + regsub {;$} $c {} c + if {$c<0} { + set c [expr ~$c] + set r [expr round((($c>>12)&63)*255/63)] + set g [expr round((($c>> 6)&63)*255/63)] + set b [expr round((($c>> 0)&63)*255/63)] + return [format #%02x%02x%02x $r $g $b] + } { + global preset_colors2 + return #[lindex $preset_colors2 $c] + } +} + +proc unparse_color {c} { + if {[string index $c 0]=="#"} {set c [string replace $c 0 0 0x]} + set r [expr round((($c>>16)&255)*63/255)] + set g [expr round((($c>> 8)&255)*63/255)] + set b [expr round((($c>> 0)&255)*63/255)] + return [expr ~0[format %02o%02o%02o $r $g $b]] +} + +############ data transfer +# note: @pdclass is the server-side class name +# and @_class is the client-side class name + +# abstract classes +set fields1 {foo bar x1 y1 class} +set fields2 {snd rcv lab ldx ldy fstyle fs bcol fcol lcol} + +# real classes +set fields(tgl) [eval list $fields1 w isa $fields2 on nonzero] +set fields(bng) [eval list $fields1 w hold break isa $fields2] +set fields(nbx) [eval list $fields1 w h min max is_log isa $fields2 val log_height] +set fields(hsl) [eval list $fields1 w h min max is_log isa $fields2 val steady] +set fields(hradio) [eval list $fields1 w change isa n $fields2 on] +set fields(vu) [eval list $fields1 w h rcv lab ldx ldy fstyle fs bcol lcol scale isa] +set fields(cnv) [eval list $fields1 hh w h snd rcv lab ldx ldy fstyle fs bcol lcol isa] +set fields(dropper) [eval list $fields1 w isa $fields2] +set fields(vsl) $fields(hsl) +set fields(vradio) $fields(hradio) +set fields(hdl) $fields(hradio) +set fields(vdl) $fields(hradio) +set fields(coords) {foo bar xfrom yfrom xto yto w h gop x1 y1} ;# goes with #N canvas +set fields(floatatom) {foo bar x1 y1 w min max pos lab rcv snd} +set fields(symbolatom) {foo bar x1 y1 w min max pos lab rcv snd} +set fields(array) {name n elemtype flags} + +proc classinfo {pdclass _class} { + global classinfo classinfo2 + set classinfo($pdclass) $_class + set classinfo2($_class) $pdclass +} + +# basic patchables +classinfo obj ObjectBox +classinfo message MessageBox +classinfo floatatom FloatBox +classinfo symbolatom SymbolBox +classinfo text Comment + +# non-patchables (scalars, arrays, ...) +classinfo array Array + +# GUI patchables +classinfo bng Bang +classinfo tgl Toggle +classinfo nbx NumBox +classinfo hsl Slider +classinfo vsl Slider +classinfo vu Vu +classinfo dropper Dropper +classinfo hradio Radio +classinfo vradio Radio +classinfo hdl Radio +classinfo vdl Radio +classinfo canvas Canvas +classinfo cnv Cnv +classinfo display Display + +# remember, _($foo:$bar) notation should die +# because objects ought to be autonomous. + +# in array objects, number of inlets is bogus? +#X array array1 1 float 3; +#A 0 0; + +def View index= {i} {set @index $i} +def View index {} {return $@index} + +proc change {self canvas index e {ninlets 0} {noutlets 0} {valid 1}} { + foreach mess [pd_mess_split $e] {change_2 $self $mess} + #the server ought to take care of this: + #if {[lindex $e 1] == "array"} {set ninlets 0; set noutlets 0} + if {$canvas != "x0"} {$self canvas= $canvas} + $self index= $index + $self ninlets= $ninlets + $self noutlets= $noutlets + if {[$self class] == "ObjectBox"} {$self valid= $valid} + if {[$self class] == "Canvas"} { + if {[$self subpatch]} {$self valid= $valid} + if {[$self gop]} {$self valid= $valid} + if {[$self abs]} {$self valid= $valid} + } + $self changed +} + +proc change_2 {self mess} { + set isnew [expr ![info exists ::_($self:_class)]] + switch -- [lindex $mess 0] { + "#N" {if {$isnew} {Canvas new_as $self $mess} else {$self reinit $mess}} + "#X" { + set i 1 + # would it be possible to merge floatatom,symbolatom as gatom ? + switch -- [lindex $mess 1] { + obj {set class [lindex $mess 4]} + msg {set class message} + default {set class [lindex $mess 1]}} + if {[info exists ::classinfo($class)]} { + set _class [lindex $::classinfo($class) 0] + } else { + if {[lindex $mess 1] == "connect"} { + set _class Wire + } else { + set _class ObjectBox + } + } + if {$isnew} {$_class new_as $self $mess} else {$self reinit $mess} + switch -- $class { + floatatom {set class gatom} + symbolatom {set class gatom} + array {$self length= [lindex $mess 3]; $self name= [lindex $mess 2]} + default {$self position= [lrange $mess 2 3]}} + $self pdclass= $class + } + "#A" { + #post "#A: $mess" + $self array_set [lrange $mess 2 end] + } + "#V" { + #post "#V: $mess" + } + default {if {$mess != ""} {error "what you say? [lindex $mess 0] in $mess"}} + } +} + +#proc pasting_count {self} { +# global paste _ +# if {$paste(count2) != $paste(count)} {incr paste(count2)} +#} + +# split at message boundaries and atom boundaries, returning a list of lists of atoms. +# spaces get temporary value \x01 +# A_SEMI gets temporary value \x02 +# backslash gets temporary value \x03 +# A_COMMA is not handled yet +proc pd_mess_split {s} { + set s [regsub -all {\\\\} $s "\x03"] + set s [regsub -all {(^|[^\\]);} $s "\\1\x02"] + set s [regsub -all {(^|[^\\])[\s\n]+} $s "\\1\x01"] + set s [regsub -all {^\n} $s "\x01"] ;# oops + set s [regsub -all {\\([\\; \{\}])} $s "\\1\\2"] + set s [regsub -all \x03 $s \\] + set r {} + foreach m [split $s \x02] { + set m [regsub -all "\x01+" $m "\x01"] + set m [regsub -all "^\x01" $m ""] + set t [split $m \x01] + lappend r $t + } + return $r +} + +proc canonical_list {list} { + set r {} + foreach e $list {lappend r $e} + return $r +} + +proc pd_mess_split_want== {a b} { + set b [canonical_list $b] + set c [pd_mess_split $a] + if {[string compare $c $b]} { + puts "[VTred]string $a\nparses to $c\ninstead of $b[VTgrey]" + } else { + puts "[VTgreen]string $a OK[VTgrey]" + } +} + +if 0 { + pd_mess_split_want== {foo;bar;baz;a\;;b\\;c\\\;;d\\\\;e} {foo bar baz {{a;}} {b\\} {{c\;}} {{d\\}} e} + pd_mess_split_want== {foo\ bar} {{{foo bar}}} + pd_mess_split_want== {foo \ bar\ foo\ bar foo} {{foo { } {bar foo bar} foo}} + pd_mess_split_want== {\\ \\\\ \\\\\\ \ \\\ \\\\\ one} [list [list "\\" "\\\\" "\\\\\\" "\ \\\ \\\\\ one"]] + pd_mess_split_want== "\n \n \n foo" foo + pd_mess_split_want== "\\\x7b\\\x7b\\\x7b\\\x7b" [list [list "\x7b\x7b\x7b\x7b"]] + pd_mess_split_want== "\\\x7d\\\x7d\\\x7d\\\x7d" [list [list "\x7d\x7d\x7d\x7d"]] + exit +} + +############ rendering + +class_new MessageBox {TextBox} + +def MessageBox init {mess} { + super $mess + set @w 15 ;# this is useless? + set @xs $@w + set @ys $@w ;# this is a bug +} + +def MessageBox draw_box {} { + mset {x1 y1} [$self xy] + set x2 [expr $x1+$@xs] + set y2 [expr $y1+$@ys] + set points [list $x1 $y1 [expr $x2+4] $y1 $x2 [expr $y1+4] $x2 [expr $y2-4] [expr $x2+4] $y2 $x1 $y2 $x2 $y2 $x1 $y2] + if {[$self selected?]} {set frcol [$self look selectframe]} {set frcol [$self look frame3]} + $self item BASE polygon $points -fill [$self look bg] -outline $frcol -width 1 + [$@canvas widget] lower ${self}BASE ${self}TEXT + [$@canvas widget] raise $self +} + +def MessageBox draw {} { + super + $self draw_io +} + +def MessageBox click {x y f target} { + $self bang 1 + netsend [list .$self bang] + after 150 $self bang 0 +} + +def MessageBox bang {flag} { + if {$flag} {set color #ffff00} {set color [$self look bg]} + [$@canvas widget] itemconfigure ${self}BASE -fill $color +} + +# it was class_new AtomBox {View Box}, which is wrong because already Box<View +# it shouldn't have mattered, but super doesn't support proper pruning yet +#class_new AtomBox {Box} +class_new AtomBox {TextBox} +def AtomBox draw_box {} { + $self update_size + mset {x1 y1 x2 y2} [$self bbox] + set points [list $x1 $y1 [expr $x2-4] $y1 $x2 [expr $y1+4] $x2 $y2 $x1 $y2] + if {[$self selected?]} {set frcol [$self look selectframe]} {set frcol [$self look frame3]} + $self item BASE polygon $points -fill [$self look bg] -outline $frcol + [[$self get_canvas] widget] lower ${self}BASE ${self}TEXT + $self draw_io +} + +def AtomBox clear {var} {set @clear $var} +def AtomBox clear= {} {return $@clear} + +def AtomBox filter_text {{for_edit 0}} { + if {$for_edit} {return ""} + if {[string length $@text] <= $@w} {return $@text} + return [string range $@text 0 [expr $@w-1]] +} + +def AtomBox update_size {} { + set width [font measure [$self look font] 0] + set ls [font metrics [$self look font] -linespace] + set @xs [expr ($width*$@w)+3] + set @ys [expr $ls+3] +} + +class_new Comment {TextBox} + +def Comment draw_box {} { + super + mset {x1 y1} [$self xy] + set x2 [expr $x1+$@xs] + set y2 [expr $y1+$@ys] + set xya [list $x1 $y1 $x2 $y2] + set xyb [l+ [list $x2 $y1 $x1 $y1 $x1 $y2] [list -1 +1 +1 +1 +1 -1]] + set xyc [l+ [list $x2 $y1 $x2 $y2 $x1 $y2] [list -1 +1 -1 -1 +1 -1]] + if {[$@canvas editmode]} { + if {[$self selected?]} {set frcol [$self look selectframe]} {set frcol [$self look frame3]} + $self item BASE rectangle $xya -fill [$self look bg] -outline $frcol + } else { + $self item_delete BASE + } + #$self item BASE1 line $xyb -fill [$self look frame1] + #$self item BASE2 line $xyc -fill [$self look frame2] + if {[$@canvas editmode]} { + [$@canvas widget] lower ${self}BASE ${self}TEXT + #[$@canvas widget] raise ${self}BASE1 ${self}BASE + #[$@canvas widget] raise ${self}BASE2 ${self}BASE + } +} + +class_new Display {Box} + +def Display height= {val} {set @height $val} + +def Display init {{mess {}}} { + set font [$self look font] + set fw [font measure $font 0] + set @max_width 40; #in chars + set @wrap [expr $fw*$@max_width]; #in pixels + set @content {display} + set @height 1 + set @xs [expr [font measure [$self look font] 0]+3] + set @ys [font metrics [$self look font] -linespace] + set @textoffset [list 2 2] + netsend [list .$self height] + super $mess +} + +def Display draw {} { + super + set font [$self look font] + mset {x y} [$self xy] + mset {xf yf} $@textoffset + set fh [font metrics [$self look font] -linespace] + set text [lindex $@content 0]; + for {set i 1} {$i < $@height} {incr i} {set text ${text}\n[lindex $@content $i]} + set h 0; set w 0 + foreach line $@content { + set tw [font measure $font $line] + set h [expr int(ceil($tw/$@wrap.0)+$h)] + set w [min [max $w $tw] $@wrap] + } + set h [max $h 1] + $self item BASE rect [list $x $y [expr $x+$w+$xf+2] [expr $y+($@ys*$h)+$yf+1]] \ + -fill [$self look bg] + + $self item TEXT text [l+ $@textoffset [$self xy]] -font $font -text $text \ + -fill [$self look fg] -anchor nw -width $@wrap + $self draw_io +} + +def Display dis {text} { + lappend @content $text + if {[llength $@content] > $@height} {set @content [lrange $@content 1 end]} + $self changed +} + +class_new IEMGUI {} +def IEMGUI text {} { + return [$self class] +} +class_new BlueBox {Labelled IEMGUI Box} +#class_new BlueBox {Box Labelled} + +def BlueBox draw_box {} { + super + set xya [$self bbox] + mset {x1 y1 x2 y2} $xya + set xyb [list [expr $x2-1] [expr $y1+1] [expr $x1+1] [expr $y1+1] [expr $x1+1] [expr $y2-1]] + set xyc [list [expr $x2-1] [expr $y1+1] [expr $x2-1] [expr $y2-1] [expr $x1+1] [expr $y2-1]] + set color [color_* [$self look bg] [parse_color $@bcol]] + if {[$self selected?]} {set frcol [$self look selectframe]} {set frcol [$self look frame3]} + $self item BASE rectangle $xya -fill $color -outline $frcol + #below lines draws the 3d box edge + #$self item BASE2 line $xyb -fill #ffffff + #$self item BASE3 line $xyc -fill [darker $color] + $self draw_io +} + +def IEMGUI popup_properties {} {IEMPropertiesDialog new $self} + +class_new PropertiesDialog {Dialog} + +def PropertiesDialog init {of} { + super cancel apply ok + set @of $of + set f .$self + checkbutton $f.auto_apply -text [say auto_apply] -anchor w -variable @auto_apply + frame $f.buttonsep2 -height 2 -borderwidth 1 -relief sunken + pack $f.auto_apply $f.buttonsep2 -side bottom -fill x + bind $f <KeyPress-Return> "break";#so that Return don't call do_auto_apply after Dialog ok + bind $f <KeyPress> [list $self do_auto_apply] + bind $f <ButtonRelease> [list $self do_auto_apply] + set @auto_apply 0 + $self none_resizable +} + +def PropertiesDialog do_auto_apply {} { + if {$@auto_apply} {$self apply} +} + +class_new IEMPropertiesDialog {PropertiesDialog} + +def IEMGUI properties_apply {list {orient -1}} { + set orig [list $self properties_apply] + foreach var [lrange $::fields($@class) 5 end] {lappend orig $@$var} + [$@canvas history] add $orig + foreach v $list {switch -- $v {{} {set v "empty"}}; lappend props $v} + netsend [concat [list .$self reload] $props] + if {$orient >= 0} { + netsend [list .$self orient $orient] + } +} + +def IEMPropertiesDialog apply {} { + set class $_($@of:class) + set props {} + foreach var [lrange $::fields($class) 5 end] { + set v $@$var + if {[regexp -nocase {^[bfl]col$} $var]} {set v [unparse_color $v]} + lappend props $v + } + if {[[$@of class] <= Slider] || [[$@of class] <= Radio]} { + $@of properties_apply $props $@orient + } else { + $@of properties_apply $props + } +} + +def IEMPropertiesDialog init {of} { + super $of + set @class $_($of:class) + wm title .$self "\[$@class\] [say popup_properties]" + if {![info exists ::fields($@class)]} {set class obj} + foreach var $::fields($@class) { + set val $_($of:$var) + switch -- $val { empty {set val ""}} + if {[regexp -nocase {^([a-z])col$} $var]} {set val [parse_color $val]} + set @$var $val + } + if {[[$of class] <= Slider] || [[$of class] <= Radio]} { + set @orient $_($of:orient) + $self add .$self [list orient choice -choices {horizontal vertical}] + } + foreach prop [lrange $::fields($@class) 5 end] { + set d [concat [list $prop] [switch $prop { + w {list integer -width 7} + h {list integer -width 7} + hold {list float -width 9} + break {list float -width 9} + min {list float -width 9} + max {list float -width 9} + is_log {list choice -choices {linear logarithmic}} + isa {list choice -choices {no yes}} + n {list integer -width 4} + steady {list choice -choices {steady_no steady_yes}} + snd {list entry -width 20} + rcv {list entry -width 20} + lab {list entry -width 20} + ldx {list integer -width 5} + ldy {list integer -width 5} + fstyle {list choice -choices {Courier Helvetica Times}} + fs {list fontsize -width 5} + bcol {list color} + fcol {list color} + lcol {list color} + val {continue} + on {continue} + change {list choice -choices {no yes}} + nonzero {list float -width 9} + log_height {list float -width 9} + hh {continue} + scale {list toggle} + default {error "huh? ($prop)"} + }]] + $self add .$self $d + } +} + +def IEMPropertiesDialog dropmenu_open {f name} {super $f} +def IEMPropertiesDialog dropmenu_set {frame var part val} { + switch $var { + orient {if {[$@of class] == "Slider"} {set tmp $@h; set @h $@w; set @w $tmp}} + default {} + } + set tmp ${var}choices + set textvar ${var}2 + set @$textvar [say [lindex $@$tmp $val]] + super $frame $var $part $val + $self do_auto_apply +} + +class_new CanvasPropertiesDialog {PropertiesDialog} + +def CanvasPropertiesDialog init {of} { + super $of + set @canvas $of + wm title .$self "[say canvas] [say popup_properties]" + set @gop [$of gop] + set @properties [list "gop" "xfrom" "xto" "yfrom" "yto" "width" "height" "xmargin" "ymargin"] + set mess [$of get_mess] + mset [list @xfrom @yfrom @xto @yto @width @height @xmargin @ymargin] $mess + if {!$@width} {set @width 85}; if {!$@height} {set @height 60} + $self add .$self [list gop toggle -command "$self gop_setting"] + for {set i 1} {$i<[llength $@properties]} {incr i} { + $self add .$self [list [lindex $@properties $i] integer -width 7] + } + $self gop_setting +} + +def CanvasPropertiesDialog gop_setting {} { + set entries [lrange $@properties 1 end] + foreach entry $entries { + if {!$@gop} { + .$self.$entry.entry configure -state disable + } else { + .$self.$entry.entry configure -state normal + } + } +} + +def CanvasPropertiesDialog apply {} { + if {![$@canvas editmode]} {$@canvas editmode= 1} + netsend [list .$@of coords $@xfrom $@yfrom $@xto $@yto $@width $@height $@gop $@xmargin $@ymargin] +} + +proc gatom_escape {sym} { + if {[string length $sym] == 0} {return "-"} + if {[string equal -length 1 $sym "-"]} {return [string replace $sym 0 0 "--"]} + return $sym +} + +proc gatom_unescape {sym} { + if {[string equal -length 1 $sym "-"]} {return [string replace $sym 0 0 ""]} + return $sym +} + +class_new BoxPropertiesDialog {PropertiesDialog} +def Box popup_properties {} {BoxPropertiesDialog new $self} +def Box popup_clear_wires {} {[$self canvas] selection= $self; [$self canvas] clear_wires} +def Box popup_remove_from_path {} {[$self canvas] selection= $self; [$self canvas] remove_obj_from_path} +def Box popup_delete_from_path {} {[$self canvas] selection= $self; [$self canvas] delete_obj_from_path} +def BoxPropertiesDialog init {of} { + super $of + wm title .$self "Box Properties" + pack [label .$self.huh -text "huh..."] + pack [label .$self.huh2 -text "this is where some #V properties should go"] +} + +class_new WirePropertiesDialog {PropertiesDialog} +def Wire popup_properties {} {WirePropertiesDialog new $self} +def WirePropertiesDialog init {of} { + super $of + wm title .$self "Wire Properties" + pack [label .$self.huh -text "huh..."] + pack [label .$self.huh2 -text "this is where some #V properties should go"] +} + +class_new GAtomPropertiesDialog {PropertiesDialog} + +def AtomBox popup_properties {} {GAtomPropertiesDialog new $self} + +# this is buggy due to miller's escapes vs iem's escapes. +def GAtomPropertiesDialog apply {} { + netsend [list .$@of reload $@w $@min $@max $@pos [gatom_escape $@lab] [gatom_escape $@rcv] [gatom_escape $@snd]] +} + +def GAtomPropertiesDialog init {of} { + super $of + foreach var {w min max pos} {set @$var $_($of:$var)} + foreach var {lab rcv snd} {set @$var [gatom_unescape $_($of:$var)]} + wm title .$self "Atom" + global properties + $self add .$self \ + {w entry -width 4} \ + {min entry -width 8} \ + {max entry -width 8} \ + {lab entry -width 20} \ + {pos side} \ + {snd entry -width 20} \ + {rcv entry -width 20} + #foreach name {w min max} {bind .$self.$name.entry <KeyPress-Return> "$self ok"} + .$self.w.entry select from 0 + .$self.w.entry select adjust end + focus .$self.w.entry +} + +class_new GraphPropertiesDialog {Dialog} + +def GraphPropertiesDialog apply {} { + netsend [list .$@of dialog $@x1 $@y1 $@x2 $@y2 $@xpix $@ypix] +} + +def GraphPropertiesDialog init {of} { + super $of + foreach var {x1 y1 x2 y2 xpix ypix} {set @$var $_($of:$var)} + wm title .$self "Graph" + pack [label .$self.label -text "GRAPH BOUNDS"] -side top + global properties + $self add .$self { + {x1 entry -width 7} \ + {x2 entry -width 7} \ + {xpix entry -width 7} \ + {y2 entry -width 7} \ + {y1 entry -width 7} \ + {ypix entry -width 7} + } + #.$self.xrangef.x2 select from 0 + #.$self.xrangef.x2 select adjust end + #focus .$self.xrangef.x2 +} + +class_new ArrayPropertiesDialog {Dialog} + +def ArrayPropertiesDialog apply {} { + regsub {^\$} $@name "#" name + netsend [list .$@apply arraydialog $name $@n $@saveit $@otherflag] +} + +def ArrayPropertiesDialog init {of} { + super $of + foreach var {name n saveit} {set @$var $_($of:$var)} + set @otherflag 0 + wm title $id "[say array] [say popup_properties]" + $self add .$self {name entry} {n entry} + pack [checkbutton .$self.saveme -text "save contents" -variable @saveit -anchor w] -side top + if {$newone != 0} { + pack [frame .$self.radio] -side top + foreach {i label} {0 "in new graph" 1 "in last graph"} { + pack [radiobutton .$self.radio.radio$i -value $i -variable @otherflag -text $label] -side top -anchor w + } + } else { + pack [checkbutton .$self.deleteme -text "delete me" -variable @otherflag -anchor w] -side top + } + if {$newone} {.$self.buttonframe.apply configure -state disabled} + bind .$self.name.entry <KeyPress-Return> "$self ok" + bind .$self.n.entry <KeyPress-Return> "$self ok" + .$self.name.entry select from 0 + .$self.name.entry select adjust end + focus .$self.name.entry +} + +class_new FloatBox {AtomBox} +class_new SymbolBox {AtomBox} + +def AtomBox init {mess} { + super $mess + set @clickpos {} + set width [font measure [$self look font] W] + set height [font metrics [$self look font] -linespace] + set @xs [expr ($width*$@w)+3] + set @ys [expr $height+3] +} + +def FloatBox init {mess} {super $mess; set @text 0;} + +def FloatBox calc {x y x1 y1} { + #puts "$@min $@max" + if {!$@min && !$@max} { + set d [expr $@ovalue+($y1-$y)*$@rate] + } else { + set span [expr $@max-$@min] + set l [expr $@max-$@min] + set d [clip [expr $@ovalue+(($y1-$y)*$span/($l+0.0))*$@rate] $@min $@max] + } + return $d +} + +def SymbolBox init {mess} {super $mess; set @text "symbol"} + +def AtomBox set {val} {set @text [format "%g" $val]; $self changed} + +def AtomBox setto {text} { + [$@canvas widget] configure -cursor {} + if { [string is double $text]} { + set @text $text; #so that the text gets updated immediately + netsend [list .$self float $text] + #[$self get_canvas] selection-= $self + } +} +def SymbolBox set {text} {set @text $text} +def SymbolBox setto {text} { + [$@canvas widget] configure -cursor {} + if {![string is double $text]} {netsend [list .$self symbol $text]} +} + +def FloatBox ftoa {} { + set f $@val + set is_exp 0 + if {[string length $@buf]>0} {return $@buf} + set buf [format %g $f] + set bufsize [string length buf] + if {$bufsize >= 5} { + # exponential mode + set is_exp [regexp -nocase e] + } + if {$bufsize > $@w} { + # must shrink number + if {$is_exp} { + #... + } { + #... + } + } + return $buf +} + +def AtomBox click {x y f target} { + set @clickx $x; set @clicky $y + set canvas [$self get_canvas] + set t [$canvas widget].${self}text + set @ovalue [clip $@text $@min $@max] + set @clickpos [list $x $y] + set @mouse [list $x $y] + $canvas focus= $self + set @rate [expr $f&1 ? 0.01 : 1.00] +} + +def AtomBox unclick {x y f target} { + if {$x == $@clickx && $y == $@clicky} {$self edit} + [$self get_canvas] focus= "" +} + +def AtomBox motion {x y f target} { + if {$@edit} {return} + mset {clx cly} $@clickpos + set @text [$self calc $x $y $clx $cly] + netsend [list .$self float [expr {0+$@text}]] +} + +def AtomBox log_ratio {} { + set diff [expr $@is_log ? log($@max/$@min) : ($@max-$@min)] + return [expr $diff / $@max] +} + +def AtomBox key_incr {val1 val2} { + set @text [expr $@text - $val2] + netsend [list .$self float [expr {0+$@text}]] +} + +def SymbolBox motion {x y f target} {} +class_new NumBox {Labelled IEMGUI AtomBox} + +def NumBox init {mess} { + super $mess + set @clicking 0 + set changed 0 + set @buf "" + set @text [clip 0 $@min $@max] +} + +def NumBox calc {x y x1 y1} { + set span [expr $@max-$@min] + set l [expr $@is_log ? $@log_height : ($@max-$@min)] + set d [expr {($y1-$y)*$span/($l+0.0)}] + set d [expr $@is_log ? $@ovalue*exp($d*$@rate*[$self log_ratio]) : $@ovalue+$d*$@rate] + set d [clip $d $@min $@max] + return $d +} + + +def NumBox reinit {mess} { + super $mess + set @text $@val +} + +def NumBox draw {} { + super + mset {x1 y1} [$self xy] + set xs [expr 4+10*$@w] + set ys $@h + set x2 [expr $x1+$xs] + set y2 [expr $y1+$ys] + set c [[$self get_canvas] widget] + set points [list $x1 $y1 [expr $x2-4] $y1 $x2 [expr $y1+4] $x2 $y2 $x1 $y2] + set xt [expr $x1+$ys/2+2] + set yt [expr $y1+$ys/2+1+$xs/34] + set points2 [list $x1 $y1 [expr $x1+$ys/2] [expr $y1+$ys/2] $x1 $y2] + set focused [$self == [$@canvas focus]] + if {$focused} {set color4 #00ff00} {set color4 [$self look bg]} + $self item BASE4 polygon $points2 -outline [$self look frame3] -fill $color4 + $c raise ${self}BASE4 +} + +def NumBox ftoa {} { + set f $@text + set is_exp 0 + if {[string length $@buf]>0} {return $@buf} + set buf [format %g $f] + set bufsize [string length buf] + if {$bufsize >= 5} { + # exponential mode + set is_exp [regexp -nocase e] + } + if {$bufsize > $@w} { + # must shrink number + if {$is_exp} { + #... + } { + #... + } + } + return $buf +} + +def NumBox unfocus {} {set @buf ""; $self changed} + +class_new Radio {BlueBox} + +def Radio reinit {mess} { + super $mess + switch [lindex $mess 4] { + hradio {set @orient 0} hdl {set @orient 0} + vradio {set @orient 1} vdl {set @orient 1} + default {set @orient 0} + } +} + +def Radio bbox {} { + mset {x1 y1} [$self xy] + set x2 [expr $x1+$@w*($@orient ?1:$@n)] + set y2 [expr $y1+$@w*($@orient ?$@n:1)] + list $x1 $y1 $x2 $y2 +} + +def Radio draw {} { + mset {x1 y1 x2 y2} [$self bbox] + super + for {set i 0} {$i<$@n} {incr i} { + $self item [list BUT$i BUT] rectangle \ + [list [expr $x1+3] [expr $y1+3] [expr $x1+$@w-3] [expr $y1+$@w-3]] \ + -fill #ffffff -outline #000000 + if {$@orient} {set y1 [expr $y1+$@w]} {set x1 [expr $x1+$@w]} + } + $self set $@on +} + +def Radio set {value} { + set c [$self get_canvas] + [$c widget] itemconfigure ${self}BUT -fill #ffffff + [$c widget] itemconfigure ${self}BUT$value -fill #000000 +} + +def Radio click {x y f target} { + mset {x1 y1} [$self xy] + set i [expr {($@orient ?$y-$y1:$x-$x1)/$@w}] + netsend [list .$self fout $i] +} + +def Radio key_incr {val1 val2} { + netsend [list .$self fout [expr $@on - $val2]] +} + +class_new Slider {BlueBox} + +# in sliders, @value is the kind of value that goes thru inlets and outlets +# whereas @val is always measured in "centipixels" (unzoomed). +def Slider reinit {mess} { + super $mess + set @knob_thick 4 + switch [lindex $mess 4] { + hsl {set @orient 0} + vsl {set @orient 1} + } + $self update_value +} + +def Slider update_value {} { + set span [expr {$@max-$@min}] + set l [expr $@orient ?$@h:$@w] + set @value [expr $@val*$span/($l-1)/100] + #set t [expr $@val * [$self slider_ratio] * 0.01] + #set @value [expr $@min*exp($t)] +} + +def Slider init {mess} { + super $mess + set @oposition $@min + $self update_value +} + +def Slider bbox {} { + mset {x1 y1} [$self xy] + if {!$@orient} { + list $x1 $y1 [expr $x1+$@w+$@knob_thick] [expr $y1+$@h] + } else { + list $x1 [expr $y1-$@knob_thick] [expr $x1+$@w] [expr $y1+$@h] + } +} + +#the value/centipixel ratio +def Slider slider_ratio {} { + set diff [expr $@is_log ? log($@max/$@min) : ($@max-$@min)] + return [expr $diff / ($@orient ? ($@h-1) : ($@w-1))] +} + +def Slider draw_knob {} { + mset {x1 y1 x2 y2} [$self bbox] + set l [expr $@orient ?$@h:$@w] + set span [expr {$@max-$@min}] + set color [$self look bg] + set scaled [expr {$@value*($l-1)/$span}] + set thick [expr $@knob_thick/2] + if {$@orient} { + set y1 [expr $y1+$@knob_thick] + set y [expr $y1+$@h-$scaled-2] + set coords [list [expr $x1+2] $y [expr $x1+$@w-2] [expr $y-2]] + } else { + set x2 [expr $x1-$@knob_thick] + set x [expr $x1+$scaled] + set coords [list $x [expr $y1+$thick] [expr $x+2] [expr $y1+$@h-$thick]] + } + $self item KNOB rectangle $coords -outline red -fill [darker $color] +} + +def Slider draw {} { + mset {x1 y1 x2 y2} [$self bbox] + #if {$@orient} {set y1 [expr $y1-2]} {set x1 [expr $x1-2]} + #if {$@orient} {set ys [expr $@h+5]} {set xs [expr $@w+5]} + super + $self draw_knob + $self update_value +} + +# not used +def Slider draw_notches {} { + if {$@orient} { + set thick [clip [expr $xs/3] 1 5] + set x3 [expr $x1+$xs-$thick/2-2] + set eighth [expr round($ys/8-1)] + set coords [list $x3 $y1 $x3 [expr $y1+$ys]] + } else { + set thick [clip [expr $ys/3] 1 5] + set y3 [expr $y1+$ys-$thick/2-2] + set eighth [expr $xs/8] + set coords [list $x1 $y3 [expr $x1+$xs] $y3] + } + # there were supposed to be 7 notches... i don't remember what happened here. + $@canvas item NOTCH $coords -dash [list 1 $eighth 1 $eighth] -width $thick -fill [darker [$self look bg]] +} + +def Slider click {x y f target} { + set canvas [$self get_canvas] + mset {type id detail} [$canvas identify_target $x $y $f] + if {$type == "label"} {return} + $canvas focus= $self + set @click_at [list $x $y] + set @rate [expr $f&1 ? 0.01 : 1.00] + if {!$@steady} { + mset {x1 y1 x2 y2} [$self bbox] + set t [expr [$self calc $x $y $x1 $y2]*$@rate] + set @value [expr $@is_log ? [expr $@min*exp($t*[$self slider_ratio])] : $t] + set @oposition $t + netsend [list .$self float $@value] + } else {set @oposition $@value} +} + +def Slider unclick {x y f target} { + ### keep focus if only clicked. do we want that feature? + # if {[distance $@click_at [list $x $y]] == 0} {return} + set canvas [$self get_canvas] + $canvas focus= "" +} + +def Slider motion {x y f target} { + set canvas [$self get_canvas] + set focused [$self == [$canvas focus]] + if {!$focused} {return} + mset {clx cly} $@click_at + set d [$self calc $x $y $clx $cly] + set t ($@oposition+$d*$@rate) + set value [expr $@is_log ? [expr $@min*exp($t*[$self slider_ratio])] : $t] + set out [clip $value $@min $@max] + netsend [list .$self float $out] +} + +def Slider key_incr {val1 val2} { + set @value [expr $@value - $val2] + netsend [list .$self float $@value] +} + +def Slider calc {x y x1 y1} { + set span [expr $@max-$@min] + set l [expr {$@orient ?$@h:$@w}] + set d [expr {($@orient ?$y1-$y:$x-$x1)*$span/($l+0.0)}] + return $d +} + +def Slider unfocus {} {$self draw} + +class_new Labelled {} + +def Labelled draw {} { + global leet + super + mset {x1 y1} [$self xy] + set lx [expr $x1+$@ldx] + set ly [expr $y1+$@ldy] + set label $@lab; switch -- $label { empty { set label "" }} + set lfont [list [lindex {courier helvetica times} $@fstyle] $@fs bold] + set lcolor [parse_color $@lcol] + if {$leet} { + set text [string map -nocase {a 4 e 3 t 7 s 5 i 1 o 0 g 9} $label] + } else { + set text $label + } + $self item LABEL text [list $lx $ly] -text $text -anchor w -font $lfont -fill $lcolor +} +#-----------------------------------------------------------------------------------# +class_new Bang {BlueBox} +def Bang init {mess} { + super $mess + set @flash 0 + set @count 0 +} + +def Bang bbox {} { + mset {x1 y1} [$self xy] + list $x1 $y1 [expr $x1+$@w] [expr $y1+$@w] +} + +def Bang draw {} { + super + mset {x1 y1 x2 y2} [$self bbox] + if {$@flash} { + set rect [list [expr $x1+2] [expr $y1+2] [expr $x2-2] [expr $y2-2]] + #$self item BUT oval $rect -fill [color_* [$self look bg] [parse_color $@fcol]] + set fcol [color_* [$self look bg] [parse_color $@fcol]] + set bcol [color_* [$self look bg] [parse_color $@bcol]] + $self item BUT oval $rect -fill $fcol + after 100 [list $self item BUT oval $rect -fill $bcol] + set @flash 0 + } else { + set colour [parse_color $@bcol] + set rect [list [expr $x1+2] [expr $y1+2] [expr $x2-2] [expr $y2-2]] + $self item BUT oval $rect -fill [color_* [$self look bg] $colour] -outline [$self look frame3] + } +} +def Bang unclick {x y f target} {} +def Bang click {x y f target} {netsend [list .$self bang]} +def Bang bang {count} {set @count $count; set @flash 1} +def Bang key_incr {val1 val2} {netsend [list .$self bang]} + +class_new Toggle {BlueBox} + +def Toggle bbox {} { + mset {x1 y1} [$self xy] + list $x1 $y1 [expr $x1+$@w] [expr $y1+$@w] +} + +def Toggle draw {} { + super + mset {x1 y1 x2 y2} [$self bbox] + set colour [parse_color $@bcol] + set t [expr int(($@w+29)/30)] + set fill [color_* [$self look bg] $colour] + set x3 [expr $x1+$t+2]; set y3 [expr $y1+$t+2] + set x4 [expr $x2-$t-2]; set y4 [expr $y2-$t-2] + if {$@on} { + set fill [parse_color $@fcol] + } { + set fill [color_* [$self look bg] [parse_color $@bcol]] + } + $self item X1 line [list $x3 $y3 [expr $x4+1] [expr $y4+1]] -width $t -fill $fill + $self item X2 line [list $x3 $y4 [expr $x4+1] [expr $y3-1]] -width $t -fill $fill +} + +def Toggle unclick {x y f target} {} +def Toggle click {x y f target} { + if {!$@on} {set @on 1} {set @on 0} + netsend [list .$self float $@on] + $self changed +} + +class_new Vu {IEMGUI Box} + +set 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 +} + +def Vu init {mess} { + super $mess + set @value 0 + set @peak 0 +} + +def Vu bbox {} { + mset {x1 y1} [$self xy] + list $x1 $y1 [expr $x1+$@w] [expr $y1+$@h] +} + +def Vu led_size {} { + set n [expr $@h/40] + if {$n < 2} {set n 2} + return [expr $n-1] +} + +def Vu draw {} { + global vu_col + mset {x1 y1 x2 y2} [$self bbox] + set colour [parse_color $@bcol] + super + $self draw_io + set led_size [$self led_size] + set x3 [expr $x1+$@w/4] + set x4 [expr $x2-$@w/4] + $self item BASE rectangle [list $x1 $y1 $x2 $y2] -width 0 -fill [color_* [$self look bg] $colour] + for {set i 1} {$i<=40} {incr i} { + set y [expr $y1 + ($led_size+1)*(41-$i) - ($led_size+1)/2] + $self item RMS${i} rectangle [list $x3 $y $x4 [expr $y+$led_size]] \ + -fill [parse_color [lindex $vu_col $i]] -width 0 + } + #if {!$@zoom} {return} + set lfont [list [lindex {courier helvetica times} $@fstyle] $@fs bold] + set lcolor [parse_color $@lcol] + set i 0 + foreach level { <-99 -50 -30 -20 -12 -6 -2 -0dB +2 +6 >+12 } { + set k1 [expr $led_size+1] + set k2 41 + set k3 [expr $k1/2] + set k4 [expr $y1-$k3] + set yyy [expr $k4 + $k1*($k2-4*$i)] + $self item SCALE{$level} text [list [expr $x2+4] [expr $yyy+$k3-3]] \ + -text $level -anchor w -font $lfont -fill $lcolor + incr i + } + set y [expr $y1 + ($led_size+1)*(41-$@value) - ($led_size+1)/2] + $self item MASK rectangle [list $x3 $y1 $x4 $y] -width 0 -fill [color_* [$self look bg] $colour] + set c [lindex $vu_col [expr int($@peak)]] + set y [expr $y1 + ($led_size+1)*(41-$@peak) - ($led_size+1)/2] + $self item PEAK rectangle [list $x1 $y $x2 [expr $y+$led_size]] -fill [parse_color $c] -width 0 +} + +def Vu rms= {rms } {set @value $rms; $self changed rms} +def Vu peak= {peak} {set @peak $peak; $self changed peak} + +def Vu set {i j} { + set @value $i + set @peak $j + $self changed +} + +catch { + package require tkdnd + dnd bindtarget . text/uri-list <Drop> {open_file %D} +} + +class_new Dropper {View} + +# somewhat broken... +def Dropper draw {} { + set c [$@canvas widget] + set isnew [expr [llength [$c gettags ${self}BASE]] == 0] + mset {x1 y1} [$self xy] + set xs $@w + set colour [parse_color $@fcol] + set lcolour [parse_color $@lcol] + super + if {$isnew} { + canvas $c.${self}DROP -width $xs -height $xs -bg $colour \ + -highlightbackground $lcolour -highlightcolor $colour + $c create window [expr $x1+7] [expr $y1-2] -window $c.${self}DROP -anchor nw -tags $c.${self}window + if {[catch { + dnd bindtarget $c.${self}DROP text/uri-list <Drop> "pd \"x[list ${self}] symbol \[ enquote %D \] ;\"" + }]} { + post "dropper: dnd not installed" + } + } { + $c coords $@canvas.${self}window [expr $x1 + 7] [expr $y1 - 2] + $c.${self}DROP configure -width $xs -height $xs -bg $colour \ + -highlightbackground $lcolour -highlightcolor $colour + } +} + +def Dropper erase {} {destroy $@canvas.${self}DROP; super} + +class_new Cnv {Labelled IEMGUI Box} + +def Cnv draw {} { + mset {x1 y1} [$self xy] + $self item BASE rectangle [list $x1 $y1 [expr $x1+$@w] [expr $y1+$@h]] -fill [parse_color $@bcol] + super +} + +def Cnv bbox {} { + mset {x1 y1} [$self xy] + return [list $x1 $y1 [expr $x1+$@w] [expr $y1+$@h]] +} + +class_new Array {Box} + +def Array init {mess} { + super $mess + set @name [lindex $mess 2] + set @length 0 + set @data {} + set @draw 0 +} + +def Array bbox {} { + return {0 0 1 1} ;# huh? +} + +def Array draw_name {} { + mset {x_off y_off} [$@canvas xy] + $self item TEXT text [lmap + [list $x_off $y_off] 2] \ + -font [View_look $self font] -text $@name \ + -fill [View_look $self fg] -anchor nw +} + +def Array draw {} { + $self draw_name + mset {x_off y_off} [$@canvas xy] + set m [$@canvas get_mess] + mset {xfrom yto xto yfrom pixwidth pixheight} $m + if {[winfo exists [$@canvas widget]]} { + mset {c_width c_height} [$@canvas get_dimen] + set width [expr $c_width / $@length] + set i 0 + foreach val $@data { + if {!$val} {set val 0.0} + set y [expr $c_height - (((double($val)+abs($yfrom))/($yto-($yfrom)) * $c_height))] + set x1 [expr $width * $i] + set x2 [expr $x1 + $width] + set line [list $x1 $y $x2 $y] + $self item elem${i} line $line -fill [$@canvas look compfg] -width 2 -tags "$self ${self}elem${i}" + #.$self.c raise ${self}elem${i} + incr i + } + } else { + set width [expr $pixwidth / $@length] + set canvas [$self get_canvas] + set i 0 + foreach val $@data { + if {!$val} {set val 0.0} + #set val2 [lindex $@data [expr $i+1]] + set y [expr ($pixheight - ((double($val)+abs($yfrom))/($yto-($yfrom)) * $pixheight)) + $y_off] + #set y2 [expr ($pixheight - ((double($val2)+abs($yfrom))/($yto-($yfrom)) * $pixheight)) + $y_off] + set x1 [expr ($width * $i) + $x_off] + set x2 [expr $x1 + $width] + set line [list $x1 $y $x2 $y] + $self item ${self}ELEM${i} line $line -fill [$self look fg] -width 2 + incr i + } + [$canvas widget] raise $self + #set width [expr $pixwidth / [expr $@length-1]] + #set canvas [$self get_canvas] + #set i 0 + #for {set i 0} {$i < [expr $@length-1]} {incr i} { + # #if {!$val} {set val 0.0} + # set val [lindex $@data [expr $i]] + # set val2 [lindex $@data [expr $i+1]] + # set y [expr ($pixheight - ((double($val)+abs($yfrom))/($yto-($yfrom)) * $pixheight)) + $y_off] + # set y2 [expr ($pixheight - ((double($val2)+abs($yfrom))/($yto-($yfrom)) * $pixheight)) + $y_off] + # set x1 [expr ($width * $i) + $x_off] + # set x2 [expr $x1 + $width] + # set line [list $x1 $y $x2 $y2] + # $self item ${self}ELEM${i} line $line -fill [$self look fg] -width 0 + #} + } +} + +def Array click {x y f target} { + if {[winfo exists [$@canvas widget]]} {set canvas $@canvas} else {set canvas [$@canvas canvas]} + $canvas focus= $self + set @draw 1 +} +def Array unclick {x y f target} { + if {[winfo exists [$@canvas widget]]} {set canvas $@canvas} else {set canvas [$@canvas canvas]} + $canvas focus= "" + set @draw 0 +} +def Array motion {x y f target} { + if {!$@draw} return + if {[winfo exists [$@canvas widget]]} { + mset {c_width c_height} [$@canvas get_dimen] + mset {xfrom yto xto yfrom pixwidth pixheight} [$@canvas get_mess] + set width [expr $c_width / $@length] + set i [format %d [expr int($x/$width)]] + set x1 [expr $width * $i] + set x2 [expr $x1 + $width] + set line [list $x1 $y $x2 $y] + set val [expr (($c_height-$y)/$c_height) * ($yto-($yfrom)) + ($yfrom)] + netsend [list .$self $i $val] + } else { + mset {xfrom yto xto yfrom pixwidth pixheight} [$@canvas get_mess] + mset {x_off y_off} [$@canvas xy] + set width [expr $pixwidth / $@length] + set i [format %d [expr int(($x-$x_off)/$width)]] + set val [expr (($pixheight-$y+$y_off)/$pixheight) * ($yto-($yfrom)) + ($yfrom)] + netsend [list .$self $i $val] + } +} +def Array length= {val} {set @length [format %f $val]} +def Array name= {val} {set @name $val} +def Array array_set {data_list} { + if {[llength $data_list] == $@length} { + set @data {} + for {set i 0} {$i < $@length} {incr i} { + lappend @data [lindex $data_list $i] + } + } else { + puts "error....." + } +} + +############ evaluator + +class_new Listener {Thing} + +def Listener init {serf name command} { + set @history [History new 20] + set @command $command + set @expanded 0 + set @serf $serf + frame $serf + pack [frame $serf.1] -side left -fill y + pack [frame $serf.1.1] -side bottom + pack [button $serf.1.1.expander -image icon_plus -command "$self toggle_expand"] -side left + pack [label $serf.1.1.label -width 11 -text "$name: " -font {Courier 10}] -side left + pack [entry $serf.entry -width 40 -font $::look(View:font)] -side left -fill x -expand yes + pack $serf -fill x -expand no + bind $serf.entry <Up> "$self scroll_history +1" + bind $serf.entry <Down> "$self scroll_history -1" + bind $serf.entry <Return> "$self eval" +} + +def Listener toggle_expand {} { + set @expanded [expr 1-$@expanded] + if {$@expanded} {$self expand} {$self unexpand} +} + +def Listener expand {} { + set e $@serf.entry + set text [$e get] + destroy $e + pack [text $e -width 40 -height 8] -side left -fill x -expand yes + $e insert 0.0 $text + $@serf.1.1.expander configure -image icon_minus + bind $e <Alt-Return> "$self eval" +} + +def Listener unexpand {} { + set e $@serf.entry + set text [$e get 0.0 end] + regsub "\n$" $text "" text + destroy $e + pack [entry $e -width 40] -side left -fill x -expand yes + $e insert 0 $text + $@serf.1.1.expander configure -image icon_plus + bind $e <Up> "$self up" + bind $e <Down> "$self down" + bind $e <Return> "$self eval" +} + +def Listener replace {stuff} { + $@serf.entry delete 0 end + $@serf.entry insert 0 $stuff + $@serf.entry icursor end +} + +def Listener scroll_history {incr} { + if {![$@history histi]} {$@history set_hist 0 [$self get_command]} + $self replace [$@history traverse $incr] + +} + +def Listener append {v} { + $@history prepend $v + lappend @hist $v; set @histi [llength $@hist] +} + +def Listener get_command {} { + set e $@serf.entry + if {$@expanded} { + set l [$e get 0.0 end]; return $l + } else { + set l [$e get]; return $l + } + +} + +def Listener eval {} { + set e $@serf.entry + $@history histi= 0 + set l [$self get_command] + $self append $l + if {$@expanded} {$e delete 0.0 end} {$e delete 0 end} + $@command $self $l +} + +proc tcl_eval {self l} {post %s "tcl: $l"; post %s "returns: [uplevel [info level] $l]"} +proc pd_eval {self l} {post %s "pd: $l"; netsend $l} +proc canvas_eval {self l} {post %s "tcl: $l"; post %s "returns: [uplevel [info level] [join [list [$self canvas] $l]]]"} +############ button bar + +set butt { + {ObjectBox Object {obj}} + {MessageBox Message {msg}} + {FloatBox Number {floatatom}} + {SymbolBox Symbol {symbolatom}} + {CommentBox Comment {text}} + {bng bng {obj bng}} + {tgl tgl {obj tgl}} + {nbx nbx {obj nbx}} + {vsl vsl {obj vsl}} + {hsl hsl {obj hsl}} + {vradio vradio {obj vradio}} + {hradio hradio {obj hradio}} + {vu vu {obj vu}} + {cnv cnv {obj cnv}} + {Graph graph {graph}} + {Array array {menuarray 0}} +} +# {dropper dropper {pd %W dropper 0}} + +proc button_bar_add {x y} { + global butt + lappend butt [list $x $y noload] +} + +if {$tk} { + set dir $cmdline(icons) + foreach icon {mode_edit mode_run pd} {image create photo icon_$icon -file $dir/$icon.gif} + foreach b $butt {mset {icon name cmd} $b; image create photo icon_$icon -file $dir/$icon.gif} +} + +class_new ButtonBar {View} + +def ButtonBar init {canvas} { + set @canvas $canvas + set bb .$@canvas.bbar + frame $bb + pack [button $bb.edit -image icon_mode_edit -border 1 -command [list $@canvas editmodeswitch]] -side left + foreach e $::butt { + mset {icon name cmd} $e + pack [button $bb._$name -image icon_$icon -border 1 -command "$@canvas new_object $cmd"] -side left + balloon $bb._$name [say $name] + } + pack [entry $bb.name -font {helvetica -12} -width 8 -border 0] -side right + pack [spinbox $bb.scale -width 5 -command "$canvas zooming %d" -state readonly] -side right + $bb.scale set [format %d%% [expr int(100*[$@canvas zoom])]] + $bb.name insert 0 $@canvas +} + +def ButtonBar widget {} {return .$@canvas.bbar} + +proc obj_create {c flag} {} + +############ crosshair + +class_new Crosshair {View} + +def Crosshair classtags {} {return {}} + +def Crosshair init {canvas} { + super + set @canvas $canvas + $self data= 0 0 {none} +} + +def Crosshair data= {x y target} { + set @x $x + set @y $y + set @target $target +} + +def Crosshair draw {} { + + mset {type id detail} $@target + set x $@x; set y $@y + + if {[$@canvas look hairsnap]} { + switch -regexp -- $type {^object|outlet|inlet$ {mset {x y x3 y3} [$id bbox]}} + } + mset {x1 y1 x2 y2} [$self display_area] + + set h1 [list $x1 $y $x2 $y] + set v1 [list $x $y1 $x $y2] + $self item VHAIR1 line $v1 -fill [$@canvas look crosshair] -width 1 -dash {4 4 4 4} + $self item HHAIR1 line $h1 -fill [$@canvas look crosshair] -width 1 -dash {4 4 4 4} +} + +#def Crosshair erase {} {$self item_delete VHAIR1; $self item_delete HHAIR1} +class_new Sense {View} + +def Sense init {canvas} { + super + set @canvas $canvas + $self data= 0 0 0 red +} + +def Sense data= {x y range col} { + set @x $x + set @y $y + set @range $range + set @col $col +} + +def Sense flash {x y sense col} { + $self data= $x $y $sense $col + $self draw + after 500 $self erase +} + +def Sense draw {} { + set c [$@canvas widget] + set x1 [expr $@x-$@range]; set y1 [expr $@y-$@range] + set x2 [expr $@x+$@range]; set y2 [expr $@y+$@range] + $self item SENSE oval [list $x1 $y1 $x2 $y2] -fill $@col -outline yellow +} + +def View display_area {} { + set c [$@canvas widget]; set z [$@canvas zoom] + set edge 10 + set x1 [expr {int([$c canvasx $edge]/$z)}] + set y1 [expr {int([$c canvasy $edge]/$z)}] + set x2 [expr {int(([$c canvasx [winfo width $c]]-$edge)/$z)}] + set y2 [expr {int(([$c canvasy [winfo height $c]]-$edge)/$z)}] + return [list $x1 $y1 $x2 $y2] +} + +class_new Grid {View} + +def Grid init {canvas} { + super + set @canvas $canvas + set c [$@canvas widget] + set @width [winfo width $c] + set @height [winfo height $c] + set @size [$@canvas look grid_size] + set @col [$@canvas look grid] + set @gap 5 +} + +def Grid classtags {} {return {}} + +def Grid update {h w} {set @width $w; set @height $h} +def Grid size= {size} {set @size $size} +def Canvas snap_grid {} {return [$self look snap_grid]} +def Canvas snap_grid= {val} {set ::look(Canvas:snap_grid) $val} + +def Canvas snap_objs2grid {} { + if {![$self editmode]} {return} + foreach obj [$@objects values] { + mset {x y} [$obj xy] + set grid [$self look grid_size] + set x [expr floor($x/$grid)*$grid] + set y [expr floor($y/$grid)*$grid] + $obj moveto $x $y + } +} + +def Grid draw {} { + mset {x1 y1 x2 y2} [$self display_area] + set c [$@canvas widget] + set lowest [$@canvas lowest_item] + $self draw_lines $x1 $x2 $y1 $y2 VL + $self draw_lines $y1 $y2 $x1 $x2 HL + if {$lowest != -1} {$c lower $self $lowest} +} + +def Grid draw_lines {v1 v2 v3 v4 tag} { + set s $@size; set g $@gap + for {set i $v1} {$i < $v2} {incr i} { + #if {$l%$g == 0} {set width 1;set dash [list 7 1]} {set width 1;set dash [list 1 4 1 4]} + if {![expr {$i % int($s*$g)}]} {set w 1;set d [list 7 1]} {set w 1;set d [list 1 4 1 4]} + if {![expr {$i % int($s)}]} { + switch $tag {VL {set line [list $i $v3 $i $v4]} HL {set line [list $v3 $i $v4 $i]}} + $self item ${tag}$i line $line -fill $@col -width $w -dash $d + } + } +} + +def Canvas lowest_item {} { + set c [$self widget] + set all [$c find withtag foo] + if {![llength $all]} {return -1} + set lowest [lindex [$c gettags [lindex $all 0]] 0] + return $lowest +} + +#def Canvas grid_size {} {return [$self look grid_size]} +def Canvas grid_size= {size} { + set ::look(Canvas:grid_size) $size + if {[$self editmode]} {$@grid size= $size; $@grid erase; after 0 $@grid draw} +} +############ tooltips (only those that are drawn as canvas items) + +class_new Tooltip {View} + +def Tooltip init {canvas pos curpos text type iserror} { + set @canvas $canvas + set @pos $pos + set @curpos $curpos + set @text $text + set @type $type + set @iserror $iserror +} + +def Tooltip iserror {} {return $@iserror} +def Tooltip curpos {} {return $@curpos} +def Tooltip text {} {return $@text} + +def Tooltip draw {} { + set c [$@canvas widget] + if {$@iserror} { + set fg "#ffffff"; set bg "#dd0000" + } else { + set fg "#000000"; set bg "#ffffcc" + } + mset {x y} $@pos + switch -- $@type { + o {set ny [expr {$y+24}]} + i {set ny [expr {$y-24}]} + } + $self item TEXT text [list [expr $x+12] $ny] -fill $fg -text $@text -anchor w + mset {x1 y1 x2 y2} [l+ [$c bbox ${self}TEXT] [list -4 -4 +4 +4]] + switch -- $@type { + o {set coords [list $x1 $y1 [expr $x1+8] $y1 $x $y [expr $x1+16] $y1 $x2 $y1 $x2 $y2 $x1 $y2 $x1 $y1]} + i {set coords [list $x1 $y1 $x2 $y1 $x2 $y2 [expr $x1+16] $y2 $x $y [expr $x1+8] $y2 $x1 $y2 $x1 $y1]} + } + $self item RECT polygon $coords -fill $bg -outline $fg + $c lower ${self}RECT ${self}TEXT +} + +# $c delete tooltip_bg tooltip_fg + +set tooltip "" + +def Canvas show_tooltip {x y text type {iserror 0}} { + global tooltip + if {$tooltip ne "" && [$tooltip text] eq $text} {return} + if {$tooltip ne ""} {$tooltip delete} + set tooltip [Tooltip new $self [list $x $y] $@curpos $text $type $iserror] + $tooltip draw +} + +############ class browser + +class_new ServerClassDict {Observable Thing} +def ServerClassDict init {} { + +} + +# Completion/Browser init can be cleaned up a bit more, do it later... +class_new ClassBrowser {Dialog} +class_new Browser {ClassBrowser} +def Browser init {name x y textbox} {super $name $x $y $textbox} +class_new Completion {ClassBrowser} +def Completion init {name x y textbox} {super $name $x $y $textbox} + +def Completion cancel {} { + bind $@textbox <Key> "$@textself key_input %W %x %y %K %A 0" + bind $@textbox <Control-Return> "$@textself key_input %W %x %y 10 %A 0" + bind $@textbox <Return> "$@textself unedit" + bind $@textbox <Tab> "$@textself key_input %W %x %y %K %A 0" + focus $@textbox + $self delete +} +def ClassBrowser delete {} {set @exist 0; super} + +def ClassBrowser init {name x y textbox} { + set @name $name + set @width 0 + set @height 0 + # so that in completion mode, it know which textbox to switch the focus to + set @textbox $textbox + netsend [list pd update-path] + netsend [list pd update-class-list $self list_callback] +} + +def ClassBrowser fill_box {s} { + global class_list + $@listbox delete 0 end + foreach class $class_list { + if {[string length $s]==0 || [string first $s $class]>=0} { + set t "\[$class\]" + if {[can_say $class]} {append t " [say $class]"} + $@listbox insert end $t + #if {[string length $t] > [string length $@width]} {set @width [string length $t]} + if {[string length $t] > $@width} {set @width [string length $t]} + } + } + set none [say no_matches] + if {![$@listbox size]} {$@listbox insert 0 $none; set @width [string length $none]} + $@listbox selection set 0 0 +} + +def Completion fill_box {s} { + super $s + wm maxsize .$self [winfo reqwidth .$self.comp] [winfo reqheight .$self.comp] +} + +def Browser fill_box {s} { + super $s + .$self.title configure -text [format [say how_many_object_classes] [$@listbox size] [llength $::class_list]] +} + +def ClassBrowser search_for_externs {} { + global pd_path class_list + foreach dir $pd_path { + catch { + set xs [glob "$dir/*.pd*"] + foreach x $xs { + set fn [lindex [file split $x] end] + set fn [join [lrange [split $fn .] 0 end-1] .] + lappend class_list $fn + } + } + } +} + +def ClassBrowser info {listbox} { + set class [$self current_class] + if {$class != ""} {netsend [list pd update-class-info $class $self info_callback]} +} + +def Browser list_callback {} { + $self search_for_externs + set class_list [luniq [lsort $::class_list]] + + toplevel .$self + set f .$self.cl + pack [frame $f] -side top -fill both -expand yes + pack [label .$self.title -text ""] -side top + listbox $f.1 -width 50 -height 20 -yscrollcommand "$f.2 set" -activestyle none + scrollbar $f.2 -command "$f.1 yview" + text $f.3 -width 30 -height 20 -yscrollcommand "$f.4 set" + scrollbar $f.4 -command "$f.3 yview" + set @listbox $f.1 + + frame $f.5 + button $f.5.help -text [say help] -command [list $self help] + pack $f.5.help -side top + pack $f.5 -side left -fill y -expand no + pack $f.1 -side left -fill both -expand yes + pack $f.2 -side left -fill y -expand no + pack $f.3 -side left -fill both -expand yes + pack $f.4 -side left -fill y -expand no + + set b .$self.butt + frame $b + pack [label $b.1 -text [say filter]] -side left + pack [entry $b.2 -width 15] -side left + pack [button $b.close -text [say close] -command "destroy .$self"] -side right + pack $b -side bottom -fill x -expand no + set @textbox $b.2 + $self fill_box "" + #bind $f.1 <Button-1> "after 1 \"$self info $f.1 \"" + foreach w [list $f.1 $b.2] { + bind $w <KeyPress> "after 1 \"$self key %K 0\"" + bind $w <Shift-KeyPress> "after 1 \"$self key %K 1\"" + bind $w <Return> [list $self help] + } + focus $@textbox +} + +def Browser help {} { + set f .$self.cl + netsend [list pd help [$self current_class]] +} + +def Completion list_callback {} { + $self search_for_externs + set class_list [luniq [lsort $::class_list]] + toplevel .$self + wm protocol .$self WM_DELETE_WINDOW "$self cancel" + wm overrideredirect .$self 1 + set canvas $@name + set f .$self.comp + set @listbox $f + set @rootx [winfo rootx .$@name.c] + set @rooty [winfo rooty .$@name.c] + set @max [wm maxsize .$self] + if {[regexp {(x[0-9a-z]{6,8})text$} $@textbox dummy textself]} {set @textself $textself} + if {[$canvas look showcomp] <= 20} {set @height [$canvas look showcomp]} else {set @height 20} + listbox $f -width $@width -height $@height -relief flat -activestyle dotbox -font $::look(View:font) \ + -bg [$@textself look bg] -selectbackground [$@textself look fg] \ + -fg [$@textself look fg] -selectforeground [$@textself look bg] + $self adjust_box + bind $f <Button-1> "after 1 \"$self complete\"" + bind $f <Return> "after 1 \"$self complete\"" + bind $f <KeyPress> "$self key %K 0" + bind $f <Shift-KeyPress> "$self key %K 1" + bind $@textbox <Tab> "$self key %K; break" + bind $@textbox <KeyPress> "$self key %K " + focus .$self.comp +} + +def Completion adjust_box {} { + mset {x1 y1 x2 y2} [lmap * [$@textself bbox] [$@name zoom]] + set x1 [format %0.f $x1];set y1 [format %0.f $y1] + set x2 [format %0.f $x2];set y2 [format %0.f $y2] + $self fill_box [$@textbox get 1.0 1.end] + set f .$self.comp + $f configure -width $@width + set box_width [winfo reqwidth $f] + set box_height [winfo reqheight $f] + pack $f -side left -expand yes + + .$self configure -width $box_width + .$self configure -height $box_height + + #test the right edge of the screen, assuming the left edge has enough space + if {[expr $x1+$@rootx+$box_width] < [lindex $@max 0]} { + set box_x [expr $x1+$@rootx] + } else { + set box_x [expr $x2 - $box_width + $@rootx] + } + #test the lower edge of the screen, assuming the upper edge has enough space + if {[expr $y2+$@rooty+$box_height] < [lindex $@max 1]} { + set box_y [expr $y2 + 5 + $@rooty] + } else { + set box_y [expr $y1 - $box_height - 2 + $@rooty] + } + + wm geometry .$self [winfo reqwidth .$self]x[winfo reqheight .$self]+$box_x+$box_y + +} + +def ClassBrowser current_class {} { + set i [$@listbox curselection] + if {$i == ""} {return {}} + return [string range [lindex [$@listbox get $i] 0] 1 end-1] +} + +def ClassBrowser complete {} { + if {[regexp {x([0-9a-z]{6,8})text$} $@textbox obj]} { + set cut [string first "text" $obj] + set obj [string range $obj 0 [expr $cut -1]] + } + set class [$self current_class] + $@textbox delete 1.0 1.end + $@textbox insert 1.0 $class + + $obj unedit + destroy .$self +} + +def ClassBrowser key {key {shift 0}} { + switch -regexp -- $key { + Up|Down { + if {[focus] != $@listbox} { + focus $@listbox + event generate $@listbox <KeyPress> -keysym $key + } else { + if {$self == "browser"} {$self info $@listbox} + } + } + Escape {after 1 "$self cancel"} ;# doesn't really work + Tab { + focus $@listbox + set next [$@listbox index active] + incr next + if {$next >= [$@listbox size]} {set next 0} + $@listbox activate $next + $@listbox selection clear 0 [expr [$@listbox size] - 1] + $@listbox selection set $next $next + #if {$next >= [expr $@height - 1]} {$@listbox yview scroll 1 units} + $@listbox see $next + if {$self == "browser"} {$self info $@listbox} + } + BackSpace { + if {[focus] == $@listbox} {focus $@textbox} + #classbrowser uses entry as input widget, where as completion is text widget... + switch $self { + browser {$self fill_box [$@textbox get]} + completion {$self adjust_box; $@textself resize; $@textself changed} + } + } + default { + $self key_default $key + } + } +} + +def Browser key_default {key} { + if {[focus] == $@listbox} { + if {[regexp {^[a-zA-Z0-9~/\._]{1}$} $key]} { + .$self.butt.2 insert end $key + $self fill_box [$@textbox get] + } + } else {$self fill_box [$@textbox get]} +} + +def Completion key_default {key} { + if {[focus] == $@listbox} { + if {[regexp {^[a-zA-Z0-9~/\._]{1}$} $key]} { + $@textbox insert 1.end $key + $@textself after_key $@textbox + $self adjust_box + focus $@textbox + } + } + if {[focus] == $@textbox & $key != "Tab"} { + $self adjust_box + $@textself resize + #hum, no idea why i need after 1 for it to work... + after 1 $@textself after_key $@textbox + } +} + +def ClassBrowser info_callback {class} { + global class_info + set f .browser.cl + set class [$self current_class] + $f.3 delete 0.0 end + $f.3 insert end "class $class\n" + foreach {k v} $class_info($class) {$f.3 insert end "$k=\"$v\"\n"} +} + +def TextBox propose_completions {} { + set c [$@canvas widget] + set widget $c.${self}text + set propose $c.${self}propose + #$propose configure -state normal + if {![info exists ::class_list]} { + netsend [list pd update-class-list $self propose_completions] + return + } + set r {} + set c {} + set n 0 + set prev "" + foreach class [luniq [lsort $::class_list]] { + if {[string length $@text]==0 || [string first $@text $class]>=0} { + if {[string compare [say $class] "{{$class}}"]} { + lappend r "$class : [say $class]" + } { + lappend r $class + } + lappend c $class + incr n + } + if {$n > 16} {lappend r ...; break} + } + set r [join $r "\n"] + mset {x1 y1 x2 y2} [$self bbox] + set @action [Completion new_as completion $@canvas $x1 $y1 $widget] +} + +############ properties_dialog ######### +proc change_entry {self val} { + set v [expr [$self get]+$val] + $self delete 0 end + $self insert 0 $v +} + +class_new Dialog {View} + +def Dialog add_stuff {f name label} { + frame $f +# frame $f.label -width $@label_width -borderwidth 2 +# pack [button $f.label.0 -image "icon_empty" -width $@label_width] -side left +# place [message $f.label.1 -text $label -width $@label_width] -x 0 -y 0 +# puts [$f.label.1 cget -height] + pack [label $f.label -text $label -width [expr $@label_width/7] -wraplength $@label_width -anchor e] -side left + balloon $f.label $name +} + +def Dialog add_side {f name label} { + $self add_stuff $f $name $label + frame $f.side -relief ridge -borderwidth 2 + foreach {i side} {0 left 1 right 2 top 3 bottom} { + radiobutton $f.side.$side -value $i -variable @$name -text $side + } + pack $f.side.left -side left -fill y + pack $f.side.right -side right -fill y + pack $f.side.top -side top + pack $f.side.bottom -side bottom + pack $f.side -side left +} + +def Dialog add_color {f name label} { + $self add_stuff $f $name $label + set v $@$name + set text_color [complement $v] + button $f.color -text $v -font {Courier 10} -width 10 -pady 2 -fg $text_color \ + -command [list $self choose_col $f $name $v] -relief sunken -background $v \ + -highlightbackground "#ffffff" -activebackground [darker $v] + button $f.preset -text [say "preset"] -pady 2 -font {Helvetica 8} \ + -command [list $self color_popup $f $name 10] + bind $f.preset <Return> "$self color_popup $f $name 10" + pack $f.color $f.preset -side left +} + + +def Dialog add_choice {f name label choices} { + $self add_stuff $f $name $label + menu $f.menu -tearoff 0 + set i 0 + foreach part $choices { + $f.menu add command -label [say $part] -command [list $self dropmenu_set $f $name $part $i] + incr i + } + set trim_name [string trimleft $name "-"] + set _($self:${name}choices) $choices + set choice $@$name + if {[string is integer $choice]} {set choice [lindex $choices $choice]} + label $f.butt -text [say $choice] -relief raised -width 20 + balloon $f.butt "click to change setting" + pack $f.label $f.butt -side left + bind $f.butt <1> [list $self dropmenu_open $f $name] +} + +def Dialog add_key {f name label} { + set text "" + set n 0 + foreach item $name { + if {$n != 0} {append text " & " [say $item]} else {set text [say $item]} + incr n + } + $self add_stuff $f $name $text + #balloon $f.label $name + foreach item $name { + set v $_($self:$item) ;# bug in objtcl + set item_lower [string tolower $item] + entry $f.$item_lower -width 15 -textvariable @$item + pack $f.$item_lower -side left + } +} + +def Dialog add_folders {f name label} { + $self add_stuff $f $name $label + set v $_($self:$name) ;# bug in poetcl + frame $f.a + listbox $f.a.list -width 40 -height 8 -yscrollcommand "$f.a.yscroll set" \ + -activestyle none -xscrollcommand "$f.a.xscroll set" + foreach line $v {$f.a.list insert end $line} + set @$name $f.a.list ;# save the listbox path at @$name instead + scrollbar $f.a.yscroll -command "$f.a.list yview" + scrollbar $f.a.xscroll -command "$f.a.list xview" -orient horizontal + pack $f.a.xscroll -side bottom -fill x + pack $f.a.list -side left -fill both -expand 1 + pack $f.a.yscroll -side left -fill y + pack $f.a -side left + frame $f.b -borderwidth 0 + foreach {cmd lab} {dir_add add listbox_remove remove listbox_up up listbox_down down} { + pack [button $f.b.$cmd -command "$self $cmd $f.a.list" -text [say $lab] -width 6] -side top + balloon $f.b.$cmd [say dir_$lab] + } + pack $f.b -side top +} + +def Dialog add_libraries {f name label} { + $self add_stuff $f $name $label + set v $_($self:$name) ;# bug in objtcl + frame $f.a + listbox $f.a.list -width 32 -height 16 -yscrollcommand "$f.a.yscroll set" \ + -activestyle none -xscrollcommand "$f.a.xscroll set" + #foreach line $@$name {$f.a.list insert end $line} + foreach line $v {$f.a.list insert end $line} + # save the listbox path at @$name instead + set @$name $f.a.list + scrollbar $f.a.yscroll -command "$f.a.list yview" + scrollbar $f.a.xscroll -command "$f.a.list xview" -orient horizontal + + pack $f.a.xscroll -side bottom -fill x + pack $f.a.list -side left -fill both -expand 1 + pack $f.a.yscroll -side left -fill y + pack $f.a -side left + + frame $f.b -borderwidth 0 + entry $f.b.entry -width 15 -borderwidth 5 -relief ridge + bind $f.b.entry <Return> "$self lib_add $f.a.list" + pack $f.b.entry -side top + + foreach {cmd lab} {lib_add add listbox_remove remove listbox_up up listbox_down down} { + pack [button $f.b.$cmd -command "$self $cmd $f.a.list" -text [say $lab] -width 6] -side top + balloon $f.b.$cmd [say dir_$lab] + } + pack $f.b -side top +} + +def Dialog dir_add {listbox} { + set dir [tk_chooseDirectory -initialdir ~ -title "Choose a folder" -parent .$self] + if {$dir == ""} {return} + $listbox insert end $dir + $listbox yview end + focus .$self +} + +# doesn't work with toplevel widget +proc upwidget {levels name} { + set l [split $name .] + return [join [lrange $l 0 end-$levels] .] +} + +def Dialog lib_add {f} { + set f [upwidget 2 $f] + set listbox $f.a.list + set entry $f.b.entry + set var [$entry get] + if {$var != ""} {$listbox insert end $var} + $listbox yview end + $entry delete 0 end + focus $entry +} + +def Dialog listbox_remove {listbox} { + set sel [$listbox curselection] + if {$sel == ""} {return} + $listbox delete $sel + $listbox selection set $sel +} + +def Dialog listbox_swap {listbox dir} { + set sel [$listbox curselection] + if {$sel == ""} {return} + if {![inside [expr $sel+$dir] 0 [$listbox size]]} {return} + set line [$listbox get $sel] + $listbox delete $sel + incr sel $dir + $listbox insert $sel $line + $listbox selection set $sel + $listbox see $sel +} + +def Dialog add_devlist {f name label} { + $self add_stuff $f $name $label + menu $f.menu -tearoff 0 + set i 0 + set trim_name [string trimleft $name "-"] + set part none ;# in case there are none + foreach part $@$trim_name { + $f.menu add command -label $part -command [list $self dropmenu_set $f $name $part $i] + incr i + } + #label $f.butt -text [lindex $@$trim_name 0] -relief raised -width 20 + label $f.butt -textvariable _($self:${trim_name}0) -relief raised -width 20 + balloon $f.butt "click to change setting" + pack $f.label $f.butt -side left + bind $f.butt <1> [list $self dropmenu_open $f $name] +} + +def Dialog add_spins {f name label option} { + global _ + $self add_stuff $f $name $label + set i 0 + set trim_name [string trimleft $name "-"] + set n [llength $@$option] + foreach part $@$trim_name { + if {$i < $n} {set s "readonly"} else {set s "disabled"} + set v "_($self:$trim_name${i})" + spinbox $f.$i -width 2 -command "$self spinning %d $v" -state $s -textvariable $v + pack $f.$i -side left + balloon $f.$i "Device [expr $i+1]" + incr i + } +} + +def Dialog spinning {mode v} { + switch $mode { + up {incr $v; puts " incr $v"} + down {incr $v -1} + } +} + +def Dialog listbox_up {listbox} {$self listbox_swap $listbox -1} +def Dialog listbox_down {listbox} {$self listbox_swap $listbox +1} + +def Dialog add {w args} { + foreach row $args { + set name [lindex $row 0] + set type [lindex $row 1] + set options [lrange $row 2 end] + set f $w.$name + set label "[say $name]: " + set k [lsearch $options -choices] + if {$k>=0} { + set choices [lindex $options [expr $k+1]] + set options [lreplace $options $k [expr $k+1]] + } + #set v $@$name + #set v $_($self:$name) ;# bug in poetcl + switch -- $type { + side {$self add_side $f $name $label} + color {$self add_color $f $name $label} + font {$self add_font $f $name $label $options} + choice {$self add_choice $f $name $label $choices} + key {set f $w.[string tolower [lindex $name 0]] + $self add_key $f $name $label} + folders {$self add_folders $f $name $label} + libraries {$self add_libraries $f $name $label} + devlist {$self add_devlist $f $name $label} + spins {$self add_spins $f $name $label $options} + section {label $f -text $label -bg "#0000aa" -fg "#ffff55" -font {helvetica -10 bold}} + subsection {label $f -text $label -bg "#0000aa" -fg "#ffff55" -font {helvetica -10 bold}} + toggle { + $self add_stuff $f $name $label + eval [concat [list checkbutton $f.toggle -variable @$name] $options] + pack $f.toggle -side left + } + default { + $self add_stuff $f $name $label + set trim_name [string trimleft $name "-"] + eval [concat [list entry $f.entry -textvariable _($self:$trim_name)] $options] + pack $f.entry -side left + bind $f.entry <Return> "$self ok" + switch -regexp -- $type { + integer|float|fontsize { + frame $f.b -borderwidth 0 + button $f.b.1 -image icon_wedge_up -command "change_entry $f.entry +1" + button $f.b.2 -image icon_wedge_down -command "change_entry $f.entry -1" + pack $f.b.1 $f.b.2 -side top + pack $f.b -side left + bind $f.entry <Button-4> "change_entry $f.entry +1" + bind $f.entry <Button-5> "change_entry $f.entry -1" + } + entry {} + default { + label $f.type -text "($type)" -fg "#808080" + pack $f.type -side right -anchor e + } + } + } + } + pack $f -side top -fill x + } +} + +proc logvar {args} { + set r {} + foreach var $args { + regsub {^_\(.*:(.*)\)$} $var {@\1} var2 + lappend r "$var2=[uplevel 1 list \$$var]" + } + puts [join $r "; "] +} + +def Dialog spinbox_update {mode} {puts " $mode"} + +def Dialog add_font {f name label class} { + $self add_stuff $f $name $label + set v $@$name + label $f.font -text $v -font [lreplace $v 1 1 -10] -width [string length $v] -height 1 -pady 3 -fg black \ + -relief sunken -bg white + button $f.preset -text [say "edit"] -pady 2 -font {Helvetica 8} \ + -command "FontDialog new_as $name $class $f.font" + pack $f.font $f.preset -side left +} + +class_new FontDialog {Dialog} + +def Canvas fd {} {FontDialog new_as view_font [$self look font] "View"} + +def FontDialog init {class orig} { + if {[winfo exists .$self]} {return} + super cancel ok + set f .$self + set @class $class + set @orig $orig + bind all <KeyPress-F1> help + set font $::look($@class:font) + set @family [lindex $font 0] + set @size [expr -[lindex $font 1]] + set @bold [expr [lsearch $font bold ]>=0] + set @italic [expr [lsearch $font italic]>=0] + set @str $font + logvar @family @size @bold @italic + pack [label $f.label -text [say font_family] -anchor w] -side top -fill x + frame $f.list -bd 2 + + pack [listbox $f.list.box -relief sunken -yscrollcommand "$f.list.scroll set"] -side left + pack [scrollbar $f.list.scroll -relief sunken -command "$f.list.box yview" -takefocus 0] -side right -fill y + bind $f.list.box <<ListboxSelect>> "$self font_update $f" + foreach name [lsort [font families]] {$f.list.box insert end $name} + + set fontlist [$f.list.box get 0 end] + set find [lsearch $fontlist $@family] + if {$find < 0} {set find 0} + $f.list.box selection set $find $find + $f.list.box activate $find + $f.list.box see $find + bind $f.list.box <ButtonRelease-1> "$self font_update $f" + bind $f.list.box <ButtonPress-1> "focus $f.list.box" + + frame $f.var + frame $f.var.size + pack [label $f.var.size.label -text [say font_size]] -side left + pack [spinbox $f.var.size.entry -relief sunken -textvariable fontsize -width 4 \ + -command "$self font_changesize $f %d"] -side left + bind $f.var.size.entry <KeyPress-Return> "$self font_update_size $f" + $f.var.size.entry delete 0 end + $f.var.size.entry insert 0 $@size + + frame $f.var.style + pack [label $f.var.style.label -text [say font_style]] -side left + set cmd [list $self font_update $f] + pack [checkbutton $f.var.style.bold -text [say font_bold] -variable @bold -command $cmd] -side top + pack [checkbutton $f.var.style.italic -text [say font_italic] -variable @italic -command $cmd] -side top + + pack $f.var.size -side left + pack $f.var.style -side left -padx 20 + + frame $f.preview + pack [label $f.preview.label -text [say font_preview]] -side left + pack [canvas $f.preview.canvas -width 250 -height 50 -relief sunken -borderwidth 1] -side left -fill x + $f.preview.canvas create text 4 4 -tags ${self}TEXT -anchor nw -text [say font_preview_2] -font $font + + pack $f.list -side left + pack $f.var -side top -fill x + pack $f.preview -side top -pady 10 + focus $f.list.box + $self none_resizable +} + +def FontDialog font_update {f} { + global font + set lb $f.list.box + set @family [$lb get [$lb curselection]] + set @str [list $@family [expr -$@size]] + if {$@bold } {lappend @str bold } + if {$@italic} {lappend @str italic} + # logvar @str + $f.preview.canvas itemconfigure ${self}TEXT -font $@str +} + +def FontDialog font_changesize {f mode} { + switch $mode { + up {set @size [expr $@size+1]} + down {set @size [expr $@size-1]} + } + $f.var.size.entry delete 0 end + $f.var.size.entry insert 0 $@size + $self font_update $f +} + +def FontDialog font_style {f bold} { + set @bold $bold + $self font_update $f +} + +def FontDialog font_update_size {f} { + set size [$f.var.size.entry get] + if [regexp {^[0-9]+$} $size] {set @size $size} + $self font_update $f +} + +def FontDialog apply {} { + set ::look($@class:font) $@str + $@orig configure -font [lreplace $@str 1 1 -10] -text $@str -width [string length $@str] +} + +############ .pdrc editor +#Turns #rgb into 3 elem list of decimal vals. +proc rgb2dec {c} { + set c [string tolower $c] + if {[regexp -nocase {^#([0-9a-f])([0-9a-f])([0-9a-f])$} $c x r g b]} { + # double'ing the value make #9fc == #99ffcc + scan "$r$r $g$g $b$b" "%x %x %x" r g b + } else { + if {![regexp {^#([0-9a-f]+)$} $c junk hex] || \ + [set len [string length $hex]]>12 || $len%3 != 0} { + if {[catch {winfo rgb . $c} rgb]} { + return -code error "bad color value \"$c\"" + } else { + return $rgb + } + } + set len [expr {$len/3}] + scan $hex "%${len}x%${len}x%${len}x" r g b + } + return [list $r $g $b] +} +#Returns a complementary color +proc complement {orig {grays 1}} { + foreach {r g b} [rgb2dec $orig] {break} + set R [expr {(~$r)%256}] + set G [expr {(~$g)%256}] + set B [expr {(~$b)%256}] + if {$grays && abs($R-$r) < 32 && abs($G-$g) < 32 && abs($B-$b) < 32} { + set R [expr {($r+128)%256}] + set G [expr {($g+128)%256}] + set B [expr {($b+128)%256}] + } + return [format "\#%02x%02x%02x" $R $G $B] +} + +# this makes the tooltip +proc balloon {w help} { + bind $w <Any-Enter> "after 500 [list balloon:show %W [list $help]]" + #bind $w <Any-Leave> "destroy %W.balloon; puts \"destroy balloon\" " + bind $w <Any-Leave> "destroy %W.balloon" +} + +proc balloon:show {w arg} { + if {[eval winfo containing [winfo pointerxy .]]!=$w} {return} + set top $w.balloon + catch {destroy $top} + toplevel $top -bd 1 -bg black + wm overrideredirect $top 1 + if {$::tcl_platform(platform) == "macintosh"} { + unsupported1 style $top floating sideTitlebar + } + pack [message $top.txt -aspect 10000 -bg lightyellow -font fixed -text $arg] + set wmx [expr [winfo rootx $w]+[winfo width $w]] + set wmy [winfo rooty $w] + wm geometry $top [winfo reqwidth $top.txt]x[winfo reqheight $top.txt]+$wmx+$wmy + raise $top +} + +def Dialog ok {} {$self apply; $self cancel} +def Dialog cancel {} {if {[info exists @nbs]} {foreach x $@nbs {$x delete}}; after 1 [list $self delete]} +def Dialog close {} {$self delete} +def Dialog apply {} {} +def Dialog delete {} {destroy .$self; super} +def Dialog erase {} {}; # so that it doesn't call View erase + +def Dialog init {args} { + super + set f .$self + set @label_width 160 ;# 20 + toplevel $f + frame $f.buttonsep -height 2 -borderwidth 1 -relief sunken + frame $f.buttonframe + set i 0 + foreach a $args { + if {[llength $args]<=1 || $i>0} { + pack [label $f.buttonframe.$i -width 1] -side left -fill x -expand 1 + } + pack [button $f.buttonframe.$a -text [say $a] -command "$self $a"] -side left + bind $f.buttonframe.$a <Return> "$self $a" + incr i + } + pack $f.buttonframe -side bottom -fill x -pady 2m + pack $f.buttonsep -side bottom -fill x + wm protocol $f WM_DELETE_WINDOW "$self cancel" + bind .$self <Tab> "$self traversal %K %W forward" + bind .$self <Control-Tab> "$self traversal %K %W back" +} + +def Dialog none_resizable {} {wm resizable .$self 0 0} + +def Dialog traversal {k w direction} { + switch $direction { + forward {focus [tk_focusNext $w]} + back {focus [tk_focusPrev $w]} + } +} + +def Dialog dropmenu_open {frame} { + set x [winfo rootx $frame.butt] + set y [expr [winfo height $frame.butt] + [winfo rooty $frame.butt]] + tk_popup $frame.menu $x $y +} + +def Dialog dropmenu_set {frame var part val} { + #if {$say} {set text [say $part]} else {set text $part} + set @$var $val + $frame.butt configure -text [say $part] +} + +def Dialog color_popup_select {frame var color} { + set @$var $color + set col [format #%6.6x $color] + if {$self == "ddrc"} {set @$var $col} + $frame.color configure -background $col -foreground [complement $col] -text $col + if {$self != "ddrc"} {$self do_auto_apply} + #$self do_auto_apply +} + +def Dialog color_popup {frame var i} { + set w $frame.color.popup + if [winfo exists $w] {destroy $w} + menu $w -tearoff false + global preset_colors + for {set i 0} {$i<[llength $preset_colors]} {incr i} { + set c [lindex $preset_colors $i] + $w add command -label " " -background "#$c" -activebackground "#$c" \ + -command [list $self color_popup_select $frame $var [expr 0x$c]] + } + tk_popup $w [expr [winfo rootx $frame.color]] [expr [winfo rooty $frame.color]] +} + +def Dialog choose_col {frame var val} { + set c 0xFFFFFF + set color [tk_chooseColor -title $val -initialcolor $val] + if {$color != ""} { + $frame.color configure -text $color + $self color_popup_select $frame $var [expr [string replace $color 0 0 "0x"]&0xFFFFFF] + } +} + +class_new PagedDialog {Dialog} +class_new Notebook {Thing} + +def PagedDialog init {args} { + eval [concat [list super] $args] + set @nb [Notebook new_as $self.1] + set @nbs $@nb + pack .$@nb -expand 1 -fill both + $self none_resizable +} +def Notebook delete {} {super} +def Notebook init {{width 590} {height 350}} { + set f .$self + frame $f + pack [frame $f.bar] -fill x + pack [frame $f.main -borderwidth 1 -relief raised -width $width -height $height] -fill both -expand yes +} + +def Notebook page_select {i} { + set f .$self + catch { + $f.bar.$@section configure -relief raised + place forget $f.main.$@section + pack $f.bar.$@section -pady {4 4} + } + set @section $i + place $f.main.$@section -x 0 -y 0 ;# -width [winfo width $f.main] -height [winfo height $f.main] + $f.bar.$@section configure -relief sunken + pack $f.bar.$@section -pady {8 0} +} + +def Notebook add_section {section text} { + set f .$self + frame $f.main.$section + pack [button $f.bar.$section -text $text -command [list $self page_select $section]] -side left -pady {4 4} + bind $f.bar.$section <Return> "$self page_select $section" +} + +# numbers in section are for the % of space taken by labels +set pdrc_options { + section {section_audio 50} + integer -r + devlist -audioindev|-soundindev + devlist -audiooutdev|-soundoutdev + spins {-inchannels audioindev} + spins {-outchannels audiooutdev} + integer -audiobuf|-soundbuf + integer -blocksize + integer -sleepgrain + void -nodac + void -noadc + choice {audio_api_choice} + void -32bit + + section {section_midi 50} + void -nomidiin + void -nomidiout + devlist -midiindev + devlist -midioutdev + + section {section_externals 20} + libraries -lib + + section {section_paths 20} + folders -path + folders -helppath + + section {section_other 50} + files -open + void -verbose + integer -d + void -noloadbang + string -send + void -listdev + void -realtime|-rt +} + +proc pdtk_audio_dialog {indevlist indevs inchans outdevlist outdevs outchans sr dspblock advance multi longform} { + pdrc audio_properties $indevlist $indevs $inchans $outdevlist $outdevs $outchans $sr $dspblock $advance $multi +} + +class_new ServerPrefsDialog {PagedDialog} + +def ServerPrefsDialog apply {} { + set audio_props [$self audio_properties=?] + #pd pd audio-dialog $audio_props + netsend [list "pd" "audio-dialog" $audio_props] + $self write +} + +def ServerPrefsDialog init_reverse_hash {} { + global pdrc_options pdrc_options_h pd_apilist2 + foreach {type names} $pdrc_options { + set name [lindex $names 0] + if {[info exists @$name]} { + if {$name != "audio_api_choice"} {set @$name ""} + } else { + set @$name "" + } + foreach alias $names {set pdrc_options_h($alias) [list $type $name]} + if {$name == "audio_api_choice"} { + foreach alias [lrange $pd_apilist2 1 end] {set pdrc_options_h($alias) [list $type $name]} + } + } +} + +def ServerPrefsDialog audio_properties {indevlist indevs inchans outdevlist outdevs outchans sr dspblock advance multi} { + set @audioindev $indevlist + set @audiooutdev $outdevlist + # the following @audioindev* is used as -textvariable for devlist + set @audioindev0 [lindex $@audioindev 0] + set @audiooutdev0 [lindex $@audiooutdev 0] + set @inchannels $inchans + set @outchannels $outchans + set @audiobuf $advance + set @blocksize $dspblock + set @usemulti $multi + set @r $sr + set @midiindev "midione" + set @midioutdev "miditwo" + # below are also used as -textvariable + mset [list @inchannels0 @inchannels1 @inchannels2 @inchannels3] $@inchannels + mset [list @outchannels0 @outchannels1 @outchannels2 @outchannels3] $@outchannels + set @audio_api_choice2 [say [lindex $::pd_apilist2 $@audio_api_choice]] + if {![winfo exists .$self.1.main.1]} { + $self init_content + } else { + $self update_content + } +} + +def ServerPrefsDialog audio_properties=? {} { + set indev0 [lsearch $@audioindev $@audioindev0] + set outdev0 [lsearch $@audiooutdev $@audiooutdev0] + return [list $indev0 0 0 0 $@inchannels0 $@inchannels1 $@inchannels2 $@inchannels3 \ + $outdev0 0 0 0 $@outchannels0 $@outchannels1 $@outchannels2 $@outchannels3 \ + $@r $@blocksize $@audiobuf] +} + +def ServerPrefsDialog read_one {type name contents i} { + switch -- $type { + folders {incr i; lappend @$name [lindex $contents $i]} + libraries {incr i; lappend @$name [lindex $contents $i]} + files {incr i; lappend @$name [lindex $contents $i]} + choice { + if {$name == "audio_api_choice"} { + if {$@$name == ""} { + set @$name [lsearch $::pd_apilist2 [lindex $contents $i]] + } + } else { + set @$name [lindex $contents $i] + } + } + void { set @$name 1} + default {incr i; set @$name [lindex $contents $i]} + } + incr i + return $i +} + +def ServerPrefsDialog read {} { + global pdrc_options pdrc_options_h cmdline + set fd [open $cmdline(rcfilename) "RDONLY CREAT"] + set contents {} + foreach line [split [read $fd] "\n"] { + if {[string index $line 0] != "#"} {lappend contents $line} + } + close $fd + set contents [concat [join $contents " "]] ;# concat casts to list type (faster) (?) + set i 0 + + # prevent Tk8.5 from showing grey checkmarks + foreach name {-nodac -noadc -32bit -nomidiin -nomidiout -verbose -noloadbang -listdev -realtime} { + set _($self:$name) 0 + } + + while {$i < [llength $contents]} { + set op [lindex $contents $i] + if {[string length $op]==0} {break} + if {![info exists pdrc_options_h($op)]} { + post "unknown option: %s" $op + incr i + continue + } + mset {type name} $pdrc_options_h($op) + set name [lindex [split $name "|"] 0] + set i [$self read_one $type $name $contents $i] + } +} + +def ServerPrefsDialog write {} { + set fd [open $::cmdline(rcfilename) w] + #set fd stdout; puts "WOULD SAVE:" + foreach {type names} $::pdrc_options { + set name [lindex [split [lindex $names 0] "|"] 0] + if {[info exists _($self:$name)]} {set v $_($self:$name)} ;# bug in objective.tcl ? + switch $type { + folders {foreach item [$v get 0 end] {puts $fd "$name $item"}} + libraries {foreach item [$v get 0 end] {puts $fd "$name $item"}} + #files {foreach item $v {puts $fd "$name $item"}} + void {if {$v != ""} {if {$v} {puts $fd $name}}} + choice { + if {$name != "audio_api_choice"} { + set vv [lindex $names [expr 1+$v]] + if {$vv != "default"} {puts $fd $vv} + } else { + set vv [lindex $::pd_apilist2 $v] + if {$vv != "default"} {puts $fd $vv} + } + } + devlist {} + default {if {[string length $v]} {puts $fd "$name $v"}} + } + } + close $fd + #puts "THE END" +} +def ServerPrefsDialog reset {} { +} + +def ServerPrefsDialog init_content {} { + global pdrc_options + set f .$self.1 + set section 0 + set @label_width 200 ;# 24 + foreach {type names} $pdrc_options { + set name [lindex [split [lindex $names 0] "|"] 0] + switch $type { void { set type toggle }} + switch $type { + section { + set @label_width [expr 6*[lindex $names 1]] ;# % of 600 px + $@nb add_section [incr section] [say $name] + } + choice { + if {$name == "audio_api_choice"} { + set ops $::pd_apilist2 + } else { + set ops [lrange $names 1 end] + } + $self add $f.main.$section [list $name choice -choices $ops] + } + devlist {$self add $f.main.$section [list $name devlist] } + spins {$self add $f.main.$section [list $name spins [lindex $names 1]] } + default {$self add $f.main.$section [list $name $type]} + } + } + $@nb page_select 1 +} + +def ServerPrefsDialog update_content {} { + $self update_channels +} + +def ServerPrefsDialog update_channels {} { + set indev_len [llength $@audioindev] + set outdev_len [llength $@audiooutdev] + set i 0 + foreach chan $@inchannels { + if {$i < $indev_len} {set s "readonly"} else {set s "disabled"} + .$self.1.main.1.-inchannels.$i configure -state $s + incr i + } + set i 0 + foreach chan $@outchannels { + if {$i < $outdev_len} {set s "readonly"} else {set s "disabled"} + .$self.1.main.1.-outchannels.$i configure -state $s + incr i + } +} + +def ServerPrefsDialog init {} { + netsend [list pd audio-properties] + $self init_reverse_hash + $self read + super reset cancel apply ok + # pd pd midi-properties +} + +def ServerPrefsDialog dropmenu_set {frame var part val} { + set trim_part [string trimleft $part "-"] + set trim_var [string trimleft $var "-"] + if {$var == "audio_api_choice"} { + foreach api $::pd_apilist { + if {$trim_part == [string tolower [lindex $api 0]]} { + netsend [list pd audio-setapi [lindex $api 1]] + after 1 [netsend [list pd audio-properties]] + } + } + } else { + set ::_($self:${trim_var}0) $part + } + super $frame $var $part $val +} +#used by choice and devlist +def ServerPrefsDialog dropmenu_open {f name} { + set trim_name [string trimleft $name "-"] + if {$trim_name != "audio_api_choice"} { + set i 0 + set m $f.menu + $m delete 0 end + foreach part $@$trim_name { + $m add command -label $part -command [list $self dropmenu_set $f $name $part $i] + incr i + } + } + super $f +} + +#################### ClientPrefsDialog +set ddrc_options { +section Client section_color + subsection Client canvas_color + color Canvas bgedit + color Canvas bgrun + color Canvas grid + subsection Client object_color + color View bg + color View fg + color View frame1 + color View frame2 + color View frame3 + color Comment bg + color Comment fg + color Comment frame1 + color Comment frame2 + color Comment frame3 + color View selectframe + font View font + subsection Client wire_color + color Wire fg + color Wire dspfg + color Wire fg2 + color FutureWire dash + subsection Client others_color + color Box inletfg + color Box outletfg + color SelRect rect + font KeyboardDialog font + font Console font +section Client keys + subsection Client put + key Canvas Object + key Canvas Message + key Canvas {Number nbx} + key Canvas Symbol + key Canvas Comment + key Canvas bng + key Canvas tgl + key Canvas {vsl hsl} + key Canvas {vradio hradio} + key Canvas vu + key Canvas cnv + key Canvas Graph + key Canvas Array + subsection Client edit + key Canvas {cut copy} + key Canvas {undo redo} + key Canvas {paste duplicate} + key Canvas select_all + key Canvas clear_selection + key Canvas {reload redraw} + key Canvas editmodeswitch + key Canvas {insert_object chain_object} + key Canvas {clear_wires auto_wire} + key Canvas subpatcherize + subsection Client general + key Canvas Pdwindow + key Canvas {new_file open_file} + key Canvas {save save_as} + key Client {server_prefs client_prefs} + key Canvas {close quit} + key Canvas {find find_again} + key Canvas {audio_on audio_off} + key Client {audio_settings midi_settings} + key Client test_audio_and_midi + key Canvas {load_meter latency_meter} + key Canvas about + subsection Canvas keynav + key Canvas {key_nav_up key_nav_up_shift} + key Canvas {key_nav_down key_nav_down_shift} + key Canvas {key_nav_right key_nav_right_shift} + key Canvas {key_nav_left key_nav_left_shift} + key Canvas key_nav_ioselect +section Client others + toggle Canvas hairstate + toggle Canvas hairsnap + toggle Canvas gridstate + integer Canvas grid_size + toggle Canvas snap_grid + toggle Canvas buttonbar + toggle Canvas statusbar + toggle Canvas menubar + toggle Canvas scrollbar + toggle View tooltip + toggle Wire wirearrow + integer Client console + choice View language + integer Canvas pointer_sense +} + +class_new ClientPrefsDialog {PagedDialog} +def ClientPrefsDialog apply {} {$self write; $self do_apply} +def ClientPrefsDialog read {} {read_ddrc} + +def ClientPrefsDialog do_apply {} { + foreach canvas $::window_list { + if {[$canvas class] == "Canvas"} { + $canvas activate_menubar= [$canvas look menubar] + $canvas activate_buttonbar= [$canvas look buttonbar] + $canvas activate_statusbar= [$canvas look statusbar] + $canvas activate_scrollbars= [$canvas look scrollbar] + $canvas activate_grid= [$canvas look gridstate] + $canvas redraw + } + } +} + +def ClientPrefsDialog write {} { + global look key + $self get_val + set fd [open $::cmdline(ddrcfilename) w] + foreach category {look key} { + set class_list {} + puts $fd "$category \{" + foreach name [array names $category] { + mset {class var} [split $name ":"] + lappend class_list $class + } + set class_list [luniq [lsort $class_list]] + foreach class $class_list { + puts $fd " $class \{" + foreach name [lsort [array names $category -glob $class:*]] { + mset {class var} [split $name ":"] + # the eval here annoys me a bit because of possible special chars -- matju + puts $fd " $var [eval list \$${category}($class:$var)]" + #puts " $var $category $class $var" + } + puts $fd " \}" + } + puts $fd "\}" + } + close $fd +} + +#this retrieves the values set in the editor +def ClientPrefsDialog get_val {} { + global ddrc_options look key accels + set check_key {} + foreach {type class name} $ddrc_options { + switch $type { + color { + set str [string tolower $class$name] + set look($class:$name) $@$str + } + key { + foreach item $name { + set new_key $@$item + set old_key $key($class:$item) + if {$key($class:$item) != $new_key} { + if {[dict exists $accels $old_key]} { + set cmd [dict get $accels $old_key] + set accels [dict remove $accels $old_key] + dict set accels $new_key $cmd + } + } + if {[dict exists $check_key $new_key] && $new_key != ""} { + error "$new_key already assigned" + } else {dict set check_key $new_key key($item)} + set key($class:$item) $new_key + } + } + toggle { set look($class:$name) $@$name} + integer {set look($class:$name) $@$name} + choice {set look($class:$name) $@$name} + #font {set look(View:font) $@str} + } + } +} + +def ClientPrefsDialog reset {} { + # this should reload defaults.ddrc ? +} + +def ClientPrefsDialog revert {} { + # this should reload currently used settings ? +} + +def ClientPrefsDialog init {} { + global ddrc_options look key + #do we need to read .ddrc each time the pref editor is opened? + #$self read + super cancel apply ok + #super cancel reset revert apply ok + set f .$self.1 + set section 0 + set subsection 0 + set @label_width 200 ;# 24 + + foreach {type class names} $ddrc_options { + set name [lindex [split $names |] 0] + switch $type { void { set type toggle }} + switch $type { + section { + $@nb add_section [incr section] [say $name] + set which_section $f.main.$section + set which_self $self + set subsection 0 + } + subsection { + set subself $self.1.main.$section.subsections + if {!$subsection} { + lappend @nbs [Notebook new_as $subself 590 300] + pack .$subself + } + $subself add_section [incr subsection] [say $name] + $subself page_select 1 + set which_section .$subself.main.$subsection + set which_self $subself + } + choice { + set @$name $look(View:language) + $self add $which_section [list $name $type -choices $::langoptions] + } + color { + set str [string tolower $class$name] + set @$str $look($class:$name) + $self add $which_section [list $str $type] + } + key { + foreach item $name { + set @$item $key($class:$item) + } + $self add $which_section [list $name $type] + } + toggle { + set @$name $look($class:$name) + $self add $which_section [list $name $type] + } + font { + set str [string tolower $class$name] + set @$str $look($class:$name) + $self add $which_section [list $str $type $class] + } + default { + switch $name { + console {set @$name $look(Client:console)} + pointer_sense {set @$name $look(Canvas:pointer_sense)} + grid_size {set @$name $look(Canvas:grid_size)} + default {} + } + $self add $which_section [list $name $type] + } + } + } + $@nb page_select 1 +} + +def ClientPrefsDialog dropmenu_set {frame var part val} { + set @$var $part + # set _($self:${var}2) [say $part] + $frame.butt configure -text [say $part] +} + +def ClientPrefsDialog dropmenu_open {f name} { + super $f +} + +############ find dialog ########### + +class_new FindDialog {Dialog} + +def FindDialog init {canvas} { + super cancel find + set @canvas $canvas + set @break 0 + set f .$self + $self add $f [list "string" "entry"] + focus .find.string.entry +} + +def FindDialog find {} {$self ok} +def FindDialog ok {} { + $@canvas find_string= $@string + $@canvas search + super +} +############ other stuff ######### + +def Client client_class_tree {} {ClientClassTreeDialog new} +class_new ClientClassTreeDialog {Dialog} + +proc* place_stuff {args} {} + +def ClientClassTreeDialog make_row {w tree} { + pack [frame $w] -fill x + pack [frame $w.row] -side top -fill x + pack [button $w.row.butt -image icon_minus] -side left + pack [label $w.row.label -text [lindex $tree 0]] -side left -fill x + pack [frame $w.dent -width 32] -side left + set i 1 + foreach node [lrange $tree 1 end] { + $self make_row $w.$i $node + incr i + } +} + +def ClientClassTreeDialog init {} { + super close + pack [frame .$self.1 -width 600 -height 400] -fill y -expand y + pack [frame .$self.1.1 -width 600 -height 400 -bg "#6688aa"] -side left -fill y -expand y + # "$w.1.scroll set" + # i'd like a scrollable frame + pack [scrollbar .$self.1.scroll -command "ClientClassTreeDialog_scroll $self"] -side left -fill y -expand y + place [frame .$self.1.1.tree] -x 0 -y 0 + set w .$self.1.1.tree.1 + $self make_row $w [Thing get_hierarchy] + after 100 "$self update_scrollbar" +} + +def ClientClassTreeDialog update_scrollbar {} { + set w .$self.1.1 + set zy [winfo height $w] + set sy [winfo height $w.tree] + set y1 [expr 0.0-[winfo y $w.tree]] + set y2 [expr 0.0+$y1+$zy] + .$self.1.scroll set [expr $y1/$sy] [expr $y2/$sy] +} + +def ClientClassTreeDialog scroll {args} { + set w .$self.1.1 + set zy [winfo height $w] + set sy [winfo height $w.tree] + switch [lindex $args 0] { + moveto { + set y [clip [expr (0.0-[lindex $args 1])*$sy] [expr $zy-$sy] 0] + place .$self.1.1.tree -x 0 -y $y + puts "args=[list $args] zy=$zy sy=$sy y=$y" + } + scroll { + } + } + after 100 "$self update_scrollbar" +} + +class_new AboutDialog {Dialog} + +def AboutDialog init {} { + super close + wm title .$self "About DesireData" + pack [label .$self.title -text $::pd_version -font {helvetica 18}] -side top + pack [text .$self.text -yscrollcommand ".$self.scroll set" -width 72 -height 36] -side left -fill both -expand yes + pack [scrollbar .$self.scroll -command ".$self.text yview"] -side right -fill y -expand yes +#12345678901234567890123456789012345678901234567890123456789012345678901 <- 72 chars + .$self.text insert 0.0 \ +"DesireData is a free (libre) real-time computer programming language +interpreter focused on realtime audio and video. +You can get DesireData from http://desiredata.goto10.org/ + +DesireData and PureData work on Linux, MacOSX, Windows, and others. + +PureData is copyrighted, but is free for you to use for any reasonable +purpose, according to the SIBSD license. DesireData's client section +also is free, according to the GPL license. DesireData's server section +is an adaptation of the PureData code and is also using the SIBSD +license. + +(insert here: links to both licenses) + +Credits: + DesireData server: Mathieu Bouchard + DesireData client: Mathieu Bouchard & Chun Lee + PureData: Miller Puckette feat. Thomas Musil, + Gnther Geiger, Krzysztof Czaja, Iohannes Zmlnig & others. + + Translations: + Franais (French): Patrice Colet + Catal (Catalan): Nria Verges + Espaol (Spanish): Mario Mora, Ramiro Cosentino + Deutsch (German): Max Neupert, Georg Holzmann, Thomas Grill + Norsk Bokml (Norwegian): Gisle Frysland + Portugus (Portuguese): Nuno Godinho + Italiano (Italian): Davide Morelli, Federico Ferri + Euskara (Basque): Ibon Rodriguez Garcia + Nihongo (Japanese): Kentaro Fukuchi + Polski (Polish): Michal Seta + Dansk (Danish): Steffen Leve Poulsen + Zong wen (Chinese): Chun Lee + Nederlands (Dutch): Tim Vets + Trke (Turkish): Koray Tahiroglu + Russkij (Russian): Ilya Dmitrichenko + +Much more documentation and other resources at http://puredata.info/ +The Pd mailing list archive at http://lists.puredata.info/pipermail/pd-list/ + +(insert here: link to \"reference documentation for Pd\", that is, chapter 1)" + + # this looks bad on OSX, iirc + .$self.text configure -state disabled +} + +set manager [Manager new] + +def Class post_hierarchy {{i 0}} { + post %*s%s [expr $i*2] "" $self + foreach sub $@subclasses {$sub post_hierarchy [expr $i+1]} +} +def Class get_hierarchy {} { + set l [list $self] + foreach sub $@subclasses {lappend l [$sub get_hierarchy]} + return $l +} + +# Thing post_hierarchy + +#---------------------------------------------------------------- +class_new ClipboardDialog {Dialog} + +def ClipboardDialog init {clipboard} { + super close + set @clipboard $clipboard + wm title .$self "Clipboard" + pack [text .$self.text -yscrollcommand ".$self.scroll set" -width 72 + ] -side left -fill both -expand yes + pack [scrollbar .$self.scroll -command ".$self.text yview"] -side right -fill y + $@clipboard subscribe $self + $self notice +} + +def ClipboardDialog notice {args} { + .$self.text delete 0.0 end + .$self.text insert 0.0 [$@clipboard value] +} + +def ClipboardDialog delete {} { + $@clipboard unsubscribe $self + super +} + +#---------------------------------------------------------------- +class_new ListDialog {Dialog} + +def ListDialog init {history title} { + super close + set @history $history + wm title .$self $title + frame .$self.1 + pack [listbox .$self.1.list -yscrollcommand ".$self.1.scroll set" -width 72 -height 20] -side left -fill both -expand yes + pack [scrollbar .$self.1.scroll -command ".$self.1.list yview"] -side right -fill y + pack .$self.1 + $@history subscribe $self + $self notice +} + +def ListDialog listbox {} {return .$self.1.list} + +def ListDialog notice {args} { + set w [$self listbox] + $w delete 0 end + foreach e [$@history list] {$w insert end $e} + $w see end +} + +def ListDialog delete {} { + $@history unsubscribe $self + super +} + +class_new EventHistoryDialog {ListDialog} + +def EventHistoryDialog init {history} { + super $history [say event_history_view] + pack [checkbutton .$self.hide -text [say hide_key_release]] -fill x + [$self listbox] configure -font {Mono -10} +} + +#---------------------------------------------------------------- +#class_new Splash {Thing} + +#def Splash init {} { +# toplevel .$self +# frame .$self.f +# canvas .$self.f.canvas +# image create photo .dd -format GIF -file desiredata.gif +# .$self.f.canvas create image 0 0 -image .dd +# pack .$self.f.canvas +# pack .$self.f +#} + +class_new KeyboardDialog {Dialog} + +set keyboard_layouts { + { + {9 " " 67 68 69 70 " " 71 72 73 74 " " 75 76 95 96} + {49 10 11 12 13 14 15 16 17 18 19 20 21 22} + {23 24 25 26 27 28 29 30 31 32 33 34 35 51} + {66 38 39 40 41 42 43 44 45 46 47 48 36} + {50 52 53 54 55 56 57 58 59 60 61 62} + {37 115 64 65 113 116 117 109} + } { + {" " " " 98 " "} + {100 " " " " 102} + {" " " " 104 " "} + {" "} + {" " " " 4 " "} + {1 2 3} + {" " " " 5 " "} + } +} + +foreach {k v} { + 9 5 + 22 5 + 23 4 51 4 + 66 5 36 7 + 50 8 62 8 + 37 4 115 4 64 4 65 24 113 4 116 4 117 4 109 4 + 98 2 100 2 102 2 104 2 + 1 2 2 2 3 2 4 2 5 2 +} {set keyboard_width_of($k) $v} + +proc namekey {i args} {foreach name $args {set ::keyboard_text_of($i) $name; incr i}} +namekey 9 Esc +namekey 67 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 +namekey 95 F11 F12 +namekey 49 ` +namekey 10 "1 !" "2 @" "3 #" "4 \$" "5 %" "6 ^" "7 &" "8 *" "9 (" "0 )" "- _" "= +" BkSp +namekey 23 Tab Q W E R T Y U I O P "\{ \[" "\} \]" +namekey 51 "\\ |" +namekey 66 Caps +namekey 38 A S D F G H J K L "\; :" "' \"" +namekey 36 Return +namekey 50 Shift +namekey 52 Z X C V B N M ", <" ". >" "/ ?" Shift +namekey 37 Ctrl +namekey 115 Sup +namekey 64 Alt Space +namekey 113 AltGr +namekey 116 Sup Menu +namekey 109 Ctrl +namekey 98 (up) +namekey 100 (left) +namekey 102 (right) +namekey 104 (down) +namekey 1 1 2 3 4 5 +#mouse clicks + +def KeyboardDialog init {history} { + super close + set @history $history + set @fade [dict create] + wm title .$self "Keyboard View" ;# say + set i 0; set j 0 + set @names {key misc} + frame .$self.board + set layouts {::keyboard_layout ::keyboard_layout2} + set @keycount 0 + foreach layout $::keyboard_layouts { + $self pack_keys $layout [lindex $@names $i] [llength [lindex $::keyboard_layouts [expr $i-1]]] + incr i + } + pack .$self.board + $@history subscribe $self + $self fade +} + +def KeyboardDialog pack_keys {keys name off} { + set i $off + frame .$self.board.$name + foreach row $keys { + frame .$self.board.$name.$i + foreach key $row { + if {$key==" "} { + pack [label .$self.board.$name.$i.shim$@keycount -image icon_empty] -side left + incr @keycount + continue + } + set ::keyboard_row_of($key) $i + if {[info exists ::keyboard_width_of($key)]} { + set width $::keyboard_width_of($key) + } else {set width 3} + if {[info exists ::keyboard_text_of($key)]} { + set text $::keyboard_text_of($key) + } else {set text $key} + if {[regexp {\((\w+)\)} $text foo bar]} { + set font [$self look font] + pack [label .$self.board.$name.$i.key$key -image icon_$bar -relief raise -border 4 -bg \ + "#dddddd" -width [expr $width*[font measure $font 0]] \ + -height [font metrics $font -linespace]] -side left + } else { + pack [label .$self.board.$name.$i.key$key -text " $text " -relief raise -border 4 \ + -bg "#dddddd" -width $width -font [$self look font]] -side left + } + } + pack .$self.board.$name.$i -fill x + if {$i==0} {pack [label .$self.board.$name.shim -image icon_empty] -fill x -expand yes} + incr i + } + switch $name { + key {pack .$self.board.key -side left} + misc {pack .$self.board.misc -side right} + } +} + +def KeyboardDialog notice {origin add event} { + mset {type W x y mod K k} $event + if {![info exists ::keyboard_row_of($k)]} {puts "unknown key $k"; return} + set i $::keyboard_row_of($k) + if {$i<[llength [lindex $::keyboard_layouts 0]]} {set section "key"} else {set section "misc"} + switch -regexp -- $type { + ^KeyPress|ButtonPress$ { + if { [dict exists $@fade $k]} {dict unset @fade $k} + .$self.board.$section.$i.key$k configure -bg "#ff0000" + } + ^KeyRelease|ButtonRelease$ {if {![dict exists $@fade $k]} {dict set @fade $k 255}} + } +} + +def KeyboardDialog fade {} { + foreach {k v} $@fade { + incr v -85 + if {$v<0} {set v 0} + set r [expr 221+$v*2/15] + set g [expr 221-$v*13/15] + set i $::keyboard_row_of($k) + if {$i<[llength [lindex $::keyboard_layouts 0]]} {set section "key"} else {set section "misc"} + .$self.board.$section.$i.key$k configure -bg [format #%02x%02x%02x $r $g $g] + if {$v} {dict set @fade $k $v} {dict unset @fade $k} + } + set @after [after 100 "$self fade"] +} + +def KeyboardDialog delete {} { + $@history unsubscribe $self + after cancel $@after + super +} + +#---------------------------------------------------------------- +# Deconstructors + +def Wire deconstruct {{selcanvas ""}} { + # the selcanvas system could be abstracted out using an ObjectList such that + # Canvas<ObjectList and $selection class == ObjectList + if {$selcanvas == ""} { + list #X connect \ + [[$@canvas objects] search $@from] $@outlet \ + [[$@canvas objects] search $@to] $@inlet + } { + list #X connect \ + $::obj_index_sel($@canvas:$@from) $@outlet \ + $::obj_index_sel($@canvas:$@to) $@inlet + } +} + +def MessageBox deconstruct {} {concat [list #X msg $@x1 $@y1] $@text} +def FloatBox deconstruct {} {concat [list #X floatatom $@x1 $@y1] $@w $@min $@max $@pos $@lab $@snd $@rcv} +def SymbolBox deconstruct {} {concat [list #X symbolatom $@x1 $@y1] $@w $@min $@max $@pos $@lab $@snd $@rcv} +def Comment deconstruct {} {concat [list #X text $@x1 $@y1] $@text} + + +def Box deconstruct {} { + if {[array names ::fields -exact $@pdclass] == ""} { + return [concat [list #X obj $@x1 $@y1] $@text] + } { + set r {} + foreach field $::fields($@pdclass) {lappend r $_($self:$field)} + return $r + } +} + +def View deconstruct_to {stream args} { + $stream << [philtre [eval [concat [list $self deconstruct] $args]]] + $stream << ";\n" +} + +def Canvas deconstruct {} { + return [concat [list #X restore $@x1 $@y1] $@text] +} +def Canvas deconstruct_to {stream args} { + set r [concat [list #N canvas] $@canvas_pos $@canvas_size] + if {$@subpatch || $@abs} {lappend r $@name $@mapped} else {lappend r $@fontsize} + $stream << "[philtre $r];\n" + foreach i [lsort -integer [$@objects keys]] {eval [concat [list [$@objects get $i] deconstruct_to $stream]]} + foreach i [lsort -integer [ $@wires keys]] {eval [concat [list [ $@wires get $i] deconstruct_to $stream]]} + $stream << [philtre [eval [concat [list $self deconstruct] $args]]] + $stream << ";\n" +} + +#---------------------------------------------------------------- +# openpanel & savepanel + +proc pdtk_openpanel {target localdir} { + if {$localdir == ""} {set localdir $::pd_opendir} + set filename [tk_getOpenFile -initialdir $localdir] + if {$filename != ""} { + set directory [string range $filename 0 [expr [string last / $filename]-1]] + set pd_opendir $directory + netsend [list $target callback [enquote $filename]] + } +} + +proc pdtk_savepanel {target localdir} { + if {$localdir == ""} { + set filename [tk_getSaveFile] + } else { + set filename [tk_getSaveFile -initialdir $localdir] + } + if {$filename != ""} { + netsend [list $target callback [enquote $filename]] + } +} + +#---------------------------------------------------------------- +# To err is human. + +#proc bgerror {err} { +# global errorInfo +# set info [error_dump] +# tk_messageBox -message "$err: $info" -type ok +#} + +class_new OopsDialog {Dialog} + +def OopsDialog init {sig1 sig2 where} { + super damn + wm title .$self "Oops..." + pack [label .$self.head -text $sig2 -font {Helvetica -14 bold}] -side top + pack [label .$self.note -text "This program has performed a silly operation and has been shut down."] -side top + pack [text .$self.text -yscrollcommand ".$self.scroll set" -width 72 -height 15] -side left -fill both -expand 1 + pack [scrollbar .$self.scroll -command ".$self.text yview"] -side right -fill y + .$self.text insert 0.0 $where +} + +# Nous sommes donc en prsence d'un incendie. C'est normal... +def OopsDialog damn {} {$self ok} + +#---------------------------------------------------------------- + +if {$tk} { + set main [Client new] + set window_list [list $main] +} else { + set cmdline(console) 0 + #foreach dir $auto_path { + # set file $dir/libtclreadline[info sharedlibextension] + # puts "trying $file" + # if {![catch {load $file}]} {puts "found tclreadline !"} + #} + package require tclreadline + proc ::tclreadline::prompt1 {} {return "desire> "} + ::tclreadline::Loop + #while {1} { + # #set line [::tclreadline::readline read] + # puts -nonewline "desire> " + # flush stdout + # set line [gets stdin] + # if {[catch {puts [eval $line]}]} { + # puts "error: $::errorInfo" + # } + #} + #vwait foo +} + +def Canvas auto_test {} { + $self editmode= 1 + $self select_all + $self selection_move +10 0 + $self selection_move +10 0 + $self selection_move +10 0 +} + +#----------------------------------------------------------------- +def Canvas visual_diff {} { + if {$@blinky != ""} { + after cancel $@blinky + return + } + #regsub {\.pd} [$self name] {} filename + set filename [$self name] + set initialfile "" + foreach suffix {gif jpg jpeg png} { + set t [$self folder]/$filename.$suffix + post %s $t + if {[file exist $t]} {set initialfile $filename.$suffix; break} + } + set filename [tk_getOpenFile -defaultextension .pd -filetypes $::image_filetypes -initialdir [$self folder] -initialfile $initialfile] + if {$filename == ""} {return} + image create photo image_$self -file $filename + $self blink_image +} + +def Canvas blink_image {} { + if {[llength [.$self.c gettags BLINKY]]} { + .$self.c delete BLINKY + } else { + .$self.c create image 0 0 -image image_$self -tag BLINKY -anchor nw + } + set @blinky [after 500 [list $self blink_image]] +} + +#----------------------------------------------------------------- + +#lappend ::auto_path /usr/local/lib/graphviz +catch {package require Tcldot} +def Canvas graphviz_sort {} { + error "this code has to be rewritten to use the new containers" + set nodes {} + set gwidth 0; set gh 0 + #toplevel .graph -height 600 -width 800 + #set c [canvas .graph.c -height 600 -width 800] + #pack $c + set g [dotnew digraph] + $g setnodeattribute style filled color white + foreach child $@children { + lappend nodes [$g addnode $child label "[$child text]" shape "record" height "0.1"] + lappend nodes $child + } + puts "$nodes" + foreach wire $@wires { + mset {from outlet to inlet} [$wire report] + set n1 [lindex $nodes [expr [lsearch $nodes $from]-1]] + set n2 [lindex $nodes [expr [lsearch $nodes $to]-1]] + $n1 addedge $n2 + } + #$g layout + ;# see what render produces + #if {$debug} {puts [$g render]} + #eval [$g render] + set f {} + set fd [open graph.txt w] + $g write $fd plain + close $fd + + set fd [open graph.txt r] + set contents [read $fd] + close $fd + exec rm graph.txt + mset {x1 y1 x2 y2} [[$self widget] bbox all] + set width [expr $x2 - $x1] + set height [expr $y2 - $y1] + foreach line [split $contents "\n"] { + switch [lindex $line 0] { + graph {set gw [lindex $line 2]; set gh [lindex $line 3]} + node { + set w [expr $width/$gw] + set h [expr $height/$gh] + set id [lindex $line 1] + set x [lindex $line 2]; set y [lindex $line 3] + $id moveto [expr $x*$w] [expr ($gh-$y)*$h] + } + edge {break} + } + } +} + + diff --git a/desiredata/src/dzinc.tcl b/desiredata/src/dzinc.tcl new file mode 100644 index 00000000..f85f5539 --- /dev/null +++ b/desiredata/src/dzinc.tcl @@ -0,0 +1,157 @@ +package provide dzinc 0.1 +package require Tkzinc + +class_new Zinc {Thing} +def Zinc init {} { + set @group [eval old_$self add group 1] +} +def Zinc unknown {args} { + upvar 1 selector selector + old_$self $selector {expand}$args +} + +rename canvas old_canvas + +proc option_replace {argz} { + #set new_args {} + foreach {option val} $argz { + switch -- $option { + -bg {set option -backcolor} + -background {set option -backcolor} + -bd {set option -borderwidth} + } + lappend new_args $option $val + } + puts "new_args:::: $new_args" + return $new_args +} +#.bgerrorDialog.bitmap create oval 0 0 31 31 -fill red -outline black +#.x80e1c00.c -width 446 -height 296 -background white +proc canvas {name args} { + set result [eval [concat [list zinc $name] [option_replace $args] -highlightthickness 1]] + Zinc new_as $name + return $result +} + +def Zinc remove {orig mess} { + set idx [lsearch $mess $orig] + if {$idx < 0} {return $mess} + set mess [lreplace $mess $idx $idx+1] + return $mess +} + + +def Zinc replace {orig new mess} { + set idx [lsearch $mess $orig] + if {$idx < 0} {return $mess} + set mess [lreplace $mess $idx $idx $new] + return $mess +} + +def Zinc replace2 {orig new mess} { + set idx [lsearch $mess $orig] + if {$idx < 0} {return $mess} + set mess [lreplace $mess $idx $idx+1] + foreach item [lreverse $new] {set mess [linsert $mess $idx $item]} + return $mess +} + +def Zinc insert {orig new mess} { + set idx [lsearch $mess $orig] + if {$idx < 0} {return $mess} + #set mess [lreplace $mess $idx $idx+1] + foreach item [lreverse $new] {set mess [linsert $mess $idx $item]} + return $mess +} + + +def Zinc canvasx {val} { + return $val +} + +def Zinc canvasy {val} { + return $val +} + +def Zinc substitude {args} { + set args [$self replace -width -linewidth $args] + set args [$self replace -fill -linecolor $args] + set args [$self replace2 -dash [list -linestyle dotted] $args] + set args [$self replace -outline -linecolor $args] + set args [$self remove -dashoffset $args] + return $args +} + +def Zinc create {args} { + set i 0 + foreach item $args { + if {[regexp {^-} $item]} {break} + incr i + } + switch [lindex $args 0] { + line { + set type curve + set args [$self replace -width -linewidth $args] + set args [$self replace -fill -linecolor $args] + set args [$self replace2 -dash [list -linestyle dotted] $args] + set args [$self remove -dashoffset $args] + } + oval { + set type arc + set args [$self replace -fill -fillcolor $args] + set args [$self replace -outline -linecolor $args] + } + window { + set type window + } + rectangle { + set type rectangle + set args [$self replace -fill -fillcolor $args] + set args [$self replace -outline -linecolor $args] + set args [$self replace -width -linewidth $args] + set args [$self replace2 -dash [list -linestyle dotted] $args] + } + text { + set type text + set args [$self replace -fill -color $args] + } + default { + puts "UNKNOWN ITEM.. [lindex $args 0]" + } + } + if {$i == 2} { + set mess [concat $type $@group [lrange $args 1 end]] + } else { + set mess [concat $type $@group [list [lrange $args 1 $i-1]] [lrange $args $i end]] + } + if {$type == "window" || $type == "text"} {set mess [linsert $mess 2 -position]} + #puts "mess >> $mess" + eval [concat [list old_$self add] $mess] +} + +def Zinc itemconfigure {args} { + set mess [$self substitude $args] + eval [concat [list old_$self itemconfig] $mess] +} + +def Zinc coords {args} { + eval [concat [list old_$self coords] [lindex $args 0] [list [lrange $args 1 end]]] +} + + +def Zinc configure {args} { + eval [concat [list old_$self configure] [option_replace $args]] +} + +def Zinc delete {args} { + eval [concat [list old_$self remove] $args] +} + +def Zinc gettags {args} { + set result {} ;# bogus + catch {set result [eval [concat [list old_$self gettags] $args]]} + return $result +} + +def Zinc lower {tag args} { +}
\ No newline at end of file diff --git a/desiredata/src/icons/array.gif b/desiredata/src/icons/array.gif Binary files differnew file mode 100644 index 00000000..1a5ecfb2 --- /dev/null +++ b/desiredata/src/icons/array.gif diff --git a/desiredata/src/icons/bang.gif b/desiredata/src/icons/bang.gif Binary files differnew file mode 100644 index 00000000..28f708e1 --- /dev/null +++ b/desiredata/src/icons/bang.gif diff --git a/desiredata/src/icons/canvas.gif b/desiredata/src/icons/canvas.gif Binary files differnew file mode 100644 index 00000000..bcff6924 --- /dev/null +++ b/desiredata/src/icons/canvas.gif diff --git a/desiredata/src/icons/comment.gif b/desiredata/src/icons/comment.gif Binary files differnew file mode 100644 index 00000000..f40d6bf2 --- /dev/null +++ b/desiredata/src/icons/comment.gif diff --git a/desiredata/src/icons/graph.gif b/desiredata/src/icons/graph.gif Binary files differnew file mode 100644 index 00000000..e543e1d3 --- /dev/null +++ b/desiredata/src/icons/graph.gif diff --git a/desiredata/src/icons/hradio.gif b/desiredata/src/icons/hradio.gif Binary files differnew file mode 100644 index 00000000..16040321 --- /dev/null +++ b/desiredata/src/icons/hradio.gif diff --git a/desiredata/src/icons/hslider.gif b/desiredata/src/icons/hslider.gif Binary files differnew file mode 100644 index 00000000..4a08a376 --- /dev/null +++ b/desiredata/src/icons/hslider.gif diff --git a/desiredata/src/icons/message.gif b/desiredata/src/icons/message.gif Binary files differnew file mode 100644 index 00000000..57983c53 --- /dev/null +++ b/desiredata/src/icons/message.gif diff --git a/desiredata/src/icons/mode_edit.gif b/desiredata/src/icons/mode_edit.gif Binary files differnew file mode 100644 index 00000000..30902f10 --- /dev/null +++ b/desiredata/src/icons/mode_edit.gif diff --git a/desiredata/src/icons/mode_run.gif b/desiredata/src/icons/mode_run.gif Binary files differnew file mode 100644 index 00000000..1d6ea182 --- /dev/null +++ b/desiredata/src/icons/mode_run.gif diff --git a/desiredata/src/icons/number.gif b/desiredata/src/icons/number.gif Binary files differnew file mode 100644 index 00000000..7830e949 --- /dev/null +++ b/desiredata/src/icons/number.gif diff --git a/desiredata/src/icons/number2.gif b/desiredata/src/icons/number2.gif Binary files differnew file mode 100644 index 00000000..027562fe --- /dev/null +++ b/desiredata/src/icons/number2.gif diff --git a/desiredata/src/icons/object.gif b/desiredata/src/icons/object.gif Binary files differnew file mode 100644 index 00000000..ddec4903 --- /dev/null +++ b/desiredata/src/icons/object.gif diff --git a/desiredata/src/icons/symbol.gif b/desiredata/src/icons/symbol.gif Binary files differnew file mode 100644 index 00000000..0169009f --- /dev/null +++ b/desiredata/src/icons/symbol.gif diff --git a/desiredata/src/icons/toggle.gif b/desiredata/src/icons/toggle.gif Binary files differnew file mode 100644 index 00000000..90c1fc05 --- /dev/null +++ b/desiredata/src/icons/toggle.gif diff --git a/desiredata/src/icons/vradio.gif b/desiredata/src/icons/vradio.gif Binary files differnew file mode 100644 index 00000000..5649c31f --- /dev/null +++ b/desiredata/src/icons/vradio.gif diff --git a/desiredata/src/icons/vslider.gif b/desiredata/src/icons/vslider.gif Binary files differnew file mode 100644 index 00000000..5920c207 --- /dev/null +++ b/desiredata/src/icons/vslider.gif diff --git a/desiredata/src/icons/vu.gif b/desiredata/src/icons/vu.gif Binary files differnew file mode 100644 index 00000000..bbc6142f --- /dev/null +++ b/desiredata/src/icons/vu.gif diff --git a/desiredata/src/install-sh b/desiredata/src/install-sh new file mode 100644 index 00000000..e9de2384 --- /dev/null +++ b/desiredata/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/desiredata/src/iostream.txt b/desiredata/src/iostream.txt new file mode 100644 index 00000000..5ed2bf25 --- /dev/null +++ b/desiredata/src/iostream.txt @@ -0,0 +1,61 @@ +------------------8<--------cut-here--------8<------------------ +Dec 26 2005 + +1.1. a OutByteStream object is one that accepts those messages on inlet 0: + * float: seen as a byte (0..255) + * list of floats: seen as a sequence of float messages. + * symbol: seen as a \0-terminated string. + * bang: forces buffer-flushing if there's a buffer. + +1.2. a OutByteStream object may also optionally respond to string + messages, in my dreams. However, in the meanwhile, it may be more + appropriate to use a new special C function that accepts a pair of + int and const char * (\0 is not honored: the int specifies the size). + This is so that there is no speed disincentive to switch to decoupled + I/O objects. + +2.1. an InByteStream object is one that accepts those messages on inlet 0: + * bang: polls for more input (unlimited size). + * float: treated as int. polls for at most N bytes. + * auto 0: requires bang for getting more input. + * auto 1: uses a t_clock (hidden [metro]) for auto-polling. + +2.2. an InByteStream may produce those messages: + * float: seen as a byte (0..255) + * list of floats: seen as a sequence of float messages. + +and when in "auto 0" mode, it will only send it when receiving a bang or +float. + +2.2. an InByteStream object may also optionally produce string + messages, in my dreams, etc. What would the C function(s) look like + in this case? + +3. an IOByteStream object is just an InByteStream object and an +OutByteStream object at the same time. There is no conflict between the +two. + +4. there would be object classes called [tcp] and [udp] which would be + InputOutputStream objects (supporting in, out and bidi connections). + They would also respond to "connect" and "disconnect" (or maybe "open" + and "close" instead) and also "listen" for enabling server mode. + +5. there would be an object class called [fudiin] which would be an +OutByteStream and [fudiout] which would be an InByteStream. Thus, to get a +bidirectional [netsend] [netreceive], use this triad: + + | +[fudiout] + | +[tcp] + | +[fudiin] + | + +Leaving out the first or the last object gives you [netsend] and +[netreceive] respectively. + +6. a [tcp]<->[fudiin] pair can replace the server-side of the GUI + connection as long as the [tcp] object supports char* input as + suggested in part 1.2. (if the rest of this proposal is not + implemented, then use a slightly modified [netreceive] instead) diff --git a/desiredata/src/kb-mode.tcl b/desiredata/src/kb-mode.tcl new file mode 100644 index 00000000..08adce28 --- /dev/null +++ b/desiredata/src/kb-mode.tcl @@ -0,0 +1,210 @@ +package provide kb-mode 0.1 + +set kbmode 1 + +proc kb-mode_init {canvas} { + puts "init kb-mode" + set self "kbcursor" + append_callback kb-mode key kb-mode_key + Kb_cursor new_as $self $canvas + $self draw + return $self +} + +def Canvas kb-mode_key {x y key iso shift} { + set c [$self widget] + set step [$@kbcursor step] + if {$@keyprefix} { + $@kbcursor get_key $key + set @keyprefix 0 + } + switch $key { + BackSpace {$self delete_selection} + Up {$@kbcursor move 0 -$step; if {$shift} {$self arrow_key 0 -$step}} + Down {$@kbcursor move 0 +$step; if {$shift} {$self arrow_key 0 +$step}} + Left {$@kbcursor move -$step 0; if {$shift} {$self arrow_key -$step 0}} + Right {$@kbcursor move +$step 0; if {$shift} {$self arrow_key +$step 0}} + t {$self kbcursor_select} + Return {$self return_key $x $y $key $iso $shift} + default {} + } +} + +def Canvas kbcursor_move_up {} {$@kbcursor move 0 -[$@kbcursor step]} +def Canvas kbcursor_move_down {} {$@kbcursor move 0 [$@kbcursor step]} +def Canvas kbcursor_move_left {} {$@kbcursor move -[$@kbcursor step] 0} +def Canvas kbcursor_move_right {} {$@kbcursor move [$@kbcursor step] 0} + +def Canvas move_selection_up {} {$self arrow_key 0 -[$@kbcursor step]} +def Canvas move_selection_down {} {$self arrow_key 0 +[$@kbcursor step]} +def Canvas move_selection_left {} {$self arrow_key -[$@kbcursor step] 0} +def Canvas move_selection_right {} {$self arrow_key 0 +[$@kbcursor step] 0} + +class_new Kb_cursor {View} + +def Kb_cursor init {canvas} { + super + set @canvas $canvas + set @x 200 + set @y 200 + set @h [expr [font metrics [$self look font] -linespace]+3] + set @step [expr ceil($@h*1.5)] + set @prefixkeys {} + $self init_keys + $self recenter +} + + +def Kb_cursor xy {} {return [list $@x $@y]} +def Kb_cursor step {} {return $@step} + +def Kb_cursor draw {} { + set c [$@canvas widget] + set line1 [list $@x $@y $@x [expr $@y+$@h]] + set line2 [list $@x $@y [expr $@x+3] $@y] + set line3 [list $@x [expr $@y+$@h-1] [expr $@x+3] [expr $@y+$@h-1]] + $self item LINE1 line $line1 -fill red -width 2 + $self item LINE2 line $line2 -fill red -width 1 + $self item LINE3 line $line3 -fill red -width 1 + + $c raise $self +} + +def Kb_cursor move {x y} { + set @x [expr $@x + $x] + set @y [expr $@y + $y] + $self test_bounds + $self draw + set action [$@canvas action] + if {$action != "none"} { + if {[$action class] == "SelRect"} { + $action motion $@x $@y 256 "none" + } + + } +} + +def Kb_cursor scroll_canvas {} { + set c [$@canvas widget] + mset {x1 y1 x2 y2} [$c bbox all] + puts "xview [$c xview]" + set x2 [expr $x2]; set y2 [expr $y2] +} + +def Kb_cursor test_bounds {} { + set c [$@canvas widget] + set width [winfo width $c]; set height [winfo height $c] + set x1 [$c canvasx 2]; set y1 [$c canvasy 2] + set x2 [expr $x1+$width-2] + set y2 [expr $y1+$height-2] + if {$@x >= $x2} {$self scroll r [expr int($@x-$x2)]} + if {$@x <= $x1} {$self scroll l [expr int($@x-$x1)]} + if {$@y >= $y2} {$self scroll b [expr int($@y-$y2)]} + if {$@y <= $y1} {$self scroll t [expr int($@y-$y1)]} +} + +def Kb_cursor scroll {direction diff} { + set c [$@canvas widget] + set width [winfo width $c]; set height [winfo height $c] + set x1 [$c canvasx 2]; set y1 [$c canvasy 2] + mset {l r} [$c xview] + mset {t b} [$c yview] + foreach {n0 n1 n2 n3 region} [$c configure -scrollregion] {mset {rx1 ry1 rx2 ry2} $region} + switch $direction { + r {set axis "x"; if {$r == 1} {set rx2 [expr $rx2+$width]}} + l {set axis "x"; if {$l == 0} {set rx1 [expr $rx1-$width]}} + b {set axis "y"; if {$b == 1} {set ry2 [expr $ry2+$height]}} + t {set axis "y"; if {$t == 0} {set ry1 [expr $ry1-$height]}} + } + $c configure -scrollregion [list $rx1 $ry1 $rx2 $ry2] + $c [list $axis]view scroll $diff units +} + +def Canvas kbcursor_recenter {} {$@kbcursor recenter} + +def Kb_cursor recenter {} { + set c [$@canvas widget] + set width [winfo width $c]; set height [winfo height $c] + set x1 [$c canvasx 2]; set y1 [$c canvasy 2] + set x2 [expr $x1+$width] + set y2 [expr $y1+$height] + set @x [expr $x1+($width/2)] + set @y [expr $y1+($height/2)] + $self draw +} + +def Canvas kbcursor_Object {} {mset {x y} [$@kbcursor xy]; $self new_objectxy [expr $x+4] $y obj} +def Canvas kbcursor_Message {} {mset {x y} [$@kbcursor xy]; $self new_objectxy [expr $x+4] $y msg} +def Canvas kbcursor_bng {} {mset {x y} [$@kbcursor xy]; $self new_objectxy [expr $x+4] $y obj bng} +def Canvas kbcursor_tgl {} {mset {x y} [$@kbcursor xy]; $self new_objectxy [expr $x+4] $y obj tgl} +def Canvas kbcursor_nbx {} {mset {x y} [$@kbcursor xy]; $self new_objectxy [expr $x+4] $y obj nbx} + +def Canvas kbcursor_select {} { + mset {x y} [lmap + [$@kbcursor xy] 4]; set x [expr $x+4] + mset {type id detail} [$self identify_target $x $y 0] + puts "type:: $type || id:: $id || detail:: $detail" + if {$type == "object"} { + if {![$id selected?]} {$self selection+= $id} else {$self selection-= $id} + } +} + +def Kb_cursor init_keys {} { + global newkey accels + #@keys used for prefix keycommands + dict set @prefixkeys "1" "kbcursor_Object" + dict set @prefixkeys "2" "kbcursor_Message" + dict set @prefixkeys "b" "kbcursor_bng" + dict set @prefixkeys "t" "kbcursor_tgl" + dict set @prefixkeys "3" "kbcursor_nbx" + #@ctrlkeys overwrites the existing keybindings + foreach {key command} $newkey { + if {[catch {set vars [dict get $accels $key]}]} {puts "$key not bound yet......";set vars {}} + set new_val {} + foreach item $vars { + mset {class cmd} [split $item ":"] + if {$class == [$@canvas class]} { + set n $class:$command + lappend new_val $n + } else { + lappend new_val $item + } + } + if {$new_val == ""} {set new_val [$@canvas class]:$command} + dict set accels $key $new_val + } + +} + +def Kb_cursor get_key {key} { + if {[catch {set command [dict get $@prefixkeys $key]}]} { + post "key Ctrl-x $key not bound" + } { + puts "run $command" + $@canvas $command + } +} + +set newkey { + Ctrl+p {kbcursor_move_up} + Ctrl+n {kbcursor_move_down} + Ctrl+f {kbcursor_move_right} + Ctrl+b {kbcursor_move_left} + Ctrl+P {move_selection_up} + Ctrl+N {move_selection_down} + Ctrl+F {move_selection_right} + Ctrl+B {move_selection_left} + Alt+f {} + Alt+b {} + Ctrl+space {kbcursor_mark} +} + +def Canvas kbcursor_mark {} { + mset {x y} [$@kbcursor xy] + if {$@action == "none"} { + set @action [SelRect new $self $x $y 256 "none"] + } else { + if {[$@action class] == "SelRect"} { + $@action unclick $x $y 236 "none" + } + } +}
\ No newline at end of file diff --git a/desiredata/src/kernel.c b/desiredata/src/kernel.c new file mode 100644 index 00000000..8552c279 --- /dev/null +++ b/desiredata/src/kernel.c @@ -0,0 +1,2348 @@ +/* $Id: kernel.c,v 1.1.2.92 2007-09-09 21:34:56 matju Exp $ + * Copyright 2006-2007 Mathieu Bouchard. + * Copyright (c) 1997-2006 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::fr::umlute:2001 + * change marked with IOhannes + */ + +#define PD_PLUSPLUS_FACE +#include "desire.h" +#include "m_simd.h" +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <sstream> +#include <fcntl.h> +#include <stdio.h> +#include <ctype.h> +#include <errno.h> +#include <pthread.h> +#ifdef UNISTD +#include <unistd.h> +#endif +#ifdef MSW +#include <io.h> +#endif + +#define a_float a_w.w_float +#define a_symbol a_w.w_symbol +#define a_gpointer a_w.w_gpointer +#define a_index a_w.w_index + +using namespace std; + +/* T.Grill - bit alignment for signal vectors (must be a multiple of 8!) */ +/* if undefined no alignment occurs */ +#ifdef SIMD_BYTEALIGN + #define VECTORALIGNMENT (SIMD_BYTEALIGN*8) +#else + #define VECTORALIGNMENT 128 +#endif + +void *getbytes(size_t nbytes) { + if (nbytes < 1) nbytes = 1; + void *ret = (void *)calloc(nbytes, 1); + if (!ret) error("pd: getbytes() failed -- out of memory"); + return ret; +} + +void *copybytes(void *src, size_t nbytes) { + void *ret = getbytes(nbytes); + if (nbytes) memcpy(ret, src, nbytes); + return ret; +} + +void *resizebytes(void *old, size_t oldsize, size_t newsize) { + if (newsize < 1) newsize = 1; + if (oldsize < 1) oldsize = 1; + void *ret = (void *)realloc((char *)old, newsize); + if (newsize > oldsize && ret) memset(((char *)ret) + oldsize, 0, newsize - oldsize); + if (!ret) error("pd: resizebytes() failed -- out of memory"); + return ret; +} + +void freebytes(void *old, size_t nbytes) {free(old);} + +/* in the following size_t is assumed to have the same size as a pointer type !!! */ + +/* T.Grill - get aligned memory */ +void *getalignedbytes(size_t nbytes) { + /* to align the region we also need some extra memory to save the original pointer location + it is saved immediately before the aligned vector memory */ + void *vec = getbytes(nbytes+(VECTORALIGNMENT/8-1)+sizeof(void *)); + if (!vec) return 0; + t_int alignment = ((t_int)vec+sizeof(void *))&(VECTORALIGNMENT/8-1); + void *ret = (unsigned char *)vec+sizeof(void *)+(alignment == 0?0:VECTORALIGNMENT/8-alignment); + *(void **)((unsigned char *)ret-sizeof(void *)) = vec; + return ret; +} + +/* T.Grill - free aligned vector memory */ +void freealignedbytes(void *ptr,size_t nbytes) { + free(*(void **)((unsigned char *)ptr-sizeof(void *))); +} + +/* T.Grill - resize aligned vector memory */ +void *resizealignedbytes(void *ptr,size_t oldsize, size_t newsize) { + if (newsize<1) newsize=1; + void *ori = *(void **)((unsigned char *)ptr-sizeof(void *)); + void *vec = realloc(ori,newsize+(VECTORALIGNMENT/8-1)+sizeof(void *)); + t_int alignment = ((t_int)vec+sizeof(void *))&(VECTORALIGNMENT/8-1); + void *ret = (unsigned char *)vec+sizeof(void *)+(alignment == 0?0:VECTORALIGNMENT/8-alignment); + *(void **)((unsigned char *)ret-sizeof(void *)) = vec; + return ret; +} + +/* TB: copy to aligned vector memory */ +void *copyalignedbytes(void *src, size_t nbytes) { + void *ret = getalignedbytes(nbytes); + if (nbytes) memcpy(ret, src, nbytes); + return ret; +} + +t_class *hash_class; + +/*extern "C"*/ void hash_setup () { + hash_class = class_new(gensym("#V"), (t_newmethod)0 /*hash_new*/, + 0 /*(t_method)hash_free*/, sizeof(t_object), CLASS_PD, A_GIMME, 0); +} + +/* 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) {return a->a_type==A_FLOAT ? a->a_float : 0;} +t_int atom_getint( t_atom *a) {return (t_int)atom_getfloat(a);} +t_symbol * atom_getsymbol(t_atom *a) {return a->a_type==A_SYMBOL ? a->a_symbol : &s_symbol;} +const char *atom_getstring(t_atom *a) {return atom_getsymbol(a)->name;} + +t_symbol *atom_gensym(t_atom *a) { /* this works better for graph labels */ + if (a->a_type == A_SYMBOL) return a->a_symbol; + if (a->a_type == A_FLOAT) {char buf[30]; sprintf(buf, "%g", a->a_float); return gensym(buf);} + return gensym("???"); +} + +t_float atom_getfloatarg(int which, int argc, t_atom *argv) { + if (argc <= which) return 0; + argv += which; + return argv->a_type==A_FLOAT ? argv->a_float : 0; +} + +t_int atom_getintarg(int which, int argc, t_atom *argv) +{return (t_int)atom_getfloatarg(which, argc, argv);} + +t_symbol *atom_getsymbolarg(int which, int argc, t_atom *argv) { + if (argc <= which) return &s_; + argv += which; + return argv->a_type==A_SYMBOL ? argv->a_symbol : &s_; +} + +const char *atom_getstringarg(int which, int argc, t_atom *argv) { + return atom_getsymbolarg(which,argc,argv)->name; +} + +/* 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. */ + +//static int should_quote(char *s) {return strchr(";,\\{}\"",*s) || isspace(*s) || (*s=='$' && isdigit(s[1]));} +static int should_quote(char *s) {return strchr(";,\\{}\" ",*s) || (*s=='$' && isdigit(s[1]));} + +void atom_ostream(t_atom *a, ostream &buf) { + switch(a->a_type) { + case A_SEMI: buf << ";"; break; + case A_COMMA: buf << ","; break; + case A_POINTER: buf << "(pointer)"; break; + case A_FLOAT: buf << a->a_float; break; + case A_SYMBOL: { + bool quote=0; + for (char *sp = a->a_symbol->name; *sp; sp++) if (should_quote(sp)) {quote = 1; break;} + if (quote) { + for (char *sp = a->a_symbol->name; *sp; sp++) { + if (should_quote(sp)) buf << '\\'; + buf << *sp; + } + } else buf << a->a_symbol->name; + } break; + case A_DOLLAR: buf << "$" << a->a_index; break; + case A_DOLLSYM: buf << a->a_symbol->name; break; + default: bug("%s",__PRETTY_FUNCTION__); + } +} + +/* this is not completely compatible with Miller's, as it won't do anything special for short bufsizes. */ +void atom_string(t_atom *a, char *buf, unsigned int bufsize) { + ostringstream b; + atom_ostream(a,b); + strncpy(buf,b.str().data(),bufsize); + buf[bufsize-1]=0; +} + +void atom_init(t_atom *a, size_t n) { + for (size_t i=0; i<n; i++) { + a[i].a_type = A_FLOAT; + a[i].a_float = 0.0; + } +} + +void atom_copy(t_atom *a, t_atom *b, size_t n) { + memcpy(a,b,n*sizeof(t_atom)); + /* here I should handle incref */ +} + +void atom_delete(t_atom *a, size_t n) { + /* here I should handle decref */ +} + +/* in which the value has bit 0 set if the key object is not a zombie, + and has bit 1 set if the object has been uploaded to the client */ +t_hash<t_pd *,long> *object_table; + +t_pd *pd_new(t_class *c) { + if (!c) bug("pd_new: apparently called before setup routine"); + t_pd *x = (t_pd *)getbytes(c->size); + x->_class = c; + object_table->set(x,1); + if (c->gobj) ((t_gobj *)x)->g_adix = appendix_new((t_gobj *)x); + if (c->patchable) { + ((t_object *)x)->inlet = 0; + ((t_object *)x)->outlet = 0; + } + return x; +} + +void pd_free_zombie(t_pd *x) { + t_class *c = x->_class; + if (c->gobj) appendix_free((t_gobj *)x); + if (c->size) free(x); + object_table->del(x); +} + +void pd_free(t_pd *x) { + t_class *c = x->_class; + if (c->freemethod) ((t_gotfn)(c->freemethod))(x); + if (c->patchable) { + t_object *y = (t_object *)x; + while (y->outlet) outlet_free(y->outlet); + while (y->inlet) inlet_free(y->inlet); + if (y->binbuf) binbuf_free(y->binbuf); + } + /* schedule for deletion if need to keep the allocation around */ + if (c->gobj && (object_table->get(x)&2)) { + object_table->set(x,object_table->get(x)&~1); + gobj_changed((t_gobj *)x,""); + //char *xx = (char *)x; for (int i=0; i<c->size; i++) xx[i]="\xde\xad\xbe\xef"[i&3]; + } else pd_free_zombie(x); +} + +void gobj_save(t_gobj *x, t_binbuf *b) { + t_class *c = x->g_pd; + if (c->savefn) c->savefn(x, b); +} + +/* deal with several objects bound to the same symbol. If more than one, + we actually bind a collection object to the symbol, which forwards messages sent to the symbol. */ + +static t_class *bindlist_class; + +struct t_bindelem { + t_pd *who; + t_bindelem *next; +}; + +struct t_bindlist : t_pd { + t_bindelem *list; +}; + +#define bind_each(e,x) for (t_bindelem *e = x->list; e; e = e->next) +static void bindlist_bang (t_bindlist *x) {bind_each(e,x) pd_bang(e->who);} +static void bindlist_float (t_bindlist *x, t_float f) {bind_each(e,x) pd_float(e->who,f);} +static void bindlist_symbol (t_bindlist *x, t_symbol *s) {bind_each(e,x) pd_symbol(e->who,s);} +static void bindlist_pointer (t_bindlist *x, t_gpointer *gp) {bind_each(e,x) pd_pointer(e->who, gp);} +static void bindlist_list (t_bindlist *x, t_symbol *s, int argc, t_atom *argv) {bind_each(e,x) pd_list(e->who, s,argc,argv);} +static void bindlist_anything(t_bindlist *x, t_symbol *s, int argc, t_atom *argv) {bind_each(e,x) pd_typedmess(e->who, s,argc,argv);} + +static t_bindelem *bindelem_new(t_pd *who, t_bindelem *next) { + t_bindelem *self = (t_bindelem *)malloc(sizeof(t_bindelem)); + self->who = who; + self->next = next; + return self; +} + +void pd_bind(t_pd *x, t_symbol *s) { + if (s->thing) { + if (s->thing->_class == bindlist_class) { + t_bindlist *b = (t_bindlist *)s->thing; + b->list = bindelem_new(x,b->list); + } else { + t_bindlist *b = (t_bindlist *)pd_new(bindlist_class); + b->list = bindelem_new(x,bindelem_new(s->thing,0)); + s->thing = b; + } + } else s->thing = x; +} + +/* 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. */ +void pd_unbind(t_pd *x, t_symbol *s) { + if (s->thing == x) {s->thing = 0; return;} + if (s->thing && s->thing->_class == bindlist_class) { + t_bindlist *b = (t_bindlist *)s->thing; + t_bindelem *e, *e2; + if ((e = b->list)->who == x) { + b->list = e->next; + free(e); + } else for (e = b->list; (e2=e->next); e = e2) if (e2->who == x) { + e->next = e2->next; + free(e2); + break; + } + if (!b->list->next) { + s->thing = b->list->who; + free(b->list); + pd_free(b); + } + } else error("%s: couldn't unbind", s->name); +} + +t_pd *pd_findbyclass(t_symbol *s, t_class *c) { + t_pd *x = 0; + if (!s->thing) return 0; + if (s->thing->_class == c) return s->thing; + if (s->thing->_class == bindlist_class) { + t_bindlist *b = (t_bindlist *)s->thing; + int warned = 0; + bind_each(e,b) if (e->who->_class == c) { + if (x && !warned) {post("warning: %s: multiply defined", s->name); warned = 1;} + x = e->who; + } + } + return x; +} + +/* stack for maintaining bindings for the #X symbol during nestable loads. */ + +#undef g_next + +struct t_gstack { + t_pd *what; + t_symbol *loading_abstr; + t_gstack *next; + long base_o_index; +}; + +static t_gstack *gstack_head = 0; +static t_pd *lastpopped; +static t_symbol *pd_loading_abstr; + +int pd_setloadingabstraction(t_symbol *sym) { + t_gstack *foo = gstack_head; + for (foo = gstack_head; foo; foo = foo->next) if (foo->loading_abstr == sym) return 1; + pd_loading_abstr = sym; + return 0; +} + +int gstack_empty() {return !gstack_head;} + +long canvas_base_o_index() { + return gstack_head ? gstack_head->base_o_index : 0; +} + +void pd_pushsym(t_pd *x) { + t_gstack *y = (t_gstack *)malloc(sizeof(*y)); + y->what = s__X.thing; + y->next = gstack_head; + y->loading_abstr = pd_loading_abstr; + y->base_o_index = x->_class == canvas_class ? ((t_canvas *)x)->next_o_index : -666; + pd_loading_abstr = 0; + gstack_head = y; + s__X.thing = x; +} + +void pd_popsym(t_pd *x) { + if (!gstack_head || s__X.thing != x) {bug("gstack_pop"); return;} + t_gstack *headwas = gstack_head; + s__X.thing = headwas->what; + gstack_head = headwas->next; + free(headwas); + lastpopped = x; +} + +static void stackerror(t_pd *x) {error("stack overflow");} + +/* to enable multithreading, make those variables "thread-local". this means that they have to go in + a thread-specific place instead of plain global. do not ever use tim's atomic counters for this, + as they count all threads together as if they're one, and they're especially incompatible with + use of the desiredata-specific stack[] variable. */ +int pd_stackn = 0; /* how much of the stack is in use */ +t_call pd_stack[STACKSIZE]; + +static inline uint64 rdtsc() {uint64 x; __asm__ volatile (".byte 0x0f, 0x31":"=A"(x)); return x;} + +//#define PROFILER +#ifdef PROFILER +#define ENTER_PROF uint64 t = rdtsc(); +#define LEAVE_PROF if (x->_class->gobj && ((t_gobj *)x)->dix) ((t_gobj *)x)->dix->elapsed += rdtsc() - t; +#else +#define ENTER_PROF +#define LEAVE_PROF +#endif + +#define ENTER(SELECTOR) if(pd_stackn >= STACKSIZE) {stackerror(x); return;} \ + pd_stack[pd_stackn].self = x; pd_stack[pd_stackn].s = SELECTOR; pd_stackn++; ENTER_PROF +#define LEAVE pd_stackn--; LEAVE_PROF + +/* matju's 2007.07.14 inlet-based stack check needs to be implemented in: + pd_bang pd_float pd_pointer pd_symbol pd_string pd_list pd_typedmess */ +void pd_bang(t_pd *x) {ENTER(&s_bang); x->_class->bangmethod(x); LEAVE;} +void pd_float(t_pd *x, t_float f) {ENTER(&s_float); x->_class->floatmethod(x,f); LEAVE;} +void pd_pointer(t_pd *x, t_gpointer *gp) {ENTER(&s_pointer); x->_class->pointermethod(x,gp); LEAVE;} +void pd_symbol(t_pd *x, t_symbol *s) {ENTER(&s_symbol); x->_class->symbolmethod(x,s); LEAVE;} +/* void pd_string(t_pd *x, const char *s){ENTER(&s_symbol); x->_class->stringmethod(x,s); LEAVE;} future use */ +void pd_list(t_pd *x, t_symbol *s, int ac, t_atom *av) {ENTER(s); x->_class->listmethod(x,&s_list,ac,av); LEAVE;} + +/* 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. */ + +union inletunion { + t_symbol *symto; + t_gpointer *pointerslot; + t_float *floatslot; + t_symbol **symslot; + t_sample floatsignalvalue; +}; + +struct _inlet : t_pd { + struct _inlet *next; + t_object *owner; + t_pd *dest; + t_symbol *symfrom; + union inletunion u; + t_symbol* tip; +}; + +static t_class *inlet_class, *pointerinlet_class, *floatinlet_class, *symbolinlet_class; + +#define ISINLET(pd) ( \ + pd->_class == inlet_class || \ + pd->_class == pointerinlet_class || \ + pd->_class == floatinlet_class || \ + pd->_class == symbolinlet_class) + +/* --------------------- generic inlets ala max ------------------ */ + +static void object_append_inlet(t_object *owner, t_inlet *x) { + t_inlet *y = owner->inlet, *y2; + if (y) { + while ((y2 = y->next)) y = y2; + y->next = x; + } else owner->inlet = x; +} + +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); + x->owner = owner; + x->dest = dest; + if (s1 == &s_signal) x->u.floatsignalvalue = 0; else x->u.symto = s2; + x->symfrom = s1; + x->next = 0; + x->tip = gensym("?"); + object_append_inlet(owner,x); + return x; +} + +t_inlet *signalinlet_new(t_object *owner, t_float f) { + t_inlet *x = inlet_new(owner, owner, &s_signal, &s_signal); + x->u.floatsignalvalue = f; + return x; +} + +static void inlet_wrong(t_inlet *x, t_symbol *s) { + error("inlet: expected '%s' but got '%s'", x->symfrom->name, s->name); +} + +void inlet_settip(t_inlet* i,t_symbol* s) {i->tip = s;} + +char* inlet_tip(t_inlet* i,int num) { + if (num < 0) return "???"; + while (num-- && i) i = i->next; + if (i && i->tip) return i->tip->name; + return "?"; +} + +/* LATER figure out how to make these efficient: */ +static void inlet_bang(t_inlet *x) { + if (x->symfrom == &s_bang) pd_vmess(x->dest, x->u.symto, ""); + else if (!x->symfrom) pd_bang(x->dest); + else inlet_wrong(x, &s_bang); +} +static void inlet_pointer(t_inlet *x, t_gpointer *gp) { + if (x->symfrom == &s_pointer) pd_vmess(x->dest, x->u.symto, "p", gp); + else if (!x->symfrom) pd_pointer(x->dest, gp); + else inlet_wrong(x, &s_pointer); +} +static void inlet_float(t_inlet *x, t_float f) { + if (x->symfrom == &s_float) pd_vmess(x->dest, x->u.symto, "f", (t_floatarg)f); + else if (x->symfrom == &s_signal) x->u.floatsignalvalue = f; + else if (!x->symfrom) pd_float(x->dest, f); + else inlet_wrong(x, &s_float); +} + +static void inlet_symbol(t_inlet *x, t_symbol *s) { + if (x->symfrom == &s_symbol) pd_vmess(x->dest, x->u.symto, "s", s); + else if (!x->symfrom) pd_symbol(x->dest, s); + else inlet_wrong(x, &s_symbol); +} + +static void inlet_list(t_inlet *x, t_symbol *s, int argc, t_atom *argv) { + if (x->symfrom == &s_list || x->symfrom == &s_float || x->symfrom == &s_symbol || x->symfrom == &s_pointer) + typedmess(x->dest, x->u.symto, argc, argv); + else if (!x->symfrom) pd_list(x->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->symfrom == s) typedmess(x->dest, x->u.symto, argc, argv); + else if (!x->symfrom) typedmess(x->dest, s, argc, argv); + else inlet_wrong(x, s); +} + +void inlet_free(t_inlet *x) { + t_object *y = x->owner; + if (y->inlet == x) y->inlet = x->next; + else for (t_inlet *x2 = y->inlet; x2; x2 = x2->next) if (x2->next == x) { + x2->next = x->next; + break; + } + pd_free(x); +} + +/* ----- pointerinlets, floatinlets, syminlets: optimized inlets ------- */ + +static void pointerinlet_pointer(t_inlet *x, t_gpointer *gp) { + gpointer_unset(x->u.pointerslot); + *(x->u.pointerslot) = *gp; + if (gp->o) gp->o->refcount++; +} + +static void floatinlet_float( t_inlet *x, t_float f) { *(x->u.floatslot) = f; } +static void symbolinlet_symbol(t_inlet *x, t_symbol *s) { *(x->u.symslot) = s; } + +#define COMMON \ + x->owner = owner; \ + x->dest = 0; \ + x->next = 0; \ + object_append_inlet(owner,x); \ + return x; + +t_inlet *floatinlet_new(t_object *owner, t_float *fp) { + t_inlet *x = (t_inlet *)pd_new(floatinlet_class); + x->symfrom = &s_float; x->u.floatslot = fp; COMMON +} +t_inlet *symbolinlet_new(t_object *owner, t_symbol **sp) { + t_inlet *x = (t_inlet *)pd_new(symbolinlet_class); + x->symfrom = &s_symbol; x->u.symslot = sp; COMMON +} +t_inlet *pointerinlet_new(t_object *owner, t_gpointer *gp) { + t_inlet *x = (t_inlet *)pd_new(pointerinlet_class); + x->symfrom = &s_pointer; x->u.pointerslot = gp; COMMON +} +#undef COMMON + +/* ---------------------- 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)->inlet; + if (!argc) return; + for (count = argc-1, ap = argv+1; ip && count--; ap++, ip = ip->next) { + if (ap->a_type == A_POINTER) pd_pointer(ip,ap->a_gpointer); + else if (ap->a_type == A_FLOAT) pd_float(ip,ap->a_float); + else pd_symbol(ip,ap->a_symbol); + } + if (argv->a_type == A_POINTER) pd_pointer(x, argv->a_gpointer); + else if (argv->a_type == A_FLOAT) pd_float(x, argv->a_float); + else pd_symbol(x, argv->a_symbol); +} + +void obj_init () { + inlet_class = class_new(gensym("inlet"), 0, 0, sizeof(t_inlet), CLASS_PD, 0); + floatinlet_class = class_new(gensym("inlet"), 0, 0, sizeof(t_inlet), CLASS_PD, 0); + symbolinlet_class = class_new(gensym("inlet"), 0, 0, sizeof(t_inlet), CLASS_PD, 0); + pointerinlet_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); + class_addfloat( floatinlet_class, floatinlet_float); + class_addsymbol( symbolinlet_class, symbolinlet_symbol); + class_addpointer(pointerinlet_class, pointerinlet_pointer); +} + +/* --------------------------- outlets ------------------------------ */ + +/* this is fairly obsolete stuff, I think */ +static int outlet_eventno; +void outlet_setstacklim () {outlet_eventno++;} +int sched_geteventno( void) {return outlet_eventno;} + +struct _outlet { + t_object *owner; + struct _outlet *next; + t_outconnect *connections; + t_symbol *sym; +}; + +t_inlet *t_object:: in(int n) {t_inlet *i= inlet; while(n--) i=i->next; return i;} +t_outlet *t_object::out(int n) {t_outlet *o=outlet; while(n--) o=o->next; return o;} + +t_class *wire_class; +t_wire *wire_new (t_symbol *s, int argc, t_atom *argv) { + t_wire *self = (t_wire *)pd_new(wire_class); + self->g_adix = appendix_new((t_gobj *)self); + return self; +} +void wire_free (t_wire *self) {/* nothing here */} + +/* this is only used for pd_upload yet, right? so, it can use the new indices instead already */ +void wire_save (t_wire *self, t_binbuf *b) { +// t_canvas *c = self->dix->canvas; + binbuf_addv(b,"ttiiii;","#X","connect", +// canvas_getindex(c,self->from), self->outlet, +// canvas_getindex(c,self->to ), self-> inlet); + self->from->dix->index, self->outlet, + self->to ->dix->index, self-> inlet); + appendix_save((t_gobj *)self,b); +} + +t_outlet *outlet_new(t_object *owner, t_symbol *s) { + t_outlet *x = (t_outlet *)malloc(sizeof(*x)), *y, *y2; + x->owner = owner; + x->next = 0; + y = owner->outlet; + if (y) { + while ((y2 = y->next)) y = y2; + y->next = x; + } else owner->outlet = x; + x->connections = 0; + x->sym = s; + return x; +} + +#define each_connect(oc,x) for (t_outconnect *oc = x->connections; oc; oc = oc->next) +void outlet_bang(t_outlet *x) {each_connect(oc,x) pd_bang(oc->oc_to);} +void outlet_pointer(t_outlet *x, t_gpointer *gp) {t_gpointer gpointer = *gp; each_connect(oc,x) pd_pointer(oc->oc_to, &gpointer);} +void outlet_float(t_outlet *x, t_float f) {each_connect(oc,x) pd_float(oc->oc_to, f);} +void outlet_symbol(t_outlet *x, t_symbol *s) {each_connect(oc,x) pd_symbol(oc->oc_to, s);} +void outlet_list(t_outlet *x, t_symbol *s, int argc, t_atom *argv) {each_connect(oc,x) pd_list( oc->oc_to,s,argc,argv);} +void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv) {each_connect(oc,x) typedmess(oc->oc_to,s,argc,argv);} + +void outlet_atom(t_outlet *x, t_atom *a) { + if (a->a_type == A_FLOAT ) outlet_float( x,a->a_float); + else if (a->a_type == A_SYMBOL ) outlet_symbol( x,a->a_symbol); + else if (a->a_type == A_POINTER) outlet_pointer(x,a->a_gpointer); + else error("can't send atom whose type is %d",a->a_type); +} + +/* get the outlet's declared symbol */ +t_symbol *outlet_getsymbol(t_outlet *x) {return x->sym;} + +void outlet_free(t_outlet *x) { + t_object *y = x->owner; + if (y->outlet == x) y->outlet = x->next; + else for (t_outlet *x2 = y->outlet; x2; x2 = x2->next) if (x2->next == x) { + x2->next = x->next; + break; + } + free(x); +} + +#define each_inlet(i,obj) for ( t_inlet *i=obj->inlet; i; i=i->next) +#define each_outlet(o,obj) for (t_outlet *o=obj->outlet; o; o=o->next) + +static t_pd *find_inlet(t_object *to, int inlet) { + if (to->_class->firstin) {if (inlet) inlet--; else return (t_pd *)to;} + each_inlet(i,to) if (inlet) inlet--; else return (t_pd *)i; + return 0; +} + +static t_outlet *find_outlet(t_object *from, int outlet) { + each_outlet(o,from) if (outlet) outlet--; else return o; + return 0; +} + +t_outconnect *obj_connect(t_object *from, int outlet, t_object *to, int inlet) { + t_outlet *o = find_outlet(from,outlet); + t_pd *i = find_inlet(to,inlet); + if (!o||!i) return 0; + t_outconnect *oc = wire_new(0,0,0), *oc2; + oc->next = 0; + oc->oc_to = i; + oc->from = from; oc->outlet = outlet; + oc->to = to; oc->inlet = inlet; + /* append it to the end of the list */ + /* LATER we might cache the last "oc" to make this faster. */ + if ((oc2 = o->connections)) { + while (oc2->next) oc2 = oc2->next; + oc2->next = oc; + } else o->connections = oc; + if (o->sym == &s_signal) canvas_update_dsp(); + return oc; +} + +void obj_disconnect(t_object *from, int outlet, t_object *to, int inlet) { + t_outlet *o = find_outlet(from,outlet); if (!o) {post("outlet does not exist"); return;} + t_pd *i = find_inlet(to, inlet); if (!i) {post( "inlet does not exist"); return;} + t_outconnect *oc = o->connections, *oc2; + if (!oc) {post("outlet has no connections"); return;} + if (oc->oc_to == i) { + o->connections = oc->next; + pd_free(oc); + goto done; + } + while ((oc2 = oc->next)) { + if (oc2->oc_to == i) { + oc->next = oc2->next; + pd_free(oc2); + goto done; + } + oc = oc2; + } + post("connection not found"); +done: + if (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=0; + each_outlet(o,x) n++; + return n; +} + +int obj_ninlets(t_object *x) { + int n=!!x->_class->firstin; + each_inlet(i,x) n++; + return n; +} + +t_outconnect *obj_starttraverseoutlet(t_object *x, t_outlet **op, int nout) { + t_outlet *o = x->outlet; + while (nout-- && o) o = o->next; + *op = o; + return o ? o->connections : 0; +} + +t_outconnect *obj_nexttraverseoutlet(t_outconnect *lastconnect, t_object **destp, t_inlet **inletp, int *whichp) { + t_pd *y = lastconnect->oc_to; + if (ISINLET(y)) { + t_inlet *i = (t_inlet *)y; + t_object *dest = i->owner; + int n = dest->_class->firstin; + each_inlet(i2,dest) if (i2==i) break; else n++; + *whichp = n; + *destp = dest; + *inletp = i; + } else { + *whichp = 0; + *inletp = 0; + *destp = (t_object *)y; + } + return lastconnect->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) { + return x->_class->patchable ? (t_object *)x : 0; +} + +/* move an inlet or outlet to the head of the list. this code is not safe with the latest additions in t_outconnect ! */ +void obj_moveinletfirst( t_object *x, t_inlet *i) { + if (x->inlet == i) return; + each_inlet( i2,x) if (i2->next == i) {i2->next = i->next; i->next = x-> inlet; x-> inlet = i; return;}} +void obj_moveoutletfirst(t_object *x, t_outlet *o) { + if (x->outlet == o) return; + each_outlet(o2,x) if (o2->next == o) {o2->next = o->next; o->next = x->outlet; x->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=0; + each_inlet(i,x) if (i->symfrom == &s_signal) n++; + if (x->_class->firstin && x->_class->floatsignalin) n++; + return n; +} +int obj_nsigoutlets(t_object *x) { + int n=0; + each_outlet(o,x) if (o->sym == &s_signal) 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; + if (x->_class->firstin && x->_class->floatsignalin) {if (!m--) return 0; else n++;} + each_inlet(i,x) if (i->symfrom == &s_signal) {if (!m) return n; else {n++; m--;}} + return -1; +} +int obj_sigoutletindex(t_object *x, int m) { + int n=0; + each_outlet(o,x) if (o->sym == &s_signal) {if (!m) return n; else {n++; m--;}} + return -1; +} + +int obj_issignalinlet(t_object *x, int m) { + if (x->_class->firstin) {if (!m) return x->_class->floatsignalin; else m--;} + t_inlet *i; + for (i = x->inlet; i && m; i = i->next, m--) {} + return i && i->symfrom==&s_signal; +} +int obj_issignaloutlet(t_object *x, int m) { + t_outlet *o2; + for (o2 = x->outlet; o2 && m--; o2 = o2->next) {} + return o2 && o2->sym==&s_signal; +} + +t_sample *obj_findsignalscalar(t_object *x, int m) { + int n = 0; + t_inlet *i; + if (x->_class->firstin && x->_class->floatsignalin) { + if (!m--) return x->_class->floatsignalin > 0 ? (t_sample *)(((char *)x) + x->_class->floatsignalin) : 0; + n++; + } + for (i = x->inlet; i; i = i->next, m--) if (i->symfrom == &s_signal) { + if (m == 0) return &i->u.floatsignalvalue; + n++; + } + return 0; +} + +/* and those two are only used in desire.c... */ +int inlet_getsignalindex(t_inlet *x) { + int n=0; for ( t_inlet *i = x->owner-> inlet; i; i = i->next) if (i==x) return n; else if (i->symfrom == &s_signal) n++; + return -1;} +int outlet_getsignalindex(t_outlet *x) { + int n=0; for (t_outlet *o = x->owner->outlet; o; o = o->next) if (o==x) return n; else if (o->sym == &s_signal) n++; + return -1;} + +#ifdef QUALIFIED_NAME +static char *pd_library_name = 0; +void pd_set_library_name(char *libname){ + pd_library_name=libname; +} +#endif + +t_hash<t_symbol *, t_class *> *class_table=0; +static t_symbol *class_loadsym; /* name under which an extern is invoked */ +static void pd_defaultfloat(t_pd *x, t_float f); +static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv); +t_pd pd_objectmaker; /* factory for creating "object" boxes */ +t_pd pd_canvasmaker; /* factory for creating canvases */ + +static t_symbol *class_extern_dir = &s_; + +static void pd_defaultanything(t_pd *x, t_symbol *s, int argc, t_atom *argv) { + error("%s: no method for '%s'", x->_class->name->name, s->name); +} + +static void pd_defaultbang(t_pd *x) { + t_class *c = pd_class(x); + if (c->listmethod != pd_defaultlist) c->listmethod(x,0,0,0); + else c->anymethod(x,&s_bang,0,0); +} + +static void pd_defaultfloat(t_pd *x, t_float f) { + t_class *c = pd_class(x); t_atom at; SETFLOAT(&at, f); + if (c->listmethod != pd_defaultlist) c->listmethod(x,0,1,&at); else c->anymethod(x,&s_float,1,&at); +} +static void pd_defaultsymbol(t_pd *x, t_symbol *s) { + t_class *c = pd_class(x); t_atom at; SETSYMBOL(&at, s); + if (c->listmethod != pd_defaultlist) c->listmethod(x,0,1,&at); else c->anymethod(x,&s_symbol,1,&at); +} +static void pd_defaultpointer(t_pd *x, t_gpointer *gp) { + t_class *c = pd_class(x); t_atom at; SETPOINTER(&at, gp); + if (c->listmethod != pd_defaultlist) c->listmethod(x,0,1,&at); else c->anymethod(x,&s_pointer,1,&at); +} + +void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv); +static void class_nosavefn(t_gobj *z, t_binbuf *b); + +/* handle "list" messages to Pds without explicit list methods defined. */ +static void pd_defaultlist(t_pd *x, t_symbol *s, int argc, t_atom *argv) { + t_class *c = pd_class(x); + /* a list with no elements is handled by the 'bang' method if one exists. */ + if (argc == 0 && c->bangmethod != pd_defaultbang) {c->bangmethod(x); return;} + /* 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) { +#define HANDLE(A,M,D,F) if (argv->a_type==A && c->M != D) {c->M(x, argv->a_w.F); return;} + HANDLE(A_FLOAT ,floatmethod ,pd_defaultfloat ,w_float) + HANDLE(A_SYMBOL ,symbolmethod ,pd_defaultsymbol ,w_symbol) + HANDLE(A_POINTER,pointermethod,pd_defaultpointer,w_gpointer) + } + /* Next try for an "anything" method; 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. otherwise gove up and complain. */ + if (c->anymethod != pd_defaultanything) c->anymethod(x,&s_list,argc,argv); + else if (c->patchable) obj_list((t_object *)x, s, argc, argv); + else pd_defaultanything(x, &s_list, argc, argv); +} + +t_symbol *qualified_name(t_symbol *s) { + char *buf; + asprintf(&buf, "%s%s%s", pd_library_name, QUALIFIED_NAME, s->name); + t_symbol *sym = gensym(buf); + free(buf); + return sym; +} + +#undef class_new2 +#undef class_addcreator2 +#undef class_addmethod2 + +/* Note that some classes such as "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. */ +t_class *class_new2(const char *ss, t_newmethod newmethod, t_method freemethod, +size_t size, int flags, const char *sig) { + t_symbol *s = gensym(ss); + int typeflag = flags & CLASS_TYPEMASK; + if (!typeflag) typeflag = CLASS_PATCHABLE; +#ifdef QUALIFIED_NAME + if (pd_library_name) s = qualified_name(s); +#endif + if (pd_objectmaker._class && newmethod) { + /* add a "new" method by the name specified by the object */ + class_addmethod2(pd_objectmaker._class, (t_method)newmethod, s->name, sig); + 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->name, l1 = strlen(s->name), l2 = strlen(loadstring); + if (l2 > l1 && !strcmp(s->name, loadstring + (l2 - l1))) + class_addmethod2(pd_objectmaker._class, (t_method)newmethod, class_loadsym->name, sig); + } + } + t_class *c = (t_class *)malloc(sizeof(*c)); + c->name = c->helpname = s; + c->size = size; + c->methods = (t_methodentry *)malloc(1); + c->nmethod = 0; + c->freemethod = (t_method)freemethod; + c->bangmethod = pd_defaultbang; + c->pointermethod = pd_defaultpointer; + c->floatmethod = pd_defaultfloat; + c->symbolmethod = pd_defaultsymbol; + c->listmethod = pd_defaultlist; + c->anymethod = pd_defaultanything; + c->firstin = ((flags & CLASS_NOINLET) == 0); + c->firsttip = gensym("?"); + c->fields = (t_symbol **)malloc(sizeof(t_symbol *)*31); + c->nfields = 0; + c->patchable = (typeflag == CLASS_PATCHABLE); + c->gobj = (typeflag >= CLASS_GOBJ); + c->drawcommand = 0; + c->floatsignalin = 0; + c->externdir = class_extern_dir; + c->savefn = (typeflag == CLASS_PATCHABLE ? text_save : class_nosavefn); +#ifdef QUALIFIED_NAME + c->helpname = gensym(ss); + // like a class_addcreator + if (pd_library_name && newmethod) + class_addmethod2(pd_objectmaker._class, (t_method)newmethod, ss, sig); +#endif + c->onsubscribe = gobj_onsubscribe; + class_table->set(c->name, c); + 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_addcreator2(const char *ss, t_newmethod newmethod, const char *sig) { + t_symbol *s = gensym(ss); + class_addmethod2(pd_objectmaker._class, (t_method)newmethod, ss, sig); +#ifdef QUALIFIED_NAME + class_addmethod2(pd_objectmaker._class, (t_method)newmethod, pd_library_name ? qualified_name(s)->name : ss, sig); +#endif + class_table->set(s,0); +} + +void class_addmethod2(t_class *c, t_method fn, const char *ss, const char *fmt) { + t_symbol *sel = gensym(ss); + t_methodentry *m; + int argtype = *fmt++; + /* "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->floatsignalin) post("warning: signal method overrides class_mainsignalin"); + c->floatsignalin = -1; + } + /* check for special cases. "Pointer" is missing here so that + pd_objectmaker's pointer method can be typechecked differently. */ + /* is anyone actually using those five cases? */ + if (sel==&s_bang) {if (argtype) goto phooey; class_addbang( c,fn);} + else if (sel==&s_float) {if (argtype!='f'||*fmt) goto phooey; class_doaddfloat( c,fn);} + else if (sel==&s_symbol) {if (argtype!='s'||*fmt) goto phooey; class_addsymbol( c,fn);} + else if (sel==&s_list) {if (argtype!='*') goto phooey; class_addlist( c,fn);} + else if (sel==&s_anything) {if (argtype!='*') goto phooey; class_addanything(c,fn);} + else { + /* SLOW, especially for [objectmaker] */ + c->methods = (t_methodentry *)realloc(c->methods, (c->nmethod+1) * sizeof(*c->methods)); + m = c->methods + c->nmethod; + c->nmethod++; + m->me_name = sel; + m->me_fun = (t_gotfn)fn; + int nargs = 0; + while (argtype && nargs < MAXPDARG) { + t_atomtype t; + switch(argtype) { + case 'f': t=A_FLOAT; break; + case 's': t=A_SYMBOL; break; + case 'p': t=A_POINTER; break; + case ';': t=A_SEMI; break; + case ',': t=A_COMMA; break; + case 'F': t=A_DEFFLOAT; break; + case 'S': t=A_DEFSYMBOL;break; + case '$': t=A_DOLLAR; break; + case '@': t=A_DOLLSYM; break; + case '*': t=A_GIMME; break; + case '!': t=A_CANT; break; + default: goto phooey; + }; + m->me_arg[nargs++] = t; + argtype = *fmt++; + } + if (argtype) error("%s_%s: only 5 arguments are typecheckable; use A_GIMME aka '*'", c->name->name, sel->name); + m->me_arg[nargs] = A_NULL; + } + return; +phooey: + bug("class_addmethod: %s_%s: bad argument types", c->name->name, sel->name); +} + +t_class *class_new(t_symbol *s, t_newmethod newmethod, t_method freemethod, +size_t size, int flags, t_atomtypearg arg1, ...) { + char fmt[42],*f=fmt; va_list ap; va_start(ap,arg1); int t=arg1; + while(t) { + if (t>A_CANT) {error("class_new: ARRGH! t=%d",t); return 0;} + *f++ = " fsp;,FS$@*!"[t]; + t=(t_atomtype)va_arg(ap,int); + } + *f=0; va_end(ap); return class_new2(s->name,newmethod,freemethod,size,flags,fmt); +} +void class_addcreator(t_newmethod newmethod, t_symbol *s, t_atomtypearg arg1, ...) { + char fmt[42],*f=fmt; va_list ap; va_start(ap,arg1); int t=arg1; + while(t) { + if (t>A_CANT) {error("class_addcreator: ARRGH! t=%d",t); return;} + *f++ = " fsp;,FS$@*!"[t]; + t=(t_atomtype)va_arg(ap,int); + } + *f=0; va_end(ap); class_addcreator2(s->name,newmethod,fmt); +} +void class_addmethod(t_class *c, t_method fn, t_symbol *sel, t_atomtypearg arg1, ...) { + char fmt[42],*f=fmt; va_list ap; va_start(ap,arg1); int t=arg1; + while(t) { + if (t>A_CANT) {error("class_addmethod: ARRGH! t=%d",t); return;} + *f++ = " fsp;,FS$@*!"[t]; + t=(t_atomtype)va_arg(ap,int); + } + *f=0; va_end(ap); class_addmethod2(c,fn,sel->name,fmt); +} + +/* see also the "class_addfloat", etc., macros in m_pd.h */ +#undef class_addbang +#undef class_addpointer +#undef class_addsymbol +#undef class_addlist +#undef class_addanything +void class_addbang( t_class *c, t_method fn) {c-> bangmethod = (t_bangmethod)fn;} +void class_addpointer( t_class *c, t_method fn) {c->pointermethod = (t_pointermethod)fn;} +void class_doaddfloat( t_class *c, t_method fn) {c-> floatmethod = (t_floatmethod)fn;} +void class_addsymbol( t_class *c, t_method fn) {c-> symbolmethod = (t_symbolmethod)fn;} +void class_addlist( t_class *c, t_method fn) {c-> listmethod = (t_listmethod)fn;} +void class_addanything(t_class *c, t_method fn) {c-> anymethod = (t_anymethod)fn;} + +char *class_getname(t_class *c) {return c->name->name;} +char *class_gethelpname(t_class *c) {return c->helpname->name;} +void class_sethelpsymbol(t_class *c, t_symbol *s) {c->helpname = s;} +void class_setdrawcommand(t_class *c) {c->drawcommand = 1;} +int class_isdrawcommand( t_class *c) {return c->drawcommand;} +void class_setnotice( t_class *c, t_notice notice ) {c->notice = notice ;} +void class_setonsubscribe(t_class *c, t_onsubscribe onsubscribe) {c->onsubscribe = onsubscribe;} + +static void pd_floatforsignal(t_pd *x, t_float f) { + int offset = x->_class->floatsignalin; + if (offset > 0) + *(t_sample *)(((char *)x) + offset) = f; + else + error("%s: float unexpected for signal input", x->_class->name->name); +} + +void class_domainsignalin(t_class *c, int onset) { + if (onset <= 0) onset = -1; + else { + if (c->floatmethod != pd_defaultfloat) + post("warning: %s: float method overwritten", c->name->name); + c->floatmethod = (t_floatmethod)pd_floatforsignal; + } + c->floatsignalin = onset; +} + +void class_set_extern_dir(t_symbol *s) {class_extern_dir = s;} +char *class_gethelpdir(t_class *c) {return c->externdir->name;} + +static void class_nosavefn(t_gobj *z, t_binbuf *b) { + bug("save function called but not defined"); +} + +void class_setsavefn(t_class *c, t_savefn f) {c->savefn = f;} +t_savefn class_getsavefn(t_class *c) {return c->savefn;} + +/* ---------------- the symbol table ------------------------ */ + +/* tb: new 16 bit hash table: multiplication hash */ +#ifndef NEWHASH +#define HASHSIZE 1024 +#else +#define HASHSIZE 65536 +#define HASHFACTOR 40503 /* donald knuth: (sqrt(5) - 1)/2*pow(2,16) */ +#endif + +#ifdef NEWHASH +static short hash(const char *s, size_t n) { + unsigned short hash1 = 0, hash2 = 0; +#else +static int hash(const char *s, size_t n) { + unsigned int hash1 = 0, hash2 = 0; +#endif + const char *s2 = s; + while (n) { + hash1 += *s2; + hash2 += hash1; + s2++; + n--; + } + return hash2; +} + +/* tb: made dogensym() threadsafe + * supported by vibrez.net */ +t_symbol *dogensym(const char *s, size_t n, t_symbol *oldsym) { + static t_symbol *symhash[HASHSIZE]; +#ifdef THREADSAFE_GENSYM + static pthread_mutex_t hash_lock = PTHREAD_MUTEX_INITIALIZER; +#endif + t_symbol **sym1, *sym2; +#ifdef NEWHASH + unsigned short hash2 = hash(s,n); +#else + unsigned int hash2 = hash(s,n); +#endif +#ifdef NEWHASH + hash2 = hash2 * HASHFACTOR; + sym1 = symhash + hash2; +#else + sym1 = symhash + (hash2 & (HASHSIZE-1)); +#endif + while ((sym2 = *sym1)) { + if (!strcmp(sym2->name, s)) return sym2; + sym1 = &sym2->next; + } +#ifdef THREADSAFE_GENSYM + pthread_mutex_lock(&hash_lock); + /* tb: maybe another thread added the symbol to the hash table; double check */ + while (sym2 = *sym1) { + if (!strcmp(sym2->name, s)) { + pthread_mutex_unlock(&hash_lock); + return sym2; + } + sym1 = &sym2->next; + } +#endif + + if (oldsym) sym2 = oldsym; + else { + sym2 = (t_symbol *)malloc(sizeof(*sym2)); + sym2->name = (char *)malloc(n+1); + sym2->next = 0; + sym2->thing = 0; + memcpy(sym2->name, s, n); + sym2->name[n]=0; + sym2->n=n; + } + *sym1 = sym2; +#ifdef THREADSAFE_GENSYM + pthread_mutex_unlock(&hash_lock); +#endif + return sym2; +} + +t_symbol *gensym( const char *s) {return dogensym(s,strlen(s),0);} +t_symbol *gensym2(const char *s, size_t n) {return dogensym(s,n,0);} +extern "C" t_symbol *symprintf(const char *s, ...) { + char *buf; + va_list args; + va_start(args,s); + vasprintf(&buf,s,args); + va_end(args); + t_symbol *r = gensym(buf); + free(buf); + return r; +} + +static int tryingalready; +extern "C" void canvas_popabstraction(t_canvas *x); +extern t_pd *newest; +t_symbol* pathsearch(t_symbol *s,char* ext); +int pd_setloadingabstraction(t_symbol *sym); + +/* this routine is called when a new "object" is requested whose class Pd + doesn't know. Pd tries to load it as an extern, then as an abstraction. */ +void new_anything(void *dummy, t_symbol *s, int argc, t_atom *argv) { + int fd; + char *dirbuf, *nameptr; + if (tryingalready) return; + newest = 0; + class_loadsym = s; + if (sys_load_lib(canvas_getcurrent(), s->name)) { + tryingalready = 1; + typedmess((t_pd *)dummy, s, argc, argv); + tryingalready = 0; + return; + } + class_loadsym = 0; + t_pd *current = s__X.thing; + if ((fd = canvas_open2(canvas_getcurrent(), s->name, ".pd", &dirbuf, &nameptr, 0)) >= 0 || + (fd = canvas_open2(canvas_getcurrent(), s->name, ".pat", &dirbuf, &nameptr, 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.thing != current) canvas_popabstraction((t_canvas *)s__X.thing); + canvas_setargs(0, 0); + } else error("%s: can't load abstraction within itself", s->name); + free(dirbuf); + } else newest = 0; +} + +#define MAKESYM(CSYM,S) t_symbol CSYM = {S,0,0,1,0xdeadbeef}; +MAKESYM(s_pointer ,"pointer") +MAKESYM(s_float ,"float") +MAKESYM(s_symbol ,"symbol") +MAKESYM(s_bang ,"bang") +MAKESYM(s_list ,"list") +MAKESYM(s_anything,"anything") +MAKESYM(s_signal ,"signal") +MAKESYM(s__N ,"#N") +MAKESYM(s__X ,"#X") +MAKESYM(s_x ,"x") +MAKESYM(s_y ,"y") +MAKESYM(s_ ,"") + +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_}; + +t_pd *newest; + +/* This is externally available, but note that it might later disappear; the +whole "newest" thing is a hack which needs to be redesigned. */ +t_pd *pd_newest () {return newest;} + + /* horribly, we need prototypes for each of the artificial function + calls in typedmess(), to keep the compiler quiet. */ +typedef t_pd *(*t_newgimme)(t_symbol *s, int argc, t_atom *argv); +typedef void(*t_messgimme)(t_pd *x, t_symbol *s, int argc, t_atom *argv); + +#define REST t_floatarg d1, t_floatarg d2, t_floatarg d3, t_floatarg d4, t_floatarg d5 +typedef t_pd *(*t_fun0)(REST); +typedef t_pd *(*t_fun1)(t_int i1, REST); +typedef t_pd *(*t_fun2)(t_int i1, t_int i2, REST); +typedef t_pd *(*t_fun3)(t_int i1, t_int i2, t_int i3, REST); +typedef t_pd *(*t_fun4)(t_int i1, t_int i2, t_int i3, t_int i4, REST); +typedef t_pd *(*t_fun5)(t_int i1, t_int i2, t_int i3, t_int i4, t_int i5, REST); +typedef t_pd *(*t_fun6)(t_int i1, t_int i2, t_int i3, t_int i4, t_int i5, t_int i6, REST); +#undef REST + +void pd_typedmess_2(t_pd *x, t_symbol *s, int argc, t_atom *argv) { + t_class *c = x->_class; + t_atomtype *wp, wanttype; + t_int ai[MAXPDARG+1], *ap = ai; + t_floatarg ad[MAXPDARG+1], *dp = ad; + int narg = 0; + /* 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->floatmethod(x, 0.); + else if (argv->a_type == A_FLOAT) c->floatmethod(x, argv->a_float); + else error("expected one float, in class [%s]", c->name->name); + return; + } + if (s == &s_bang) {c->bangmethod(x); return;} + if (s == &s_list) {c->listmethod(x,s,argc,argv); return;} + if (s == &s_symbol) {c->symbolmethod(x, argc && argv->a_type==A_SYMBOL ? argv->a_symbol : &s_); return;} + t_methodentry *m = c->methods; + for (int i = c->nmethod; i--; m++) if (m->me_name == s) { + wp = m->me_arg; + if (*wp == A_GIMME) { + if (x == &pd_objectmaker) pd_set_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; + if (argv->a_type!=A_POINTER) goto badarg; + *ap = t_int(argv->a_gpointer); + 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) goto badarg; + *dp = argv->a_float; + 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_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_float == 0) + *ap = t_int(&s_); + else goto badarg; + argc--; argv++; + } + narg++; + ap++; + default: {} + } + } + t_pd *bonzo; + switch (narg) { +#define REST ad[0],ad[1],ad[2],ad[3],ad[4] + case 0 : bonzo = ((t_fun0)(m->me_fun))( REST); break; + case 1 : bonzo = ((t_fun1)(m->me_fun))(ai[0], REST); break; + case 2 : bonzo = ((t_fun2)(m->me_fun))(ai[0],ai[1], REST); break; + case 3 : bonzo = ((t_fun3)(m->me_fun))(ai[0],ai[1],ai[2], REST); break; + case 4 : bonzo = ((t_fun4)(m->me_fun))(ai[0],ai[1],ai[2],ai[3], REST); break; + case 5 : bonzo = ((t_fun5)(m->me_fun))(ai[0],ai[1],ai[2],ai[3],ai[4], REST); break; + case 6 : bonzo = ((t_fun6)(m->me_fun))(ai[0],ai[1],ai[2],ai[3],ai[4],ai[5],REST); break; + default: bonzo = 0; + } + if (x == &pd_objectmaker) pd_set_newest(bonzo); + return; + } + c->anymethod(x, s, argc, argv); + return; +badarg: + error("Bad arguments for message '%s' to object '%s'", s->name, c->name->name); +} + +void pd_typedmess(t_pd *x, t_symbol *s, int argc, t_atom *argv) { + ENTER(s); pd_typedmess_2(x,s,argc,argv); LEAVE; +} + +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) { + error("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_symbol, argc-1, argv+1); + else if (t == A_POINTER) {if (argc==1) pd_pointer(x, argv->a_gpointer); else pd_list(x, &s_list, argc, argv);} + else if (t == A_FLOAT) {if (argc==1) pd_float( x, argv->a_float); else pd_list(x, &s_list, argc, argv);} + else bug("pd_forwardmess"); + } +} + +void nullfn () {} + +t_gotfn getfn(t_pd *x, t_symbol *s) { + t_class *c = x->_class; + t_methodentry *m = c->methods; + for (int i=c->nmethod; i--; m++) if (m->me_name == s) return m->me_fun; + error("%s: no method for message '%s'", c->name->name, s->name); + return (t_gotfn)nullfn; +} + +t_gotfn zgetfn(t_pd *x, t_symbol *s) { + t_class *c = x->_class; + t_methodentry *m = c->methods; + for (int i=c->nmethod; i--; m++) if (m->me_name == s) return m->me_fun; + return 0; +} + +void class_settip(t_class *x,t_symbol* s) {x->firsttip = s;} + +/* must be called only once */ +void class_setfieldnames(t_class *x, const char *s) { + char foo[64]; + while (*s) { + char *t = strchr(s,' '); + int i = t-s; + if (!t) return; + memcpy(foo,s,i); + foo[i]=0; + x->fields[x->nfields++] = gensym(foo); + s=s+i+1; + } +} + +int class_getfieldindex(t_class *x, const char *s) { + t_symbol *sy = gensym((char *)s); + for (int i=0; i<x->nfields; i++) if (x->fields[i]==sy) return i; + return -1; +} + +/* O(n) asymptotic time :-} */ +/* only looks for already loaded classes though. */ + +t_class *class_find (t_symbol *s) {return (t_class *)class_table->get(s);} + +void glob_update_class_info (t_pd *bogus, t_symbol *s, t_symbol *cb_recv, t_symbol *cb_sel) { + t_class *c = class_find(s); + if (!c) { post("class not found!"); return; } + sys_vgui("global class_info; set class_info(%s) [list " + "helpname \"%s\" externdir \"%s\" size \"%d\" " +/* + t_methodentry *c_methods; int c_nmethod; + t_method c_freemethod; + t_savefn c_savefn; + int c_floatsignalin; +*/ + "gobj \"%d\" patchable \"%d\" firstin \"%d\" " + "firsttip \"%s\" methods {",s->name,c->helpname->name,c->externdir->name, + c->size,c->gobj,c->patchable,c->firstin,c->firsttip->name); + if (c-> bangmethod != pd_defaultbang) sys_vgui("<bang> "); + if (c->pointermethod != pd_defaultpointer) sys_vgui("<pointer> "); + if (c-> floatmethod != pd_defaultfloat) sys_vgui("<float> "); + if (c-> symbolmethod != pd_defaultsymbol) sys_vgui("<symbol> "); + if (c-> listmethod != pd_defaultlist) sys_vgui("<list> "); + if (c-> anymethod != pd_defaultanything) sys_vgui("<any> "); + for (int i=0; i<c->nmethod; i++) sys_vgui("%s ",c->methods[i].me_name->name); + sys_vgui("}]; %s %s %s\n",cb_recv->name, cb_sel->name, s->name); +} + +t_class *binbuf_class; + +t_binbuf *binbuf_new () { + t_binbuf *x = (t_binbuf *)pd_new(binbuf_class); + x->n = 0; + x->capa = 1; + x->v = (t_atom *)malloc(1*sizeof(t_atom)); + return x; +} + +/* caution: capa >= x->n and capa >= 1 too */ +static void binbuf_capa(t_binbuf *x, int capa) { + x->v = (t_atom *)realloc(x->v, capa*sizeof(*x->v)); + x->capa = capa; +} + +void binbuf_free(t_binbuf *x) {pd_free(x);} +void binbuf_free2(t_binbuf *x) {free(x->v);} + +t_binbuf *binbuf_duplicate(t_binbuf *y) { + t_binbuf *x = (t_binbuf *)malloc(sizeof(*x)); + x->capa = x->n = y->n; + x->v = (t_atom *)malloc(x->n * sizeof(*x->v)); + memcpy(x->v,y->v,x->n*sizeof(*x->v)); + return x; +} + +void binbuf_clear(t_binbuf *x) { + x->n = 0; + x->v = (t_atom *)realloc(x->v,4); + x->capa = 4; +} + +/* called just after a doublequote in version 1 parsing */ +char *binbuf_text_quoted(t_binbuf *x, char *t, char *end) { + ostringstream buf; + while (t!=end) { + char c = *t++; + if (c=='"') break; + if (c!='\\') {buf << c; continue;} + c = *t++; + if (c=='a') {buf << '\a'; continue;} + if (c=='b') {buf << '\b'; continue;} + if (c=='f') {buf << '\f'; continue;} + if (c=='n') {buf << '\n'; continue;} + if (c=='r') {buf << '\r'; continue;} + if (c=='v') {buf << '\v'; continue;} + if (c=='t') {buf << '\t'; continue;} + if (c=='"') {buf << '\"'; continue;} + if (c=='\\'){buf << '\\'; continue;} + if (c=='\n'){continue;} + /* if (c=='u') ... */ + /* if (c=='x') ... */ + /* if (isdigit(c)) ... */ + buf << c; /* ignore syntax error (should it?) */ + } + binbuf_addv(x,"t",buf.str().data()); + return t; /* ignore syntax error (should it?) */ +} + +/* find the first atom in text, in any, and add it to this binbuf; + returns pointer to end of atom text */ +/* this one is for pd format version 1 */ +/* TODO: double-quotes, braces, test backslashes&dollars */ +char *binbuf_text_matju(t_binbuf *x, char *t, char *end) { + int doll=0; + while (t!=end && isspace(*t)) t++; + if (t==end) return t; + if (*t==';') {binbuf_addv(x,";"); return t+1;} + if (*t==',') {binbuf_addv(x,","); return t+1;} + /* if (*t=='"') return binbuf_text_quoted(x,t,end); */ + if (*t=='+' || *t=='-' || *t=='.' || isdigit(*t)) { + char *token; + double v = strtod(t,&token); + if (t==end || isspace(*token)) {binbuf_addv(x,"f",v); return token;} + } + ostringstream buf; + for (; t!=end && *t!=',' && *t!=';' && !isspace(*t); ) { + doll |= t[0]=='$' && t+1!=end && isdigit(t[1]); + if (*t=='\\') t++; + if (t!=end) buf << *t++; + } + if (doll) { + const char *b = buf.str().data(); + if (b[0]!='$') doll=0; + for (b++; *b; b++) if (!isdigit(*b)) doll=0; + if (doll) binbuf_addv(x,"$",atoi(buf.str().data()+1)); + else binbuf_addv(x,"&",gensym(buf.str().data())); + } else binbuf_addv(x,"t",buf.str().data()); + return t; +} + +/* this one is for pd format version 0 */ +char *binbuf_text_miller(t_binbuf *x, char *t, char *end) { + ostringstream buf; + /* it's an atom other than a comma or semi */ + int q = 0, slash = 0, lastslash = 0, dollar = 0; + /* skip leading space */ + while (t!=end && isspace(*t)) t++; + if (t==end) return t; + if (*t==';') {binbuf_addv(x,";"); return t+1;} + if (*t==',') {binbuf_addv(x,","); return t+1;} + do { + char c = *t++; + lastslash = slash; + slash = c=='\\'; + if (q >= 0) { + int digit = isdigit(c), dot=c=='.', minus=c=='-', plusminus=minus||c=='+', expon=c=='e'||c=='E'; + if (q==0) { /* beginning */ if (minus) q=1; else if (digit) q=2; else if (dot) q=3; else q=-1;} + else if (q==1) { /* got minus */ if (digit) q=2; else if (dot) q=3; else q=-1;} + else if (q==2) { /* got digits */ if (dot) q=4; else if (expon) q=6; else if (!digit) q=-1;} + else if (q==3) { /* got '.' without digits */ if (digit) q=5; else q=-1;} + else if (q==4) { /* got '.' after digits */ if (digit) q=5; else if (expon) q=6; else q=-1;} + else if (q==5) { /* got digits after . */ if (expon) q=6; else if (!digit) q=-1;} + else if (q==6) { /* got 'e' */ if (plusminus) q=7; else if (digit) q=8; else q=-1;} + else if (q==7) { /* got plus or minus */ if (digit) q=8; else q=-1;} + else if (q==8) { /* got digits */ if (!digit) q=-1;} + } + if (!lastslash && c == '$' && t!=end && isdigit(*t)) dollar = 1; +#if 1 + if (slash&&lastslash) slash=0; +#endif + if (!slash) buf << c; + } while (t!=end && (slash || !strchr(" \n\r\t,;",*t))); + if (q == 2 || q == 4 || q == 5 || q == 8) {binbuf_addv(x,"f",atof(buf.str().data())); return t;} + /* LATER try to figure out how to mix "$" and "\$" correctly; here, the backslashes were already + stripped so we assume all "$" chars are real dollars. In fact, we only know at least one was. */ + if (dollar) { + const char *b = buf.str().data(); + if (*b != '$') dollar = 0; + for (b++; *b; b++) if (!isdigit(*b)) dollar = 0; + if (dollar) binbuf_addv(x,"$",atoi(buf.str().data()+1)); + else binbuf_addv(x,"&",gensym(buf.str().data())); + } else binbuf_addv(x,"t",buf.str().data()); + return t; +} + +int sys_syntax = 0; + +void binbuf_text(t_binbuf *x, char *t, size_t size) { + char *end=t+size; + binbuf_clear(x); + while (t!=end) t = sys_syntax ? binbuf_text_matju(x,t,end) : binbuf_text_miller(x,t,end); + binbuf_capa(x,x->n); +} + +void pd_eval_text(char *t, size_t size) { + t_binbuf *x = binbuf_new(); + char *end = t+size; + while (t!=end) { + t = sys_syntax ? binbuf_text_matju(x,t,end) : binbuf_text_miller(x,t,end); + if (x->n && x->v[x->n-1].a_type == A_SEMI) { + binbuf_eval(x,0,0,0); + binbuf_clear(x); + } + } + binbuf_free(x); +} + +void voprintf(ostream &buf, const char *s, va_list args) { + char *b; + vasprintf(&b,s,args); + buf << b; + free(b); +} +void oprintf(ostream &buf, const char *s, ...) { + va_list args; + va_start(args,s); + voprintf(buf,s,args); + va_end(args); +} + +/* convert a binbuf to text; no null termination. */ +void binbuf_gettext(t_binbuf *x, char **bufp, int *lengthp) { + ostringstream buf; + t_atom *ap = x->v; + char nextdelim=0; + for (int i=x->n; i--; ap++) { + if (ap->a_type != A_SEMI && ap->a_type != A_COMMA && nextdelim) buf << (char)nextdelim; + atom_ostream(ap,buf); + nextdelim = ap->a_type == A_SEMI ? '\n' : ' '; + } + //if (nextdelim) buf << (char)nextdelim; + *bufp = strdup(buf.str().data()); + *lengthp = buf.str().size();// - (nextdelim == ' '); +} + +/* convert a binbuf to text with null termination, as return value */ +char *binbuf_gettext2(t_binbuf *x) { + char *buf; int n; + binbuf_gettext(x,&buf,&n); + buf[n] = 0; + return (char *)realloc(buf,n+1); +} + +/* Miller said: fix this so that writing to file doesn't buffer everything together. */ +/* matju said: make this use vector size doubling as it used to be in binbuf_text */ +void binbuf_add(t_binbuf *x, int argc, t_atom *argv) { + int newsize = x->n + argc; + t_atom *ap = (t_atom *)realloc(x->v,newsize*sizeof(*x->v)); + x->v = ap; + ap += x->n; + for (int i = argc; i--; ap++) *ap = *(argv++); + x->capa = x->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, int)); break; + case 'f': SETFLOAT(at, va_arg(ap, double)); break; + case 's': SETSYMBOL(at, va_arg(ap, t_symbol *)); break; + case 't': SETSYMBOL(at, gensym(va_arg(ap, char *))); break; + case ';': SETSEMI(at); break; + case ',': SETCOMMA(at); break; + case '$': SETDOLLAR(at, va_arg(ap, int)); break; + case '&': SETDOLLSYM(at, va_arg(ap, t_symbol *)); 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(); + binbuf_add(z, y->n, y->v); + t_atom *ap = z->v; + for (size_t i=0; i < z->n; i++, ap++) { + 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: SETSYMBOL(ap, symprintf("$%ld", ap->a_index)); break; + case A_DOLLSYM: { + ostringstream b; + atom_ostream(ap,b); + SETSYMBOL(ap, gensym(b.str().data()));} break; + case A_SYMBOL: + /* FIXME make this general */ + if (!strcmp(ap->a_symbol->name, ";")) SETSYMBOL(ap, gensym(";")); + else if (!strcmp(ap->a_symbol->name, ",")) SETSYMBOL(ap, gensym(",")); + break; + default: + //bug("binbuf_addbinbuf: stray atom of type %d",ap->a_type); + //abort(); + ; + } + } + binbuf_add(x, z->n, z->v); +} + +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->n + argc; + t_atom *ap = (t_atom *)realloc(x->v,(newsize+1)*sizeof(*x->v)); + if (!ap) {error("binbuf_addmessage: out of space"); return;} + x->v = ap; + ap = x->v + x->n; + for (int i = argc; i--; ap++) { + if (argv->a_type == A_SYMBOL) { + char *str = argv->a_symbol->name, *str2; + if (!strcmp(str, ";")) SETSEMI(ap); + else if (!strcmp(str, ",")) SETCOMMA(ap); + else if ((str2 = strchr(str, '$')) && isdigit(str2[1])) { + int dollsym = 0; + if (*str != '$') dollsym = 1; + else for (str2 = str + 1; *str2; str2++) if (!isdigit(*str2)) { + dollsym = 1; + break; + } + if (dollsym) SETDOLLSYM(ap, gensym(str)); + else { + int dollar = 0; + sscanf(argv->a_symbol->name + 1, "%d", &dollar); + SETDOLLAR(ap, dollar); + } + } else *ap = *argv; + argv++; + } else *ap = *(argv++); + } + x->n = newsize; +} + +#define MSTACKSIZE 2048 + +void binbuf_print(t_binbuf *x) { + int startedpost = 0, newline = 1; + for (size_t i=0; i < x->n; i++) { + if (newline) { + if (startedpost) endpost(); + startpost(""); + startedpost = 1; + } + postatom(1, x->v + i); + newline = !! x->v[i].a_type == A_SEMI; + } + if (startedpost) endpost(); +} + +int binbuf_getnatom(t_binbuf *x) {return x->n;} +t_atom *binbuf_getvec(t_binbuf *x) {return x->v;} + +int canvas_getdollarzero (); + +/* JMZ: + * s points to the first character after the $ + * (e.g. if the org.symbol is "$1-bla", then s will point to "1-bla") + * (e.g. org.symbol="hu-$1mu", s="1mu") + * LATER: think about more complex $args, like ${$1+3} + * + * the return value holds the length of the $arg (in most cases: 1) + * buf holds the expanded $arg + * + * if some error occurred, "-1" is returned + * + * e.g. "$1-bla" with list "10 20 30" + * s="1-bla" + * buf="10" + * return value = 1; (s+1=="-bla") + */ +static int binbuf_expanddollsym(char *s, std::ostream &buf, t_atom dollar0, int ac, t_atom *av, int tonew) { + int argno=atol(s); + int arglen=0; + char*cs=s; + char c=*cs; + while (c && isdigit(c)) { + c=*cs++; + arglen++; + } + /* invalid $-expansion (like "$bla") */ + if (cs==s) {buf << "$"; return 0;} + if (argno < 0 || argno > ac) { /* undefined argument */ + if(!tonew) return 0; + buf << "$" << argno; + } else if (argno == 0) { /* $0 */ + atom_ostream(&dollar0, buf); + } else { /* fine! */ + atom_ostream(av+(argno-1), buf); + } + return arglen-1; +} + +/* LATER remove the dependence on the current canvas for $0; should be another argument. */ +t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, t_atom *av, int tonew) { + ostringstream buf2; + char *str=s->name; + t_atom dollarnull; + SETFLOAT(&dollarnull, canvas_getdollarzero()); + /* JMZ: currently, a symbol is detected to be A_DOLLSYM if it starts with '$' + * the leading $ is stripped and the rest stored in "s". i would suggest to NOT strip the leading $ + * and make everything a A_DOLLSYM that contains(!) a $ whenever this happened, enable this code */ + char *substr=strchr(str, '$'); + if(!substr) return s; + oprintf(buf2,"%.*s",substr-str,str); + str=substr+1; + for (;;) { + std::ostringstream buf; + int next = binbuf_expanddollsym(str, buf, dollarnull, ac, av, tonew); + if (next<0) break; + /* JMZ: i am not sure what this means, so i might have broken it. it seems like that if "tonew" is + set and the $arg cannot be expanded (or the dollarsym is in reality a A_DOLLAR). + 0 is returned from binbuf_realizedollsym; this happens when expanding in a message-box, + but does not happen when the A_DOLLSYM is the name of a subpatch */ + /* JMZ: this should mimick the original behaviour */ + if(!tonew && !next && buf.str().size()==0) return 0; + buf2 << buf; + str+=next; + substr=strchr(str, '$'); + if(substr) { + oprintf(buf2,"%.*s",substr-str,str); + str=substr+1; + } else { + buf2 << str; + return gensym(buf2.str().data()); + } + } + return gensym(buf2.str().data()); +} + +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->v; + int ac = x->n; + int nargs; + while (1) { + t_pd *nexttarget; + 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_index <= 0 || at->a_index > argc) {error("$%d: not enough arguments supplied", at->a_index); goto cleanup;} + else if (argv[at->a_index-1].a_type != A_SYMBOL) {error("$%d: symbol needed as receiver", at->a_index); goto cleanup;} + else s = argv[at->a_index-1].a_symbol; + } else if (at->a_type == A_DOLLSYM) { + s = binbuf_realizedollsym(at->a_symbol, argc, argv, 0); + if (!s) {error("$%s: not enough arguments supplied", at->a_symbol->name); goto cleanup;} + } else s = atom_getsymbol(at); + target = s->thing; + /* IMPD: allows messages to unbound objects, via pointers */ + if (!target) { + if (!sscanf(s->name,".x%lx",(long*)&target)) target=0; + if (target) { + if (!object_table->exists(target) || !object_table->get(target)) { + error("%s target is not a currently valid pointer",s->name); + return; + } + } + } + if (!target) {error("%s: no such object", s->name); goto cleanup;} + at++; + ac--; + break; + cleanup: + do {at++; ac--;} while (ac && at->a_type != A_SEMI); /* is this the correct thing to do? */ + continue; + } + if (!ac) break; + nargs = 0; + nexttarget = target; + while (1) { + if (!ac) goto gotmess; + if (msp >= ems) {error("message too long"); goto broken;} + switch (at->a_type) { + /* semis and commas in new message just get bashed to a symbol. This is needed so you can pass them to "expr." */ + case A_SEMI: 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_index > 0 && at->a_index <= argc) *msp = argv[at->a_index-1]; + else if (at->a_index == 0) SETFLOAT(msp, canvas_getdollarzero()); + else { + SETFLOAT(msp, 0); + if (target != &pd_objectmaker) error("$%d: argument number out of range", at->a_index); + } + break; + case A_DOLLSYM: { + t_symbol *s9 = binbuf_realizedollsym(at->a_symbol, argc, argv, target == &pd_objectmaker); + if (!s9) { + error("%s: argument number out of range", at->a_symbol->name); + SETSYMBOL(msp, at->a_symbol); + } else 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_symbol, nargs-1, stackwas+1); break; + case A_FLOAT: if (nargs == 1) pd_float(target, stackwas->a_float); else pd_list(target, 0, nargs, stackwas); break; + default: {} + } + } + msp = stackwas; + if (!ac) break; + target = nexttarget; + at++; + ac--; + } + return; +broken: + msp = stackwas; +} + +static int binbuf_doopen(char *s, int mode) { + char namebuf[strlen(s)+1]; +#ifdef MSW + mode |= O_BINARY; +#endif + sys_bashfilename(s, namebuf); + return open(namebuf, mode); +} + +static FILE *binbuf_dofopen(const char *s, char *mode) { + char namebuf[strlen(s)+1]; + sys_bashfilename(s, namebuf); + return fopen(namebuf, mode); +} + +int binbuf_read(t_binbuf *b, char *filename, char *dirname, int flags) { + long length; + char *buf; + char *namebuf=0; + if (*dirname) asprintf(&namebuf,"%s/%s",dirname,filename); + else asprintf(&namebuf, "%s", filename); + int fd = binbuf_doopen(namebuf, 0); + if (fd < 0) {error("open: %s: %s",namebuf,strerror(errno)); return 1;} + if ((length = lseek(fd, 0, SEEK_END)) < 0 || lseek(fd, 0, SEEK_SET) < 0 || !(buf = (char *)malloc(length))) { + error("lseek: %s: %s",namebuf,strerror(errno)); + close(fd); free(namebuf); + return 1; + } + int readret = read(fd, buf, length); + if (readret < length) { + error("read (%d %ld) -> %d; %s: %s", fd, length, readret, namebuf, strerror(errno)); + close(fd); free(namebuf); free(buf); + return 1; + } + if (flags&1) for (int i=0; i<length; i++) if (buf[i]=='\n') buf[i] = ';'; + if (flags&2) pd_eval_text(buf,length); else binbuf_text(b, buf, length); + close(fd); free(namebuf); free(buf); + return 0; +} + +/* read a binbuf from a file, via the search patch of a canvas */ +int binbuf_read_via_canvas(t_binbuf *b, char *filename, t_canvas *canvas, int flags) { + char *buf, *bufptr; + int fd = canvas_open2(canvas, filename, "", &buf, &bufptr, 0); + if (fd<0) {error("%s: can't open", filename); return 1;} + close(fd); free(buf); + return !!binbuf_read(b, bufptr, buf, flags); +} + +/* old version */ +int binbuf_read_via_path(t_binbuf *b, char *filename, char *dirname, int flags) { + char *buf, *bufptr; + int fd = open_via_path2(dirname, filename, "", &buf, &bufptr, 0); + if (fd<0) {error("%s: can't open", filename); return 1;} + close(fd); + bool r = binbuf_read(b, bufptr, buf, flags); + free(buf); + return r; +} + +#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) { + char sbuf[WBUFSIZE]; + ostringstream fbuf; + char *bp = sbuf, *ep = sbuf + WBUFSIZE; + int indx; bool deleteit = 0; + int ncolumn = 0; + if (*dir) fbuf << dir << "/"; + fbuf << filename; + if (!strcmp(filename + strlen(filename) - 4, ".pat")) { + x = binbuf_convert(x, 0); + deleteit = 1; + } + FILE *f = binbuf_dofopen(fbuf.str().data(), "w"); + if (!f) {error("open: %s: %s",fbuf.str().data(),strerror(errno)); goto fail;} + indx = x->n; + for (t_atom *ap = x->v; indx--; ap++) { + /* estimate how many characters will be needed. Printing out symbols may need extra characters for inserting backslashes. */ + int length = (ap->a_type == A_SYMBOL || ap->a_type == A_DOLLSYM) ? 80 + strlen(ap->a_symbol->name) : 40; + if (ep - bp < length) { + if (fwrite(sbuf, bp-sbuf, 1, f) < 1) {error("write: %s: %s",fbuf.str().data(),strerror(errno)); goto fail;} + bp = sbuf; + } + if ((ap->a_type == A_SEMI || ap->a_type == A_COMMA) && bp > sbuf && bp[-1] == ' ') bp--; + if (!crflag || ap->a_type != A_SEMI) { + atom_string(ap, bp, (ep-bp)-2); + length = strlen(bp); + bp += length; + ncolumn += length; + } + if (ap->a_type == A_SEMI || (!crflag && ncolumn > 65)) { + *bp++ = '\n'; + ncolumn = 0; + } else { + *bp++ = ' '; + ncolumn++; + } + } + if (fwrite(sbuf, bp-sbuf, 1, f) < 1) {error("write: %s: %s",fbuf.str().data(),strerror(errno)); 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_symbol->name, (b))) +#define GETF(i) atom_getfloatarg(i,natom,nextmess) +static t_binbuf *binbuf_convert(t_binbuf *oldb, int maxtopd) { + t_binbuf *newb = binbuf_new(); + t_atom *vec = oldb->v; + t_int n = oldb->n, nextindex, stackdepth = 0, stack[MAXSTACK], nobj = 0; + t_atom outmess[MAXSTACK], *nextmess; + if (!maxtopd) binbuf_addv(newb,"tt;","max","v2"); + for (nextindex = 0; nextindex < n; ) { + int endmess, natom; + 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; + char *first = nextmess ->a_symbol->name; + char *second = (nextmess+1)->a_symbol->name; + if (maxtopd) { /* case 1: importing a ".pat" file into Pd. */ + /* dollar signs in file translate to symbols */ + for (int i=0; i<natom; i++) { + if (nextmess[i].a_type == A_DOLLAR) { + SETSYMBOL(nextmess+i, symprintf("$%ld",nextmess[i].a_index)); + } else if (nextmess[i].a_type == A_DOLLSYM) { + SETSYMBOL(nextmess+i, gensym(nextmess[i].a_symbol->name)); + } + } + 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,"ttfffff;","#N","canvas", GETF(2), GETF(3), GETF(4)-GETF(2), GETF(5)-GETF(3), 10.); + } + } + if (!strcmp(first, "#P")) { + /* drop initial "hidden" flag */ + if (!strcmp(second, "hidden")) { + nextmess++; + natom--; + second = (nextmess+1)->a_symbol->name; + } + if (natom >= 7 && !strcmp(second, "newobj") + && (ISSYMBOL(&nextmess[6], "patcher") || ISSYMBOL(&nextmess[6], "p"))) { + binbuf_addv(newb,"ttffts;","#X","restore", GETF(2), GETF(3), + "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 (int i=7; i<natom; i++) + if (nextmess[i].a_type == A_SYMBOL && nextmess[i].a_symbol == gensym("i")) + nextmess[i].a_symbol = gensym("f"); + } + if (classname == gensym("table")) classname = gensym("TABLE"); + SETSYMBOL(outmess, gensym("#X")); + SETSYMBOL(outmess + 1, gensym("obj")); + outmess[2] = nextmess[2]; + outmess[3] = nextmess[3]; + SETSYMBOL(outmess+4, classname); + for (int i=7; i<natom; i++) outmess[i-2] = nextmess[i]; + SETSEMI(outmess + natom - 2); + binbuf_add(newb, natom - 1, outmess); + nobj++; + } else if (!strcmp(second, "message") || !strcmp(second, "comment")) { + SETSYMBOL(outmess, gensym("#X")); + SETSYMBOL(outmess + 1, gensym((char *)(strcmp(second, "message") ? "text" : "msg"))); + outmess[2] = nextmess[2]; + outmess[3] = nextmess[3]; + for (int 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,"ttfft;","#X","obj",GETF(2),GETF(3),"bng"); + nobj++; + } else if (!strcmp(second, "number") || !strcmp(second, "flonum")) { + binbuf_addv(newb,"ttff;","#X","floatatom",GETF(2),GETF(3)); + nobj++; + } else if (!strcmp(second, "slider")) { + float inc = GETF(7); + if (inc <= 0) inc = 1; + binbuf_addv(newb, "ttfftfffffftttfffffffff;","#X","obj", + GETF(2), GETF(3), "vsl", GETF(4), GETF(5), GETF(6), GETF(6)+(GETF(5)-1)*inc, + 0., 0., "empty", "empty", "empty", 0., -8., 0., 8., -262144., -1., -1., 0., 1.); + nobj++; + } else if (!strcmp(second, "toggle")) { + binbuf_addv(newb,"ttfft;","#X","obj",GETF(2),GETF(3),"tgl"); + nobj++; + } else if (!strcmp(second, "inlet")) { + binbuf_addv(newb,"ttfft;","#X","obj",GETF(2),GETF(3), natom > 5 ? "inlet~" : "inlet"); + nobj++; + } else if (!strcmp(second, "outlet")) { + binbuf_addv(newb,"ttfft;","#X","obj",GETF(2),GETF(3), natom > 5 ? "outlet~" : "outlet"); + nobj++; + } else if (!strcmp(second, "user")) { + binbuf_addv(newb,"ttffs;","#X","obj", GETF(3), GETF(4), atom_getsymbolarg(2, natom, nextmess)); + nobj++; + } else if (!strcmp(second, "connect") || !strcmp(second, "fasten")) { + binbuf_addv(newb,"ttffff;","#X","connect", nobj-GETF(2)-1, GETF(3), nobj-GETF(4)-1, GETF(5)); + } + } + } 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,"ttffff;","#N","vpatcher", GETF(2), GETF(3), GETF(4), GETF(5)); + } + } + if (!strcmp(first, "#X")) { + if (natom >= 5 && !strcmp(second, "restore") && (ISSYMBOL (&nextmess[4], "pd"))) { + binbuf_addv(newb,"tt;","#P","pop"); + binbuf_addv(newb,"ttffffts;","#P","newobj", GETF(2), GETF(3), 50., 1., + "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,"ttfff;","#P","inlet", GETF(2), GETF(3), 15.); + else if (classname == gensym("inlet~")) binbuf_addv(newb,"ttffff;","#P","inlet", GETF(2), GETF(3), 15., 1.); + else if (classname == gensym("outlet")) binbuf_addv(newb,"ttfff;","#P","outlet", GETF(2), GETF(3), 15.); + else if (classname == gensym("outlet~")) binbuf_addv(newb,"ttffff;","#P","outlet", GETF(2), GETF(3), 15., 1.); + else if (classname == gensym("bng")) binbuf_addv(newb,"ttffff;","#P","button", GETF(2), GETF(3), GETF(5), 0.); + else if (classname == gensym("tgl")) binbuf_addv(newb,"ttffff;","#P","toggle", GETF(2), GETF(3), GETF(5), 0.); + else if (classname == gensym("vsl")) binbuf_addv(newb,"ttffffff;","#P","slider", + GETF(2), GETF(3), GETF(5), GETF(6), (GETF(8)-GETF(7)) / (GETF(6)==1?1:GETF(6)-1), GETF(7)); + else { + binbuf_addv(newb,"ttffff","#P","newex", GETF(2), GETF(3), 50., 1.); + for (int i=4; i<natom; i++) outmess[i-4] = nextmess[i]; + binbuf_add(newb, natom-4, outmess); + binbuf_addv(newb,";"); + } + nobj++; + } else if (!strcmp(second, "msg") || !strcmp(second, "text")) { + binbuf_addv(newb,"ttffff","#P",strcmp(second, "msg") ? "comment" : "message",GETF(2),GETF(3),50.,1.); + for (int i=4; i<natom; i++) outmess[i-4] = nextmess[i]; + binbuf_add(newb, natom-4, outmess); + binbuf_addv(newb,";"); + nobj++; + } else if (!strcmp(second, "floatatom")) { + binbuf_addv(newb, "ttfff;", "#P", "flonum", GETF(2), GETF(3), 35); + nobj++; + } else if (!strcmp(second, "connect")) { + binbuf_addv(newb, "ttffff;", "#P", "connect", nobj-GETF(2)-1, GETF(3), nobj-GETF(4)-1, GETF(5)); + } + } + } + nextindex = endmess + 1; + } + if (!maxtopd) binbuf_addv(newb, "tt;", "#P", "pop"); +#if 0 + binbuf_write(newb, "import-result.pd", "/tmp", 0); +#endif + return newb; +} + +/* function to support searching */ +int binbuf_match(t_binbuf *inbuf, t_binbuf *searchbuf) { + for (size_t indexin = 0; indexin <= inbuf->n - searchbuf->n; indexin++) { + for (size_t nmatched = 0; nmatched < searchbuf->n; nmatched++) { + t_atom *a1 = &inbuf->v[indexin + nmatched], *a2 = &searchbuf->v[nmatched]; + if (a1->a_type != a2->a_type || + a1->a_type == A_SYMBOL && a1->a_symbol != a2->a_symbol || + a1->a_type == A_FLOAT && a1->a_float != a2->a_float || + a1->a_type == A_DOLLAR && a1->a_index != a2->a_index || + a1->a_type == A_DOLLSYM && a1->a_symbol != a2->a_symbol) goto nomatch; + } + return 1; + nomatch: ; + } + return 0; +} + +/* 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->name + strlen(name->name) - 4, ".pat"); + /* set filename so that new canvases can pick them up */ + int dspstate = canvas_suspend_dsp(); + glob_setfilename(0, name, dir); + if (import) { + if (binbuf_read(b, name->name, dir->name, 0)) {perror(name->name); goto bye;} + t_binbuf *newb = binbuf_convert(b, 1); + binbuf_free(b); + b = newb; + } else { + if (binbuf_read(b, name->name, dir->name, 2)) perror(name->name); + } +bye: + 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) { + /* 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); + t_pd *x = 0; + while ((x != s__X.thing) && (x = s__X.thing)) vmess(x, gensym("pop"), "i", 1); + if (lastpopped) pd_vmess(lastpopped, gensym("loadbang"), ""); + lastpopped = 0; + canvas_resume_dsp(dspstate); +} + +//copied from m_pd.h +#define class_new2(NAME,NU,FREE,SIZE,FLAGS,SIG) class_new2(NAME,(t_newmethod)NU,(t_method)FREE,SIZE,FLAGS,SIG) + +extern "C" { +void conf_init(); +void glob_init(); +void boxes_init(); +void garray_init(); +void pd_init() { + object_table = new t_hash<t_pd *,long>(127); + bindlist_class = class_new(gensym("bindlist"), 0, 0, sizeof(t_bindlist), CLASS_PD, 0); + class_addbang(bindlist_class, (t_method)bindlist_bang); + class_addfloat(bindlist_class, (t_method)bindlist_float); + class_addsymbol(bindlist_class, (t_method)bindlist_symbol); + class_addpointer(bindlist_class, (t_method)bindlist_pointer); + class_addlist(bindlist_class, (t_method)bindlist_list); + class_addanything(bindlist_class, (t_method)bindlist_anything); + binbuf_class = class_new2("__list", binbuf_new, binbuf_free2, sizeof(t_binbuf), CLASS_PD, "*"); + wire_class = class_new2("__wire", wire_new, wire_free, sizeof(t_wire), CLASS_GOBJ, "*"); + class_setsavefn(wire_class,(t_savefn)wire_save); + if (pd_objectmaker._class) bug("ARGH"); + for (size_t i=0; i<sizeof(symlist)/sizeof(*symlist); i++) { + symlist[i]->n = strlen(symlist[i]->name); + dogensym(symlist[i]->name, symlist[i]->n, symlist[i]); /* why does this take three args? */ + } + pd_objectmaker._class = class_new2("objectmaker", 0, 0, sizeof(t_pd), CLASS_DEFAULT, ""); + pd_canvasmaker._class = class_new2("canvasmaker", 0, 0, sizeof(t_pd), CLASS_DEFAULT, ""); + pd_bind(&pd_canvasmaker, &s__N); + class_addanything(pd_objectmaker._class, (t_method)new_anything); + obj_init(); + conf_init(); + glob_init(); + boxes_init(); + garray_init(); +} +}; diff --git a/desiredata/src/locale/bokmal.tcl b/desiredata/src/locale/bokmal.tcl new file mode 100644 index 00000000..8db87129 --- /dev/null +++ b/desiredata/src/locale/bokmal.tcl @@ -0,0 +1,453 @@ +#!/usr/bin/env tclsh +# Norwegian (Norsk Bokml) translations for PureData +# $Id: bokmal.tcl,v 1.1.2.5.2.2 2007-08-06 15:39:57 matju Exp $ +# by Gisle Frysland + +### Menus + +say file "Fil" + say new_file "Ny Fil" + say open_file "pne Fil..." + # say pdrc_editor ".pdrc Redigerer" + say server_prefs "Tjener Egenskaper..." + say client_prefs "Klient Egenskaper..." + say send_message "Send Melding..." + say paths "Stier..." + say close "Lukk" + say save "Lagre" + say save_as "Lagre Som..." + say print "Skriv Ut..." + say abort_server "Avbryt Tjener" + say quit "Avslutt" + + say canvasnew_file "Ny Fil" + say canvasopen_file "pne Fil..." + say canvassave "Lagre" + say canvassave_as "Lagre Som..." + say clientpdrc_editor ".pdrc Redigering" + say clientddrc_editor ".ddrc Redigering" + say canvasclose "Lukk" + say canvasquit "Avslutt" + +say edit "Rediger" + say undo "Angre" + say redo "Gjr Om" + say cut "Klipp Ut" + say copy "Kopier" + say paste "Lim Inn" + say duplicate "Dupliser" + say select_all "Merk Alt" + say clear_selection "Fjern merking" + say text_editor "Tekst Redigerer..." + say font "Skrift" + say tidy_up "Rydd Opp" + say edit_mode "Redigeringsmodus" + say editmodeswitch "Rediger/Kjr modus" + say subpatcherize "Subpatcherize" + + say canvascut "Klipp Ut" + say canvascopy "Kopier" + say canvasundo "Angre" + say canvasredo "Gjr Om" + say canvaspaste "Lim Inn" + say canvasduplicate "Dupliser" + say canvasselect_all "Merk Alt" + say canvaseditmodeswitch "Rediger/Kjr modus" + +say view "Vis" + say reload "Oppdater" + say redraw "Tegn P Nytt" + + say canvasreload "Oppdater" + say canvasredraw "Tegn P Nytt" + +say find "Finn" + say find_again "Finn P Nytt" + say find_last_error "Finn Siste Feil" + say string "Finn Tekst" +say canvasfind "Finn" + say canvasfind_again "Finn P Nytt" + +# contents of Put menu is Phase 5C +say put "Sett Inn" + say Object "Objekt" + say Message "Melding" + say Number "Nummer" + say Symbol "Symbol" + say Comment "Kommentar" + say Graph "Graf" + say Array "Tabell" + +say media "Media" + say audio_on "Audio P" + say audio_off "Audio AV" + say test_audio_and_midi "Test Audio og MIDI" + say load_meter "Belastningsmler" + + say canvasaudio_on "Audio P" + say canvasaudio_off "Audio AV" + say clienttest_audio_and_midi "Test Audio og MIDI" + say canvasload_meter "Belastningsmler" + +say window "Vindu" + +say help "Hjelp" + say about "Om..." + say documentation "Dokumentasjon..." + say class_browser "Klasseleser..." + + say canvasabout "Om..." + +say properties "Egenskaper" +say open "pne" + +### for key binding editor +say general "Generelt" +say audio_settings "Audio Innstillinger" +say midi_settings "Midi Innstillinger" +say latency_meter "Forsinkelsesmler" +say Pdwindow "Pd vindu" + +say canvaspdwindow "Pd vindu" +say canvaslatency_meter "Forsinkelsesmler" +say clientaudio_settings "Audio Innstillinger" +say clientmidi_settings "Midi Innstillinger" + + +say_namespace summary { + say_category IEMGUI + say bng "Bang Boks" + say tgl "Av/P Boks" + say nbx "Nummer Boks (IEM)" + say hsl "Glidebryter (Horisontal" + say vsl "Glidebryter (Vertikal)" + say hradio "Velger Boks (Horisontal)" + say vradio "Velger Boks (Vertikal)" + say cnv "Bakgrunn (IEM)" + say vu "Vumler" + say dropper "Dra-og-Slipp Boks" + + say_category LIM + say bang "send ut en bang melding" + say float "lagre og hente frem et tall" + say symbol "lagre og hente frem et symbol" + say int "lagre og hente frem et heltall" + say send "send en melding til et navngitt objekt" + say receive "ta i mot sendte meldinger" + say select "sjekk etter passende tall eller symboler" + say route "videresend meldinger i henhold til frste element" + say pack "lag sammensatte meldinger" + say unpack "hent elementer fra sammensatte meldinger" + say trigger "sekvenser og konverter meldinger" + say spigot "avbrytbar meldingsforbindelse" + say moses "bryt opp en numerisk strm" + say until "mekanisme for repetisjon" + say print "skriv ut meldinger" + say makefilename "formater et symbol med et variabelt felt" + say change "fjern repeterte tall fra en strm" + say swap "bytt om to tall" + say value "delt numerisk verdi" + + say_category TID + say delay "send en melding etter en tidsforsinkelse" + say metro "send en melding periodisk" + say line "send en serie av linert stegvise tall" + say timer "ml tidsintervaller" + say cputime "ml CPU tid" + say realtime "ml sann tid" + say pipe "dynamisk skalerbar forsinkelsesk for tall" + + say_category MATTEMATIKK + say + "legg til" + say - "trekk fra" + say * "multipliser" + say {/ div} "divider" + say {% mod} "delingsrestere" + say pow "eksponensiere" + say == "er lik?" + say != "er ikke lik?" + + + say > "strre enn?" + say < "mindre enn?" + say >= "ikke mindre enn?" + say <= "ikke strre enn?" + say & "bitvis konjunksjon (og)" + say | "bitvis atskillelse (eller)" + say && "logisk konjunksjon (og)" + say || "logisk atskillelse (eller)" + say mtof "MIDI til Hertz" + say ftom "Hertz til MIDI" + say powtodb "Watt til dB" + say dbtopow "dB til Watt" + say rmstodb "Volt til dB" + say dbtorms "dB til Volt" + say {sin cos tan atan atan2 sqrt} "trigonometri" + say log "Euler logaritme" + say exp "Euler eksponential" + say abs "absolutt verdi" + say random "tilfeldig" + say max "strste av to tall" + say min "minste av to tall" + say clip "tvinge et tall inn i et omrde" + + say_category MIDI + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} \ + "MIDI inndata" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} \ + "MIDI utdata" + say makenote \ + "sett inn en forsinket \"note off\" melding som korresponderer med en note-on" + say stripnote "fjern \"note off\" meldinger" + + say_category TABELLER + say tabread "les et tall fra en tabell" + say tabread4 "les et tall fra en tabell, med 4 punkts interpolasjon" + say tabwrite "skriv et tall til en tabell" + say soundfiler "les og skriv tabeller til lydfiler" + + say_category DIVERSE + say loadbang "bang ved lasting" + say serial "seriell enhetskontroll kun for NT" + say netsend "send meldinger over internettet" + say netreceive "motta dem" + say qlist "\"sequencer\" for meldinger" + say textfile "fil til melding konverterer" + say openpanel "\"pne\" dialog" + say savepanel "\"Lagre som\" dialog" + say bag "tallsett" + say poly "polyfonisk stemmeallokering" + say {key keyup} "numeriske noteverdier fra keyboard" + say keyname "symbolsk notenavn" + say_category "AUDIO MATTEMATIKK" + foreach word {+ - * /} {say $word~ "[say $word] (for signaler)"} + say max~ "supremum for signaler" + say min~ "infimum for signaler" + say clip~ "innskrenk signal til ligge mellom to grenseverdier" + say q8_rsqrt~ "billig resiprokal kvadratrot (obs -- 8 bits!)" + say q8_sqrt~ "billig kvadratrot (obs-- 8 bits!)" + say wrap~ "brett rundt (fractional part, sort of)" + say fft~ "kompleks forlengs diskr Fourier transform" + say ifft~ "kompleks invers diskr Fourier transform" + say rfft~ "sann forlengs diskr Fourier transform" + say rifft~ "sann invers diskr Fourier transform" + say framp~ "send ut en rampe for hver blokk" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (for signaler)" + } +} + +# phase 4 (pdrc & ddrc) + +say section_audio "Audio" + say -r "sample rate" + say -audioindev "audio inn enheter" + say -audiooutdev "audio ut enheter" + say -inchannels "audio inndata kanaler (pr. enhet, som \"2\" el. \"16,8\")" + say -outchannels "antall audio ut kanaler (samme)" + say -audiobuf "spesifiser strrelse p audio buffer i ms" + say -blocksize "spesifiser audio I/U blokk strrelse i sample rammer" + say -sleepgrain "spesifiser antall millisekunder g i dvale nr inaktiv" + say -nodac "hindre audio utdata" + say -noadc "hindre audio inndata" + say audio_api_choice "Audio API" + say default "standard" + say -alsa "bruk ALSA audio API" + say -jack "bruk JACK audio API" + say -mmio "bruk MMIO audio API (standard for Windows)" + say -portaudio "bruk ASIO audio driver (via Portaudio)" + say -oss "bruk OSS audio API" + say -32bit "tillat 32 bit OSS audio (for RME Hammerfall)" + say {} "standard" + +say section_midi "MIDI" + say -nomidiin "hindre MIDI inndata" + say -nomidiout "hindre MIDI utdata" + say -midiindev "midi inn enhetsiste; d.v.s., \"1,3\" for frste og tredje" + say -midioutdev "midi ut enhetsliste, samme format" + +say section_externals "Eksterne" + say -path "fil skesti" + say -helppath "helpefil skesti" + say -lib "last objektbiblioteker" + +say section_gui "Gooey" + say -nogui "hindre oppstart av GUI (forsiktig)" + say -guicmd "bytt med annet GUI program (d.v.s., rsh)" + say -look "knappelinje ikoner" + say -font "spesifiser standard skriftstrrelse i punkter" + +say section_other "Annet" + say -open "pne fil(er) ved oppstart" + say -verbose "ekstra utprint ved oppstart og ved sking etter filer" + say -d "feilskingsniv" + say -noloadbang "sl av effekten av \[loadbang\]" + say -send "send en melding ved oppstart (etter patcher er lastet)" + say -listdev "vis audio og MIDI enheter ved oppstart" + say -realtime "bruk sanntids prioritet (trenger superbruker rettigheter)" + +say section_paths "Stier" + +# phase 4B: ddrc (keyword names not finalized!) +say console "konsoll rullefelt linjer (0 = sl av konsoll)" +say lang "Sprkvalg" +say pointer_sense "Musepeker sensitivitet" +say section_color "farger" + say canvas_color "lerret" + say canvasbgedit "lerretsbakgrunn (redigeringsmodus)" + say canvasbgrun "lerretsbakgrunn (kjremodus)" + say object_color "objekt" + say viewframe1 "objektboks farge" + say viewframe2 "objektboks farge" + say viewframe3 "objektboks farge" + say viewframe4 "objektboks framhevingsfarge" + say viewbg "objekt bakgrunn" + say viewfg "objekt forgrunn" + say commentbg "kommentar bakgrunn" + say commentfg "kommentar forgrunn" + say commentframe1 "kommentar ramme" + say commentframe2 "kommentar ramme" + say commentframe3 "kommentar ramme" + say viewselectframe "framhevingsboks" + say wire_color "trd" + say wirefg "trdfarge" + say wirefg2 "trd framhevingstone" + say wiredspfg "dsp trdfarge" + say futurewiredash "ny (streket) trd" + say others_color "andre" + say boxinletfg "inngangsfarge" + say boxoutletfg "utgangsfarge" + say selrectrect "markeringsboks" +say keys "taster" +say others "andre" +say hairstate "Sl p siktekryss" +say hairsnap "Siktekryss fest til objekt" +say statusbar "Sl p statuslinje" +say buttonbar "Sl p knappelinje" +say menubar "Sl p menulinje" +say scrollbar "Sl p auto rullefelt" +say wirearrow "Trd Pil" +say tooltip "VerktysTips" +say insert_object "Sett Inn objekt" +say chain_object "Kjedeobjekt" +say clear_wires "Fjern trder" +say auto_wire "Fjern objekt" +say subpatcherize "Subpatcherize" +say keynav "tastatur navigering" +say key_nav_up "flytt opp" +say key_nav_up_shift "pluss valg" +say key_nav_down "flytt ned" +say key_nav_down_shift "pluss valg" +say key_nav_right "flytt hyre" +say key_nav_right_shift "pluss valg" +say key_nav_left "flytt venstre" +say key_nav_left_shift "pluss valg" +say key_nav_ioselect "velg inn/utganger" + +#phaze 5A +say cannot "kan ikke" +say cancel "Avbryt" +say apply "Bruk" +say ok "OK" +say popup_open "pne" +say popup_properties "Egenskaper" +say popup_help "Hjelp" +say filter "Filter: " +say how_many_object_classes "%d av %d objektklasser" +say do_what_i_mean "Gjr Det Jeg Mener" +say save_changes? "Lagre endringer?" +say reset "Nullstille" +say Courier "Courier (monospaced)" +say Helvetica "Helvetica (sansserif)" +say Times "Times (serif)" +say add "Legg til" +say up "Opp" +say down "Ned" +say remove "Fjern" +say lib_add "legg navnet du skrev til listen" +say lib_up "bytt rekkeflge med forrige bibliotek" +say lib_down "bytt rekkeflge med neste bibliotek" +say lib_remove "fjern det valgte biblioteket" +say dir_add "legg til en mappe ved bruke en filvelger" +say dir_up "bytt rekkeflge med forrige mappe" +say dir_down "bytt rekkeflge med neste mappe" +say dir_remove "fjern den valgte mappen" +say client_class_tree "Klient KlasseTre" +say clipboard_view "Utklippstavlevisning" +say history_view "Historievisning" + +### for Properties Dialog (phase 5B) +say_category IEM +say w "bredde(px)" +say h "hyde(px)" +say hold "ventetid(ms)" +say break "pausetid(ms)" +say min "minimumsverdi" +say max "maksimumsverdi" +say is_log "modus" +say linear "liner" +say logarithmic "logaritmisk" +say isa "initiering" +say n "antall valg" +say steady "stdighet" +say steady_no "hopp ved klikk" +say steady_yes "stdig ved klikk" +say snd "send-symbol" +say rcv "motta-symbol" +say lab "merkelapp" +say ldx "merkelapp x forskyvning" +say ldy "merkelapp y forskyvning" +say fstyle "Skrift" +say fs "skriftstrrelse" +say bcol "bakgrunnsfarge" +say fcol "forgrunnsfarge" +say lcol "merkelappfarge" +say yes "ja" +say no "nei" +say courier "courier (skrivemaskin)" +say helvetica "helvetica (sansserif)" +say times "times (serif)" +say coords "graph on parent" + +say_category GAtomProperties +say width "bredde" +say lo "nedre grense" +say hi "vre grense" +say label "merkelapp" +say wherelabel "vis merkelapp p" +say symto "send symbol" +say symfrom "motta symbol" + +say_category GraphProperties +say x1 "x fra" +say x2 "x til" +say xpix "skjermbredde" +say y2 "y fra" +say y1 "y til" +say ypix "skjermhyde" + +say_category CanvasProperties +#say xscale "X enheter/px" +#say yscale "Y enheter/px" +say gop "Graph on Parent" +say xmargin "X margin" +say ymargin "Y margin" +say height "hyde" + +say_category ArrayProperties +say name "navn" +say n "strrelse" +say xfrom "x omrde fra" +say xto "x omrde til" +say yfrom "y omrde fra" +say yto "y omrde til" + +say_category MainWindow +say in "inn" +say out "ut" +say audio "Audio" +say meters "Mlere" +say io_errors "IU Feil" +say tcl_console "Tcl Klient" +say pd_console "Pd Tjener" + diff --git a/desiredata/src/locale/brasiliano.tcl b/desiredata/src/locale/brasiliano.tcl new file mode 100644 index 00000000..34e5aaff --- /dev/null +++ b/desiredata/src/locale/brasiliano.tcl @@ -0,0 +1,577 @@ +#!/usr/bin/env tclsh +# Portuguese translations for DesireData +# $Id: brasiliano.tcl,v 1.1.2.1 2007-10-05 23:15:45 matju Exp $ +# Author: chgp@riseup.net revision 0.4 + +### Menus + +say file "Arquivo" + say new_file "Novo" + say open_file "Abrir..." + say server_prefs "Config. servidor..." + say client_prefs "Config. cliente..." + say send_message "Enviar mensagem..." + say paths "Caminhos..." + say close "Fechar Janela" + say save "Salvar" + say save_as "Salvar como..." + say print "Imprimir..." + say abort_server "Terminar o servidor" + say quit "Sair" + + say canvasnew_file "Novo Arquivo" + say canvasopen_file "Abrir Arquivo..." + say canvassave "Salvar" + say canvassave_as "Salvar como..." + say clientpdrc_editor "Editor .pdrc" + say clientddrc_editor "Editor .ddrc" + say canvasclose "Fechar" + say canvasquit "Sair" + +say edit "Edição" + say undo "Desfazer" + say redo "Refazer" + say cut "Cortar" + say copy "Copiar" + say paste "Colar" + say duplicate "Duplicar" + say select_all "Selecionar tudo" + say clear_selection "Limpar seleção" + say text_editor "Editor de Texto..." + say font "Fonte" + say tidy_up "Tidy Up" + say edit_mode "Modo de edição" + say editmodeswitch "Modo de edição/execução" + say subpatcherize "Subpatcherizar :)" + + say canvascut "Cortar" + say canvascopy "Copiar" + say canvasundo "Colar" + say canvasredo "Refazer" + say canvaspaste "Colar" + say canvasduplicate "Duplicar" + say canvasselect_all "Selecionar tudo" + say canvaseditmodeswitch "Modo de edição/execução" + +say view "Visualizar" + say reload "Recarregar" + say redraw "Redesenhar" + + say canvasreload "Recarregar" + say canvasredraw "Redesenhar" +say visual_diff "visual diff??" +say get_elapsed "get elapsed??" + +say find "Procurar" + say find_again "Procura novamente" + say find_last_error "Encontrar o último erro" + say string "Procurar por sequência de caracteres" +say canvasfind "Buscar" + say canvasfind_again "Busca novamente" + +# contents of Put menu is Phase 5C +say put "Inserir" + say Object "Objeto" + say Message "Mensagem" + say Number "Número" + say Symbol "Símbolo" + say Comment "Comentário" + say Graph "Gráfico" + say Array "Tabela" + +say media "Mídia" + say audio_on "Ligar Áudio" + say audio_off "Desligar Áudio" + say test_audio_and_midi "Testar áudio e MIDI" + say load_meter "Carregar Meter" + + say canvasaudio_on "Áudio Ligado" + say canvasaudio_off "Áudio Desligado" + say clienttest_audio_and_midi "Testar áudio e MIDI" + say canvasload_meter "Carregar Meter" + +say window "Janelas" + +say help "Ajuda" + say about "Sobre..." + say documentation "Documentação..." + say class_browser "Navegador..." + + say canvasabout "Sobre..." + +say properties "Propriedades" +say open "Abrir" + +### for key binding editor +say general "Geral" +say audio_settings "Configurações de Áudio" +say midi_settings "Configurações Midi" +say latency_meter "Medidor de latência" +say Pdwindow "Janela do Pd" + +say canvaspdwindow "Janela do Pd" +say canvaslatency_meter "Medidor de latência" +say clientaudio_settings "Configurações de áudio" +say clientmidi_settings "Configurações Midi" + +### for Properties Dialog (phase 5B) +say_category IEM +say w "largura(px)" +say h "altura(px)" +say hold "tempo de espera(ms)" +say break "tempo de quebra(ms)" +say min "valor mínimo" +say max "valor máximo" +say is_log "mode? (is_log)" +say linear "linear" +say logarithmic "logarítimico" +say isa "início" +say n "número de alternativas" +say steady "steadiness?" +say steady_no "pula no clique" +say steady_yes "steady? no clique" +say snd "envia-símbolo" +say rcv "recebe-símbolo" +say lab "etiqueta" +say ldx "offset etiqueta x" +say ldy "offset etiqueta y" +say fstyle "Fonte tipo" +say fs "tamanho da fonte" +say bcol "cor de fundo" +say fcol "cor da frente" +say lcol "cor da etiqueta" +say yes "sim" +say no "não" +say courier "courier (typewriter)" +say helvetica "helvetica (sansserif)" +say times "times (serif)" +say coords "gráfico no pai" + +say_category GAtomProperties +say width "largura" +say lo "limite inferior" +say hi "limite superior" +say label "etiqueta" +say wherelabel "exibe etiqueta" +say symto "envia símbolo" +say symfrom "recebe símbolo" + +say_category GraphProperties +say x1 "de x" +say x2 "para x" +say xpix "largura da tela" +say y2 "de y" +say y1 "para y" +say ypix "altura da tela" + +say_category CanvasProperties +#say xscale "X units/px" +#say yscale "Y units/px" +say gop "gráfico no pai" +say xmargin "margem x" +say ymargin "margem y" +say height "altura" +say_category ArrayProperties +say name "nome" +say n "tamanho" +say xfrom "escala x de?" +say xto "escala x até" +say yfrom "escala y de" +say yto "escala y até" + + +say_category MainWindow +say in "entrada" +say out "saída" +say audio "Áudio" +say meters "Medidores" +say io_errors "Erros E/S" +say tcl_console "Cliente Tcl" +say pd_console "Servidor Pd" + +### phase 2 + +say_category Other +say_namespace summary { + say_category IEMGUI + say bng "Bang" + say tgl "Interruptor (Toggle)" + say nbx "Caixa numérica (IEM)" + say hsl "Slider (Horizontal)" + say vsl "Slider (Vertical)" + say hradio "Caixa de seleção (horizontal)" + say vradio "Caixa de seleção (Vertical)" + say cnv "Canvas (IEM)" + say dropper "Caixa arrasta-e-solta" + say vu "Medidor VU" + + say_category GLUE + say bang "envia uma mensagem bang" + say float "armazena e acessa um número" + say symbol "armazena e acessa um símbolo" + say int "armazena e acessa um inteiro" + say send "armazena uma mensagem para um objeto rotulado" + say receive "rebe mensagens enviadas" + say select "testar por números ou símbolos coincidentes" + say route "refaz a rota das mensagens de acordo com o primeiro elemento" + say pack "\"empacotador\" de mensagens" + say unpack "\"desempacota\" mensagens compostas" + say trigger "sequencia e converte mensagens" + say spigot "interruptor de fluxo de mensagens" + say moses "part a numeric stream" + say until "mecanismo de loop" + say print "exibe mensagens" + say makefilename "formata um símbolo com um campo variável??" + say change "remover números repetidos de uma sequência" + say swap "troca dois números" + say value "compartilha valor numérico" + + say_category TIME + say delay "envia uma mensagem após um intervalo de tempo" + say metro "Metrônomo - envia mensagens periodicamente" + say line "envia mensagem com números lineares" + say timer "mede intervalos de tempo" + say cputime "mede tempo de processamento" + say realtime "mede tempo real" + say pipe "linha de intervalo de tempo númerico que cresce dinamicamente" + + say_category MATH + say + "adiciona" + say - "subtrai" + say * "multiplica" + say {/ div} "divisão" + say {% mod} "resto da divisão" + say pow "potência" + say == "igua a?" + say != "diferente de?" + say > "maior que?" + say < "menor que?" + say >= "menor ou igual a?" + say <= "maior ou igual a?" + say & "conjunção bitwise (e)" + say | "disjunção bitwise (ou)" + say && "conjunção lógica (e)" + say || "disjunção lógica (ou)" + say mtof "MIDI para Hertz" + say ftom "Hertz para MIDI" + say powtodb "Watts para dB" + say dbtopow "dB para Watts" + say rmstodb "Volts para dB" + say dbtorms "dB para Volts" + say {sin cos tan atan atan2 sqrt} "trigonometria" + say log "Logarítimo Euler" + say exp "Exponenciação Euler" + say abs "valor absoluto" + say random "randômico" + say max "máximo de dois números" + say min "mínimo de dois números" + say clip "forçar um número numa faixa" + + say_category MIDI + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} "entrada MIDI" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} "saída MIDI" + say makenote "agenda uma mensagem \"note off\" correspondente a uma note-on" + say stripnote "separa mensagens \"note off\" " + + say_category TABLES + say tabread "le um número de uma tabela" + say tabread4 "lê um número de uma tabela com 4 pontos de interpolação" + say tabwrite "escreve um número numa tabela" + say soundfiler "lê e escreve tabelas em arquivos de som" + + say_category MISC + say loadbang "bang quando carregar patch" + say serial "controle de dispositivo para MS NT" + say netsend "envia mensagens através da internet" + say netreceive "recebe as mensagens através da internet" + say qlist "sequenciador de mensagens" + say textfile "conversor de arquivo para mensagens" + say openpanel "Diálogo \"Abrir\"" + say savepanel "Diálogo \"Salvar como\" " + say bag "conjunto de números" + say poly "alocação de voz polifônica" + say {key keyup} "valor numéricos de teclas de teclado alfanumérico" + say keyname "nome simbólico da chave?" + + say_category "Matemática com Áudio" + foreach word {+ - * /} {say $word~ "[say $word] (for signals)"} + say max~ "supremacia dos sinais" + say min~ "infinidade dos sinais" + say clip~ "constrict signal to lie between two bounds" + say q8_rsqrt~ "raiz quadrada reciproca barata (cuidado -- 8 bits!)" + say q8_sqrt~ "raiz quadrada barata (cuidado -- 8 bits!)" + say wrap~ "wraparound (tipo de parte fracionária)" + say fft~ "transformação discreta Fourier complex forward" + say ifft~ "transformação discreta Fourier complex inverse" + say rfft~ "transformação discreta Fourier real forward " + say rifft~ "transformação discreta Fourier real inverse " + say framp~ "dá saída numa rampa para cada bloco" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (para sinais)" + } +} + +### phase 3 + +say_namespace summary { + say_category "Cola de Áudio" + say dac~ "saída de áudio" + say adc~ "entrada de áudio" + say sig~ "converte números para sinais de áudio" + say line~ "gera rampas de áudio" + say vline~ "line~ de luxo" + say threshold~ "detecta sinal thresholds" + say snapshot~ "sample a signal (convert it back to a number)" + say vsnapshot~ "deluxe snapshot~" + say bang~ "send a bang message after each DSP block" + say samplerate~ "get the sample rate" + say send~ "nonlocal signal connection with fanout" + say receive~ "obtêm sinal do send~" + say throw~ "adiciona a um summing bus" + say catch~ "definir e ler um summing bus??" + say block~ "especificar tamanho do bloco e overlap" + say switch~ "liga e desliga computação DSP" + say readsf~ "le de arquivos de som no disco" + say writesf~ "grava arquivos de som no disco" + + say_category "Osciladores de áudio e tabelas" + say phasor~ "oscilador dente de serra (sawtooth)" + say {cos~ osc~} "oscilador coseno" + say tabwrite~ "escrever em tabela" + say tabplay~ "tocar de uma tabela (sem transpor)" + say tabread~ "leitura de tabela não interpolada" + say tabread4~ "leitura de tabela com 4 ponto de interpolação" + say tabosc4~ "oscilador com tabela de ondas (wavetable)" + say tabsend~ "escreve um bloco continuamente em uma tabela" + say tabreceive~ "le um bloco continuamente de uma tabela" + + say_category "Filtros de áudio" + say vcf~ "filtro controlador de voltagem" + say noise~ "gerador de ruido branco" + say env~ "envelope follower" + say hip~ "filtro passo alto" + say lop~ "filtro passo baixo" + say bp~ "filtro passo banda" + say biquad~ "filtro raw" + say samphold~ "unidade sample and hold" + say print~ "exibe um ou mais \"blocose \"blocks\"" + say rpole~ "raw real-valued one-pole filter" + say rzero~ "raw real-valued one-zero filter" + say rzero_rev~ "[say rzero~] (tempo-revertido)" + say cpole~ "[say rpole~] (valor complexo)" + say czero~ "[say rzero~] (valor complexo)" + say czero_rev~ "[say rzero_rev~] (valor complexo)" + + say_category "AUDIO DELAY" + say delwrite~ "escreve para uma linha de atraso(delay)" + say delread~ "ler de uma linha de atraso(delay)" + say vd~ "ler de uma linha de atraso num read from a delay line at a variable delay time" + + say_category "Subjanelas" + say pd "define uma subjanela" + say table "array de números em uma subjanela" + say inlet "add an inlet to a pd" + say outlet "add an outlet to a pd" + say inlet~ "[say inlet] (for signal)" + say outlet~ "[say outlet] (for signal)" + + say_category "Modelos de DADOS (templates)" + say struct "define uma estrutura de dados" + say {drawcurve filledcurve} "desenha uma curva" + say {drawpolygon filledpolygon} "desenha um polígono" + say plot "plotar um campo array" + say drawnumber "imprime um valor numérico" + + say_category "Acessando DADOS" + say pointer "aponta para um objeto pertencente a um modelo" + say get "obtêm campos numéricos" + say set "altera campos numéricos" + say element "obtêm um elemento array" + say getsize "obtêm o tamanho do array" + say setsize "altera o tamanho de um array" + say append "adiciona um elemento a uma lista" + say sublist "obtêm um ponteiro em uma lista que é um elemento para uma outra escalar??" + say scalar "desenha uma escalar no pai" + + say_category "OBSOLETE" + say scope~ "(usar tabwrite~ agora)" + say namecanvas "" ;# what was this anyway? + say template "(usar struct agora)" +} + +# phase 4 (pdrc & ddrc) + +say section_audio "Áudio" + say -r "Frequência da amostra" + say -audioindev "dispositivos de entrada" + say -audiooutdev "dispositivos de saída" + say -inchannels "número de canais de entrada (por dispositivo, tipo \"2\" ou \"16,8\")" + say -outchannels "número de canais de saída (o mesmo)" + say -audiobuf "especificar tamanho do buffer de áudio em mseg." + say -blocksize "espcificar tamanho do bloco de E/S em quadros de amostras" + say -sleepgrain "especificar número para dormir quando inativa em milisegundos" + say -nodac "não processa áudio" + say -noadc "não processa(supress?) áudio" + say audio_api_choice "API de áudio" + say default "padrão" + say -alsa "usar API ALSA de áudio" + say -jack "usar API JACK de áudio" + say -mmio "usar API MMIO de áudio (padrão para windows)" + say -portaudio "usar driver de áudio ASIO (via Portaudio)" + say -oss "usar API OSS de áudio" + say -32bit "permitir acesso 32 bits a áudio OSS (para RME Hammerfall)" + say {} "padrão" + +say section_midi "MIDI" + say -nomidiin "não processa entrada MIDI" + say -nomidiout "não processa saída MIDI" + say -midiindev "dispositivos de entrada MIDI; ex, \"1,3\" para primeiro e terceiro" +say -midioutdev "dispositivos de saída MIDI; lista de dispositivos de saída midi, mesmo formato" + +say section_externals "Externos" + say -path "caminho de procura por arquivos" + say -helppath "caminho de procura por arquivos de ajuda" + say -lib "carrega bibliotecas de objetos" + +say section_gui "Interface" + say -nogui "desabilita o início da interface gráfica (cuidado)" + say -guicmd "substitui outro programa de interface (ex., rsh)" + say -look "ícones da barra de butões" + say -font "especificar o padrão do tamanho da fonte em pontos" + +say section_other "Outros" + say -open "abrir arquivo(s) no início" + say -verbose "exibir mais detalhes no início e quando pesquisar por arquivos" + say -d "grau de depuração" + say -noloadbang "desabilita o efeito de \[loadbang\]" + say -send "envia uma mensagem no início (depois dos patches carregados)" + say -listdev "listar dispositivos de áudio e MIDI carga do logicial" + say -realtime "usar prioridade de tempo-real (necessita conta com privilégio especial ou root)" + +say section_paths "Caminhos" + +# phase 4B: ddrc (keyword names not finalized!) +say console "linhas de rolamento da console (1 = desabilita console)" +say lang "Linguagem" +say pointer_sense "Sensibilidade do ponteiro do mouse" +say section_color "Aparencia" + say canvas_color "canvas" + say canvasbgedit "fundo do canvas (modo edição)" + say canvasbgrun "canvas background (modo execução)" + say object_color "objeto" + say viewframe1 "cor da caixa de objeto" + say viewframe2 "cor da caixa de objeto" + say viewframe3 "cor da caixa de objeto" + say viewframe4 "cor de destaque da caixa de objeto" + say viewbg "fundo do objeto" + say viewfg "frente do objeto" + say commentbg "fundo do comentário" + say commentfg "frente do comentário" + say commentframe1 "quadro de comentário" + say commentframe2 "quadro de comentário" + say commentframe3 "quadro de comentário" + say viewselectframe "hilight box" + say wire_color "fio" + say wirefg "cor do fio" + say wirefg2 "destaque do fio" + say wiredspfg "cor do fio de áudio" + say futurewiredash "novo fio (esmagado?)" + say others_color "outras" + say boxinletfg "cor das entradas" + say boxoutletfg "cor das saídas" + say selrectrect "caixa de seleção" +say keys "chaves" +say others "outras" +say hairstate "Ativa crosshair" +say hairsnap "Crosshair gruda no objeto" +say statusbar "Ativa barra de estado" +say buttonbar "Ativa barra de butões" +say menubar "Ativa barra de menus" +say scrollbar "Ativa barra de rolagem automática" +say wirearrow "Cabo com seta" +say tooltip "Dica" +say insert_object "Inserir objeto" +say chain_object "Objeto corrente (Chain)" +say clear_wires "Limpar cabos" +say auto_wire "Remove objeto" +say subpatcherize "Subpatcherizar :)" +say keynav "navegação do teclado" +say key_nav_up "move para cima" +say key_nav_up_shift "selecione mais" +say key_nav_down "mover para baixo" +say key_nav_down_shift "selecione mais" +say key_nav_right "move right" +say key_nav_right_shift "selecione mais" +say key_nav_left "mover para esquerda" +say key_nav_left_shift "selecione mais" +say key_nav_ioselect "seleciona entradas/saídas" +# phase 5A + +say cannot "não consigo" +say cancel "Cancelar" +say apply "Aplicar" +say ok "OK" +say popup_open "Abrir" +say popup_insert "Inserir" +say popup_properties "Propriedades" +say popup_clear_wires "Limpar conexões" +say popup_remove_from_path "Remove objeto do caminho" +say popup_delete_from_path "Apaga objeto do caminho" +say popup_copy_id "copia identificação" +say popup_help "Ajuda" +say filter "Filtro: " +say how_many_object_classes "%d de %d classes de objeto" +say do_what_i_mean "Realize o que eu imagino!" +say ask_cool "Este recurso quando estiver implementado vai ficar muito, foda." +say save_changes? "Salvar as bobagens que você fez?" +say reset "Reiniciar" +say Courier "Courier (monospaced)" +say Helvetica "Helvetica (sansserif)" +say Times "Times (serif)" +say add "Adiciona" +say up "Acima" +say down "Abaixo" +say remove "Remove" +say lib_add "adiciona o nome que você digitou na lista" +say lib_up "altera ordem com biblioteca anterior" +say lib_down "altera ordem com próxima biblioteca" +say lib_remove "remove biblioteca selecionada na lista" +say dir_add "adiciona um campo usando um diálogo de arquivo" +say dir_up "troca ordem com campo anterior" +say dir_down "troca ordem com próximo campo" +say dir_remove "remove pasta selecionada na lista" +say client_class_tree "Árvore de Classes pro freguês" +say clipboard_view "Visão Clipboard" +say command_history_view "Visão do histórico de comandos" +say event_history_view "Ver histórico de eventos" + +# during/after piksel: + +say auto_apply "Auto-Aplicar" +say font_preview "Prévia:" +say font_preview_2 "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n0123456789" +say font_style "Estilo:" +say font_bold "Negrito" +say font_italic "Itálico" +say font_family "Nome:" +say font_size "Tamanho:" +say damn "Maldito!" +say console_clear "Limpar Console" +say horizontal "Horizontal" +say vertical "Vertical" +say language "Língua" + +# 2007: + +say no_matches "o padrão especificado não foi encontrado" +say preset "modelo??" +say canvasgrid "Cor da Grade" +say grid_size "Tamanho da grade" +say gridstate "Ativar grade de fundo" +say snap_grid "Grudar na grade" +say viewfont "fonte do objeto" +say consolefont "fonte do console" +say keyboarddialogfont "fonte do teclado virtual" +say keyboard_view "Teclado Virtual" +say log_height "Tamanho do Histórico" + diff --git a/desiredata/src/locale/catala.tcl b/desiredata/src/locale/catala.tcl new file mode 100644 index 00000000..8f244256 --- /dev/null +++ b/desiredata/src/locale/catala.tcl @@ -0,0 +1,58 @@ +#!/usr/bin/env tclsh +# Catalan (catal) translations for PureData +# $Id: catala.tcl,v 1.1.2.5 2006-10-13 16:00:56 matju Exp $ +# by Nria Verges + +say file "Fitxer" + say new_file "Nou Fitxer" + say open_file "Obrir Fitxer..." + say pdrc_editor "Editor de .pdrc" + say send_message "Enviar Missatge..." + say paths "Camins..." + say close "Tancar" + say save "Desar" + say save_as "Guardar com a..." + say print "Imprimir..." + say quit "Sortir" + +say edit "Editar" + say undo "Desfer" + say redo "Refer" + say cut "Tallar" + say copy "Copiar" + say paste "Enganxar" + say duplicate "Duplicar" + say select_all "Seleccionar-ho tot" + say text_editor "Editor de text..." + say tidy_up "Netejar" + say edit_mode "Mode d'edici" + +say view "Veure" + say reload "Recarregar" + say redraw "Redissenyar" + +say find "Trobar" + say find_again "Trobar novament" + say find_last_error "Trobar l'ltima errada" + +say put "Posar" + +say media "Media" + say audio_on "Audio ON" + say audio_off "Audio OFF" + +say window "Finestra" + +say help "Ajuda" + say about "Sobre..." + say pure_documentation "Pure Documentaci..." + say class_browser "Cercador de Classes..." + +### Main Window + +say in "in" +say out "out" +say audio "Audio" +say meters "Meters" +say io_errors "Errades d'E/S" + diff --git a/desiredata/src/locale/chinese.tcl b/desiredata/src/locale/chinese.tcl new file mode 100644 index 00000000..bdbd0a37 --- /dev/null +++ b/desiredata/src/locale/chinese.tcl @@ -0,0 +1,560 @@ +#!/usr/bin/env tclsh +# English translations for PureData +# $Id: chinese.tcl,v 1.1.2.3 2007-08-09 02:09:07 chunlee Exp $ + +### Menus + +say file "案檔" + say new_file "新案檔" + say open_file "開啟舊檔..." + say server_prefs "伺服器設定..." + say client_prefs "客戶端設定..." + say send_message "傳送旨令..." + say paths "Paths..." + say close "關閉" + say save "存檔" + say save_as "另存新檔..." + say print "印出..." + say quit "結束" + + say canvasnew_file "開啟新檔" + say canvasopen_file "開啟舊檔..." + say canvassave "存檔" + say canvassave_as "另存新檔..." + say clientpdrc_editor "遠端設定" + say clientddrc_editor "終端設定" + say canvasclose "關閉" + say canvasquit "結束" + +say edit "編輯" + say undo "回上一步" + say redo "回下一步" + say cut "剪下" + say copy "拷貝" + say paste "後貼" + say duplicate "復製" + say select_all "全選" + say text_editor "Text Editor..." + say font "字體" + say tidy_up "自動排列" + say edit_mode "編輯模式" + say editmodeswitch "編輯/執行 模式" + + say canvascut "剪下" + say canvascopy "拷貝" + say canvasundo "回上一步" + say canvasredo "回下一步" + say canvaspaste "後貼" + say canvasduplicate "復製" + say canvasselect_all "全選" + say canvaseditmodeswitch "編輯/執行 模式" + +say view "顯示" + say reload "重新下載" + say redraw "重新畫製" + + say canvasreload "重新下載" + say canvasredraw "重新畫製" + +say find "尋找" + say find_again "再次尋找" + say find_last_error "尋找上一錯誤" + say string "Find string" +say canvasfind "尋找" + say canvasfind_again "再次尋找" + +# contents of Put menu is Phase 5C +say put "放置" + say Object "物件" + say Message "信息" + say Number "數字" + say Symbol "符號" + say Comment "註譯" + say Graph "圖" + say Array "陣列" + +say media "Media" + say audio_on "聲頻開啟" + say audio_off "聲頻關閉" + say test_audio_and_midi "測試聲頻及MIDI" + say load_meter "負載錶" + + say canvasaudio_on "聲頻開啟" + say canvasaudio_off "聲頻關閉" + say clienttest_audio_and_midi "測試聲頻及MIDI" + say canvasload_meter "負載錶" + +say window "視窗" + +say help "幫助" + say about "相關..." + say documentation "使用說明..." + say class_browser "物件瀏覽器..." + + say canvasabout "相關..." + +say properties "內容" +say open "開啟" + +### for key binding editor +say general "一般" +say audio_settings "聲頻設定" +say midi_settings "Midi設定" +say latency_meter "時延錶" +say Pdwindow "Pd主視窗" + +say canvaspdwindow "Pd主視窗" +say canvaslatency_meter "時延錶" +say clientaudio_settings "聲頻設定" +say clientmidi_settings "Midi設定" + +### for Properties Dialog (phase 5B) +say_category IEM +say w "寬(像素)" +say h "高(像素)" +say hold "有效顯示時限(毫秒)" +say break "有效空閒時限(毫秒)" +say min "最低數限" +say max "最高數限" +say is_log "模式" +say linear "線性" +say logarithmic "對數" +say isa "起始" +say n "選擇數量" +say steady "穩定性" +say steady_no "點選跳躍式" +say steady_yes "點選持續式" +say snd "傳送符號" +say rcv "接收符號" +say lab "標簽" +say ldx "標簽橫落差" +say ldy "標簽縱落差" +say fstyle "字形" +say fs "字體大小" +say bcol "背景研色" +say fcol "前景研色" +say lcol "標簽研色" +say yes "是" +say no "否" +say courier "courier (typewriter)" +say helvetica "helvetica (sansserif)" +say times "times (serif)" +say coords "頂端顯示" + +say_category GAtomProperties +say width "寬" +say lo "最低數限" +say hi "最高數限" +say label "標簽" +say wherelabel "標簽位置" +say symto "傳送符號" +say symfrom "接收符號" +say pos "標簽位置" +say_category GraphProperties +say x1 "x from" +say x2 "x to" +say xpix "screen width" +say y2 "y from" +say y1 "y to" +say ypix "screen height" + +say_category CanvasProperties +#say xscale "X units/px" +#say yscale "Y units/px" +say gop "頂端顯示" +say xmargin "橫軸邊緣限度" +say ymargin "縱軸邊緣限度" +say height "高" +say_category ArrayProperties +say name "名子" +say n "大小" +say xfrom "橫軸範圍啟" +say xto "橫軸範圍使" +say yfrom "縱軸範圍啟" +say yto "縱軸範圍使" + + +say_category MainWindow +say in "進" +say out "出" +say audio "聲音" +say meters "聲頻錶" +say io_errors "聲頻錯誤" +say console_clear "清空顯示" +say tcl_console "TCL 旨令" +say pd_console "Pd 旨令" +### phase 2 + +say_category Other +say_namespace summary { + say_category IEMGUI + say bng "Bang Box" + say tgl "Toggle Box" + say nbx "Number Box (IEM)" + say hsl "Slider (Horizontal)" + say vsl "Slider (Vertical)" + say hradio "Choice Box (Horizontal)" + say vradio "Choice Box (Vertical)" + say cnv "Canvas (IEM)" + say dropper "Drag-and-Drop Box" + say vu "Vumeter" + + say_category GLUE + say bang "output a bang message" + say float "store and recall a number" + say symbol "store and recall a symbol" + say int "store and recall an integer" + say send "send a message to a named object" + say receive "catch sent messages" + say select "test for matching numbers or symbols" + say route "route messages according to first element" + say pack "make compound messages" + say unpack "get elements of compound messages" + say trigger "sequence and convert messagess" + say spigot "interruptible message connection" + say moses "part a numeric stream" + say until "looping mechanism" + say print "print out messages" + say makefilename "format a symbol with a variable field" + say change "remove repeated numbers from a stream" + say swap "swap two numbers" + say value "shared numeric value" + + say_category TIME + say delay "send a message after a time delay" + say metro "send a message periodically" + say line "send a series of linearly stepped numbers" + say timer "measure time intervals" + say cputime "measure CPU time" + say realtime "measure real time" + say pipe "dynamically growable delay line for numbers" + + say_category MATH + say + "add" + say - "substract" + say * "multiply" + say {/ div} "divide" + say {% mod} "division remainder" + say pow "exponentiate" + say == "equal?" + say != "not equal?" + say > "more than?" + say < "less than?" + say >= "not less than?" + say <= "not more than?" + say & "bitwise conjunction (and)" + say | "bitwise disjunction (or)" + say && "logical conjunction (and)" + say || "logical disjunction (or)" + say mtof "MIDI to Hertz" + say ftom "Hertz to MIDI" + say powtodb "Watts to dB" + say dbtopow "dB to Watts" + say rmstodb "Volts to dB" + say dbtorms "dB to Volts" + say {sin cos tan atan atan2 sqrt} "trigonometry" + say log "Euler logarithm" + say exp "Euler exponential" + say abs "absolute value" + say random "random" + say max "greater of two numbers" + say min "lesser of two numbers" + say clip "force a number into a range" + + say_category MIDI + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} "MIDI input" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} "MIDI output" + say makenote "schedule a delayed \"note off\" message corresponding to a note-on" + say stripnote "strip \"note off\" messages" + + say_category TABLES + say tabread "read a number from a table" + say tabread4 "read a number from a table, with 4 point interpolation" + say tabwrite "write a number to a table" + say soundfiler "read and write tables to soundfiles" + + say_category MISC + say loadbang "bang on load" + say serial "serial device control for NT only" + say netsend "send messages over the internet" + say netreceive "receive them" + say qlist "message sequencer" + say textfile "file to message converter" + say openpanel "\"Open\" dialog" + say savepanel "\"Save as\" dialog" + say bag "set of numbers" + say poly "polyphonic voice allocation" + say {key keyup} "numeric key values from keyboard" + say keyname "symbolic key name" + + say_category "AUDIO MATH" + foreach word {+ - * /} {say $word~ "[say $word] (for signals)"} + say max~ "supremum of signals" + say min~ "infimum of signals" + say clip~ "constrict signal to lie between two bounds" + say q8_rsqrt~ "cheap reciprocal square root (beware -- 8 bits!)" + say q8_sqrt~ "cheap square root (beware -- 8 bits!)" + say wrap~ "wraparound (fractional part, sort of)" + say fft~ "complex forward discrete Fourier transform" + say ifft~ "complex inverse discrete Fourier transform" + say rfft~ "real forward discrete Fourier transform" + say rifft~ "real inverse discrete Fourier transform" + say framp~ "output a ramp for each block" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (for signals)" + } +} + +### phase 3 + +say_namespace summary { + say_category "AUDIO GLUE" + say dac~ "audio output" + say adc~ "audio input" + say sig~ "convert numbers to audio signals" + say line~ "generate audio ramps" + say vline~ "deluxe line~" + say threshold~ "detect signal thresholds" + say snapshot~ "sample a signal (convert it back to a number)" + say vsnapshot~ "deluxe snapshot~" + say bang~ "send a bang message after each DSP block" + say samplerate~ "get the sample rate" + say send~ "nonlocal signal connection with fanout" + say receive~ "get signal from send~" + say throw~ "add to a summing bus" + say catch~ "define and read a summing bus" + say block~ "specify block size and overlap" + say switch~ "switch DSP computation on and off" + say readsf~ "soundfile playback from disk" + say writesf~ "record sound to disk" + + say_category "AUDIO OSCILLATORS AND TABLES" + say phasor~ "sawtooth oscillator" + say {cos~ osc~} "cosine oscillator" + say tabwrite~ "write to a table" + say tabplay~ "play back from a table (non-transposing)" + say tabread~ "non-interpolating table read" + say tabread4~ "four-point interpolating table read" + say tabosc4~ "wavetable oscillator" + say tabsend~ "write one block continuously to a table" + say tabreceive~ "read one block continuously from a table" + + say_category "AUDIO FILTERS" + say vcf~ "voltage controlled filter" + say noise~ "white noise generator" + say env~ "envelope follower" + say hip~ "high pass filter" + say lop~ "low pass filter" + say bp~ "band pass filter" + say biquad~ "raw filter" + say samphold~ "sample and hold unit" + say print~ "print out one or more \"blocks\"" + say rpole~ "raw real-valued one-pole filter" + say rzero~ "raw real-valued one-zero filter" + say rzero_rev~ "[say rzero~] (time-reversed)" + say cpole~ "[say rpole~] (complex-valued)" + say czero~ "[say rzero~] (complex-valued)" + say czero_rev~ "[say rzero_rev~] (complex-valued)" + + say_category "AUDIO DELAY" + say delwrite~ "write to a delay line" + say delread~ "read from a delay line" + say vd~ "read from a delay line at a variable delay time" + + say_category "SUBWINDOWS" + say pd "define a subwindow" + say table "array of numbers in a subwindow" + say inlet "add an inlet to a pd" + say outlet "add an outlet to a pd" + say inlet~ "[say inlet] (for signal)" + say outlet~ "[say outlet] (for signal)" + + say_category "DATA TEMPLATES" + say struct "define a data structure" + say {drawcurve filledcurve} "draw a curve" + say {drawpolygon filledpolygon} "draw a polygon" + say plot "plot an array field" + say drawnumber "print a numeric value" + + say_category "ACCESSING DATA" + say pointer "point to an object belonging to a template" + say get "get numeric fields" + say set "change numeric fields" + say element "get an array element" + say getsize "get the size of an array" + say setsize "change the size of an array" + say append "add an element to a list" + say sublist "get a pointer into a list which is an element of another scalar" + say scalar "draw a scalar on parent" + + say_category "OBSOLETE" + say scope~ "(use tabwrite~ now)" + say namecanvas "" ;# what was this anyway? + say template "(use struct now)" +} + +# phase 4 (pdrc & ddrc) + +say section_audio "聲頻" + say -r "取樣率" + say -audioindev "輸入裝置" + say -audiooutdev "輸出裝置" + say -inchannels "輸入音軌" + say -outchannels "輸出音軌" + say -audiobuf "聲頻緩衝器大小(毫秒)" + say -blocksize "聲頻輸出/入區塊大小(樣框數目)" + say -sleepgrain "空閒至睡眠時間(毫秒)" + say -nodac "停用聲頻輸出" + say -noadc "停用聲頻輸入" + say audio_api_choice "聲頻介面" + say default "內定值" + say -alsa "使用ALSA" + say -jack "使用JACK" + say -mmio "使用MMIO(Windows內定值)" + say -portaudio "使用ASIO(透過Portaudio)" + say -oss "使用OSS" + say -32bit "允許32位元OSS(for RME Hammerfall)" + say {} "內定值" + +say section_midi "MIDI" + say -nomidiin "停用MIDI輸入" + say -nomidiout "停用MIDI輸出" + say -midiindev "Midi輸入裝置名單" + say -midioutdev "Midi輸出裝置名單" + +say section_externals "外加功能" + say -path "尋找路徑" + say -helppath "說明文件路徑" + say -lib "加載功能組" + +say section_gui "使用者介面" + say -nogui "suppress starting the GUI (caution)" + say -guicmd "substitute another GUI program (e.g., rsh)" + say -console "console scrollback lines (0 = disable console)" + say -look "buttonbar icons" + say -statusbar "enable statusbar" + say -font "specify default font size in points" + +say section_other "其它" + say -open "自動開啟檔案" + say -verbose "詳係回報" + say -d "除錯階級" + say -noloadbang "停用 \[loadbang\]" + say -send "啟動後傳送旨令" + say -listdev "程式開啟時列出聲頻及MIDI裝置名單" + say -realtime "使用即時配給 (須要根權)" + +say section_paths "路徑" + +# phase 4B: ddrc (keyword names not finalized!) + +say section_color "色彩" + say canvas_color "畫布" + say canvasbgedit "畫布背景 (編輯模式)" + say canvasbgrun "畫布背景 (執行模式)" + say object_color "物件" + say viewframe1 "物件外框" + say viewframe2 "物件外框" + say viewframe3 "物件外框" + say viewframe4 "物件反白" + say viewbg "物件背景" + say viewfg "物件前景" + say commentbg "註譯背景" + say commentfg "註譯前景" + say commentframe1 "註譯外框" + say commentframe2 "註譯背景" + say commentframe3 "註譯背景" + say viewselectframe "物件反白外框" + say wire_color "通路" + say wirefg "通路研色" + say wirefg2 "通路反白" + say wiredspfg "聲頻通路" + say futurewiredash "未接通路" + say others_color "其它" + say boxinletfg "輸入點研色" + say boxoutletfg "輸出點研色" + say selrectrect "多選框" +say keys "快速鍵" +say others "其它" +say hairstate "顯示十字器" +say hairsnap "十字器停留物件左上角" +say statusbar "顯示目前狀態" +say buttonbar "顯示按鈕排" +say menubar "顯示清單" +say scrollbar "自動拉霸顯示" +say wirearrow "通路箭頭" +say tooltip "提示" +say insert_object "嵌入物件" +say chain_object "自動連結新物件" +say clear_wires "清除通路" +say auto_wire "移除物件" +say subpatcherize "自動封裝" +say keynav "鍵盤導覽" +say key_nav_up "上移" +say key_nav_up_shift "加入選區" +say key_nav_down "下移" +say key_nav_down_shift "加入選區" +say key_nav_right "右移" +say key_nav_right_shift "加入選區" +say key_nav_left "左移" +say key_nav_left_shift "加入選區" +say key_nav_ioselect "選擇輸出/入點" + +# phase 5A + +say cannot "不能" +say cancel "取消" +say apply "使用" +say ok "好" +say popup_open "打開" +say popup_insert "嵌入物件" +say popup_properties "內容" +say popup_clear_wires "清除通路" +say popup_auto_wire "移除物件" +say popup_help "輔助" +say filter "過濾: " +say how_many_object_classes "%d of %d object classes" +say do_what_i_mean "照我意思做" +say save_changes? "確定存檔?" +say reset "重新設定" +say Courier "Courier (monospaced)" +say Helvetica "Helvetica (sansserif)" +say Times "Times (serif)" +say add "加入" +say up "上移" +say down "下移" +say remove "移除" +say lib_add "add the name you typed to the list" +say lib_up "swap order with previous library" +say lib_down "swap order with next library" +say lib_remove "remove library selected in the list" +say dir_add "add a folder using a file dialog" +say dir_up "swap order with previous folder" +say dir_down "swap order with next folder" +say dir_remove "remove folder selected in the list" +say client_class_tree "客戶端類別樹" +say clipboard_view "閱覽筆記板" +say command_history_view "閱覽旨令歷史記錄" +say event_history_view "閱覽事件歷史記錄" +say keyboard_view "顯示鍵盤" +say abort_server "強迫中止伺服器" +# during/after piksel: + +say auto_apply "自動更新" +say font_preview "預覽:" +say font_preview_2 "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n0123456789" +say font_style "形態:" +say font_bold "粗體" +say font_italic "斜體" +say font_family "字體名:" +say font_size "大小:" +say damn "可惡!" +say preset "預設" +say console "儲存旨令行數" +say language "語言" +say pointer_sense "滑鼠游標敏感度" +say clear_selection "全部反選" +say popup_remove_from_path "移除通路中物件" +say popup_delete_from_path "刪除通路中物件" +say popup_copy_id "考背愛低" + diff --git a/desiredata/src/locale/dansk.tcl b/desiredata/src/locale/dansk.tcl new file mode 100644 index 00000000..4c789f15 --- /dev/null +++ b/desiredata/src/locale/dansk.tcl @@ -0,0 +1,564 @@ +#!/usr/bin/env tclsh +# $Id: dansk.tcl,v 1.1.2.2 2007-08-18 18:01:10 matju Exp $ +# Danish translations for PureData +# by Steffen Leve Poulsen + +### Menus + +say file "Fil" + say new_file "Ny fil" + say open_file "bn fil..." + say server_prefs "Server Preferenser..." + say client_prefs "Klient Preferenser..." + say send_message "Send besked..." + say paths "Stier..." + say close "Luk" + say save "Gem" + say save_as "Gem som..." + say print "Udskriv..." + say abort_server "Stop server" + say quit "Afslut" + + say canvasnew_file "Ny Fil" + say canvasopen_file "bn Fil..." + say canvassave "Gem" + say canvassave_as "Gem som..." + say clientpdrc_editor ".pdrc reidgering" + say clientddrc_editor ".ddrc redigering" + say canvasclose "Luk" + say canvasquit "Afslut" + +say edit "Rediger" + say undo "Fortryd" + say redo "Genskab" + say cut "Klip" + say copy "Kopier" + say paste "St ind" + say duplicate "Dubler" + say select_all "Vlg alt" + say clear_selection "Afvlg" + say text_editor "Tekstredigering..." + say font "Font" + say tidy_up "Ordn" + say edit_mode "Rediger" + say editmodeswitch "Rediger/Kr tilstand" + say subpatcherize "Gr til underlap" + + say canvascut "Klip" + say canvascopy "Kopier" + say canvasundo "Fortryd" + say canvasredo "Genskab" + say canvaspaste "St ind" + say canvasduplicate "Dubler" + say canvasselect_all "Vlg alt" + say canvaseditmodeswitch "Rediger/Kr tilstand" + +say view "Se" + say reload "bn igen" + say redraw "Gentegn" + + say canvasreload "bn igen" + say canvasredraw "Gentegn" + +say find "Sg" + say find_again "Sg igen" + say find_last_error "Find sidste fejl" + say string "Find streng" +say canvasfind "Sg" + say canvasfind_again "Sg igen" + +# contents of Put menu is Phase 5C +say put "Indst" + say Object "Objekt" + say Message "Besked" + say Number "Tal" + say Symbol "Symbol" + say Comment "Kommentar" + say Graph "Graf" + say Array "Tabel" + +say media "Medier" + say audio_on "Start lyd" + say audio_off "Stop lyd" + say test_audio_and_midi "Test lyd og MIDI" + say load_meter "Belastning" + + say canvasaudio_on "Start lyd" + say canvasaudio_off "Stop lyd" + say clienttest_audio_and_midi "Test lyd og MIDI" + say canvasload_meter "Belastning" + +say window "Vindue" + +say help "Hjlp" + say about "Om..." + say documentation "Dokumentation..." + say class_browser "Se klasser..." + + say canvasabout "Om..." + +say properties "Egenskaber" +say open "bn" + +### for key binding editor +say general "Generelt" +say audio_settings "Lyd indstillinger" +say midi_settings "MIDI indstillinger" +say latency_meter "Forsinkelse" +say Pdwindow "Pd vindue" + +say canvaspdwindow "Pd vindue" +say canvaslatency_meter "Forsinkelse" +say clientaudio_settings "Lyd indstillinger" +say clientmidi_settings "MIDI indstillinger" + +### for Properties Dialog (phase 5B) +say_category IEM +say w "bredde(px)" +say h "hjde(px)" +say hold "vis (ms)" +say break "afbryd (ms)" +say min "minimum vrdi" +say max "maximum vrdi" +say is_log "modus" +say linear "liner" +say logarithmic "logaritmisk" +say isa "startvrdi" +say n "antal valg" +say steady "reaktion" +say steady_no "Hop ved klik" +say steady_yes "Bliv ved klik" +say snd "sende-navn" +say rcv "modtage-navn" +say lab "etiket" +say ldx "etiket x afst" +say ldy "etiket y afst" +say fstyle "Udseende" +say fs "font strrelse" +say bcol "baggrundsfarve" +say fcol "forgrundsfarve" +say lcol "farve etiket" +say yes "ja" +say no "nej" +say courier "courier (typewriter)" +say helvetica "helvetica (sansserif)" +say times "times (serif)" +say coords "vis p forldre" + +say_category GAtomProperties +say width "bredde" +say lo "nedre grnse" +say hi "vre grnse" +say label "etiket" +say wherelabel "vis label" +say symto "sende-navn" +say symfrom "modtage-navn" + +say_category GraphProperties +say x1 "x fra" +say x2 "x til" +say xpix "bredde" +say y2 "y fra" +say y1 "y til" +say ypix "hjde" + +say_category CanvasProperties +#say xscale "X enheder/px" +#say yscale "Y enheder/px" +say gop "vis p forlder" +say xmargin "x margen" +say ymargin "y margen" +say height "hjde" +say_category ArrayProperties +say name "navn" +say n "strrelse" +say xfrom "x fra" +say xto "x til" +say yfrom "y fra" +say yto "y til" + + +say_category MainWindow +say in "ind" +say out "ud" +say audio "Lyd" +say meters "Meter" +say io_errors "IO fejl" +say tcl_console "Tcl Klient" +say pd_console "Pd Server" + +### phase 2 + +say_category Other +say_namespace summary { + say_category IEMGUI + say bng "Bang Boks" + say tgl "Toggle Boks" + say nbx "Tal Boks (IEM)" + say hsl "Fader (vandret)" + say vsl "Fader (lodret)" + say hradio "Valgboks (vandret)" + say vradio "Valgbox (lodret)" + say cnv "Lrred (IEM)" + say dropper "Trk og slip box" + say vu "Vumeter" + + say_category GLUE + say bang "send et bang" + say float "gem og genkald decimaltal" + say symbol "gem og genkald symbol" + say int "gem og genkald heltal" + say send "send besked til objekt med navn" + say receive "modtag sendte beskeder" + say select "vlg symbol eller tal" + say route "rute efter frste element" + say pack "pak elementer" + say unpack "pak elementer ud" + say trigger "rkkeflge og omdannelse" + say spigot "afbryder" + say moses "del talstrm" + say until "gentagne bangs" + say print "skriv til promt" + say makefilename "formater navn med variabel" + say change "fjern gentagelser" + say swap "ombyt to tal" + say value "global variabel" + + say_category TIME + say delay "forsink bang" + say metro "metronom" + say line "skridtvis liner interpolation" + say timer "ml tid mellem beskeder" + say cputime "ml CPU tid" + say realtime "ml reel tid" + say pipe "dynamisk voksende forsinkelse af tal" + + say_category MATH + say + "adder" + say - "subtraher" + say * "multiplicer" + say {/ div} "divider" + say {% mod} "heltal modulus" + say pow "eksponent" + say == "ens?" + say != "forskellig?" + say > "strrer end?" + say < "mindre end?" + say >= "strrer eller lig med?" + say <= "mindre eller lig med?" + say & "bitmssig (og)" + say | "bitmssig (eller)" + say && "boelsk (og)" + say || "boelsk (eller)" + say mtof "MIDI til Hertz" + say ftom "Hertz til MIDI" + say powtodb "W til dB" + say dbtopow "dB til W" + say rmstodb "Volt til dB" + say dbtorms "dB til Volt" + say {sin cos tan atan atan2 sqrt} "trigonometri" + say log "Euler logaritme" + say exp "Euler exponentiale" + say abs "absolutte vrdi" + say random "tilfldigt heltal" + say max "strst af to tal" + say min "mindst af to tal" + say clip "klip tal hvis udenfor grnser" + + say_category MIDI + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} "MIDI ind" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} "MIDI ud" + say makenote "formater miditone" + say stripnote "fjern \"note off\" beskeder" + + say_category TABLES + say tabread "ls tal fra tabel" + say tabread4 "ls tal fra tabel, med kubisk interpolation" + say tabwrite "skriv tal ind i tabel" + say soundfiler "ls og skriv tabeller" + + say_category MISC + say loadbang "bang ved start" + say serial "serielport (kun NT)" + say netsend "send bekeder via netvrk" + say netreceive "modtag beskeder fra netvrk" + say qlist "besked sequencer" + say textfile "fil til besked konverter" + say openpanel "\"bn\" dialog" + say savepanel "\"Gem som\" dialog" + say bag "talst" + say poly "polyfonisk stemmekontrol" + say {key keyup} "tastaturvrdier" + say keyname "tastnavn" + + say_category "Signal matematik" + foreach word {+ - * /} {say $word~ "[say $word] (for signals)"} + say max~ "strst af to signaler" + say min~ "mindst af to signaler" + say clip~ "begrns signal" + say q8_rsqrt~ "billig omvendt kvadratrod (8 bit!)" + say q8_sqrt~ "billig kvadratrod (8 bit!)" + say wrap~ "decimaldel af signal" + say fft~ "compleks diskret Fourier transformation" + say ifft~ "omvendt compleks diskret Fourier transformation" + say rfft~ "reel diskret Fourier transformation" + say rifft~ "omvendt reel diskret Fourier transformation" + say framp~ "rampe per blok" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (for signals)" + } +} + +### phase 3 + +say_namespace summary { + say_category "LYD LIM" + say dac~ "lyd ud" + say adc~ "lyd ind" + say sig~ "konverter tal til signal" + say line~ "signal rampe" + say vline~ "deluxe line~" + say threshold~ "find signalgrnse" + say snapshot~ "konverter signal til tal" + say vsnapshot~ "deluxe snapshot~" + say bang~ "bang efter hver blok" + say samplerate~ "hent samplefrekvens" + say send~ "send et signal til mange modtagere" + say receive~ "modtag fra send~" + say throw~ "mange signalafsendere til en modtager" + say catch~ "modtag fra throw~" + say block~ "st blokstrrelse og overlap" + say switch~ "tnd/sluk DSP lokalt" + say readsf~ "afspil lydfil fra hrdskive" + say writesf~ "optag lyd p hrdskive" + + say_category "signal oscillatorer og tabeller" + say phasor~ "savtak-generator" + say {cos~ osc~} "cubisk interpoleret cosinus tabelopslag" + say tabwrite~ "skriv signal til tabel" + say tabplay~ "afspil tabel (uden transponering)" + say tabread~ "ikke interpoleret tabelafspilning" + say tabread4~ "kubisk interpoleret tabelafspilning" + say tabosc4~ "tabel oscillator" + say tabsend~ "skriv en blok kontinuerligt til tabel" + say tabreceive~ "ls en blok kontinuerligt fra tabel" + + say_category "Signal filtre" + say vcf~ "volt controlleret filter" + say noise~ "hvid stj" + say env~ "flg signal" + say hip~ "hjpas filter" + say lop~ "lavpas filter" + say bp~ "bndpas filter" + say biquad~ "2-pol-2-nul-filter" + say samphold~ "sample og frys dims" + say print~ "skriv en eller flere \"blocks\" ud" + say rpole~ "en pol reel filter" + say rzero~ "et nul reel filter" + say rzero_rev~ "[say rzero~] (baglns)" + say cpole~ "[say rpole~] (kompleks)" + say czero~ "[say rzero~] (kompleks)" + say czero_rev~ "[say rzero_rev~] (kompleks)" + + say_category "AUDIO DELAY" + say delwrite~ "forsink" + say delread~ "ls efter forsinkelse" + say vd~ "ls efter varieret forsinkelse" + + say_category "SUBWINDOWS" + say pd "definer undervindue" + say table "tabel i undervindue" + say inlet "tilfj indgang til et vindue" + say outlet "tilfj udgang fra et vindue" + say inlet~ "[say inlet] (signal)" + say outlet~ "[say outlet] (signal)" + + say_category "DATA skabeloner" + say struct "definer en data struktur" + say {drawcurve filledcurve} "tegn en kurve" + say {drawpolygon filledpolygon} "tegn en polygon" + say plot "tegn en tabel" + say drawnumber "skriv et tal" + + say_category "ACCESSING DATA" + say pointer "peg p et objekt der hrer til en skabelon" + say get "hent numeriske felt" + say set "skift numerisk felt" + say element "hent element fra tabel" + say getsize "hent strrelse p tabel" + say setsize "st lngde p tabel" + say append "tilfj element til liste" + say sublist "peger p start af undertabel" + say scalar "tegn skalar p forlder" + + say_category "FOORLDET" + say scope~ "(brug tabwrite~ nu)" + say namecanvas "" ;# what was this anyway? + say template "(brug struct)" +} + +# phase 4 (pdrc & ddrc) + +say section_audio "Lyd" + say -r "sample rate" + say -audioindev "LYD-ind enheder" + say -audiooutdev "LYD-ud enheder" + say -inchannels "antal lydkanaler ind (per enhed, som \"2\" or \"16,8\")" + say -outchannels "antal lydkanaler ud (samme)" + say -audiobuf "Lydbuffer i millisekunder" + say -blocksize "specificer lyd I/O blok i samples" + say -sleepgrain "specificer pause i millisekunder ved ledighed" + say -nodac "ingen lyd ud" + say -noadc "ingen lyd ind" + say audio_api_choice "Audio API" + say default "forudindstillet" + say -alsa "brug ALSA lyd API" + say -jack "brug JACK lyd API" + say -mmio "brug MMIO lyd API (forudindstillet til Windows)" + say -portaudio "brug ASIO lyd (via Portaudio)" + say -oss "brug OSS lyd API" + say -32bit "tillad 32 bit OSS lyd (til RME Hammerfall)" + say {} "forudindstillet" + +say section_midi "MIDI" + say -nomidiin "ingen MIDI ind" + say -nomidiout "ingen MIDI ud" + say -midiindev "midi ind enhedsliste; e.g., \"1,3\" frste og tredje" + say -midioutdev "midi ud enhedsliste (samme format)" + +say section_externals "Eksterne biblioteker" + say -path "fil sgesti" + say -helppath "hjlpefil sgesti" + say -lib "bn biblioteker" + +say section_gui "Gooey" + say -nogui "ingen grafisk brugerflade (pas p)" + say -guicmd "brug anden grafisk brugerflade (e.g., rsh)" + say -look "knap ikoner" + say -font "st forudindstillet fontstrrelse i points" + +say section_other "Andet" + say -open "bn fil(er) ved start" + say -verbose "ekstra beskeder ved opstart og filsgning" + say -d "fejlsgning" + say -noloadbang "ingen \[loadbang\]" + say -send "send besked ved start (efter alt er bent)" + say -listdev "list lyd og MIDI enheder ved start" + say -realtime "brug real-time priority (behver root privilege)" + +say section_paths "Stier" + +# phase 4B: ddrc (keyword names not finalized!) +say console "spol antal linier tilbage i konsollen (0 = skriv ikke til konsol)" +say lang "Sprog" +say pointer_sense "Musens flsomhed" +say section_color "farver" + say canvas_color "lrred" + say canvasbgedit "farve p lrred ved redigering" + say canvasbgrun "farve p lrred ved Kr" + say object_color "objekt" + say viewframe1 "farve p objektboks" + say viewframe2 "farve p objektboks" + say viewframe3 "farve p objektboks" + say viewframe4 "farve p valgt objektboks" + say viewbg "baggrundsfarve p objektboks" + say viewfg "forgrundsfarve p objektboks" + say commentbg "baggrundsfarve p kommentar" + say commentfg "forgrundsfarve p kommentar" + say commentframe1 "ramme p kommentar" + say commentframe2 "ramme p kommentar" + say commentframe3 "ramme p kommentar" + say viewselectframe "valgt boks" + say wire_color "trd" + say wirefg "trd farve" + say wirefg2 "valgt trd" + say wiredspfg "signaltrd" + say futurewiredash "ny (stiplet) trd" + say others_color "andet" + say boxinletfg "farve p indgang" + say boxoutletfg "farve p udgang" + say selrectrect "valgt omrde" +say keys "taster" +say others "andet" +say hairstate "aktiver kors" +say hairsnap "snap kors til objekt" +say statusbar "Aktiver statusvisning" +say buttonbar "Aktiver objekt knapper" +say menubar "Aktiver menubar" +say scrollbar "Aktiver autospoleknap" +say wirearrow "trd pil" +say tooltip "Vrktjstip" +say insert_object "Indst objekt" +say chain_object "Forbind objekt(er)" +say clear_wires "Fjern trde" +say auto_wire "Fjern objekt" +say subpatcherize "Subpatcherize" +say keynav "tastatur navigation" +say key_nav_up "flyt op" +say key_nav_up_shift "plus select" +say key_nav_down "flyt ned" +say key_nav_down_shift "plus select" +say key_nav_right "flyt til hjre" +say key_nav_right_shift "plus select" +say key_nav_left "flyt til venstre" +say key_nav_left_shift "plus select" +say key_nav_ioselect "vlg ind/udgang" +# phase 5A + +say cannot "Kan ikke" +say cancel "fortryd" +say apply "Gr det" +say ok "OK" +say popup_open "bn" +say popup_insert "St ind" +say popup_properties "Egenskaber" +say popup_clear_wires "Slet trde" +say popup_remove_from_path "Fjern objekt fra sti" +say popup_delete_from_path "Slet objekt fra sti" +say popup_help "Hjlp" +say filter "Filter: " +say how_many_object_classes "%d af %d objektklasser" +say do_what_i_mean "Gr hvad jeg mener" +say ask_cool "Det ville have vret fedt at kunne gre, ikk?" +say save_changes? "Gem ndringer?" +say reset "Nulstil" +say Courier "Courier (monospaced)" +say Helvetica "Helvetica (sansserif)" +say Times "Times (serif)" +say add "Tilfj" +say up "Op" +say down "Ned" +say remove "Fjern" +say lib_add "tilfj bibliotek til listen" +say lib_up "byt med forrige bibliotek" +say lib_down "byt med nste bibliotek" +say lib_remove "fjern valgte fra listen" +say dir_add "tilfj mappe vha. dialog" +say dir_up "byt med forrige mappe" +say dir_down "byt med nste mappe" +say dir_remove "fjern valgt mappe fra listen" +say client_class_tree "Klient klasse tr" +say clipboard_view "Se klipbord" +say command_history_view "se kommando historie" +say event_history_view "se handlings historie" + +# during/after piksel: + +say auto_apply "Automatisk" +say font_preview "Se:" +say font_preview_2 "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n0123456789" +say font_style "Stil:" +say font_bold "Fremhvet" +say font_italic "Skr" +say font_family "Navn:" +say font_size "Strrelse:" +say damn "Pokkers!" +say console_clear "Slet indholdet i konsollen" +say horizontal "Vandret" +say vertical "Lodret" +say language "Sprog" + +# 2007: + +say no_matches "(ingen svarer til)" +say preset "forudsat" diff --git a/desiredata/src/locale/deutsch.tcl b/desiredata/src/locale/deutsch.tcl new file mode 100644 index 00000000..439e672b --- /dev/null +++ b/desiredata/src/locale/deutsch.tcl @@ -0,0 +1,280 @@ +#!/usr/bin/env tclsh +# German translations for PureData +# $Id: deutsch.tcl,v 1.1.2.13 2006-11-25 01:45:05 matju Exp $ +# by Max Neupert, Georg Holzmann, Thomas Grill + +say file "Datei" + say new_file "Neu" + say open_file "ffnen..." + say pdrc_editor ".pdrc Editor" + say send_message "Sende Nachricht..." ;# georg&foo say "Message" + say paths "Pfade..." + say close "Schlieen" + say save "Speichern" + say save_as "Speichern Unter..." + say print "Drucken..." + say quit "Beenden" + +say edit "Bearbeiten" + say undo "Rckgngig" + say redo "Wiederherstellen" + say cut "Ausschneiden" + say copy "Kopieren" + say paste "Einfgen" + say duplicate "Duplizieren" + say select_all "Alles auswhlen" + say text_editor "Texteditor..." + say tidy_up "Aufrumen" + say edit_mode "Editiermodus" ;# georg says "Edit Modus" + +say view "Ansicht" + say reload "Neu Laden" + say redraw "Neu Zeichnen" + +say find "Suchen" + say find_again "Weitersuchen" ;# georg: "Suche Nochmal" + say find_last_error "Finde letzten Fehler" ;# georg: "Finde Letzten Error" + +say put "Erstelle" + +say media "Media" + say audio_on "Audio AN" + say audio_off "Audio AUS" + +say window "Fenster" + +say help "Hilfe" + say about "ber..." + say pure_documentation "Pure Documentation..." + say class_browser "Class Browser..." + + + +### Main Window + +say in "Eingang" +say out "Ausgang" +say audio "Audio" +say meters "Pegel" +say io_errors "IO Fehler" + + +say_namespace summary { + say_category "IEMGUI" + say bng "Bang-Feld" + say tgl "Schalter" + say nbx "Zahlenfeld (IEM)" + say hsl "Schieberegler (Horizontal)" + say vsl "Schieberegler (Vertical)" + say hradio "Radioknopf (Horizontal)" + say vradio "Radioknopf (Vertical)" + say cnv "Hintergrund (IEM)" + say vu "VU-Pegel" + say dropper "\"Drag-and-Drop\" Box" + + say_category "GLUE" + say bang "Bang-Nachricht ausgeben" + say float "Zahl speichern und abrufen" + say symbol "Symbol speichern und abrufen" + say int "Ganzzahl speichern und abrufen" + say send "Nachricht an benanntes Objekt schicken" + say receive "Nachrichten empfangen" + say select "auf bereinstimmende Symbole oder Zahlen prfen" + say route "Nachrichten gem ihrem ersten Element umleiten" + say pack "Nachrichten packen" + say unpack "gepackte Nachrichten auflsen" + say trigger "Nachrichten auslsen und umwandeln" + say spigot "eine steuerbare Nachrichtenverbindung" + say moses "einen Zahlenstrom aufteilen" + say until "Schleife" + say print "Ausgabe in Konsole umleiten" + say makefilename "Symbol mit Variable formatieren" + say change "entfernt wiederholte Nachrichten" + say swap "zwei Zahlen austauschen" + say value "gemeinsamer Zahlenspeicher" + + say_category "TIME" + say delay "verzgert Nachrichten" + say metro "sendet Nachrichten im Takt" + say line "interpoliert zwischen zwei Zahlen" + say timer "misst Zeitabstnde" + say cputime "Prozessorzeit-Messung" + say realtime "Echtzeit-Messung" + say pipe "dynamisch verlngerbare Verzgerungsstrecke" + + say_category "MATH" + say + "addieren" + say - "subtrahieren" + say * "multiplizieren" + say {/ div} "dividieren" + say {% mod} "Divisionsrest" + say pow "potenzieren" + say == "gleich?" + say != "ungleich?" + say > "grer als?" + say < "kleiner als?" + say >= "grer gleich?" + say <= "kleiner gleich?" + say & "bitweise UND-Funktion" + say | "bitweise ODER-Funktion" + say && "logische UND-Funktion " + say || "logische ODER-Funktion" + say mtof "MIDI zu Hertz Umrechnung" + say ftom "Hertz zu MIDI Umrechnung" + say powtodb "Watt zu dB Umrechnung" + say dbtopow "dB zu Watt Umrechnung" + say rmstodb "Volt zu dB Umrechnung" + say dbtorms "dB zu Volt Umrechnung" + say {sin cos tan atan atan2 sqrt} "Trigonometrie" + say log "natrlicher Logarithmus" + say exp "Exponentialfunktion" + say abs "absoluter Wert" + say random "Zufallszahl" + say max "grere zweier Zahlen" + say min "kleinere zweier Zahlen" + say clip "Begrenzung einer Zahlenreihe" + + say_category "MIDI" + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} \ +"MIDI Eingang" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} \ +"MIDI Ausgang" + say makenote \ + "eine verzgerte \"note off\" Nachricht nach einer \"noteon\"-Nachricht einplanen" + say stripnote "entferne \"note off\" Nachrichten" + + say_category "TABLES" + say tabread "liest Zahl aus einer Tabelle" + say tabread4 "liest Zahl aus einer Tabelle, mit 4-Punkt-Interpolation" + say tabwrite "schreibt Zahl in eine Tabelle" + say soundfiler "schreibt und liest Samples in/aus einer Tabelle" + + say_category "MISC" + say loadbang "Bang beim ffnen der Datei" + say serial "Serielle Schnittstelle, nur NT" + say netsend "sendet Nachrichten ber das Netzwerk" + say netreceive "empfngt Nachrichten ber das Netzwerk" + say qlist "Nachrichten-Sequenzer" + say textfile "Datei zu Nachricht bersetzung" + say openpanel "\"ffnen...\" Dialog" + say savepanel "\"Speichern unter...\" Dialog" + say bag "Zahlenpaar" + say poly "Polyphone Stimmenbelegung" + say {key keyup} "Numerische Tastenwerte der Tastatur" + say keyname "Symbolischer Tastenname" + + say_category "AUDIO MATH" + foreach word {+ - * /} {say $word~ "[say $word] (fr Audiosignale)"} + say max~ "Maximum eines Audiosignals" + say min~ "Minimum eines Audiosignals" + say clip~ "begrenzt ein Audiosignal von unten und oben" + say q8_rsqrt~ "schnelle reziproke Quadratwurzel (Achtung! - 8 Bit)" + say q8_sqrt~ "schnelle Quadratwurzel (Achtung! - 8 Bit)" + say wrap~ "wraparound (fractional part, sort of)" + say fft~ "komplexe Fouriertransformation vorwrts" + say ifft~ "komplexe inverse Fast-Fouriertransformation" + say rfft~ "reelle Fouriertransformation vorwrts" + say rifft~ "reelle inverse Fast-Fouriertransformation" + say framp~ "erzeugt eine Rampe fr jeden Block" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (fr Audiosignale)" + } +} + +### phase 3 + +say_namespace summary { + say_category "AUDIO GLUE" + say dac~ "Audio Ausgabe" + say adc~ "Audio Eingabe" + say sig~ "wandelt Zahlen in Audiosignal um" + say line~ "generiert Audio Rampen" + say vline~ "deluxe line~" + say threshold~ "erkennt Schwellenwerte des Audiosignals" + say snapshot~ "tastet ein Audiosignal ab (konvertiert es zu Zahlen)" + say vsnapshot~ "deluxe snapshot~" + say bang~ "sendet Bang nach jedem DSP-Block" + say samplerate~ "gibt die Abtastrate aus" + say send~ "nonlocal signal connection with fanout" + say receive~ "empfngt Audiosignal von send~" + say throw~ "fgt Audiosignal zu einem sich summierenden Bus zu" + say catch~ "definiert und liest einen Bus" + say block~ "legt Block-Gre und berlappung fest" + say switch~ "schaltet DSP-Berechnung an/aus" + say readsf~ "spielt Audiodateien von Festplatte ab" + say writesf~ "nimmt Audio auf Festplatte auf" + + say_category "AUDIO-OSZILLATOREN AND TABELLEN" + say phasor~ "Sgezahn-Oszillator" + say {cos~ osc~} "Cosinus-Oszillator" + say tabwrite~ "in eine Tabelle schreiben" + say tabplay~ "aus einer Tabelle abspielen (nicht-transponierend)" + say tabread~ "Tabelle auslesen (ohne Interpolation)" + say tabread4~ "Tabelle auslesen (mit Vier-Punkt Interpolation)" + say tabosc4~ "wavetable Oszillator" + say tabsend~ "einen Block kontinuierlich in eine Tabelle schreiben" + say tabreceive~ "einen Block kontinuierlich aus einer Tabelle lesen" + + say_category "AUDIO FILTER" + say vcf~ "Spannungsgesteuerter Filter" + say noise~ "generiert weies Rauschen" + say env~ "Hllkurven-Abtaster" + say hip~ "Hochpassfilter" + say lop~ "Tiefpassfilter" + say bp~ "Bandpassfilter" + say biquad~ "grober Filter" + say samphold~ "sample and hold unit" + say print~ "print out one or more \"blocks\"" + say rpole~ "raw real-valued one-pole filter" + say rzero~ "raw real-valued one-zero filter" + say rzero_rev~ "[say rzero~] (time-reversed)" + say cpole~ "[say rpole~] (complex-valued)" + say czero~ "[say rzero~] (complex-valued)" + say czero_rev~ "[say rzero_rev~] (complex-valued)" + + say_category "AUDIO VERZGERUNG" + say delwrite~ "in eine Verzgerungsstrecke schreiben" + say delread~ "von einer Verzgerungsstrecke lesen" + say vd~ "von einer Verzgerungsstrecke in variabler Verzgerungszeit lesen" + + say_category "UNTERFENSTER" + say pd "definiere ein Unterfenster" + say table "Zahlentabelle in einem Unterfenster" + say inlet "Eingang hinzufgen" + say outlet "Ausgang hinzufgen" + say inlet~ "[say inlet] (fr Audiosignale)" + say outlet~ "[say outlet] (fr Audiosignale)" + + say_category "DATA TEMPLATES" + say struct "define a data structure" + say {drawcurve filledcurve} "zeichnet eine Kurve" + say {drawpolygon filledpolygon} "zeichnet ein Polygon" + say plot "plot an array field" + say drawnumber "einen Zahlenwert ausgeben" + + say_category "DATENZUGRIFF" + say pointer "point to an object belonging to a template" + say get "get numeric fields" + say set "legt Wert in Zahlenfeldern fest" + say element "get an array element" + say getsize "Gre eines Arrays feststellen" + say setsize "Gre eines Arrays bestimmen" + say append "Element an eine Liste anfgen" + say sublist "get a pointer into a list which is an element of another \ +scalar" + say scalar "draw a scalar on parent" + + say_category "BERHOLT" + say scope~ "(benutze tabwrite~ anstatt)" + say namecanvas "" ;# what was this anyway? + say template "(benutze struct anstatt)" +} + +say cannot "kann nicht" + +say filter "Filter" ;# sorry +say how_many_object_classes "%d/%d" + +say save_changes? "nderungen speichern?" + + diff --git a/desiredata/src/locale/english.tcl b/desiredata/src/locale/english.tcl new file mode 100644 index 00000000..f4de0445 --- /dev/null +++ b/desiredata/src/locale/english.tcl @@ -0,0 +1,573 @@ +#!/usr/bin/env tclsh +# English translations for PureData +# $Id: english.tcl,v 1.1.2.34.2.22 2007-09-04 11:24:02 chunlee Exp $ + +### Menus + +say file "File" + say new_file "New File" + say open_file "Open File..." + say server_prefs "Server Preferences..." + say client_prefs "Client Preferences..." + say send_message "Send Message..." + say paths "Paths..." + say close "Close" + say save "Save" + say save_as "Save As..." + say print "Print..." + say abort_server "Abort Server" + say quit "Quit" + + say canvasnew_file "New File" + say canvasopen_file "Open File..." + say canvassave "Save" + say canvassave_as "Save As..." + say clientpdrc_editor ".pdrc Editor" + say clientddrc_editor ".ddrc Editor" + say canvasclose "Close" + say canvasquit "Quit" + +say edit "Edit" + say undo "Undo" + say redo "Redo" + say cut "Cut" + say copy "Copy" + say paste "Paste" + say duplicate "Duplicate" + say select_all "Select All" + say clear_selection "Deselect selection" + say text_editor "Text Editor..." + say font "Font" + say tidy_up "Tidy Up" + say edit_mode "Edit Mode" + say editmodeswitch "Edit/Run mode" + say subpatcherize "Subpatcherize" + + say canvascut "Cut" + say canvascopy "Copy" + say canvasundo "Undo" + say canvasredo "Redo" + say canvaspaste "Paste" + say canvasduplicate "Duplicate" + say canvasselect_all "Select All" + say canvaseditmodeswitch "Edit/Run mode" + +say view "View" + say reload "Reload" + say redraw "Redraw" + + say canvasreload "Reload" + say canvasredraw "Redraw" + +say find "Find" + say find_again "Find Again" + say find_last_error "Find Last Error" + say string "Find string" +say canvasfind "Find" + say canvasfind_again "Find Again" + +# contents of Put menu is Phase 5C +say put "Put" + say Object "Object" + say Message "Message" + say Number "Number" + say Symbol "Symbol" + say Comment "Comment" + say Graph "Graph" + say Array "Array" + +say media "Media" + say audio_on "Audio ON" + say audio_off "Audio OFF" + say test_audio_and_midi "Test Audio and MIDI" + say load_meter "Load Meter" + + say canvasaudio_on "Audio ON" + say canvasaudio_off "Audio OFF" + say clienttest_audio_and_midi "Test Audio and MIDI" + say canvasload_meter "Load Meter" + +say window "Window" + +say help "Help" + say about "About..." + say documentation "Documentation..." + say class_browser "Class Browser..." + + say canvasabout "About..." + +say properties "Properties" +say open "Open" + +### for key binding editor +say general "General" +say audio_settings "Audio Settings" +say midi_settings "Midi Settings" +say latency_meter "Latency Meter" +say Pdwindow "Pd window" + +say canvaspdwindow "Pd window" +say canvaslatency_meter "Latency Meter" +say clientaudio_settings "Audio Settings" +say clientmidi_settings "Midi Settings" + +### for Properties Dialog (phase 5B) +say_category IEM +say w "width(px)" +say h "height(px)" +say hold "hold time(ms)" +say break "break time(ms)" +say min "minimum value" +say max "maximum value" +say is_log "mode" +say linear "linear" +say logarithmic "logarithmic" +say isa "init" +say n "number of choices" +say steady "steadiness" +say steady_no "jump on click" +say steady_yes "steady on click" +say snd "send-symbol" +say rcv "receive-symbol" +say lab "label" +say ldx "label x offset" +say ldy "label y offset" +say fstyle "Typeface" +say fs "font size" +say bcol "background color" +say fcol "forground color" +say lcol "label color" +say yes "yes" +say no "no" +say courier "courier (typewriter)" +say helvetica "helvetica (sansserif)" +say times "times (serif)" +say coords "graph on parent" + +say_category GAtomProperties +say width "width" +say lo "lower limit" +say hi "upper limit" +say label "label" +say wherelabel "show label on" +say symto "send symbol" +say symfrom "receive symbol" + +say_category GraphProperties +say x1 "x from" +say x2 "x to" +say xpix "screen width" +say y2 "y from" +say y1 "y to" +say ypix "screen height" + +say_category CanvasProperties +#say xscale "X units/px" +#say yscale "Y units/px" +say gop "graph on parent" +say xmargin "xmargin" +say ymargin "ymargin" +say height "height" +say_category ArrayProperties +say name "name" +say n "size" +say xfrom "x range from" +say xto "x range to" +say yfrom "y range from" +say yto "y range to" + + +say_category MainWindow +say in "in" +say out "out" +say audio "Audio" +say meters "Meters" +say io_errors "IO Errors" +say tcl_console "Tcl Client" +say pd_console "Pd Server" + +### phase 2 + +say_category Other +say_namespace summary { + say_category IEMGUI + say bng "Bang Box" + say tgl "Toggle Box" + say nbx "Number Box (IEM)" + say hsl "Slider (Horizontal)" + say vsl "Slider (Vertical)" + say hradio "Choice Box (Horizontal)" + say vradio "Choice Box (Vertical)" + say cnv "Canvas (IEM)" + say dropper "Drag-and-Drop Box" + say vu "Vumeter" + + say_category GLUE + say bang "output a bang message" + say float "store and recall a number" + say symbol "store and recall a symbol" + say int "store and recall an integer" + say send "send a message to a named object" + say receive "catch sent messages" + say select "test for matching numbers or symbols" + say route "route messages according to first element" + say pack "make compound messages" + say unpack "get elements of compound messages" + say trigger "sequence and convert messagess" + say spigot "interruptible message connection" + say moses "part a numeric stream" + say until "looping mechanism" + say print "print out messages" + say makefilename "format a symbol with a variable field" + say change "remove repeated numbers from a stream" + say swap "swap two numbers" + say value "shared numeric value" + + say_category TIME + say delay "send a message after a time delay" + say metro "send a message periodically" + say line "send a series of linearly stepped numbers" + say timer "measure time intervals" + say cputime "measure CPU time" + say realtime "measure real time" + say pipe "dynamically growable delay line for numbers" + + say_category MATH + say + "add" + say - "substract" + say * "multiply" + say {/ div} "divide" + say {% mod} "division remainder" + say pow "exponentiate" + say == "equal?" + say != "not equal?" + say > "more than?" + say < "less than?" + say >= "not less than?" + say <= "not more than?" + say & "bitwise conjunction (and)" + say | "bitwise disjunction (or)" + say && "logical conjunction (and)" + say || "logical disjunction (or)" + say mtof "MIDI to Hertz" + say ftom "Hertz to MIDI" + say powtodb "Watts to dB" + say dbtopow "dB to Watts" + say rmstodb "Volts to dB" + say dbtorms "dB to Volts" + say {sin cos tan atan atan2 sqrt} "trigonometry" + say log "Euler logarithm" + say exp "Euler exponential" + say abs "absolute value" + say random "random" + say max "greater of two numbers" + say min "lesser of two numbers" + say clip "force a number into a range" + + say_category MIDI + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} "MIDI input" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} "MIDI output" + say makenote "schedule a delayed \"note off\" message corresponding to a note-on" + say stripnote "strip \"note off\" messages" + + say_category TABLES + say tabread "read a number from a table" + say tabread4 "read a number from a table, with 4 point interpolation" + say tabwrite "write a number to a table" + say soundfiler "read and write tables to soundfiles" + + say_category MISC + say loadbang "bang on load" + say serial "serial device control for NT only" + say netsend "send messages over the internet" + say netreceive "receive them" + say qlist "message sequencer" + say textfile "file to message converter" + say openpanel "\"Open\" dialog" + say savepanel "\"Save as\" dialog" + say bag "set of numbers" + say poly "polyphonic voice allocation" + say {key keyup} "numeric key values from keyboard" + say keyname "symbolic key name" + + say_category "AUDIO MATH" + foreach word {+ - * /} {say $word~ "[say $word] (for signals)"} + say max~ "supremum of signals" + say min~ "infimum of signals" + say clip~ "constrict signal to lie between two bounds" + say q8_rsqrt~ "cheap reciprocal square root (beware -- 8 bits!)" + say q8_sqrt~ "cheap square root (beware -- 8 bits!)" + say wrap~ "wraparound (fractional part, sort of)" + say fft~ "complex forward discrete Fourier transform" + say ifft~ "complex inverse discrete Fourier transform" + say rfft~ "real forward discrete Fourier transform" + say rifft~ "real inverse discrete Fourier transform" + say framp~ "output a ramp for each block" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (for signals)" + } +} + +### phase 3 + +say_namespace summary { + say_category "AUDIO GLUE" + say dac~ "audio output" + say adc~ "audio input" + say sig~ "convert numbers to audio signals" + say line~ "generate audio ramps" + say vline~ "deluxe line~" + say threshold~ "detect signal thresholds" + say snapshot~ "sample a signal (convert it back to a number)" + say vsnapshot~ "deluxe snapshot~" + say bang~ "send a bang message after each DSP block" + say samplerate~ "get the sample rate" + say send~ "nonlocal signal connection with fanout" + say receive~ "get signal from send~" + say throw~ "add to a summing bus" + say catch~ "define and read a summing bus" + say block~ "specify block size and overlap" + say switch~ "switch DSP computation on and off" + say readsf~ "soundfile playback from disk" + say writesf~ "record sound to disk" + + say_category "AUDIO OSCILLATORS AND TABLES" + say phasor~ "sawtooth oscillator" + say {cos~ osc~} "cosine oscillator" + say tabwrite~ "write to a table" + say tabplay~ "play back from a table (non-transposing)" + say tabread~ "non-interpolating table read" + say tabread4~ "four-point interpolating table read" + say tabosc4~ "wavetable oscillator" + say tabsend~ "write one block continuously to a table" + say tabreceive~ "read one block continuously from a table" + + say_category "AUDIO FILTERS" + say vcf~ "voltage controlled filter" + say noise~ "white noise generator" + say env~ "envelope follower" + say hip~ "high pass filter" + say lop~ "low pass filter" + say bp~ "band pass filter" + say biquad~ "raw filter" + say samphold~ "sample and hold unit" + say print~ "print out one or more \"blocks\"" + say rpole~ "raw real-valued one-pole filter" + say rzero~ "raw real-valued one-zero filter" + say rzero_rev~ "[say rzero~] (time-reversed)" + say cpole~ "[say rpole~] (complex-valued)" + say czero~ "[say rzero~] (complex-valued)" + say czero_rev~ "[say rzero_rev~] (complex-valued)" + + say_category "AUDIO DELAY" + say delwrite~ "write to a delay line" + say delread~ "read from a delay line" + say vd~ "read from a delay line at a variable delay time" + + say_category "SUBWINDOWS" + say pd "define a subwindow" + say table "array of numbers in a subwindow" + say inlet "add an inlet to a pd" + say outlet "add an outlet to a pd" + say inlet~ "[say inlet] (for signal)" + say outlet~ "[say outlet] (for signal)" + + say_category "DATA TEMPLATES" + say struct "define a data structure" + say {drawcurve filledcurve} "draw a curve" + say {drawpolygon filledpolygon} "draw a polygon" + say plot "plot an array field" + say drawnumber "print a numeric value" + + say_category "ACCESSING DATA" + say pointer "point to an object belonging to a template" + say get "get numeric fields" + say set "change numeric fields" + say element "get an array element" + say getsize "get the size of an array" + say setsize "change the size of an array" + say append "add an element to a list" + say sublist "get a pointer into a list which is an element of another scalar" + say scalar "draw a scalar on parent" + + say_category "OBSOLETE" + say scope~ "(use tabwrite~ now)" + say namecanvas "" ;# what was this anyway? + say template "(use struct now)" +} + +# phase 4 (pdrc & ddrc) + +say section_audio "Audio" + say -r "sample rate" + say -audioindev "audio in devices" + say -audiooutdev "audio out devices" + say -inchannels "audio input channels (by device, like \"2\" or \"16,8\")" + say -outchannels "number of audio out channels (same)" + say -audiobuf "specify size of audio buffer in msec" + say -blocksize "specify audio I/O block size in sample frames" + say -sleepgrain "specify number of milliseconds to sleep when idle" + say -nodac "suppress audio output" + say -noadc "suppress audio input" + say audio_api_choice "Audio API" + say default "default" + say -alsa "use ALSA audio API" + say -jack "use JACK audio API" + say -mmio "use MMIO audio API (default for Windows)" + say -portaudio "use ASIO audio driver (via Portaudio)" + say -oss "use OSS audio API" + say -32bit "allow 32 bit OSS audio (for RME Hammerfall)" + say {} "default" + +say section_midi "MIDI" + say -nomidiin "suppress MIDI input" + say -nomidiout "suppress MIDI output" + say -midiindev "midi in device list; e.g., \"1,3\" for first and third" + say -midioutdev "midi out device list, same format" + +say section_externals "Externals" + say -path "file search path" + say -helppath "help file search path" + say -lib "load object libraries" + +say section_gui "Gooey" + say -nogui "suppress starting the GUI (caution)" + say -guicmd "substitute another GUI program (e.g., rsh)" + say -look "buttonbar icons" + say -font "specify default font size in points" + +say section_other "Other" + say -open "open file(s) on startup" + say -verbose "extra printout on startup and when searching for files" + say -d "debug level" + say -noloadbang "disable the effect of \[loadbang\]" + say -send "send a message at startup (after patches are loaded)" + say -listdev "list audio and MIDI devices upon startup" + say -realtime "use real-time priority (needs root privilege)" + +say section_paths "Paths" + +# phase 4B: ddrc (keyword names not finalized!) +say console "console scrollback lines (0 = disable console)" +say lang "Language to use" +say pointer_sense "Mouse pointer sensitivity" +say section_color "Appearance" + say canvas_color "canvas" + say canvasbgedit "canvas background (edit mode)" + say canvasbgrun "canvas background (run mode)" + say object_color "object" + say viewframe1 "objectbox color" + say viewframe2 "objectbox color" + say viewframe3 "objectbox color" + say viewframe4 "objectbox highlight color" + say viewbg "object background" + say viewfg "object foreground" + say commentbg "comment background" + say commentfg "comment forground" + say commentframe1 "comment frame" + say commentframe2 "comment frame" + say commentframe3 "comment frame" + say viewselectframe "hilight box" + say wire_color "wire" + say wirefg "wire color" + say wirefg2 "wire highlight" + say wiredspfg "dsp wire color" + say futurewiredash "new (dashed) wire" + say others_color "others" + say boxinletfg "inlet color" + say boxoutletfg "outlet color" + say selrectrect "selection box" +say keys "keys" +say others "others" +say hairstate "Activate crosshair" +say hairsnap "Crosshair snap to object" +say statusbar "Activate statusbar" +say buttonbar "Activate buttonbar" +say menubar "Activate menubar" +say scrollbar "Active auto scrollbar" +say wirearrow "Wire Arrow" +say tooltip "ToolTip" +say insert_object "Insert object" +say chain_object "Chain object" +say clear_wires "Clear wires" +say auto_wire "Remove object" +say subpatcherize "Subpatcherize" +say keynav "keyboard navigation" +say key_nav_up "move up" +say key_nav_up_shift "plus select" +say key_nav_down "move down" +say key_nav_down_shift "plus select" +say key_nav_right "move right" +say key_nav_right_shift "plus select" +say key_nav_left "move left" +say key_nav_left_shift "plus select" +say key_nav_ioselect "select in/outlets" +# phase 5A + +say cannot "can't" +say cancel "Cancel" +say apply "Apply" +say ok "OK" +say popup_open "Open" +say popup_insert "Insert" +say popup_properties "Properties" +say popup_clear_wires "Clear wires" +say popup_remove_from_path "Remove object from path" +say popup_delete_from_path "Delete object from path" +say popup_help "Help" +say filter "Filter: " +say how_many_object_classes "%d of %d object classes" +say do_what_i_mean "Do What I Mean" +say ask_cool "This would be a cool feature, eh?" +say save_changes? "Save changes?" +say reset "Reset" +say Courier "Courier (monospaced)" +say Helvetica "Helvetica (sansserif)" +say Times "Times (serif)" +say add "Add" +say up "Up" +say down "Down" +say remove "Remove" +say lib_add "add the name you typed to the list" +say lib_up "swap order with previous library" +say lib_down "swap order with next library" +say lib_remove "remove library selected in the list" +say dir_add "add a folder using a file dialog" +say dir_up "swap order with previous folder" +say dir_down "swap order with next folder" +say dir_remove "remove folder selected in the list" +say client_class_tree "Client Class Tree" +say clipboard_view "Clipboard View" +say command_history_view "Command History View" +say event_history_view "Event History View" + +# during/after piksel: + +say auto_apply "Auto-Apply" +say font_preview "Preview:" +say font_preview_2 "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n0123456789" +say font_style "Style:" +say font_bold "Bold" +say font_italic "Italic" +say font_family "Name:" +say font_size "Size:" +say damn "Damn!" +say console_clear "Clear Console" +say horizontal "Horizontal" +say vertical "Vertical" +say language "Language" + +# 2007: + +say no_matches "(no matches)" +say preset "preset" +say canvasgrid "Grid color" +say grid_size "Grid size" +say gridstate "Activate background grid" +say snap_grid "Snap to grid" +say viewfont "object font" +say consolefont "console font" +say keyboarddialogfont "virtual keyboard font" +say keyboard_view "Virtual keyboard" +say log_height "Log Height" + diff --git a/desiredata/src/locale/espanol.tcl b/desiredata/src/locale/espanol.tcl new file mode 100644 index 00000000..32a46202 --- /dev/null +++ b/desiredata/src/locale/espanol.tcl @@ -0,0 +1,526 @@ +#!/usr/bin/env tclsh +# Español translations for PureData +# $Id: espanol.tcl,v 1.1.2.3.2.1 2006-12-05 04:51:47 matju Exp $ +# translated by Mario Mora (makro) & Ramiro Cosentino (rama) + +### Menus + +say file "Archivo" + say new_file "Nuevo Archivo" + say open_file "Abrir Archivo..." + say pdrc_editor "Editor .pdrc" + say send_message "Enviar Mensaje..." + say paths "Rutas..." + say close "Cerrar" + say save "Guardar" + say save_as "Guardar Como..." + say print "Imprimir..." + say quit "Salir" + + say canvasnew_file "Archivo Nuevo" + say canvasopen_file "Abrir Archivo..." + say canvassave "Guardar" + say canvassave_as "Guardar como..." + say clientpdrc_editor "Editor .pdrc " + say clientddrc_editor "Editor .ddrc " + say canvasclose "Cerrar" + say canvasquit "Salir" + +say edit "Edición" + say undo "Deshacer" + say redo "Rehacer" + say cut "Cortar" + say copy "Copiar" + say paste "Pegar" + say duplicate "Duplicar" + say select_all "Seleccionar Todo" + say text_editor "Editor de Texto..." + say tidy_up "Ordenar" + say edit_mode "Modo Edición" + say editmodeswitch "Modo edicion/ejecucion" + + say canvascut "Cortar" + say canvascopy "Copiar" + say canvasundo "Deshacer" + say canvasredo "Rehacer" + say canvaspaste "Pegar" + say canvasselect_all "Seleccionar todo" + say canvaseditmodeswitch "Modo edicion/ejecucion" + +say view "Ver" + say reload "Recargar" + say redraw "Refrescar" + + say canvasreload "Recargar" + say canvasredraw "Refrescar" + +say find "Buscar" + say find_again "Buscar Nuevamente" + say find_last_error "Buscar Ultimo Error" + +say canvasfind "Buscar" + say canvasfind_again "Buscar Nuevamente" + +# contents of Put menu is Phase 5C +say put "Poner" + say Object "Objeto" + say Message "Mensaje" + say Number "Numero" + say Symbol "Simbolo" + say Comment "Comentario" + say Canvas "Canvas";# + say Array "Deposito";#array as "desposito"? + + say canvasobject "Objeto" + say canvasmessage "Mensaje" + say canvasnumber "Numero" + say canvassymbol "Simbolo" + say canvascomment "Comentario" + say canvasbang "Bang";# + say canvastoggle "Interruptor";# toggle as "interruptor"? + say canvasnumber2 "Numero2" + say canvasvslider "Deslizador Vertical";#slider as "deslizador"?? + say canvashslider "Deslizador Horizontal" + say canvasvradio "Rango Vertical";#radio as "Rango"?? + say canvashradio "Rango Horizontal";# + say canvascanvas "Canvas";# + say canvasarray "Array";# + +say media "Media" + say audio_on "Audio ON" + say audio_off "Audio OFF" + say test_audio_and_midi "Probar Audio y MIDI" + say load_meter "Medidor de Carga" + + say canvasaudio_on "Audio ON" + say canvasaudio_off "Audio OFF" + say clienttest_audio_and_midi "Probar Audio y MIDI" + say canvasload_meter "Medidor de Carga" + +say window "Ventana" + +say help "Ayuda" + say about "Acerca de..." + say pure_documentation "Pura Documentacion..." + say class_browser "Navegador de Clases..." + say canvasabout "Acerca de..." + +say properties "Propiedades" +say open "Abrir" + +### for key binding editor +say general "General" +say audio_settings "Configuracion Audio " +say midi_settings "Configuracion Midi " +say latency_meter "Medidor de Latencia" +say Pdwindow "Ventana Pd " + +say canvaspdwindow "Ventana Pd" +say canvaslatency_meter "Medidor de Latencia" +say clientaudio_settings "Configuracion Audio" +say clientmidi_settings "Configuracion Midi" + +### for Properties Dialog (phase 5B) +say_category IEM +say w "ancho(px)" +say h "alto(px)" +say hold "tiempo de mantencion(ms)" +say break "tiempo de quiebre(ms)" +say min "valor minimo" +say max "valor maximo" +say is_log "modo" +say linear "linear" +say logarithmic "logaritmico" +say isa "inicio" +say n "numero de posibilidades" +say steady "regularidad" +say steady_no "saltar en click";# +say steady_yes "estabilizar en click";# +say snd "enviar-simbolo" +say rcv "recibir-simbolo" +say lab "etiqueta" +say ldx "etiqueta compensacion x";# +say ldy "etiqueta compensacion y";# +say fstyle "Tipografia" +say fs "Tamaño de fuente" +say bcol "Color Fondo" +say fcol "color primer plano" +say lcol "color etiqueta" +say yes "si" +say no "no" +say courier "courier (typewriter)" +say helvetica "helvetica (sansserif)" +say times "times (serif)" +say coords "grafico en pariente" ;# parent?? as "pariente"?? + +say_category GAtomProperties +say width "ancho" +say lo "limite bajo" +say hi "limite alto" +say label "etiquetal" +say wherelabel "mostrar etiqueta on" +say symto "enviar simbolo" +say symfrom "recibir simbolo" + +say_category GraphProperties +say x1 "desde x" +say x2 "hacia x" +say xpix "ancho pantalla" +say y2 "desde y" +say y1 "hacia y" +say ypix "altura pantalla" + +say_category CanvasProperties +#say xscale "X units/px" +#say yscale "Y units/px" + +say_category ArrayProperties +say name "nombre" +say n "Tamaño" + +### Main Window +say_category MainWindow +say in "in" +say out "out" +say audio "Audio" +say meters "Medidores" +say io_errors "Errores de E/S" + +### phase 2 + +say_category Other +say_namespace summary { + say_category IEMGUI + say bng "Bang Box";# Caja de bang? +# say Bang "Bang" +# say Toggle "Interruptor";# toggle as "interruptor"? + say tgl "Toggle Box";#Caja palanca? caja interruptora? + say nbx "Number Box (IEM)";#Caja de numeros? +# say Number2 "Numero2" + say hsl "deslizador (Horizontal)";# + say vsl "Deslizador (Vertical)";# + say hradio "Choice Box (Horizontal)";# Caja seleccionadora? + say vradio "Choice Box (Vertical)";# +# say Vradio "Rango Vertical";#radio as "rango"? +# say Hradio "Rango Horizontal";# + say cnv "Canvas (IEM)" + say dropper "Drag-and-Drop Box";#Caja agarrar y soltar? + say vu "Vumeter";# medidor VU? + + say_category GLUE + say bang "envia un mensaje de bang" + say float "guarda y recuerda un numero" + say symbol "guarda y recuerda un simbolo" + say int "guarda y recuerda un entero" + say send "envia un mensaje a un objeto nombrado" + say receive "catch sent messages" + say select "test para numeros o simbolos coincidentes" + say route "rutea mensajes de acuerdo al primer elemento" + say pack "genera mensajes compuestos" + say unpack "obtiene elementos de mensajes compuestos" + say trigger "ordena y convierte mensajes" + say spigot "conexion mensajes interrumpible" + say moses "divide un flujo de numeros" + say until "mecanismo de loop" + say print "imprime mensajes" + say makefilename "formatea un simbolo con un campo variable";# + say change "remover numeros repetidos de un flujo de datos";# + say swap "intercambiar dos numeros";# + say value "compartir valor numerico" + + say_category TIME + say delay "envia un mensaje despues de un retraso de tiempo" + say metro "envia un mensaje periodicamente" + say line "envia una serie de numeros encaminados linearmente" + say timer "medicion de intervalos de tiempo" + say cputime "medicion tiempo CPU" + say realtime "medicion tiempo real" + say pipe "linea de retraso dinamicamente creciente para numeros" + + say_category MATH + say + "sumar" + say - "sustraer" + say * "multiplicar" + say {/ div} "dividir" + say {% mod} "resto de la division" + say pow "exponencial" + say == "igual?" + say != "no igual?" + say > "mas que?" + say < "menos que?" + say >= "no menos que?" + say <= "no mas que?" + say & "bitwise conjunction (and)";#bitwise? conjuncion (y) + say | "bitwise disjunction (or)";#bitwise? conjuncion (o) + say && "conjuncion logica (y)" + say || "separacion logica (o)" + say mtof "MIDI a Hertz" + say ftom "Hertz a MIDI" + say powtodb "Watts a dB" + say dbtopow "dB a Watts" + say rmstodb "Volts a dB" + say dbtorms "dB a Volts" + say {sin cos tan atan atan2 sqrt} "trigonometria" + say log "logaritmo Euler" + say exp "exponencial Euler" + say abs "valor absoluto" + say random "aleatorio" + say max "mayor de dos numeros" + say min "menor de dos numeros" + say clip "fuerza un numero en un rango" + + say_category MIDI + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} "entrada MIDI" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} "salida MIDI" + say makenote "programa un retrasado \"note off\" mensaje correspondiente a un note-on";# + say stripnote "tira \"note off\" mensajes";# + + say_category TABLES + say tabread "lee un numero desde una tabla" + say tabread4 "lee un numero desde una tabla, con 4 puntos de interpolacion" + say tabwrite "escribe un numero a una tabla" + say soundfiler "lee y escribe tablas a archivos de audio" + + say_category MISC + say loadbang "bang en carga" + say serial "control recurso serial for NT only" + say netsend "envia mensajes sobre internet" + say netreceive "recibirlos" + say qlist "secuencia mensajes" + say textfile "convertidor de archivo a mensaje" + say openpanel "\"Abrir\" dialogo" + say savepanel "\"Guardar como\" dialogo" + say bag "sistema de numeros" + say poly "asignacion de voz polifonica" + say {key keyup} "valores numericos de teclas del teclado" + say keyname "simbolo de la tecla";# + + say_category "AUDIO MATH" + foreach word {+ - * /} {say $word~ "[say $word] (for signals)"};#this has to be translated too? + say max~ "supremo de señales" + say min~ "infimo de señales" + say clip~ "fuerza la señal a permanecer entre dos limites" + say q8_rsqrt~ "raiz cuadrada reciproca economica (cuidado -- 8 bits!)";# + say q8_sqrt~ "raiz cuadrada economica (cuidado -- 8 bits!)";# + say wrap~ "abrigo alrededor (parte fraccional, tipo de)";#wrap? as abrigo?? around as "alrededor"?? + say fft~ "transformada fourier discreta compleja delantero";# + say ifft~ "transformada fourier discreta compleja inversa";# + say rfft~ "transformada fourier discreta real delantero";# + say rifft~ "transformada fourier discreta real delantero";# + say framp~ "arroja una rampa para cada bloque" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (para señales)";# + } +} + +### phase 3 + +say_namespace summary { + say_category "UTILIDADES AUDIO" + say dac~ "salida audio" + say adc~ "entrada audio" + say sig~ "convierte numeros a señales de audio" + say line~ "genera rampas de audio" + say vline~ "line~ delujo" + say threshold~ "detectar umbrales de la señal" + say snapshot~ "toma muestras de una señal (reconvierte a numeros)" + say vsnapshot~ "snapshot~ delujo";# + say bang~ "envia un mensaje bang despues de cada block DSP" + say samplerate~ "obtener rango de muestreo" + say send~ "conexion señal no local con fanout";# fanout?? + say receive~ "obtener señal desde send~" + say throw~ "agrega a un bus sumador";# + say catch~ "define y lee bus sumador";# + say block~ "especifica tamaño del bloque y overlap" + say switch~ "selecciona computacion DSP on y off";# + say readsf~ "reproduce archivo de audio desde disco" + say writesf~ "graba sonido a disco" + + say_category "OSCILADORES DE AUDIO Y TABLAS" + say phasor~ "oscilador diente de sierra" + say {cos~ osc~} "oscilador coseno" + say tabwrite~ "escribe a una tabla" + say tabplay~ "reproduce desde una tabla (sin transponer)" + say tabread~ "lee tabla sin interpolacion" + say tabread4~ "lee tabla con 4 puntos de interpolacion" + say tabosc4~ "oscilador de tabla de ondas" + say tabsend~ "escribe un bloque continuamente a una tabla" + say tabreceive~ "lee un bloque continuamente desde una tabla" + + say_category "FILTROS AUDIO" + say vcf~ "filtro controlado por voltaje" + say noise~ "generador ruido blanco" + say env~ "lector envolvente" + say hip~ "filtro pasa agudos" + say lop~ "filtro pasa bajos" + say bp~ "filtro pasa banda" + say biquad~ "filtro crudo" + say samphold~ "unidad muestra y mantener";# sample as "muestra" + say print~ "imprimir uno o mas \"bloques\"" + say rpole~ "filtro crudo valor real de un polo" + say rzero~ "fitro crudo valor real de un cero" + say rzero_rev~ "[say rzero~] (tiempo-invertido)" + say cpole~ "[say rpole~] (complejo-valorado)" + say czero~ "[say rzero~] (complejo-valorado)" + say czero_rev~ "[say rzero_rev~] (complejo-valorado)" + + say_category "AUDIO DELAY" + say delwrite~ "escribir a una linea de retraso" + say delread~ "leer desde una linea de retraso" + say vd~ "leer desde una linea de retraso a un tiempo de retraso variable" + + say_category "SUBVENTANAS" + say pd "define una subventana" + say table "arsenal de numeros en una subventana" + say inlet "agrega una entrada a pd" + say outlet "agrega una salida a pd" + say inlet~ "[say entrada] (para señal)";# + say outlet~ "[say salida] (para señal)";# + + say_category "PLANTILLAS DE DATOS" + say struct "define una estructura de datos" + say {drawcurve filledcurve} "dibuja una curva" + say {drawpolygon filledpolygon} "dibuja un poligono" + say plot "trace un campo del arsenal";# + say drawnumber "imprime un valor numerico" + + say_category "ACCEDIENDO A DATOS" + say pointer "señale a un objeto que pertenece a una plantilla" + say get "obtener campos numericos" + say set "cambiar campos numericos" + say element "obtener un elemento del deposito";#array as "deposito" + say getsize "obtener el tamaño del deposito" + say setsize "cambiar el tamaño del deposito" + say append "agregar un elemento a una lista" + say sublist "obtenga un indicador en una lista que sea un elemento de otro escalar";# scalar as escalar?? some maths expert should give his opinion + say scalar "dibuje un escalar en pariente";#same + + say_category "OBSOLETE" + say scope~ "(use tabwrite~ ahora)" + say namecanvas "" ;# what was this anyway? + say template "(use struct ahora)" +} + +# phase 4 (pdrc & ddrc) + +say section_audio "Audio" + say -r "rango de muestreo" + say -audioindev "dispositivos entrada de audio" + say -audiooutdev "dispositivos salida de audio" + say -inchannels "canales entrada audio (por dispositivo, como \"2\" or \"16,8\")" + say -outchannels "numero de canales salida audio (igual)" + say -audiobuf "especificar tamaño de almacenador de audio en mseg" + say -blocksize "especificar tamaño block E/S audio en muestras por cuadro" + say -sleepgrain "especificar numero de milisegundos para suspension cuando este inactivo";# + say -nodac "suprimir salida de audio" + say -noadc "suprimir entrada de audio" + say audio_api_choice "Audio API" + say default "defecto" + say -alsa "usar ALSA audio API" + say -jack "usar JACK audio API" + say -mmio "usar MMIO audio API (por defecto para Windows)" + say -portaudio "usar ASIO audio driver (via Portaudio)" + say -oss "usar OSS audio API" + say -32bit "permitir 32 bit OSS audio (para RME Hammerfall)" + say {} "defecto" + +say section_midi "MIDI" + say -nomidiin "suprime entrada MIDI" + say -nomidiout "suprime salida MIDI" + say -midiindev "lista dispositivos midi in; e.g., \"1,3\" para primero y tercero" + say -midioutdev "lista dispositivos midi out, mismo formato" + +say section_externals "Externals" + say -path "ruta busqueda de archivo" + say -helppath "ruta busqueda archivo de ayuda" + say -lib "cargar librerias de objeto" + +say section_gui "Gooey";# what?? + say -nogui "suprime inicio de gui (precaucion)" + say -guicmd "substituye por otro programa GUI (e.g., rsh)" + say -console "lineas retroceso consola (0 = desactivar consola)" + say -look "iconos barra de botones" + say -statusbar "activar barra de status" + say -font "especificar tamaño de fuente por defecto en puntos" + +say section_other "Otro" + say -open "abrir archivo(s) en inicio" + say -verbose "impresion extra en inicio y al buscar archivos";# + say -d "nivel chequeo errores";# + say -noloadbang "desactivar el efecto de \[loadbang\]" + say -send "enviar un mensaje al inicio (despues patches estan cargados)" + say -listdev "lista dispositivos audio y MIDI al inicio" + say -realtime "usar prioridad tiempo-real (necesita privilegios de administrador)" + +say section_paths "Rutas" + +# ddrc + +say section_color "colores" + say canvas_color "canvas";# + say canvasbgedit "canvas fondo (modo edicion)" + say canvasbgrun "canvas fondo (modo ejecucion)" + say object_color "objeto" + say viewframe1 "color objeto";# objetbox as "objeto" + say viewframe2 "color objeto";# + say viewframe3 "color objeto";# + say viewframe4 "color resaltado objeto";# + say viewbg "objeto fondo" + say viewfg "objeto primer plano" + say commentbg "comentario fondo" + say commentfg "comentario primer plano" + say commentframe1 "comentario cuadro";#frame as "cuadro" + say commentframe2 "comentario cuadro" + say commentframe3 "comentario cuadro" + say viewselectframe "caja resaltada";# + say wire_color "cable";# wire as "cable" + say wirefg "color cable" + say wirefg2 "cable resaltado" + say wiredash "cable rociado" + say others_color "otros" + say inletfg "color entrada" + say outletfg "color salida" + say selrect "caja de seleccion";# selection box +say keys "teclas" +say others "otros" +say canvashairstate "Activar malla";# crosshair como "malla" +say canvashairsnap "Malla ajustada a objeto" +say canvasstatusbar "Activar barra de estatus" +say canvasbuttonbar "Activar barra de botones" + + +# phase 5A + +say cannot "no puedo" +say cancel "Cancelar" +say apply "Aplicar" +say ok "OK" +say popup_open "Abrir" +say popup_properties "Propiedades" +say popup_help "Ayuda" +say filter "Filtro: " +say how_many_object_classes "%d of %d object classes";# this has to be translated? +say do_what_i_mean "haga lo que quiero decir" +say save_changes? "Guardar cambios?" +say reset "Reiniciar" +say Courier "Courier (monospaced)" +say Helvetica "Helvetica (sansserif)" +say Times "Times (serif)" +say add "Agregar" +say up "Arriba" +say down "Abajo" +say remove "Remover" +say lib_add "agrega el nombre tipeado en la lista" +say lib_up "intercambiar orden con libreria previa" +say lib_down "intercambiar orden con libreria proxima" +say lib_remove "remover libreria seleccionada en la lista" +say dir_add "agregar una carpeta usando un cuadro de dialogo" +say dir_up "intercambiar orden con carpeta previa" +say dir_down "intercambiar orden con carpeta proxima" +say dir_remove "remover carpeta seleccionada en la lista" + + +### Other + + + diff --git a/desiredata/src/locale/euskara.tcl b/desiredata/src/locale/euskara.tcl new file mode 100644 index 00000000..9a27d1f5 --- /dev/null +++ b/desiredata/src/locale/euskara.tcl @@ -0,0 +1,554 @@ +#!/usr/bin/env tclsh +# Basque translations for PureData +# translated by Ibon Rodriguez Garcia +### Menus + +say file "Fitxategia" + say new_file "Berria" + say open_file "Ireki..." + say server_prefs "Zerbitzari Hobespenak..." + say client_prefs "Bezero Hobespenak..." + say send_message "Mezua Bidali..." + say paths "Bide Izena..." + say close "Itxi" + say save "Gorde" + say save_as "Gorde Honela..." + say print "Inprimatu..." + say quit "Irten" + + say canvasnew_file "Berria" + say canvasopen_file "Ireki..." + say canvassave "Gorde" + say canvassave_as "Gorde Honela..." + say clientpdrc_editor ".pdrc Editorea" + say clientddrc_editor ".ddrc Editorea" + say canvasclose "Itxi" + say canvasquit "Irten" + +say edit "Editatu" + say undo "Desegin" + say redo "Berregin" + say cut "Ebaki" + say copy "Kopiatu" + say paste "Itsatsi" + say duplicate "Bikoiztu" + say select_all "Hautatu Dena" + say text_editor "Testu Editorea..." + say font "Letra Tipoa" + say tidy_up "Doitu" + say edit_mode "Edizio Modua" + say editmodeswitch "Edizio/Exekuzio modua" + + say canvascut "Ebaki" + say canvascopy "Kopiatu" + say canvasundo "Desegin" + say canvasredo "Berregin" + say canvaspaste "Itsatsi" + say canvasduplicate "Bikoiztu" + say canvasselect_all "Hautatu Dena" + say canvaseditmodeswitch "Edizio/Rxekuzio Modua" + +say view "Ikusi" + say reload "Berriz Kargatu" + say redraw "Eguneratu" + + say canvasreload "Berriz Kargatu" + say canvasredraw "Eguneratu" + +say find "Bilatu" + say find_again "Bilatu Berriro" + say find_last_error "Bilatu Azken Errorea" + say string "Bilatu Katea" +say canvasfind "Bilatu" + say canvasfind_again "Bilatu Berriro" + +# contents of Put menu is Phase 5C +say put "Jarri" + say Object "Objektua" + say Message "Mezua" + say Number "Zenbakia" + say Symbol "Ikurra" + say Comment "Iruzkina" + say Bang "Bang" + say Toggle "Konmutagailua" + say Number2 "2 Zenbakia" + say Vslider "Graduatzaile Bertikala" + say Hslider "Graduatzaile Horizontala" + say Vradio "Maila Bertikala" + say Hradio "Maila Horizontala" + say Canvas "Canvas" + say Array "Taula" + + say canvasobject "Objektua" + say canvasmessage "Mezua" + say canvasnumber "Zenbakia" + say canvassymbol "Ikurra" + say canvascomment "Iruzkina" + say canvasbang "Bang" + say canvastoggle "Konmutagailua" + say canvasnumber2 "2 Zenbakia" + say canvasvslider "Graduatzaile Bertikala" + say canvashslider "Graduatzaile Horizontala" + say canvasvradio "Maila Bertikala" + say canvashradio "Maila Horizontala" + say canvascanvas "Canvas" + say canvasarray "Taula" + +say media "Media" + say audio_on "Audio ON" + say audio_off "Audio OFF" + say test_audio_and_midi "Probatu Audio eta MIDI" + say load_meter "Kargatu Neurgailua" + + say canvasaudio_on "Audio ON" + say canvasaudio_off "Audio OFF" + say clienttest_audio_and_midi "Probatu Audio eta MIDI" + say canvasload_meter "Kargatu Neurgailua" + +say window "Leihoa" + +say help "Laguntza" + say about "...(r)i buruz" + say pure_documentation "Dokumentazio Hutsa..." + say class_browser "Klase Arakatzailea..." + + say canvasabout "...(r)i buruz" + +say properties "Propietateak" +say open "Ireki" + +### for key binding editor +say general "Nagusia" +say audio_settings "Audio Ezarpenak" +say midi_settings "Midi Ezarpenak" +say latency_meter "Latentzia Neurgailua" +say Pdwindow "Pd Leihoa" + +say canvaspdwindow "Pd Leihoa" +say canvaslatency_meter "Latentzia Neurgailua" +say clientaudio_settings "Audio Ezarpenak" +say clientmidi_settings "Midi Ezarpenak" + +### for Properties Dialog (phase 5B) +say_category IEM +say w "Zabalera(px)" +say h "Garaiera(px)" +say hold "Euste Denbora(ms)" +say break "Eten Denbora(ms)" +say min "Gutxienezko Balioa" +say max "Gehienezko Balioa" +say is_log "Modua" +say linear "Lineala" +say logarithmic "Logaritmikoa" +say isa "Hasi" +say n "Aukera Kopurua" +say steady "Erregulartasuna" +say steady_no "Klik egitean jauzi" +say steady_yes "Klik Egitean Finkatu" +say snd "Bidaltze Ikurra" +say rcv "Jasotze Ikurra" +say lab "Etiketa" +say ldx "Desplazatu x Etiketa" +say ldy "Desplazatu y Etiketa" +say fstyle "Letra Tipoa" +say fs "Letra Tamaina" +say bcol "Atzeko Planoaren Kolorea" +say fcol "Aurreko Planoaren Kolorea" +say lcol "Etiketaren Kolorea" +say yes "Bai" +say no "Ez" +say courier "Courier (idazmakina)" +say helvetica "Helvetica (sansserif)" +say times "Times (serif)" +say coords "Grafikoa Nagusian" + +say_category GAtomProperties +say width "Zabalera" +say lo "Beheko Muga" +say hi "Goiko Muga" +say label "Etiketa" +say wherelabel "Erakutsi On Etiketa" +say symto "Bidali Ikurra" +say symfrom "Jaso Ikurra" + +say_category GraphProperties +say x1 "x(e)tik" +say x2 "x(e)ra" +say xpix "Pantailaren Zabalera" +say y2 "y(e)tik" +say y1 "y(e)ra" +say ypix "Pantailaren Garaiera" + +say_category CanvasProperties +#say xscale "X Unitate/px" +#say yscale "Y Unitate/px" +say gop "Grafikoa Nagusian" +say xmargin "xmarjina" +say ymargin "ymarjina" +say height "Garaiera" +say_category ArrayProperties +say name "Izena" +say n "Tamaina" +say xfrom "x(e)tiko tartea" +say xto "x(e)rako tartea" +say yfrom "y(e)tiko tartea" +say yto "y(e)rako tartea" + + +say_category MainWindow +say in "in" +say out "out" +say audio "Audio" +say meters "Neurgailuak" +say io_errors "IO Akatsak" + +### phase 2 + +say_category Other +say_namespace summary { + say_category IEMGUI + say bng "Bang Kutxa" + say tgl "Toggle Kutxa" + say nbx "Zenbaki Kutxa (IEM)" + say hsl "Graduatzailea (Horizontala)" + say vsl "Graduatzailea (Bertikala)" + say hradio "Aukera Kutxa (Horizontala)" + say vradio "Aukera Kutxa (Bertikala)" + say cnv "Canvas (IEM)" + say dropper "Arrastatu eta Jaregin Kutxa" + say vu "Vumeter" + + say_category GLUE + say bang "Bang Mezua Erakutsi" + say float "Zenbaki Bat Gorde eta Gogoratu" + say symbol "Ikur Bat Gorde eta Gogoratu" + say int "Osoko Bat Gorde eta Gogoratu" + say send "Mezu Bat Bidali Objektu Izendun Bati" + say receive "Bidalitako Mezuak Jaso" + say select "Pareko Zenbaki edo Ikurrak Aztertu" + say route "Lehenengo Elementuarekin Bat Datozen Mezuak Bideratu" + say pack "Mezu Konposatuak Egin" + say unpack "MEzu Konposatuetako Elementuak Jaso" + say trigger "Mezuak Sekuentziatu eta Bihurtu" + say spigot "Mezuen Konexio Etengarria" + say moses "Zenbakizko Transmisioa Zatitu" + say until "Begiztatze Mekanismoa" + say print "Inprimatu Mezuak" + say makefilename "Ikur Bat Formateatu Eremu Aldakor Batekin" + say change "Ezabatu Transmisio Batetik Errepikatutako Zenbakiak" + say swap "Trukatu Bi Zenbaki" + say value "Zenbakizko Balioa Partekatu" + + say_category TIME + say delay "Bidali Mezua Atzerapen Denbora eta Gero" + say metro "Bidali Mezua Aldizka" + say line "Bidali Linealki Mailakatutako Zenbaki Saila" + say timer "Neurtu Denbora Tartea" + say cputime "Neurtu CPU Denbora" + say realtime "Neurtu Denbora Erreala" + say pipe "Zenbakientzako Dinamikoki Hazkorra Den Atzerapen Linea" + + say_category MATH + say + "Gehitu" + say - "Kendu" + say * "Biderkatu" + say {/ div} "Zatitu" + say {% mod} "Zatiketaren Hondarra" + say pow "exponentziala" + say == "Berdin?" + say != "Ezberdin?" + say > "baino Gehiago?" + say < "baino Gutxiago?" + say >= "baino Gutxiago Ez?" + say <= "baino Gehiago Ez?" + say & "bitwise konjuntzioa (eta)" + say | "bitwise disjuntzioa (edo)" + say && "konjuntzio logikoa (eta)" + say || "disjuntzio logikoa (edo)" + say mtof "MIDItik Hertzetara" + say ftom "Hertzetatik MIDIra" + say powtodb "Wattetatik dBtara" + say dbtopow "dBtatik to Wattetara" + say rmstodb "Voltetatik dBetara" + say dbtorms "dBetatik Voltetara" + say {sin cos tan atan atan2 sqrt} "trigonometria" + say log "Euler logaritmoa" + say exp "Euler exponentziala" + say abs "balio absolutua" + say random "ausaz" + say max "Bi zenbakitan handiena" + say min "Bi zenbakitan handiena" + say clip "Behartu Zenbaki Bat Bitarte Batera" + + say_category MIDI + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} "MIDI sarrera" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} "MIDI irteera" + say makenote "antolatu note-on bati dagokion \"note off\" mezu atzeratu bat" + say stripnote "Kendu \"note off\" mezuak" + + say_category TABLES + say tabread "Irakurri zenbaki bat taula batetik" + say tabread4 "Irakurri zenbaki bat taula batetik, 4 interpolazio-punturekin" + say tabwrite "Idatzi Zenbaki Bat Taula Batean" + say soundfiler "Irakurri eta Idatzi Taulak Soinu Fitxategiei" + + say_category MISC + say loadbang "Bang Kargatu" + say serial "Serieko Atakaren Kontrolatzailea, NTrako soilik" + say netsend "Bidali Mezuak Interneten Gainetik" + say netreceive "Jaso Mezuak Interneten Gainetik" + say qlist "Mezu Sekuentziadorea" + say textfile "Bihurtu fitxategia mezu" + say openpanel "\"zabaldu\" Elkarrizketa" + say savepanel "\"Gorde Honela\" Elkarrizketa" + say bag "Zenbaki Multzoa" + say poly "Ahots Polifonikoaren Esleipena" + say {key keyup} "Teklaturako Zenbakizko Teklen Balioak" + say keyname "Tekla Izen Sinbolikoa" + + say_category "AUDIO MATH" + foreach word {+ - * /} {say $word~ "[say $word] (for signals)"} + say max~ "Seinalearen Gehienezko Balioa" + say min~ "Seinalearen Gutxienezko Balioa" + say clip~ "Behartu Seinalea Bi Mugen Artean Egotera" + say q8_rsqrt~ "Elkarrekiko Erro Karratu Ekonomikoa (Kontuz -- 8 bit!)" + say q8_sqrt~ "Erro Karratu Ekonomikoa (Kontuz -- 8 bit!)" + say wrap~ "Wraparound (Alde Zatikiar Mota)" + say fft~ "Aurreko Fourier-en transformatu Diskretu Konplexua" + say ifft~ "Alderantzizko Fourier-en transformatu Diskretu Konplexua" + say rfft~ "Aurreko Fourier-en transformatu Diskretu Erreala" + say rifft~ "Alderantzizko Fourier-en transformatu Diskretu Erreala" + say framp~ "Atera arrapala bat bloke bakoitzarentzat" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (Seinalearentzat)" + } +} + +### phase 3 + +say_namespace summary { + say_category "AUDIO GLUE" + say dac~ "audio irteera" + say adc~ "audio sarrera" + say sig~ "Bihurtu Zenbakiak Audio Seinale" + say line~ "Sortu Audio Arrapalak" + say vline~ "Luxuzko linea~" + say threshold~ "Antzeman Seinalearen Atalaseak" + say snapshot~ "Lagindu seinale bat (Bihurtu Zenbaki)" + say vsnapshot~ "Luxuzko Argazkia~" + say bang~ "Badili Bang Mezu Bat DSP block Bakoitzaren Ostean" + say samplerate~ "Lortu Laginaren Maiztasuna" + say send~ "Konektatu Seinale Ez-Lokal Bat Banagune Batekin" + say receive~ "Lortu Seinatea send~etik" + say throw~ "Gehitu Summing Bus Batera" + say catch~ "Definitu eta Irakurri Summing Bus Bat" + say block~ "Zehaztu Blokearen Tamaina eta Teilakapena" + say switch~ "Piztu eta Itzali DSP kalkulagailua" + say readsf~ "Erreproduzitu Soinu Fitxategia Diskotik" + say writesf~ "Grabatu Soinua Diskoan" + + say_category "AUDIO OSZILADOREAK ETA TAULAK" + say phasor~ "Zerra Hortz Motako Osziladorea" + say {cos~ osc~} "Kosinu Osziladorea" + say tabwrite~ "Idatzi Taula Batean" + say tabplay~ "Erreproduzitu Taula Batetik (transposiziorik gabe)" + say tabread~ "Irakurri Interpolazio Gabeko Taula" + say tabread4~ "Irakurri 4 Interpolazio Puntuko Taula" + say tabosc4~ "Uhin Taulako Osziladorea" + say tabsend~ "Idatzi Bloke Bat Taula Batera Etengabe" + say tabreceive~ "Irakurri Bloke Bat Taula Batetik Etengabe" + + say_category "AUDIO IRAGAZKIAK" + say vcf~ "Tentsio Kontrolatuko Iragazkia" + say noise~ "Zarata Zuri Sortzailea" + say env~ "Inguratze Jarraitzailea" + say hip~ "Goi Paseko Iragazkia" + say lop~ "Behe Paseko Iragazkia" + say bp~ "Banda Paseko Iragazkia" + say biquad~ "Iragazki Gordina" + say samphold~ "Lagindu eta blokeatu Unitatea" + say print~ "Inprimatu \"Blocks\" Bat Edo Gehiago" + say rpole~ "Polo Bakarreko Balio Errealeko Iragazki Gordina" + say rzero~ "Zero-Bat Balio Errealeko Iragazki Gordina" + say rzero_rev~ "[say rzero~] (Denbora Alderantzizkatua)" + say cpole~ "[say rpole~] (Konplexua-Zenbatetsia)" + say czero~ "[say rzero~] (Konplexua-Zenbatetsia)" + say czero_rev~ "[say rzero_rev~] (Konplexua-Zenbatetsia)" + + say_category "AUDIO ATZERAPENA" + say delwrite~ "Idatzi Atzerapen Lerro Batean" + say delread~ "Irakurri Atzerapen Lerro Batetik" + say vd~ "Irakurri Atzerapen Lerro Batetik Atzerapen Lerro Aldakor Batean" + + say_category "AZPILEIHOAK" + say pd "Azpileiho Bat Definitu" + say table "Zenbaki Taula Azpileiho Batean" + say inlet "Gehitu Sarrera Bat PD Batean" + say outlet "Gehitu Irteera Bat PD Batean" + say inlet~ "[say inlet] (seinalearentzat)" + say outlet~ "[say outlet] (seinalearentzat)" + + say_category "DATU TXANTILIOIAK" + say struct "Definitu Datu Egitura Bat" + say {drawcurve filledcurve} "Marraztu Kurba Bat" + say {drawpolygon filledpolygon} "Marraztu Poligono Bat" + say plot "Trazatu Taula Bateko Eremu Bat" + say drawnumber "Inprimatu Zenbakizko Balio Bat" + + say_category "DATUETARAKO SARBIDEA" + say pointer "Seinalatu Txantilioi Batekoa Den Elementu Bat" + say get "Lortu Zenbakizko Eremuak" + say set "Zenbakizko Eremuak Aldatu" + say element "Lortu Taula Bateko Elementu Bat" + say getsize "Lortu Taula Baten Tamaina" + say setsize "Aldatu Taula Baten Tamaina" + say append "Gehitu Elementu Bat Zerrenda Batera" + say sublist "Lortu Erakusle Bat Beste Eskalar Bateko Elementua Den Zerrenda Batean" + say scalar "Marraztu Eskalar Bat Nagusian" + + say_category "ZAHARKITUA" + say scope~ "(Erabili tabwrite~ Orain)" + say namecanvas "" ;# what was this anyway? + say template "(Erabili struct now)" +} + +# phase 4 (pdrc & ddrc) + +say section_audio "Audio" + say -r "LAgin-Maiztasuna" + say -audioindev "Audio Sarrerako Gailuak" + say -audiooutdev "Audio Irteerako Gailuak" + say -inchannels "Audio Sarrerako Kanalak (Gailuko, adib. \"2\" edo \"16,8\")" + say -outchannels "Audio Irteerako Kanalen Kopurua (Berbera)" + say -audiobuf "Zehaztu Audio Bufferraren Tamaina Milisegundotan" + say -blocksize "Zehaztu S/I Audio Blokeak Lagin Markoetan" + say -sleepgrain "Zehaztu Lotarako Milisegundo Kopurua Inaktibo Dagoenean" + say -nodac "Ezabatu Audio Irteera" + say -noadc "Ezabatu Audio Sarrera" + say audio_api_choice "Audio API" + say default "Lehenetsia" + say -alsa "Erabili ALSA audio API" + say -jack "Erabili JACK audio API" + say -mmio "Erabili MMIO audio API (Windowserako Lehenetsia)" + say -portaudio "Erabili ASIO audio driver (Portaudio bidez)" + say -oss "Erabili OSS audio API" + say -32bit "Baimendu 32 bit OSS audio (RME Hammerfallerako)" + say {} "Lehenetsia" + +say section_midi "MIDI" + say -nomidiin "MIDI Sarrera Kendu" + say -nomidiout "MIDI Irteera Kendu" + say -midiindev "midi in Gailuen Zerrenda; adib., \"1,3\" lehenengo eta hirugarrenerako" + say -midioutdev "midi out Gailuen Zerrenda, Formatu Berbera" + +say section_externals "Kanpokoak" + say -path "Fitxategiak Topatzeko Bidea" + say -helppath "Laguntza Fitxategiak Topatzeko Bidea" + say -lib "Kargatu Objektuen Liburutegiak" + +say section_gui "Gooey" + say -nogui "Desgaitu GUI Hastea (Kontuz)" + say -guicmd "Ordeztu Beste GUI Programa (adib., rsh)" + say -console "Kontsolaren Atzerapen Lineak (0 = Desgaitu Kontsola)" + say -look "Botoi Barraren Ikonoak" + say -statusbar "Gaitu Egoera Barra" + say -font "Zehaztu LEtra Mota Lehenetsia Puntutan" + +say section_other "Bestelakoak" + say -open "Ireki Fitxategia(k) Hasieran" + say -verbose "Inprimatze Gehigarria Hasieran eta Fitxategiak Bilatzean" + say -d "Arazte Maila" + say -noloadbang "Desgaitu \[loadbang\] Efektua" + say -send "Bidali Mezu Bat Hasieran (Adabakiak Kargatu Eta Gero)" + say -listdev "Zerrendatu Audio eta MIDI Gailuak Hasieraren Gainetik" + say -realtime "Erabili Denbora Errealeko Lehenespena (sustrai-pribilegioa behar du)" + +say section_paths "Ibilbideak" + +# phase 4B: ddrc (keyword names not finalized!) + +say section_color "Koloreak" + say canvas_color "canvas" + say canvasbgedit "canvas Atzeko Planoa (edizio-modua)" + say canvasbgrun "canvas Atzeko Planoa (exekutatze-modua)" + say object_color "objektua" + say viewframe1 "Objektu Kutxaren Kolorea" + say viewframe2 "Objektu Kutxaren Kolorea" + say viewframe3 "Objektu Kutxaren Kolorea" + say viewframe4 "Objektu Kutxa Nabarmentzeko Kolorea" + say viewbg "Objektuaren Atzeko Planoa" + say viewfg "Objektuaren Aurreko Planoa" + say commentbg "Iruzkinaren Atzeko Planoa" + say commentfg "Iruzkinaren Aurreko Planoa" + say commentframe1 "Iruzkin Markoa" + say commentframe2 "Iruzkin Markoa" + say commentframe3 "Iruzkin Markoa" + say viewselectframe "Nabarmendu Kutxa" + say wire_color "Haria" + say wirefg "Hariaren Kolorea" + say wirefg2 "Nabarmendu Haria" + say wiredspfg "dsp Hari Kolorea" + say futurewiredash "Hari (Marratu) Berria" + say others_color "Bestelakoak" + say boxinletfg "Sarrera Kolorea" + say boxoutletfg "Irteera Kolorea" + say selrectrect "Hautapen Kutxa" +say keys "Teklak" +say others "Bestelakoak" +say canvashairstate "Gaitu Amarauna" +say canvashairsnap "Doitu Amarauna Objektuari" +say canvasstatusbar "Gaitu Egoera Barra" +say canvasbuttonbar "Gaitu Botoi Barra" +say wirewirearrow "Gezia Lotu" +say viewtooltip "Argibidea" +say canvasinsert_object "Txertatu Objektua" +say canvaschain_object "Kateatu Objektua" +say canvasclear_wires "Garbitu Hariak" +say canvasauto_wire "Ezabatu Objektua" +# phase 5A + +say cannot "Ezin" +say cancel "Ezeztatu" +say apply "Aplikatu" +say ok "Ados" +say popup_open "Ireki" +say popup_insert "Txertatu" +say popup_properties "Propietateak" +say popup_clear_wires "Garbitu Hariak" +say popup_auto_wire "Ezabatu Objektua" +say popup_help "Laguntza" +say filter "Iragazkia : " +say how_many_object_classes "Objektu Klaseen %d(r)en %d" +say do_what_i_mean "Egin Esan Nahi Dudana" +say save_changes? "Aldaketak Gorde?" +say reset "Garbitu" +say Courier "Courier (monospaced)" +say Helvetica "Helvetica (sansserif)" +say Times "Times (serif)" +say add "Gehitu" +say up "Gora" +say down "Behera" +say remove "Ezabatu" +say lib_add "Gehitu Idatzitako Izena Zerrendara" +say lib_up "Trukatu Agindua Aurreko Liburutegiarekin" +say lib_down "Trukatu Agindua Hurrengo Liburutegiarekin" +say lib_remove "Ezabatu Zerrendan Aurkeratutako Liburutegia" +say dir_add "Gehitu Karpeta Bat Elkarrizketa Fitxategia Erabiliz" +say dir_up "Trukatu Agindua Aurreko Karpetarekin" +say dir_down "Trukatu Agindua Hurrengo Karpetarekin" +say dir_remove "Ezabatu Zerrendan Aukeratutako Karpeta" +say client_class_tree "Bezero Klaseen Zuhaitza" +say clipboard_view "Ikusi Arbela" +say history_view "Ikusi Historia" + +# during/after piksel: + +say auto_apply "Auto-Aplikatu" +say font_preview "Aurrebista:" +say font_preview_2 "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n0123456789" +say font_style "Estiloa:" +say font_bold "Lodia" +say font_italic "Etzana" +say font_family "Izena:" +say font_size "Tamaina:" +say damn "Arraioa!" diff --git a/desiredata/src/locale/francais.tcl b/desiredata/src/locale/francais.tcl new file mode 100644 index 00000000..1101bff9 --- /dev/null +++ b/desiredata/src/locale/francais.tcl @@ -0,0 +1,539 @@ +#!/usr/bin/env tclsh +# French (franais) translations for PureData +# $Id: francais.tcl,v 1.1.2.5.2.8 2007-07-11 19:21:32 matju Exp $ +# by Patrice Colet + +say file "Fichier" + say new_file "Nouveau Fichier" + say open_file "Ouvrir Fichier..." + say server_prefs "Prfrences serveur..." + say client_prefs "Prfrences client..." + say send_message "Envoyer Message..." + say paths "Chemins..." + say close "Fermer" + say save "Sauvegarder" + say save_as "Sauvegarder Sous..." + say print "Imprimer..." + say quit "Quitter" + + say canvasnew_file "Nouveau Fichier" + say canvasopen_file "Ouvrir Fichier..." + say canvassave "Sauver" + say canvassave_as "Sauver sous..." + #say clientpdrc_editor "diteur de .pdrc" + #say clientddrc_editor "diteur de .ddrc" + say canvasclose "Fermer" + say canvasquit "Quitter" + +say edit "dition" + say undo "Dfaire" + say redo "Refaire" + say cut "Couper" + say copy "Copier" + say paste "Coller" + say duplicate "Dupliquer" + say select_all "Slectionner tout" + say text_editor "diteur de texte..." + say font "Police" + say tidy_up "Aligner" + say edit_mode "Mode d'dition" + say editmodeswitch "Mode dition/execution" + + say canvascut "Couper" + say canvascopy "Copier" + say canvasundo "Annuler" + say canvasredo "Rtablir" + say canvaspaste "Coller" + say canvasduplicate "Dupliquer" + say canvasselect_all "Slectionner Tout" + say canvaseditmodeswitch "Mode dition/execution" + +say view "Vue" + say reload "Recharger" + say redraw "Redessiner" + + say canvasreload "Recharger" + say canvasredraw "Redessiner" + + say find "Trouver" + say find_again "Trouver encore" + say find_last_error "Trouver la dernire erreur" + say string "Trouver chane de charactres" + say canvasfind "Trouver" + say canvasfind_again "Trouver encore" + +say put "Mettre" + say Object "Objet" + say Message "Message" + say Number "Nombre" + say Symbol "Symbole" + say Comment "Commentaire" + say Array "Tableau" + +say media "Mdia" + say audio_on "Audio ON" + say audio_off "Audio OFF" + say test_audio_and_midi "Tester l'audio et le MIDI" + say load_meter "CPU-mtre" + + say canvasaudio_on "Audio ON" + say canvasaudio_off "Audio OFF" + say clienttest_audio_and_midi "Tester l'audio et le MIDI" + say canvasload_meter "CPU-mtre" + +say window "Fentre" + +say help "Aide" + say about " propos de..." + say pure_documentation "Pure Documentation..." + say class_browser "Explorateur de classes..." + + say canvasabout " propos de..." + + say properties "Proprits" +say open "Ouvrir" + +### for key binding editor +say general "Gnerale" +say audio_settings "Paramtres audio" +say midi_settings "Paramtres MIDI" +say latency_meter "Latence-mtre" +say Pdwindow "Console PD" + +say canvaspdwindow "Console PD" +say canvaslatency_meter "Latence-mtre" +say clientaudio_settings "Paramtres audio" +say clientmidi_settings "Paramtres audio" + +### for Properties Dialog (phase 5B) +say_category IEM +say w "Largeur(px)" +say h "Hauteur(px)" +say hold "Temps de maintien(ms)" +say break "temps d'arrt(ms)" +say min "Valeur minimum" +say max "Valeur maximum" +say is_log "Mode" +say linear "Lineaire" +say logarithmic "Logarithmique" +say isa "Initialisation" +say n "Nombre de choix" +say steady "Rgularit" +say steady_no "Caler sur le click" +say steady_yes "Suivre le click" +say snd "symbole d'envoie" +say rcv "symbole de reception" +say lab "label" +say ldx "Dcalage x du label" +say ldy "Dcalage du label" +say fstyle "Style de police" +say fs "Taille de la police" +say bcol "Couleur d'arrire plan" +say fcol "Couleur du premier plan" +say lcol "couleur du label" +say yes "Oui" +say no "Non" +say courier "courrier (typewriter)" +say helvetica "helvetique (sansserif)" +say times "times (serif)" +say coords "Afficher sur le parent" + +say_category "Proprits du Gatom" +say width "Largeur" +say lo "Plus Petite Limite" +say hi "Plus Grande Limite" +say label "Label" +say wherelabel "Montrer le label allum" +say symto "Symbole d'envoie" +say symfrom "Symbole de reception" + +say_category "Proprits du graph" +say x1 "Depuis x" +say x2 "Vers x" +say xpix "Largeur d'cran" +say y2 "Depuis y" +say y1 "Vers y" +say ypix "Hauteur d'cran" + +say_category "Proprits du canevas" +#say xscale "X units/px" +#say yscale "Y units/px" +say gop "Afficher sur le parent" +say xmargin "Marge x" +say ymargin "Marge y" +say height "Hauteur" +say_category MainWindow +say name "Nom" +say n "Taille" +say xfrom "tendue sur x depuis" +say xto "tendue sur x jusque" +say yfrom "tendue sur y depuis" +say yto "tendue sur x jusque" + + +### Main Window + +say in "entre" +say out "sortie" +say audio "Audio" +say meters "Niveaux" +say io_errors "Erreurs d'E/S" +say cannot "ne peut" + +say_category Autre +say_namespace summary { + say_category IEMGUI +# say bng "Dclencheur" + say bng "Bang" + say tgl "Interrupteur" + say nbx "Boite de Nombre (IEM)" +# say Number2 "Nombre2" + say hsl "Glissire (Horizontale)" + say vsl "Glissire (Verticale)" +# say hsl "Slecteur (Horizontal)" + say hradio "Bote De Selection (Horizontale)" + say vradio "Bote De Selection (Verticale)" +# say cnv "Illustration (IEM)" + say cnv "Canevas (IEM)" + say dropper "Boite Pour Glisser-Dposer" + say vu "Vu-mtre" + + say_category GLUE + say bang "Envoyer un Dclenchement" + say float "Stocker et rapeller un nombre flottant" + say symbol "Stocker et rapeller un symbole" + say int "Stocker et rapeller un nombre entier" + say send "Envoyer un message vers un objet dfini" + say receive "Recevoir un message envoy" + say select "Tester la reception d'un chiffre ou d'un symbole" + say route "Diriger un message en fonction du premier lement" + say pack "Empaqueter un message" + say unpack "Dsempaqueter un message" + say trigger "Mettre en squence un message" + say spigot "Interrupteur de message" + say moses "partitionner un flot numrique en deux (mose)" + say until "Mchanisme de bouclage" + say print "Afficher un message dans la console" + say makefilename "Formater un symbole avec un champ variable" + say change "Supprimer les rpetitions d'un flot" + say swap "Permuter deux chiffres" + say value "Partager une valeur numrique" + + say_category TEMPS + say delay "Envoyer un dclenchement aprs un dlai" + say metro "Envoyer un dclenchement priodiquement" + say line "Envoyer linairement une serie de nombres " + say timer "Mesurer l'intervalle de temps" + say cputime "Mesurer le temps de calcul" + say realtime "Mesurer le temps rel" + say pipe "Dlai dynamique pour les nombres" + + say_category MATH + say + "Ajouter" + say - "Soustraire" + say * "Multiplier" + say {/ div} "Diviser" + say {% mod} "Retenue d'une Division" + say pow "Exponentiel" + say == "gal?" + say != "Different?" + say > "Plus Grand Que?" + say < "Plus Petit Que?" + say >= "Plus Grand ou gal ?" + say <= "Plus Petit ou gal ?" + say & "et logique (and)" + say | "ou logique (or)" + say && "et logique (and)" + say || "ou logique (or)" + say mtof "MIDI vers Hertz" + say ftom "Hertz vers MIDI" + say powtodb "Watts vers dB" + say dbtopow "dB vers Watts" + say rmstodb "Volts vers dB" + say dbtorms "dB vers Volts" + say {sin cos tan atan atan2 sqrt} "Trigonometrie" + say log "Logarithme d'Euler" + say exp "Exponentiel d'Euler" + say abs "Valeur absolue" + say random "Alatoire" + say max "Le plus grand des deux nombres" + say min "Le plus petit des deux nombres" + say clip "Forcer un nombre entre deux valeurs" + + say_category MIDI + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} "Entre MIDI" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} "Sortie MIDI" + say makenote "Programmer un message dcal \"note off\" correspondant un note-on" + say stripnote "ignorer les messages \"note off\" " + + say_category TABLEAUX + say tabread "Lire Un Nombre Depuis Une Tableau" + say tabread4 "Lire Un Nombre Depuis Une Tableau, avec 4 points d'interpolation" + say tabwrite "Ecrire un nombre dans une tableau" + say soundfiler "Lire et crire un tableau vers un fichier" + + say_category MISC + say loadbang "Dclencher au lancement" + say serial "Contrle du port serie pour NT seulement" + say netsend "Envoyer messages travers internet" + say netreceive "Recevoir messages travers internet" + say qlist "Squenceur de messages" + say textfile "Convertisseur de fichier vers messages" + say openpanel "dialogue \"Ouvrir\" " + say savepanel " dialogue \"Sauver sous\"" + say bag "Srie de nombres" + say poly "Allocation de voix polyphoniques" + say {key keyup} "Valeurs Numriques Du Clavier" + say keyname "Nom symbolique d'une touche de clavier" + + say_category "AUDIO MATH" + foreach word {+ - * /} {say $word~ "[say $word] (for signals)"} + say max~ "Valeur maximale de signaux" + say min~ "Valeur minimale de signaux" + say clip~ "Contraint le signal rester entre deux valeurs" + say q8_rsqrt~ "Racine carre rciproque (attention -- 8 bits!)" + say q8_sqrt~ "Racine carre (attention -- 8 bits!)" + say wrap~ "Envelopper autour (Genre De partie fractionnelle)" + say fft~ "Transformation discrte de Fourier" + say ifft~ "Partie complexe de la transforme de fourier" + say rfft~ "Partie relle de la transforme de fourier" + say rifft~ "Inverse de la Partie relle de la transforme de fourier" + say framp~ "Donne la rampe pour chaque block" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (pour les signaux)" + } +} + +### phase 3 + +say_namespace summary { + say_category "AUDIO GLUE" + say dac~ "Sortie audio" + say adc~ "Entre audio" + say sig~ "Conversion d'un nombre vers un signal" + say line~ "Gnre une rampe audio" + say vline~ "line~ de luxe" + say threshold~ "Dtecte les seuils d'un signal" + say snapshot~ "chantillonne un signal (converti en nombres)" + say vsnapshot~ "snapshot~ de luxe" + say bang~ "Envoie un signal de dclenchement aprs chaque block DSP" + say samplerate~ "Obtient la frquence d'chantillonnage" + say send~ "Connection d'un signal non-local avec un transcepteur" #fanout=transceiver=transcepteur + say receive~ "Reoit le signal venant de send~" + say throw~ "Additionne le Signal un bus" + say catch~ "Dfinit et lit la somme des signaux dans un bus" + say block~ "spcifie la taille et la convergence d'un block" + say switch~ "Permutation marche/arrt du calcul DSP" + say readsf~ "Lit un fichier audio depuis le disque" + say writesf~ "Enregistre un fichier audio sur le disque" + + say_category "OSCILLATEURS AUDIO ET TABLEAUX" + say phasor~ "Oscillateur en Dent De Scie" + say {cos~ osc~} "oscillateur cosinusodal" + say tabwrite~ "crit dans un tableau" + say tabplay~ "Lecture d'un tableau (non-transpositeur)" + say tabread~ "Lecture non-interpolatrice d'un tableau" + say tabread4~ "Lecture d'un tableau avec quatre points d'interpolation" + say tabosc4~ "Oscillateur table d'onde" + say tabsend~ "crit continuellement un block dans un tableau" + say tabreceive~ "Lit continuellement un block dans un tableau" + + say_category "FILTRES AUDIO" + say vcf~ "filtre contrl par un signal (voltage)" + say noise~ "gnrateur de bruit blanc" + say env~ "suiveur d'enveloppe" + say hip~ "filtre passe haut" + say lop~ "filtre passe bas" + say bp~ "filtre passe bande" + say biquad~ "filtre brut" + say samphold~ "chantillonneur bloqueur" + say print~ "imprime un ou plusieurs \"blocks\"" + say rpole~ "filtre valeurs relles brut un-ple" + say rzero~ "filtre valeurs relles brut zro-ple" + say rzero_rev~ "[say rzero~] (temps-invers)" + say cpole~ "[say rpole~] (valeurs-complexes)" + say czero~ "[say rzero~] (valeurs-complexes)" + say czero_rev~ "[say rzero_rev~] (valeurs-complexes)" + + say_category "DELAI AUDIO" + say delwrite~ "crit dans une ligne de delai" + say delread~ "lit depuis une ligne de delai" + say vd~ "lit depuis une ligne de delai un temps de delai variable" + + say_category "SUBWINDOWS" + say pd "Definie un subwindow" + say table "Tableau de nombres dans un subwindow" + say inlet "Ajouter une entre un pd" ###LOL + say outlet "Ajouter une sortie un pd" ###LOL + say inlet~ "[say inlet] (pour un signal)" + say outlet~ "[say outlet] (pour un signal)" + + say_category "CALIBRAGE DE DONNEES" + say struct "dfinit une structure de donnes" + say {drawcurve filledcurve} "Dessine une courbe" + say {drawpolygon filledpolygon} "Dessine un polygone" + say plot "parcelle le champ d'un tableau" + say drawnumber "Imprime une valeur numrique" + + say_category "ACCEDER AUX DONNEES" + say pointer "Pointe vers un objet appartenant un calibrage" + say get "Obtient des champs numriques" + say set "Change des champs numriques" + say element "Obtient l'lment d'un tableau" + say getsize "Obtient la taille d'un tableau" + say setsize "Change la taille d'un tableau" + say append "Ajoute un lment la liste" + say sublist "Obtient un pointeur dans la liste qui est l'lment d'un autre scalaire" + say scalar "Dessine un scalaire sur le parent" + + say_category "OBSOLETE" + say scope~ "(utiliser tabwrite~)" + say namecanvas "Renomme un patch" ;# what was this anyway? + say template "(utiliser struct)" +} + +# phase 4 (pdrc & ddrc) + +say section_audio "Audio" + say -r "Frquence d'chantillonage" + say -audioindev "Appareils audio en entre" + say -audiooutdev "Appareils audio en sortie" + say -inchannels "Nombre de canaux d'entre audio(par appareil, comme \"2\" ou \"16,8\")" + say -outchannels "Nombre de canaux de sortie audio (pareil)" + say -audiobuf "specifie la taille de la mmoire tampon en msec" + say -blocksize "specifie la taille du block audio I/O en nombre d'chantillons" + say -sleepgrain "specify number of milliseconds to sleep when idle" + say -nodac "Supprime la sortie audio" + say -noadc "Supprime l'entre audio" + say audio_api_choice "Audio API" + say default "default" + say -alsa "use ALSA audio API" + say -jack "use JACK audio API" + say -mmio "use MMIO audio API (default for Windows)" + say -portaudio "use ASIO audio driver (via Portaudio)" + say -oss "use OSS audio API" + say -32bit "allow 32 bit OSS audio (for RME Hammerfall)" + say {} "default" + +say section_midi "MIDI" + say -nomidiin "Supprime l'entre MIDI" + say -nomidiout "Supprime la sortie MIDI" + say -midiindev "Liste des appareils MIDI en entre; e.g., \"1,3\" pour le premier, et le troisime" + say -midioutdev "Liste des appareils MIDI en sortie, mme format" + +say section_externals "Externals" + say -path "Chemin de recherche de fichiers" + say -helppath "Chemin de recherche des fichiers d'aides" + say -lib "Charge une librairie d'objets" + +say section_gui "Gooey" + say -nogui "supprime le dmarrage du GUI (attention)" + say -guicmd "substitue le GUI un autre programme (e.g., rsh)" + say -console "console scrollback lines (0 = disable console)" + say -look "icne de la barre des boutons" + say -statusbar "active la barre de status" + say -font "Specifie la taille par dfaut de la police" + +say section_other "Autre" + say -open "ouvrir un ou plusieurs fichier(s) au dmarrage" + say -verbose "extra printout on startup and when searching for files" + say -d "debug level" + say -noloadbang "disable the effect of \[loadbang\]" + say -send "envoie un message au dmarrage (aprs que les patches soient chargs)" + say -listdev "list audio and MIDI devices upon startup" + say -realtime "use real-time priority (needs root privilege)" + +say section_paths "Paths" + +# phase 4B: ddrc (keyword names not finalized!) + +say section_color "couleurs" + say canvas_color "canevas" + say canvasbgedit "arrire plan d'un canevas (edit mode)" + say canvasbgrun "arrire plan d'un canevas (run mode)" + say object_color "objet" + say viewframe1 "couleur de la boite d'objet" + say viewframe2 "couleur de la boite d'objet" + say viewframe3 "couleur de la boite d'objet" + say viewframe4 "couleur du point culminant de la boite d'objet" + say viewbg "arrire plan d'un objet" + say viewfg "arrire plan d'un objet" + say commentbg "arrire plan d'un commentaire" + say commentfg "premier plan d'un commentaire" + say commentframe1 "comment frame" + say commentframe2 "comment frame" + say commentframe3 "comment frame" + say viewselectframe "boite selectionne" + say wire_color "cordon" + say wirefg "couleur d'un cordon" + say wirefg2 "couleur du point culminant d'un cordon" + say wiredspfg "couleur d'un cordon DSP" + say futurewiredash "nouveau cordon (en pointillets)" + say others_color "couleur des autres" + say boxinletfg "couleur de l'entre" + say boxoutletfg "couleur de la sortie" + say selrectrect "boite de selection" +say keys "touches" +say others "autres" +say canvashairstate "Activer le rticule" +say canvashairsnap "Crosshair snap to object" +say canvasstatusbar "Activer la barre du status" +say canvasbuttonbar "Activer la barre des boutons" +say wirewirearrow "Flche blanche" +say viewtooltip "ToolTip" +say canvasinsert_object "Inserer un objet" +say canvaschain_object "Enchanner un objet" +say canvasclear_wires "Supprimer les cordons" +say canvasauto_wire "Supprimer un objet" +# phase 5A + +say cannot "ne peut" +say cancel "Annuler" +say apply "Appliquer" +say ok "OK" +say popup_open "Ouvrir" +say popup_insert "Insrer" +say popup_properties "Proprits" +say popup_clear_wires "Supprimer les cordons" +say popup_auto_wire "Supprimer l'objet" +say popup_help "Aide" +say filter "Filtre: " +say how_many_object_classes "%d of %d classes d'objet" +say do_what_i_mean "Fais ce que je te dis" +say ask_cool "a serait cool, hein?" +say save_changes? "Sauvergarder les changements?" +say reset "Remise zero" +say Courier "Courier (monospaced)" +say Helvetica "Helvetica (sansserif)" +say Times "Times (serif)" +say add "Ajouter" +say up "Vers le haut" +say down "Vers le haut" +say remove "Supprimer" +say lib_add "Ajouter le nom ajout la liste" +say lib_up "Inverser l'ordre avec la lbrairie prcdente" +say lib_down "Inverser l'ordre avec la lbrairie suivante" +say lib_remove "Supprimer la librairie selectionne dans la liste" +say dir_add "Ajouter un dossier avec la boite de dialogue" +say dir_up "Inverser l'ordre avec le dossier prcdent" +say dir_down "Inverser l'ordre avec le dossier suivant" +say dir_remove "Supprimer le dossier selectionn dans la liste" +say client_class_tree "Client Class Tree" +say clipboard_view "Vue du presse-papier" +say command_history_view "Vue de l'historique de commandes" +say event_history_view "Vue de l'historique d'vnements" + +# during/after piksel: + +say auto_apply "Appliquer automatiquement" +say font_preview "Aperu" +say font_preview_2 "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n0123456789" +say font_style "Style:" +say font_bold "Gras" +say font_italic "Italique" +say font_family "Nom:" +say font_size "Taille:" +say damn "Zut alors!" +say console_clear "Effacer la console" +say horizontal "Horizontal" +say vertical "Vertical" diff --git a/desiredata/src/locale/index.tcl b/desiredata/src/locale/index.tcl new file mode 100644 index 00000000..c35c2156 --- /dev/null +++ b/desiredata/src/locale/index.tcl @@ -0,0 +1,21 @@ +say english "English" +say francais "Franais" +say espanol "Espaol" +say deutsch "Deutsch" +say bokmal "Norsk Bokml" +say italiano "Italiano" +say portugues "Portugus" +say catala "Catal" +say euskara "Euskara" +say polski "Polski" +say dansk "Dansk" +say nederlands "Nederlands" +say turkce "Trke" +say brasiliano "Brasiliano" + +# those were made using: +# iconv -f utf-8 -t ucs-2 | od -tx2 -An | sed 's/ /\\u/g' + +say chinese "\u4e2d\u6587" +say nihongo "\u65e5\u672c\u8a9e" +say russkij "\u0420\u0443\u0441\u0441\u043a\u0438\u0439" diff --git a/desiredata/src/locale/italiano.tcl b/desiredata/src/locale/italiano.tcl new file mode 100644 index 00000000..8513e4b9 --- /dev/null +++ b/desiredata/src/locale/italiano.tcl @@ -0,0 +1,544 @@ +#!/usr/bin/env tclsh +# English translations for PureData +# $Id: italiano.tcl,v 1.1.2.5.2.1 2006-12-05 04:51:47 matju Exp $ +# by Davide Morelli and Federico Ferri + +### Menus + +say file "File" + say new_file "Nuovo File" + say open_file "Apri File..." + say pdrc_editor "Editor di .pdrc" + say ddrc_editor "Editor di .ddrc" + say send_message "Invia un Messaggio..." + say paths "Percorsi..." + say close "Chiudi" + say save "Salva" + say save_as "Salva con nome..." + say print "Stampa..." + say quit "Esci" + + say canvasnew_file "Nuovo File" + say canvasopen_file "Apri File..." + say canvassave "Salva" + say canvassave_as "Salva con nome..." + say clientpdrc_editor ".pdrc Editor" + say clientddrc_editor ".ddrc Editor" + say canvasclose "Chiudi" + say canvasquit "Esci" + +say edit "Modifica" + say undo "Annulla" + say redo "Ripristina" + say cut "Taglia" + say copy "Copia" + say paste "Incolla" + say duplicate "Duplica" + say select_all "Seleziona tutto" + say text_editor "Editor di Testi..." + say font "Font" + say tidy_up "Tidy Up" + say edit_mode "Modalit modifica" + say editmodeswitch "Modalit modifica/esegui" + + say canvascut "Taglia" + say canvascopy "Copia" + say canvasundo "Annulla" + say canvasredo "Ripristina" + say canvaspaste "Incolla" + say canvasduplicate "Duplica" + say canvasselect_all "Seleziona tutto" + say canvaseditmodeswitch "Modalit modifica/esegui" + +say view "Visualizza" + say reload "Ricarica" + say redraw "Ridisegna" + + say canvasreload "Ricarica" + say canvasredraw "Ridisegna" + +say find "Trova" + say find_again "Cerca Ancora" + say find_last_error "Cerca l'ultimo errore" + say string "Cerca stringa" +say canvasfind "Trova" + say canvasfind_again "Cerca Ancora" + +# ??? +say put "Inserisci" + say Object "Oggetto" + say Message "Messaggio" + say Number "Numero" + say Symbol "Simbolo" + say Comment "Commento" + say Array "Array" + say Graph "Grafico" + say VU "Misuratore VU" + +say media "Media" + say audio_on "Audio ON" + say audio_off "Audio OFF" + say test_audio_and_midi "Test Audio e MIDI" + say load_meter "Misura carico cpu" + + say canvasaudio_on "Audio ON" + say canvasaudio_off "Audio OFF" + say clienttest_audio_and_midi "Test Audio e MIDI" + say canvasload_meter "Misura carico cpu" + +say window "Finestra" + +say help "Aiuto" + say about "Informazioni su..." + say pure_documentation "Guida..." + say class_browser "Elenco delle classi..." + + say canvasabout "Informazioni su..." + +say properties "Propriet" +say open "Apri" + +### for key binding editor +say general "Generali" +say audio_settings "Impostazioni audio" +say midi_settings "Impostazioni MIDI" +say latency_meter "Misura latenza" +say Pdwindow "Pd window" + +say canvaspdwindow "Pd window" +say canvaslatency_meter "Misura latenza" +say clientaudio_settings "Impostazioni audio" +say clientmidi_settings "Impostazioni MIDI" + +### for Properties Dialog (phase 5B) +say_category IEM +say w "larghezza(px)" +say h "altezza(px)" +say hold "hold time(ms)" +say break "break time(ms)" +say min "minimo" +say max "massimo" +say is_log "modo" +say linear "lineare" +say logarithmic "logaritmico" +say isa "val. iniziale" +say n "numero di scelte" +say steady "comportamento" +say steady_no "salta" +say steady_yes "stabile" +say snd "send-symbol" +say rcv "receive-symbol" +say lab "etichetta" +say ldx "etichetta offset-x" +say ldy "etichetta offset-y" +say fstyle "font" +say fs "dim. font" +say bcol "Colore sfondo" +say fcol "Colore primo piano" +say lcol "Colore etichetta" +say yes "si" +say no "no" +say courier "courier (typewriter)" +say helvetica "helvetica (sansserif)" +say times "times (serif)" +say coords "grafico su parent" + +say_category GAtomProperties +say width "larghezza" +say lo "limite inferiore" +say hi "limite superiore" +say label "etichetta" +say wherelabel "mostra etichetta" +say symto "send symbol" +say symfrom "receive symbol" + +say_category GraphProperties +say x1 "x da" +say x2 "x a" +say xpix "larghezza schermo" +say y2 "y da" +say y1 "y a" +say ypix "altezza schermo" + +say_category CanvasProperties +#say xscale "X units/px" +#say yscale "Y units/px" +say gop "graph on parent" +say xmargin "margine-x" +say ymargin "margine-y" +say height "altezza" +say_category ArrayProperties +say name "nome" +say n "dimensione" +say xfrom "x range da" +say xto "x range a" +say yfrom "y range da" +say yto "y range a" + + +say_category MainWindow +say in "in" +say out "out" +say audio "Audio" +say meters "Livelli" +say io_errors "Errori IO" + +### phase 2 + +say_category Other +say_namespace summary { + say_category IEMGUI + say bng "Bang Box" +# say Bang "Bang" +# say Toggle "Toggle" + say tgl "Toggle Box" + say nbx "Number Box (IEM)" +# say Number2 "Number2" + say hsl "Slider (Orizzontale)" + say vsl "Slider (Verticale)" + say hradio "Choice Box (Orizzontale)" + say vradio "Choice Box (Verticale)" +# say Vradio "Radio verticale" +# say Hradio "Radio orizzontale" + say cnv "Canvas (IEM)" +# say Canvas "Canvas" + say vu "Vumeter" + say dropper "Drag-and-Drop Box" + + say_category GLUE + say bang "invia un bang" + say float "salva e richiama un numero in virgola mobile" + say symbol "salva e richiama un simbolo" + say int "salva e richiama un numero intero" + say send "invia un messaggio ad un oggetto con nome" + say receive "cattura i messaggi inviati" + say select "invia un bang quando i numeri o i simboli combaciano" + say route "instrada i messaggi in base al primo elemento della lista" + say pack "impacchetta i valori in un messaggio" + say unpack "ricava gli elementi di un pacchetto" + say trigger "converte i messaggi ed esegue in sequenza" + say spigot "connessione interrompibile" + say moses "divide un flusso numerico" + say until "meccanismo per creare un ciclo" + say print "stampa messaggi in console" + say makefilename "formatta un simbolo con argomenti" + say change "rimuove le ripetizioni numeriche da un flusso" + say swap "scambia tra loro due numeri" + say value "valore numerico condiviso" + + say_category TIME + say delay "invia un messaggio dopo un ritardo temporale" + say metro "invia un messaggio periodicamente" + say line "invia una serie di numeri interpolati linearmente" + say timer "misura intervalli di tempo" + say cputime "misura l'utilizzo della CPU" + say realtime "misura il tempo reale" + say pipe "linea di ritardo dinamicamente dimensionabile per numeri" + + say_category MATH + say + "somma" + say - "sottrai" + say * "moltiplica" + say {/ div} "dividi" + say {% mod} "resto delal divisione intera" + say pow "esponenziale" + say == "uguale?" + say != "non uguale?" + say > "maggiore?" + say < "minore?" + say >= "non maggiore?" + say <= "non minore?" + say & "congiunzione binaria (and)" + say | "disgiunzione binaria (or)" + say && "congiunzione logica (and)" + say || "disgiunzione logica (or)" + say mtof "da MIDI a Hertz" + say mtof "da Hertz a MIDI" + say powtodb "da Watts a dB" + say dbtopow "da dB a Watts" + say rmstodb "da Volts a dB" + say dbtorms "da dB a Volts" + say {sin cos tan atan atan2 sqrt} "trigonometria" + say log "logaritmo di Eulero" + say exp "esponenziale di Eulero" + say abs "valore assoluto" + say random "valore casuale" + say max "il maggiore tra due numeri" + say min "il minore tra due numeri" + say clip "forza un numero dentro un intervallo" + + say_category MIDI + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} \ + "MIDI input" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} \ + "MIDI output" + say makenote \ + "crea e metti in coda un messaggio \"note off\" corrispondente a questo note-on" + say stripnote "togli i messaggi di \"note off\"" + + say_category TABLES + say tabread "leggi un valore da una tabella" + say tabread4 "leggi un valore da una tabella con interpolazione su 4 punti" + say tabwrite "scrivi un valore in una tabella" + say soundfiler "leggi e scrivi tabelle in file audio" + + say_category MISC + say loadbang "bang al caricamento" + say serial "controllo per la porta seriale (solo per NT)" + say netsend "manda messaggi su internet" + say netreceive "riceve messaggi da internet" + say qlist "sequencer di messaggi" + say textfile "convertitore di file in messaggi" + say openpanel "\"Apri\" dialog" + say savepanel "\"salva con nome\" dialog" + say bag "insieme di numeri" + say poly "allocazione polifonica di voci" + say {key keyup} "valori numerici da tastiera" + say keyname "valori in simboli da tastiera" + + say_category "AUDIO MATH" + foreach word {+ - * /} {say $word~ "[say $word] (per segnali)"} + say max~ "il valore massimo" + say min~ "il valore minimo" + say clip~ "costringe il segnale all'interno di un intervallo" + say q8_rsqrt~ "radice quadrata reciproca veloce (attenzione -- 8 bits!)" + say q8_sqrt~ "radice quadrata veloce (attenzione -- 8 bits!)" + say wrap~ "wraparound (fractional part, sort of)" + say fft~ "Trasformata di Fourier complessa discreta" + say ifft~ "Trasformata di Fourier complessa discreta inversa" + say rfft~ "Trasformata di Fourier reale discreta" + say rifft~ "Trasformata di Fourier reale discreta inversa" + say framp~ "output a ramp for each block" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (per segnali)" + } +} + + + ### phase 3 + +say_namespace summary { + say_category "AUDIO GLUE" + say dac~ "audio output" + say adc~ "audio input" + say sig~ "converte numeri in segnali audio" + say line~ "genera una rampa sui segnali" + say vline~ "line~ con maggiore precisione" + say threshold~ "avverte il superamento di una soglia" + say snapshot~ "converte un segnale in numero" + say vsnapshot~ "snapshot~ con maggiore precisione" + say bang~ "invia un bang dopo ogni blicco DSP" + say samplerate~ "invia la frequenza di campionamento" + say send~ "invia segnali" + say receive~ "riceve segnali da send~" + say throw~ "aggiunge il segnale ad un bus" + say catch~ "legge il segnale da un bus" + say block~ "specifica la dimensione del blocco DSP" + say switch~ "attiva o disattiva la computazione DSP in questa finestra" + say readsf~ "riproduzione di un file audio" + say writesf~ "registrazione di un file audio" + + say_category "AUDIO OSCILLATORS AND TABLES" + say phasor~ "oscillatore a onda triangolare" + say {cos~ osc~} "oscillatore a onda sinusoidale" + say tabwrite~ "scrive il segnale in una tabella" + say tabplay~ "riproduce il segnale registrato in una tabella (senza trasportare)" + say tabread~ "legge il segnale da una tabella senza interpolazione" + say tabread4~ "legge il segnale da una tabella con interpolazione su 4 punti" + say tabosc4~ "oscillatore wavetable" + say tabsend~ "scrive continuamente un blocco su una tabella" + say tabreceive~ "legge continuamente un blocco da una tabella" + + say_category "AUDIO FILTERS" + say vcf~ "filtro a voltaggio controllato" + say noise~ "rumore bianco" + say env~ "indicatore di inviluppo" + say hip~ "filtro passa alto" + say lop~ "filtro passa basso" + say bp~ "filtro a banda passante" + say biquad~ "filtro biquadratico" + say samphold~ "campiona e trattiene un segnale" + say print~ "scrive uno o pi blocchi sulla console" + say rpole~ "raw real-valued one-pole filter" + say rzero~ "raw real-valued one-zero filter" + say rzero_rev~ "[say rzero~] (time-reversed)" + say cpole~ "[say rpole~] (complex-valued)" + say czero~ "[say rzero~] (complex-valued)" + say czero_rev~ "[say rzero_rev~] (complex-valued)" + + say_category "AUDIO DELAY" + say delwrite~ "scrive una linea di ritardo" + say delread~ "legge una linea di ritardo" + say vd~ "legge una linea di ritardo con tempo variabile" + + say_category "SUBWINDOWS" + say pd "definisce una sottofinestra" + say table "un vettore di numeri in una sottofinestra" + say inlet "aggiunge un inlet" + say outlet "aggiunge un outlet" + say inlet~ "[say inlet] (per segnali)" + say outlet~ "[say outlet] (per segnali)" + + say_category "DATA TEMPLATES" + say struct "definisce una struttura dati" + say {drawcurve filledcurve} "disegna una curva" + say {drawpolygon filledpolygon} "disegna un poligono" + say plot "disegna un vettore" + say drawnumber "stampa un valore numerico" + + say_category "ACCESSING DATA" + say pointer "puntatore ad un oggetto che appartiene ad un template" + say get "ottiene una propriet numerica" + say set "imposta una propriet numerica" + say element "ottiene una propriet vettore" + say getsize "imposta una propriet vettore" + say setsize "cambia le dimensioni di un vettore" + say append "aggiunge un elemento ad una lista" + say sublist "ottiene un puntatore da una lista che un elemento di un'altro scalare" + say scalar "disegna uno scalare sulla finestra" + + say_category "OBSOLETE" + say scope~ "(usa tabwrite~)" + say namecanvas "" ;# what was this anyway? + say template "(usa struct)" +} + +# phase 4 (pdrc & ddrc) + +say section_audio "Audio" + say -r "frequenza di campionamento" + say -audioindev "periferiche audio in" + say -audiooutdev "periferiche audio out" + say -inchannels "canali audio input (per periferica, es.: \"2\" o \"16,8\")" + say -outchannels "numero di canali audio out (come sopra)" + say -audiobuf "dimensione del buffer audio in msec" + say -blocksize "dimensione del blocco I/O block in campioni" + say -sleepgrain "numero di millisecondi di attesa quando idle" + say -nodac "senza audio output" + say -noadc "senza audio input" + say audio_api_choice "Audio API" + say default "default" + say -alsa "usa ALSA audio API" + say -jack "usa JACK audio API" + say -mmio "usa MMIO audio API (default for Windows)" + say -portaudio "usa ASIO audio driver (via Portaudio)" + say -oss "usa OSS audio API" + say -32bit "permetti audio OSS a 32 bit (per RME Hammerfall)" + say {} "default" + +say section_midi "MIDI" + say -nomidiin "senza MIDI input" + say -nomidiout "senza MIDI output" + say -midiindev "lista periferiche midi in; es.: \"1,3\" per la prima e la terza" + say -midioutdev "lista periferiche midi out; come sopra" + +say section_externals "Externals" + say -path "percorso di ricerca files" + say -helppath "percorso di ricerca help" + say -lib "carica le seguenti librerie" + +say section_gui "Gooey" + say -nogui "senza avviare la GUI (attenzione)" + say -guicmd "programma GUI alternativo (es.: rsh)" + say -console "n. linee scrollback console (0 = disabilita la console)" + say -look "icone pulsantiera" + say -statusbar "abilita barra di stato" + say -font "dimensione di default per i font (in punti)" + +say section_other "Other" + say -open "apri file(s) all'avvio" + say -verbose "stampa messaggi extra all'avvio e durante la ricerca di files" + say -d "levello di debug" + say -noloadbang "disabilita l'effetto di \[loadbang\]" + say -send "manda un messaggio all'avvio (dopo il caricamento delle patch)" + say -listdev "elenca le periferiche audio e MIDI all'avvio" + say -realtime "usa real-time priority (necessita privilegi root)" + +say section_paths "Paths" + +# phase 4B: ddrc (keyword names not finalized!) + +say section_color "colors" + say canvas_color "canvas" + say canvasbgedit "sfondo canvas (modalit edit)" + say canvasbgrun "sfondo canvas (modalit run)" + say object_color "oggetto" + say viewframe1 "colore objectbox" + say viewframe2 "colore objectbox" + say viewframe3 "colore objectbox" + say viewframe4 "colore objectbox selezionato" + say viewbg "sfondo oggetto" + say viewfg "primo piano oggetto" + say commentbg "sfondo commento" + say commentfg "testo del commento" + say commentframe1 "cornice commento" + say commentframe2 "cornice commento" + say commentframe3 "cornice commento" + say viewselectframe "box selezione (viewselectframe)" + say wire_color "cavo" + say wirefg "colore cavo" + say wirefg2 "colore cavo selezionato" + say wiredspfg "colore cavo audio" + say futurewiredash "nuovo cavo (tratteggiato)" + say others_color "altro" + say boxinletfg "colore inlet color" + say boxoutletfg "colore outlet color" + say selrectrect "box selezione (selrectrect)" +say keys "tasti" +say others "altro" +say canvashairstate "Attiva cursore di precisione" +say canvashairsnap "Cursore \"magnetico\"" +say canvasstatusbar "Attiva barra di stato" +say canvasbuttonbar "Attiva pulsantiera" +say wirewirearrow "Wire Arrow" +say viewtooltip "Suggerimenti" +say canvasinsert_object "Inserisci oggetto" +say canvaschain_object "Cascata oggetto" +say canvasclear_wires "Elimina connessioni" +say canvasauto_wire "Rimuovi oggetto" +# phase 5A + +say cannot "non posso" +say cancel "Annulla" +say apply "Applica" +say ok "OK" +say popup_open "Apri" +say popup_insert "Inserisci" +say popup_properties "Propriet" +say popup_clear_wires "Elimina connessioni" +say popup_auto_wire "Rimuovi connessioni" +say popup_help "Aiuto" +say filter "Filtra: " +say how_many_object_classes "%d su %d oggetti" +say do_what_i_mean "Do What I Mean" +say save_changes? "Salvare le modifiche?" +say reset "Reset" +say Courier "Courier (monospaced)" +say Helvetica "Helvetica (sansserif)" +say Times "Times (serif)" +say add "Aggiungi" +say up "Spusta su" +say down "Sposta gi" +say remove "Rimuovi" +say lib_add "aggiungi il nome che hai scritto alla lista" +say lib_up "inverti di ordine con la libreria precedente" +say lib_down "inverti di ordine con la libreria successiva" +say lib_remove "rimuove la libreria selezionata dalla lista" +say dir_add "aggiunge una directory" +say dir_up "inverti di ordine con la directory precedente" +say dir_down "inverti di ordine con la directory successiva" +say dir_remove "rimuove la directory selezionata dalla lista" +say client_class_tree "Albero delle Classi" +say clipboard_view "Appunti" +say history_view "Storia" + +# during/after piksel: + +say auto_apply "Auto-Applica" +say font_preview "Anteprima:" +say font_preview_2 "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n0123456789" +say font_style "Stile:" +say font_bold "Grassetto" +say font_italic "Corsivo" +say font_family "Nome:" +say font_size "Dimensione font" diff --git a/desiredata/src/locale/localeutils.tcl b/desiredata/src/locale/localeutils.tcl new file mode 100755 index 00000000..f53c5b66 --- /dev/null +++ b/desiredata/src/locale/localeutils.tcl @@ -0,0 +1,109 @@ +#!/usr/bin/env tclsh +# to check the difference between locale files +# arguments: +# -list view the list of supported locales +# -lang <locale> compare <locale> to english +# no option: summary of all locales + +proc lwithout {a b} { + set r {} + foreach x $b {set c($x) {}} + foreach x $a {if {![info exists c($x)]} {lappend r $x}} + return $r +} + +proc lintersection {a b} { + set r {} + foreach x $b {set c($x) {}} + foreach x $a {if {[info exists c($x)]} {lappend r $x}} + return $r +} + +proc say {k args} { + global text + if {[llength $args]} { + set ${::name}($k) [lindex $args 0] + } else { + if {[info exist text($k)]} { + puts "------------" + return $text($k) + } else {return "{{$k}}"} + } +} + +proc say_namespace {k code} {uplevel 1 $code} +proc say_category {text} {} + +set ::name ::index +array set ::index {} +source index.tcl + +foreach lang [array names ::index] { + set ::name ::$lang + source $lang.tcl +} + +proc summary {} { + foreach lang [lsort [lwithout [array names ::index] english]] { + puts "#-----------------------------8<-----CUT------8<-----------------------------#" + key_compare $lang; value_compare $lang + } +} + +proc compare {lang} { + key_compare $lang + value_compare $lang +} + +proc value_compare {lang} { + puts "\nFollowing entries have the same value as English:" + set values {} + set english2 [array names ::english] + set locale [array names ::$lang] + set intersect [lintersection $english2 $locale] + foreach item [lsort $intersect] { + set val1 $::english($item) + set val2 [set ::${lang}($item)] + if {$val1 == $val2} { + lappend values $item + } + } + foreach item $values {puts "\t $item"} +} + + +proc key_compare {lang} { + set english2 [array names ::english] + set locale [array names ::$lang] + set diff_missing [lwithout $english2 $locale] + set diff_extra [lwithout $locale $english2] + set percent_missing [expr int(([llength $diff_missing] / [llength $english2].0)*100)] + set percent_extra [expr int(([llength $diff_extra] / [llength $english2].0)*100)] + puts "\ncompare $lang to english --> ${percent_missing}% difference" + foreach item [lsort $diff_missing] { + puts " - $item" + } + puts "\ncompare english to $lang --> ${percent_extra}% difference" + foreach item [lsort $diff_extra] { + puts " + $item" + } +} + + +if {![llength $argv]} { + summary +} else { + switch -regexp -- [lindex $argv 0] { + ^-list\$ {puts [lwithout [array names ::index] english]} + ^-lang\$ { + set lang [lindex $argv 1] + if {[lsearch [array names ::index] $lang] > 0} {compare $lang} {puts "Unknown locale '$lang'"} + } + ^-h\$ { + puts "-list view the list of supported locales" + puts "-lang <locale> only report about <locale> instead of about all locales" + } + default {puts "Unknown option '[lindex $argv 0]'. try -h for help"} + } +} + diff --git a/desiredata/src/locale/nederlands.tcl b/desiredata/src/locale/nederlands.tcl new file mode 100644 index 00000000..bb910dc7 --- /dev/null +++ b/desiredata/src/locale/nederlands.tcl @@ -0,0 +1,599 @@ +#!/usr/bin/env tclsh +# English translations for PureData +# $Id: nederlands.tcl,v 1.1.2.1 2007-10-05 23:14:58 matju Exp $ + +### Menus + +say file "Bestand" + say new_file "Nieuw Bestand" + say open_file "Open Bestand" + say server_prefs "Server Voorkeuren..." + say client_prefs "Client Voorkeuren..." + say pdrc_editor ".pdrc Bewerken" + say ddrc_editor ".ddrc Bewerken" + say send_message "Zend Boodschap..." + say paths "Paden..." + say close "Sluiten" + say save "Opslaan" + say save_as "Opslaan Als..." + say print "Afdrukken..." + say abort_server "Server Afsluiten" + say quit "Sluiten" + + say canvasnew_file "Nieuw Bestand" + say canvasopen_file "Open Bestand..." + say canvassave "Opslaan" + say canvassave_as "Opslaan Als..." + say clientpdrc_editor ".pdrc Editor" + say clientddrc_editor ".ddrc Editor" + say canvasclose "Venster Sluiten" + say canvasquit "Sluiten" + +say edit "Bewerken" + say undo "Ongedaan Maken" + say redo "Bewerking Herdoen" + say cut "Knippen" + say copy "Kopieren" + say paste "Plakken" + say duplicate "Dupliceren" + say select_all "Alles Selecteren" + say text_editor "Text Editor..." + say font "Lettertype" + say tidy_up "Opkuisen" + say edit_mode "Edit modus" + say editmodeswitch "Edit/Run modus" + + say canvascut "Knip" + say canvascopy "Kopiëer" + say canvasundo "Maak Ongedaan" + say canvasredo "Bewerking Opnieuw" + say canvaspaste "Plak" + say canvasduplicate "Dupliceer" + say canvasselect_all "Selecteer Alles" + say canvaseditmodeswitch "Edit/Run modus" + +say view "Beeld" + say reload "Herlaad" + say redraw "Herteken" + + say canvasreload "Herlaad" + say canvasredraw "Herteken" + +say find "Zoek" + say find_again "Zoek Opnieuw" + say find_last_error "Zoek Laatste Fout" + say string "Zoek String" +say canvasfind "Zoek" + say canvasfind_again "Zoek Opnieuw" + +# contents of Put menu is Phase 5C +say put "Plaats" + say Object "Object" + say Message "Boodschap" + say Number "Nummer" + say Symbol "Symbool" + say Comment "Commentaar" + say Graph "Graph" + say Bang "Bang" + say Toggle "Toggle" + say Number2 "Nummer2" + say Vslider "Verticale Schuifregelaar" + say Hslider "Horizontale Schuifregelaar" + say Vradio "Verticale Radio" + say Hradio "Horizontale Radio" + say Canvas "Canvas" + say Array "Array" + + say canvasobject "Object" + say canvasmessage "Boodschap" + say canvasnumber "Nummer" + say canvassymbol "Symbool" + say canvascomment "Commentaar" + say canvasbang "Bang" + say canvastoggle "Toggle" + say canvasnumber2 "Nummer2" + say canvasvslider "Verticale Schuifregelaar" + say canvashslider "Horizontale Schuifregelaar" + say canvasvradio "Verticale Radio" + say canvashradio "Horizontale Radio" + say canvascanvas "Canvas" + say canvasarray "Array" + +say media "Media" + say audio_on "Audio AAN" + say audio_off "Audio UIT" + say test_audio_and_midi "Test Audio en MIDI" + say load_meter "Belasting Meter" + + say canvasaudio_on "Audio AAN" + say canvasaudio_off "Audio UIT" + say clienttest_audio_and_midi "Test Audio en MIDI" + say canvasload_meter "Belasting Meter" + +say window "Venster" + +say help "Hulp" + say about "Over..." + say pure_documentation "Pure Documentatie..." + say class_browser "Class Browser..." + + say canvasabout "Over..." + +say properties "Eigenschappen" +say open "Open" +say documentation "Documentatie..." + +### for key binding editor +say general "Algemeen" +say audio_Instellingen "Audio Instellingen" +say midi_Instellingen "Midi Instellingen" +say latency_meter "Vertraging Meter" +say Pdwindow "Pd venster" + +say canvaspdwindow "Pd venster" +say canvaslatency_meter "Vertraging Meter" +say clientaudio_Instellingen "Audio Istellingen" +say clientmidi_Instellingen "Midi Instellingen" + +### for Properties Dialog (phase 5B) +say_category IEM +say w "breedte(px)" +say h "hoogte(px)" +say hold "houd tijd(ms)" +say break "breek tijd(ms)" +say min "minimum waarde" +say max "maximum waarde" +say is_log "modus" +say linear "lineair" +say logarithmic "logarithmisch" +say isa "init" +say n "aantal keuzen" +say steady "gelijkmatigheid" +say steady_no "verspring bij klik" +say steady_yes "blijf staan bij klik" +say snd "zend-symbool" +say rcv "ontvang-symbool" +say lab "etiket" +say ldx "etiket x afstand" +say ldy "etiket y afstand" +say fstyle "Lettertype" +say fs "letter grootte" +say bcol "achtergrond kleur" +say fcol "voorgrond kleur" +say lcol "etiket kleur" +say yes "ja" +say no "nee" +say courier "courier (typewriter)" +say helvetica "helvetica (sansserif)" +say times "times (serif)" +say coords "graph on parent" + +say_category GAtomProperties +say width "breedte" +say lo "ondergrens" +say hi "bovengrens" +say label "etiket" +say wherelabel "toon etiket aan" +say symto "zend symbool" +say symfrom "ontvang symbool" + +say_category GraphProperties +say x1 "x van" +say x2 "x tot" +say xpix "scherm breedte" +say y2 "y van" +say y1 "y tot" +say ypix "scherm hoogte" + +say_category CanvasProperties +#say xscale "X units/px" +#say yscale "Y units/px" +say gop "graph on parent" +say xmargin "xmarge" +say ymargin "ymarge" +say height "hoogte" +say_category ArrayProperties +say name "naam" +say n "grootte" +say xfrom "x omvang van" +say xto "x omvang tot" +say yfrom "y omvang van" +say yto "y omvang tot" + + +say_category MainWindow +say in "in" +say out "uit" +say audio "Audio" +say meters "Meters" +say io_errors "IO Foutmeldingen" + +### phase 2 + +say_category Other +say_namespace summary { + say_category IEMGUI + say bng "Bang Box" + say tgl "Toggle Box" + say nbx "Nummer Box (IEM)" + say hsl "Schuifregelaar (Horizontaal)" + say vsl "Schuifregelaar (Verticaal)" + say hradio "Keuze Box (Horizontaal)" + say vradio "Keuze Box (Verticaal)" + say cnv "Canvas (IEM)" + say dropper "Drag-and-Drop Box" + say vu "Vumeter" + + say_category GLUE + say bang "stuur een bang boodschap uit" + say float "een nummer opslaan en opvragen" + say symbol "een symbool opslaan en opvragen" + say int "een geheel getal opslaan en opvragen" + say send "stuur een boodschap naar benoemd object" + say receive "ontvang verzonden boodschappen" + say select "geef bang bij overeenkomstige nummers of symbolen" + say route "leid boodschappen om volgens eerste element" + say pack "maak samengestelde boodschappen" + say unpack "splits samengestelde boodschappen" + say trigger "sequentie en conversie van boodschappen" + say spigot "onderbreekbare verbinding" + say moses "een nummer-stroom splitsen" + say until "lus mechanisme" + say print "druk boodschap af" + say makefilename "maak een symbool met een variabel teken" + say change "verwijder herhalingen uit een nummer-stroom" + say swap "twee nummers omwisselen" + say value "gedeelde waarde" + + say_category TIME + say delay "stuur een boodschap na een vertraging" + say metro "stuur een periodieke bang" + say line "stuur een reeks lineair gerangschikte nummers" + say timer "meet tijdsintervallen" + say cputime "meet CPU tijd" + say realtime "meet echte tijd" + say pipe "dynamisch aanpasbare vertraging" + + say_category MATH + say + "plus" + say - "min" + say * "vermenigvuldig" + say {/ div} "delen" + say {% mod} "rest na deling" + say pow "machtsverheffing" + say == "is gelijk aan?" + say != "verschillend van?" + say > "is groter dan?" + say < "is kleiner dan?" + say >= "is groter dan of gelijk aan?" + say <= "is kleiner dan of gelijk aan?" + say & "bitsgewijze conjunctie (and)" + say | "bitsgewijze disjunctie (or)" + say && "logische conjunctie (and)" + say || "logische disjunctie (or)" + say mtof "MIDI naar Hertz" + say ftom "Hertz naar MIDI" + say powtodb "Watt naar dB" + say dbtopow "dB naar Watt" + say rmstodb "Volt naar dB" + say dbtorms "dB naar Volt" + say {sin cos tan atan atan2 sqrt} "driehoeksmeting" + say log "Euler logaritme" + say exp "Euler exponentieel" + say abs "absolute waarde" + say random "toeval" + say max "bovengrens waarde" + say min "ondergrens waarde" + say clip "houd een getal binnen een bereik" + + say_category MIDI + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} "MIDI ingang" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} "MIDI uitgang" + say makenote "plan een uitgestelde \"note off\" boodschap voor elke note-on" + say stripnote "verwijder \"note off\" boodschappen" + + say_category TABLES + say tabread "lees een nummer uit een tabel" + say tabread4 "lees een nummer uit een tabel, met 4 punts interpolatie" + say tabwrite "schrijf een nummer in een tabel" + say soundfiler "lees en schrijf tabellen van en naar bestanden" + + say_category MISC + say loadbang "bang bij laden" + say serial "seriele apparaatcontrole enkel voor NT" + say netsend "stuur boodschappen over het internet" + say netreceive "ontvang boodschappen over het internet" + say qlist "boodschap sequencer" + say textfile "bestand naar boodschap omzetter" + say openpanel "\"Open\" dialoogvenster" + say savepanel "\"Save as\" dialoogvenster" + say bag "verzameling nummers" + say poly "polyphonische stemtoewijzing" + say {key keyup} "numerieke waarden van toetsen" + say keyname "symbolische naam van toetsen" + + say_category "AUDIO WISKUNDE" + foreach word {+ - * /} {say $word~ "[say $word] (for signals)"} + say max~ "supremum van signalen" + say min~ "infimum van signalen" + say clip~ "beperk signaal tussen twee begrensingen" + say q8_rsqrt~ "goedkope reciprocal vierkantswortel (let op -- 8 bits!)" + say q8_sqrt~ "goedkope vierkantswortel (let op -- 8 bits!)" + say wrap~ "wraparound (fractional part, sort of)" + say fft~ "complex forward discrete Fourier transform" + say ifft~ "complex inverse discrete Fourier transform" + say rfft~ "real forward discrete Fourier transform" + say rifft~ "real inverse discrete Fourier transform" + say framp~ "output a ramp for each block" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (for signals)" + } +} + +### phase 3 + +say_namespace summary { + say_category "AUDIO LIJM" + say dac~ "audio uitgang" + say adc~ "audio ingang" + say sig~ "zet nummers om naar audiosignaal" + say line~ "genereer audio helling" + say vline~ "deluxe line~" + say threshold~ "detecteer audio grens" + say snapshot~ "neem staal van signaal (converteer terug naar nummer)" + say vsnapshot~ "deluxe snapshot~" + say bang~ "stuur een bang boodschap na elke DSP blok" + say samplerate~ "geef de sample rate" + say send~ "niet-locale signaal verbinding met uitwaaiering" + say receive~ "ontvang signaal van send~" + say throw~ "voeg toe aan een summing bus" + say catch~ "definieer en lees van een summing bus" + say block~ "specifieer blok grootte en overlapping" + say switch~ "schakel DSP berekeningen aan een uit" + say readsf~ "speel een geluidsbestand af van schijf" + say writesf~ "neem geluid op naar schijf" + + say_category "AUDIO OSCILLATOREN AND TABELLEN" + say phasor~ "zaagtand oscillator" + say {cos~ osc~} "cosinus oscillator" + say tabwrite~ "schrijf naar een tabel" + say tabplay~ "speel af van een tabel (niet-transponerend)" + say tabread~ "niet-interpolerend lezen van tabel" + say tabread4~ "4 punts interpolerend lezen van tabel" + say tabosc4~ "golfvormtabel oscillator" + say tabsend~ "schijf een blok continu naar een tabel" + say tabreceive~ "lees een blok continu van een tabel" + + say_category "AUDIO FILTERS" + say vcf~ "voltage controlled filter" + say noise~ "witte ruis generator" + say env~ "envelope follower" + say hip~ "hoog doorlaat filter" + say lop~ "laag doorlaat filter" + say bp~ "band doorlaat filter" + say biquad~ "rauwe filter" + say samphold~ "sample and hold eenheid" + say print~ "druk een of meer \"blokken\" af" + say rpole~ "raw real-valued one-pole filter" + say rzero~ "raw real-valued one-zero filter" + say rzero_rev~ "[say rzero~] (time-reversed)" + say cpole~ "[say rpole~] (complex-valued)" + say czero~ "[say rzero~] (complex-valued)" + say czero_rev~ "[say rzero_rev~] (complex-valued)" + + say_category "AUDIO VERTRAGING" + say delwrite~ "schrijf naar vertragingslijn" + say delread~ "lees van vertragingslijn" + say vd~ "lees van een vertragingslijn met een variabele tijd" + + say_category "SUBVENSTERS" + say pd "definieer een subvenster" + say table "reeks nummers in een subvenster" + say inlet "voeg een ingang toe aan een subvenster" + say outlet "voeg een uitgang toe aan een subvenster" + say inlet~ "[say inlet] (voor signaal)" + say outlet~ "[say outlet] (voor signaal)" + + say_category "DATA TEMPLATES" + say struct "definieer een datastructuur" + say {drawcurve filledcurve} "teken een curve" + say {drawpolygon filledpolygon} "teken een veelhoek" + say plot "teken een array veld" + say drawnumber "druk een numerieke waarde af" + + say_category "DATA BEREIKEN" + say pointer "wijs naar een object dat bij een template hoort" + say get "numerieke velden lezen" + say set "numerieke velden schrijven" + say element "een array element lezen" + say getsize "de grootte van een array opvragen" + say setsize "de grootte van een array wijzigen" + say append "een element aan een lijst toevoegen" + say sublist "een pointer in een lijst krijgen die een element is van een andere scalar" + say scalar "teken een scalar op parent" + + say_category "OBSOLETE" + say scope~ "(use tabwrite~ now)" + say namecanvas "" ;# what was this anyway? + say template "(use struct now)" +} + +# phase 4 (pdrc & ddrc) + +say section_audio "Audio" + say -r "sample rate" + say -audioindev "audio in apparaten" + say -audiooutdev "audio out apparaten" + say -inchannels "aantal audio-in kanalen (per apparaat, bijvoorbeeld \"2\" of \"16,8\")" + say -outchannels "aantal audio-uit kanalen (idem)" + say -audiobuf "specifieer grootte van audio buffer in msec" + say -blocksize "specifieer audio IN/UIT blok grootte in sample frames" + say -sleepgrain "specifieer het aantal milliseconden slaap wanneer inactief" + say -nodac "onderdruk audio uitgang" + say -noadc "onderdruk audio ingang" + say audio_api_choice "Audio API" + say default "default" + say -alsa "gebruik ALSA audio API" + say -jack "gebruik JACK audio API" + say -mmio "gebruik MMIO audio API (default voor Windows)" + say -portaudio "gebruik ASIO audio stuurprogramma (via Portaudio)" + say -oss "gebruik OSS audio API" + say -32bit "sta 32 bit OSS audio toe (voor RME Hammerfall)" + say {} "default" + +say section_midi "MIDI" + say -nomidiin "onderdruk MIDI ingang" + say -nomidiout "onderdruk MIDI uitgang" + say -midiindev "midi in apparatenlijst; bijvb., \"1,3\" voor eerste en derde" + say -midioutdev "midi uit apparatenlijst, (idem)" + +say section_externals "Externals" + say -path "zoek-pad voor bestanden" + say -helppath "zoek-pad voor help bestanden" + say -lib "laad objecten bibliotheek" + +say section_gui "Goe-wie" + say -nogui "start zonder grafische interface (opgelet)" + say -guicmd "gebruik een andere grafische interface (bijvb., rsh)" + say -console "console terug scroll lijnen (0 = schakel console uit)" + say -look "ikonen knoppenbalk" + say -statusbar "activeer statusbalk" + say -font "specifieer lettertekengrootte" + +say section_other "Andere" + say -open "open bestand bij opstarten" + say -verbose "extra informatie afdrukken vij opstarten en het zoeken naar bestanden" + say -d "debug niveau" + say -noloadbang "desactiveer \[loadbang\]" + say -send "stuur een boodschap (nadat de patches geladen zijn)" + say -listdev "geef lijst weer van audio en midi apparaten bij opstarten" + say -realtime "gebruik realtime prioriteit (vereist root-privileges)" + +say section_paths "Paden" + +# phase 4B: ddrc (keyword names not finalized!) + +say section_color "kleuren" + say canvas_color "canvas" + say canvasbgedit "canvas achtergrond (edit modus)" + say canvasbgrun "canvas achtergrond (run modus)" + say object_color "object" + say viewframe1 "objectbox kleur" + say viewframe2 "objectbox kleur" + say viewframe3 "objectbox kleur" + say viewframe4 "objectbox geselecteerd kleur" + say viewbg "object achtergrond" + say viewfg "object voorgrond" + say commentbg "comment achtergrond" + say commentfg "comment voorgrond" + say commentframe1 "commentaar frame" + say commentframe2 "commentaar frame" + say commentframe3 "commentaar frame" + say viewselectframe "geselecteerde box" + say wire_color "draad" + say wirefg "draad kleur" + say wirefg2 "draad geselecteerd" + say wiredspfg "dsp draad kleur" + say futurewiredash "nieuwe (stippelijn) draad" + say others_color "andere" + say boxinletfg "inlet kleur" + say boxoutletfg "outlet kleur" + say selrectrect "selectie box" +say keys "toetsen" +say others "andere" +say canvashairstate "Activeer crosshair" +say canvashairsnap "Crosshair spring naar object" +say canvasstatusbar "Activeer statusbalk" +say canvasbuttonbar "Activeer knoppenbalk" +say wirewirearrow "Draad Pijl" +say viewtooltip "ToolTip" +say canvasinsert_object "Voeg object in" +say canvaschain_object "Ketting object" +say canvasclear_wires "Wis draden" +say canvasauto_wire "Verwijder object" +say subpatcherize "Subpatcherizeer" +say keynav "toetsenbord navigatie" +say key_nav_up "omhoog" +say key_nav_up_shift "voeg toe aan selectie" +say key_nav_down "omlaag" +say key_nav_down_shift "voeg toe aan selectie" +say key_nav_right "rechts" +say key_nav_right_shift "voeg toe aan selectie" +say key_nav_left "links" +say key_nav_left_shift "voeg toe aan selectie" +say key_nav_ioselect "selecteer in/outlets" + +# phase 5A + +say cannot "Kan niet" +say cancel "Annuleer" +say apply "Pas Toe" +say ok "Akkoord" +say popup_open "Open" +say popup_insert "Invoegen" +say popup_properties "Eigenschappen" +say popup_clear_wires "Wis draden" +say popup_auto_wire "Verwijder object (autowire)" +say popup_help "Help" +say popup_remove_from_path "Verwijder object uit pad" +say popup_delete_from_path "Wis object uit pad" +say popup_help "Hulp" +say filter "Filter: " +say do_what_i_mean "Do What I Mean" +say ask_cool "Dit zou een leuke functie zijn he ?" +say reset "Reset" +say filter "Filter: " +say how_many_object_classes "%d of %d object classes" +say do_what_i_mean "Doe wat ik bedoel" +say save_changes? "Wijzigingen opslaan?" +say reset "Herstel" +say Courier "Courier (monospaced)" +say Helvetica "Helvetica (sansserif)" +say Times "Times (serif)" +say add "Voeg toe" +say up "Opwaards" +say down "Neerwaards" +say remove "Verwijder" +say lib_add "Voeg de naam die u ingaf toe aan de lijst" +say lib_up "Verwissel volgorde met vorige bibliotheek" +say lib_down "Verwissel volgorde met volgende bibliotheek" +say lib_remove "verwijder bibliotheek geselecteerd in de lijst" +say dir_add "voeg een map toe door middel van een dialoogvenster" +say dir_up "verwissel volgorde met vorige map" +say dir_down "verwissel volgorde met volgende map" +say dir_remove "verwijder map geselecteerd in de lijst" +say client_class_tree "Client Class Tree" +say clipboard_view "Kladblok Weergave" +say history_view "Geschiedenis Weergave" +say command_history_view "Geschiedenis Commando's" +say event_history_view "Geschiedenis Gebeurtenissen" + +# during/after piksel: + +say auto_apply "Automatisch-Toepassen" +say font_preview "Voorsmaak:" +say font_preview_2 "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n0123456789" +say font_style "Stijl:" +say font_bold "Vet" +say font_italic "Scontchuingedrukt" +say font_family "Naam:" +say font_size "Lettertekengrootte" +say damn "Miljaar!" +say console_clear "Console Leegmaken" +say horizontal "Horizontaal" +say vertical "Verticaal" +say language "Taal" + +# 2007: + +say no_matches "(geen overeenkomsten)" +say preset "preset" +say canvasgrid "Grid kleur" +say grid_size "Grid grootte" +say gridstate "Activateer achtergrond grid" +say snap_grid "Verspring volgens grid eenheden" +say viewfont "lettertype voor objecten" +say consolefont "lettertype voor console" +say keyboarddialogfont "lettertype voor virtueel toetsenbord" +say keyboard_view "Virtueel toetsenbord" +say log_height "Log Hoogte" diff --git a/desiredata/src/locale/nihongo.tcl b/desiredata/src/locale/nihongo.tcl new file mode 100644 index 00000000..ecedbf44 --- /dev/null +++ b/desiredata/src/locale/nihongo.tcl @@ -0,0 +1,574 @@ +#!/usr/bin/env tclsh +# Japanese translations for PureData +# $Id: nihongo.tcl,v 1.1.2.1 2007-08-12 05:56:49 matju Exp $ +# by Kentaro Fukuchi and friends + +### Menus +say file "ファイル" + say new_file "新規作成" + say open_file "ファイルを開く..." + say server_prefs "サーバー設定..." + say client_prefs "クライアント設定..." + say send_message "メッセージ送信..." + say paths "パス..." + say close "閉じる" + say save "保存" + say save_as "名前を付けて保存..." + say print "印刷..." + say abort_server "サーバー停止" + say quit "終了" + + say canvasnew_file "新規作成" + say canvasopen_file "ファイルを開く..." + say canvassave "保存" + say canvassave_as "名前を付けて保存..." + say clientpdrc_editor ".pdrc エディター" + say clientddrc_editor ".ddrc エディター" + say canvasclose "閉じる" + say canvasquit "終了" + +say edit "編集" + say undo "元に戻す" + say redo "やり直し" + say cut "カット" + say copy "コピー" + say paste "ペースト" + say duplicate "複製" + say select_all "すべてを選択" + say clear_selection "選択を解除" + say text_editor "テキストエディタ..." + say font "フォント" + say tidy_up "整列";## tidy up これでよいか? + say edit_mode "編集モード" + say editmodeswitch "編集/実行モード切替" + say subpatcherize "サブパッチ化";## Subpacherizer これでよいか? + + say canvascut "カット" + say canvascopy "コピー" + say canvasundo "元に戻す" + say canvasredo "やり直し" + say canvaspaste "ペースト" + say canvasduplicate "複製" + say canvasselect_all "すべてを選択" + say canvaseditmodeswitch "編集/実行モード切替" + +say view "表示" + say reload "再読込み" + say redraw "再描画" + + say canvasreload "再読込み" + say canvasredraw "再描画" + +say find "検索" + say find_again "再検索" + say find_last_error "最後のエラーを検索" + say string "文字列の検索" +say canvasfind "Find" + say canvasfind_again "再検索" + +# contents of Put menu is Phase 5C +say put "挿入";## オブジェクトの挿入というニュアンスでよいか? + say Object "オブジェクト" + say Message "メッセージ" + say Number "ナンバー" + say Symbol "シンボル" + say Comment "コメント" + say Graph "グラフ" + say Array "配列" + +say media "メディア" + say audio_on "オーディオON" + say audio_off "オーディオOFF" + say test_audio_and_midi "オーディオとMIDIのテスト" + say load_meter "負荷メーター" + + say canvasaudio_on "オーディオON" + say canvasaudio_off "オーディオOFF" + say clienttest_audio_and_midi "オーディオとMIDIのテスト" + say canvasload_meter "負荷メーター" + +say window "ウィンドウ" + +say help "ヘルプ" + say about "Desire Dataについて..." + say documentation "ドキュメント..." + say class_browser "クラス・ブラウザー..." + + say canvasabout "Desire Dataについて..." + +say properties "プロパティ" +say open "開く" + +### for key binding editor +say general "一般" +say audio_settings "オーディオの設定" +say midi_settings "MIDIの設定" +say latency_meter "レイテンシ・メーター" +say Pdwindow "Pdウィンドウ" + +say canvaspdwindow "Pdウィンドウ" +say canvaslatency_meter "レイテンシ・メーター" +say clientaudio_settings "オーディオの設定" +say clientmidi_settings "MIDIの設定" + +### for Properties Dialog (phase 5B) +say_category IEM +say w "幅(px)" +say h "高さ(px)" +say hold "ホールド(ms)" +say break "break time(ms)";## すみません、ニュアンスわかりません +say min "最小値" +say max "最大値" +say is_log "モード" +say linear "線型" +say logarithmic "対数" +say isa "初期化" +say n "number of choices" +say steady "クリック時動作";##Steadiness 意訳でよいか? +say steady_no "ジャンプ" +say steady_yes "ジャンプ無";##steady on clickこれでよいか? +say snd "送信先シンボル" +say rcv "受信元シンボル" +say lab "ラベル" +say ldx "ラベル x方向オフセット" +say ldy "ラベル y方向オフセット" +say fstyle "フォント" +say fs "サイズ" +say bcol "背景カラー";##「背景色」も検討したが「ラベル色」としないよう統一 +say fcol "前景カラー" +say lcol "ラベルカラー" +say yes "はい" +say no "いいえ" +say courier "courier (typewriter)";##フォント名のためママでよいか? +say helvetica "helvetica (sansserif)" +say times "times (serif)" +say coords "描画";##Graph on parent 意訳でよいか? + +say_category GAtomProperties +say width "幅" +say lo "最小値" +say hi "最大値" +say label "ラベル" +say wherelabel "ラベル表示位置";##おそらく未使用 +say symto "送信先シンボル" +say symfrom "受信元シンボル" + +say_category GraphProperties +say x1 "x 開始値" +say x2 "x 終了値" +say xpix "幅" +say y2 "y 開始値" +say y1 "y 終了値" +say ypix "高さ" + +say_category CanvasProperties +#say xscale "X ピクセル毎単位";##unitx/px この訳でよいか +#say yscale "Y ピクセル毎単位" +say gop "描画";##この訳でよいか? +say xmargin "xマージン" +say ymargin "yマージン" +say height "高さ" +say_category ArrayProperties +say name "名前" +say n "サイズ" +say xfrom "x 開始値" +say xto "x 終了値" +say yfrom "y 開始値" +say yto "y 終了値" + + +say_category MainWindow +say in "in" +say out "out" +say audio "オーディオ" +say meters "メーター" +say io_errors "I/Oエラー" +say tcl_console "Tclクライアント" +say pd_console "Pdサーバー" + +### phase 2 + +say_category Other +say_namespace summary { + say_category IEMGUI + say bng "Bangボックス" + say tgl "トグル・ボックス" + say nbx "ナンバー・ボックス(IEM)" + say hsl "スライダー(水平)" + say vsl "スライダー(垂直)" + say hradio "選択ボックス(水平)" + say vradio "選択ボックス(垂直)" + say cnv "キャンバス(IEM)" + say dropper "ドラッグ&ドロップ・ボックス" + say vu "VUメーター" + + say_category GLUE + say bang "Bangを送信" + say float "数値の保存/読出し" + say symbol "シンボルの保存/読出し" + say int "整数の保存/読出し" + say send "オブジェクトへメッセージを送信" + say receive "送信されたメッセージの受信" + say select "数値またはシンボルの一致を検査する" + say route "先頭の要素を評価し、ルートを分岐する" + say pack "メッセージを結合する" + say unpack "結合されたメッセージを分離する" + say trigger "任意の形式に変換したメッセージを、任意の順で送信" + say spigot "メッセージをフィルタ" + say moses "連続する数値を、指定した値を境に分岐して出力" + say until "ループ機能" + say print "メッセージを表示" + say makefilename "変数を含むシンボルをファイル名の形式に変換" + say change "連続する数値のうち、重複するものをフィルタ" + say swap "二つの値を入れ替える" + say value "グローバル変数の保存/読出し" + + say_category TIME + say delay "メッセージを遅延させる" + say metro "定期的にメッセージを送信" + say line "直線的に変化する連続した数値を送信" + say timer "経過時間を計測" + say cputime "CPU時間を計測" + say realtime "実時間を計測" + say pipe "数値送信に用いる、可変長のディレイラインを作成" + + say_category MATH + say + "加算" + say - "減算" + say * "乗算" + say {/ div} "除算" + say {% mod} "余り" + say pow "対数" + say == "等号" + say != "不等号" + say > "大なり" + say < "小なり" + say >= "大なりイコール" + say <= "小なりイコール" + say & "ビット演算 (and)" + say | "ビット演算 (or)" + say && "論理積 (and)" + say || "論理和 (or)" + say mtof "MIDIノー・トナンバーを周波数に変換" + say ftom "周波数をMIDIノート・ナンバーに変換" + say powtodb "ワット数をdBに変換" + say dbtopow "dBをワット数に変換" + say rmstodb "電圧をdBに変換" + say dbtorms "dBを電圧に変換" + say {sin cos tan atan atan2 sqrt} "三角関数" + say log "自然対数" + say exp "指数関数" + say abs "絶対値" + say random "乱数" + say max "二項のうち、より大きい数" + say min "二項のうち、より小さい数" + say clip "数値をしきい値内におさめる" + + say_category MIDI + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} "MIDI入力" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} "MIDI出力" + say makenote "ノートオンを送信し、指定した時間経過後にノート・オフを送信" + say stripnote "連続するノート・オフをフィルター" + + say_category TABLES + say tabread "テーブルから数値読出し" + say tabread4 "4点による擬似補間を用いて、テーブルから数値を読出し" + say tabwrite "テーブルに数値を書込み" + say soundfiler "テーブルからファイルへ、相互に読出し/書込み" + + say_category MISC + say loadbang "読込時にBangを送信" + say serial "シリアル・デバイス・コントロール(NTのみ)" + say netsend "インターネットを介してメッセージ送信" + say netreceive "インターネットを介してメッセージ受信" + say qlist "メッセージ・シーケンサー" + say textfile "ファイルを読み込みメッセージを生成" + say openpanel "「ファイルを開く」ダイアログを表示" + say savepanel "「ファイルを保存」ダイアログを表示" + say bag "数値の集合を保持" + say poly "ポリフォニックの入力信号を管理" + say {key keyup} "キーボード入力のアスキー・コードを送信" + say keyname "キーボード入力文字を送信" + + say_category "AUDIO MATH" + foreach word {+ - * /} {say $word~ "[say $word] (シグナル用)"} + say max~ "シグナルの最大値" + say min~ "シグナルの最小値" + say clip~ "シグナルの値をしきい値内に強制変換" + say q8_rsqrt~ "簡易版平方根の逆数 (注意:8ビット)" + say q8_sqrt~ "簡易版平方根 (注意:8ビット)" + say wrap~ "入力値にもっとも近い整数との差 (入力が正の場合は小数部)" + say fft~ "複素離散フーリエ変換" + say ifft~ "複素逆離散フーリエ変換" + say rfft~ "実離散フーリエ変換" + say rifft~ "逆離散フーリエ変換" + say framp~ "output a ramp for each block" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (シグナル用)" + } +} + +### phase 3 + +say_namespace summary { + say_category "音声結線" + say dac~ "音声出力" + say adc~ "音声入力" + say sig~ "数値を音声信号に変換" + say line~ "音声に時間変化の勾配を付加" + say vline~ "line~の機能拡張版" + say threshold~ "信号からしきい値を検出" + say snapshot~ "信号をサンプリング(数値に書き戻す)" + say vsnapshot~ "snapshot~の機能拡張版" + say bang~ "BANGメッセージを以降の全てのDSPブロックに出力" + + say samplerate~ "サンプリング周波数を取得" + + say send~ "複数の出力を備えた遠隔接続" + say receive~ "send~より信号を受け取る" + say throw~ "加算バス(ミキサ)に追加する" + say catch~ "加算バス(ミキサ)の内容を読み出す" + say block~ "ブロックの大きさとオーバーラップを指定" + say switch~ "DSP処理をオン/オフ" + say readsf~ "ディスク上の音声ファイルを再生" + say writesf~ "音声をディスクに記録" + + say_category "オシレータとテーブル" + say phasor~ "鋸状波オシレータ" + say {cos~ osc~} "サイン波オシレータ" + say tabwrite~ "テーブルへ書き込む" + say tabplay~ "テーブルから再生(移調は伴わない)" + say tabread~ "補間を行わずにテーブルから読み込む" + say tabread4~ "四点多項式による補間を行いながらテーブルから読み込む" + say tabosc4~ "ウェーブテーブルオシレータ" + say tabsend~ "テーブルへ1ブロックを連続的に書き込む" + say tabreceive~ "テーブルから1ブロックを連続的に読み出す" + + say_category "フィルタ" + say vcf~ "電圧制御式バンドパスフィルタ" + say noise~ "ホワイトノイズ発生器" + say env~ "エンヴェロープフォロワ " + say hip~ "ハイパスフィルタ" + say lop~ "ローパスフィルタ" + say bp~ "バンドパスフィルタ" + say biquad~ "引数により様々な設計ができるフィルタ" + say samphold~ "サンプルアンドホールド" + say print~ "ひとつまたは複数のブロックの音声信号をコンソールに表示" + say rpole~ "単極(再帰)フィルタ" + say rzero~ "1ゼロ点(非再帰)フィルタ" + say rzero_rev~ "反転1ゼロ点(非再帰)フィルタ" + say cpole~ "複素単極(再帰)フィルタ" + say czero~ "複素1ゼロ点(非再帰)フィルタ" + say czero_rev~ "複素反転1ゼロ点(非再帰)フィルタ" + + say_category "ディレイ" + say delwrite~ "ディレイラインに書き込み" + say delread~ "ディレイラインから読み出す" + say vd~ "ディレイラインから任意のタイミングで読み出す" + + say_category "サブウインドウ" + say pd "サブウインドウを定義" + say table "サブウインドウ内で数値を配列" + say inlet "サブウインドウ内へ結線" + say outlet "サブウインドウ外へ結線" + say inlet~ "サブウインドウ内へ結線(音声信号用)" + say outlet~ "サブウインドウ外へ結線(音声信号用)" + + say_category "データテンプレート" + say struct "データの構造を定義" + say {drawcurve filledcurve} "曲線を描く" + say {drawpolygon filledpolygon} "多角形を描く" + say plot "配列を描画" + say drawnumber "数値を表示" + + say_category "データアクセス" + say pointer "テンプレートに属するオブジェクトを指定" + say get "数値データを取得" + say set "数値データを任意の値に書き換え" + say element "配列の要素を取得" + say getsize "配列の大きさを取得" + say setsize "配列の大きさを変更" + say append "リストに要素を付加" + say sublist "リストからポインタを取得(これは他のスケーラの一要素です)" + say scalar "スケーラを親ウインドウに表示" + + say_category "もう使う必要のないもの" + say scope~ "(tabwrite~に統合されました)" + say namecanvas "" ;# what was this anyway? 正直、これってなんだっけ? + say template "(structに統合されました)" +} + +# phase 4 (pdrc & ddrc) + +say section_audio "オーディオ" + say -r "サンプリング周波数" + say -audioindev "音声入力デバイス" + say -audiooutdev "音声出力デバイス" + say -inchannels "音声入力チャンネル(デバイスによる。例えば“2”や“16,8”のように。)" + say -outchannels "音声出力チャンネル(入力に同じ)" + say -audiobuf "音声バッファの大きさをミリ秒で定義" + say -blocksize "音声入力/出力のブロックの大きさをサンプルフレーム数で定義" + say -sleepgrain "ミリ秒で定義される値をアイドル時にスリープさせる" + say -nodac "音声出力を停止" + say -noadc "音声入力を停止" + say audio_api_choice "オーディオAPI" + say default "デフォルト" + say -alsa "オーディオAPIとしてALSAを使う" + say -jack "オーディオAPIとしてJACKを使う" + say -mmio "オーディオAPIとしてMMIOを使う(Windows標準)" + say -portaudio "ASIOドライバを使う(Portaudioを通じて)" + say -oss "オーディオAPIとしてOSSを使う" + say -32bit "32ビットのOSSオーディオを許可する(RME Hammerfallのみ)" + say {} "デフォルト" + +say section_midi "MIDI" + say -nomidiin "MIDI入力を停止" + say -nomidiout "MIDI出力を停止" + say -midiindev "MIDIインデバイスのリスト(用例:“1,3”で1番目と3番目)" + say -midioutdev "MIDIアウトデバイスのリスト(用例:“1,3”で1番目と3番目)" + +say section_externals "エクスターナル" + say -path "ファイルの検索パス" + say -helppath "ヘルプファイルの検索パス" + say -lib "オブジェクトのライブラリをロード" + +say section_gui "GUI" + say -nogui "GUIなしで起動する(危険です)" + say -guicmd "他のGUIプログラムと置き換える(例えばrshのような)" + say -look "ボタンバーのアイコン" + say -font "起動時のフォントのサイズをpointで定義" + +say section_other "その他" + say -open "起動時にファイルを開く" + say -verbose "起動時とファイル検索時のコンソールへの表示を詳細化" + say -d "デバッグレベル" + say -noloadbang "“loadbang”を無効にする" + say -send "起動時にメッセージを送信する(全てのパッチが読み込まれた直後に)" + say -listdev "オーディオデバイスとMIDIデバイスのリストを起動時に表示する" + say -realtime "優先度を最優先にする(管理者権限が必要)" + +say section_paths "パス" + +# phase 4B: ddrc (keyword names not finalized!) +say console "コンソールウインドウの表示行数 (0 = コンソールを停止)" +say lang "使用言語" +say pointer_sense "マウス感度" +say section_color "アピアランス" + say canvas_color "カンバス" + say canvasbgedit "カンバス背景(エディットモード時)" + say canvasbgrun "カンバス背景(実行モード時)" + say object_color "オブジェクト" + say viewframe1 "オブジェクトボックスの色" + say viewframe2 "オブジェクトボックスの色" + say viewframe3 "オブジェクトボックスの色" + say viewframe4 "オブジェクトボックス選択時の色" + say viewbg "オブジェクト背景" + say viewfg "オブジェクト前景" + say commentbg "コメント背景" + say commentfg "コメント前景" + say commentframe1 "コメントフレーム" + say commentframe2 "コメントフレーム" + say commentframe3 "コメントフレーム" + say viewselectframe "選択されたボックス" + say wire_color "結線" + say wirefg "結線色" + say wirefg2 "選択された結線" + say wiredspfg "音声信号用結線の色" + say futurewiredash "新規結線" + say others_color "その他" + say boxinletfg "インレットの色" + say boxoutletfg "アウトレットの色" + say selrectrect "セレクションボックス" +say keys "キー" +say others "その他" +say hairstate "十字型カーソルを表示" +say hairsnap "十字型カーソルをオブジェクトにスナップ" +say statusbar "ステータスバーを表示" +say buttonbar "ボタンバーを表示" +say menubar "メニューバーを表示" +say scrollbar "オートスクロールバーを表示" +say wirearrow "結線端の信号の方向を示す矢印" +say tooltip "ツールチップ" +say insert_object "オブジェクトを挿入" +say chain_object "オブジェクトを繋ぐ" +say clear_wires "結線を全て外す" +say auto_wire "オブジェクトを消去" +say subpatcherize "サブパッチとして独立させる" +say keynav "キーボードナビゲーション" +say key_nav_up "上へ移動" +say key_nav_up_shift "上へ移動して選択" +say key_nav_down "下へ移動" +say key_nav_down_shift "下へ移動して選択" +say key_nav_right "右へ移動" +say key_nav_right_shift "右へ移動して選択" +say key_nav_left "左へ移動" +say key_nav_left_shift "左へ移動して選択" +say key_nav_ioselect "インレット/アウトレットを選択" + +# phase 5A + +say cannot "不可能です" +say cancel "キャンセル" +say apply "適用する" +say ok "OK" +say popup_open "開く" +say popup_insert "挿入する" +say popup_properties "プロパティ" +say popup_clear_wires "結線を全て外す" +say popup_remove_from_path "パスからオブジェクトを外す" +say popup_delete_from_path "パスからオブジェクトを消去する" +say popup_help "ヘルプ" +say filter "フィルタ: " +say how_many_object_classes "%2\$d 中 %1\$d 個のオブジェクトクラス" +say do_what_i_mean "言ったとおりにやってよね" +say ask_cool "これってイカス機能だよね〜" +say save_changes? "変更を保存しますか?" +say reset "リセット" +say Courier "Courier (monospaced)" +say Helvetica "Helvetica (sansserif)" +say Times "Times (serif)" +say add "追加" +say up "上へ" +say down "下へ" +say remove "消去" +say lib_add "リストに書いた名前を加える" +say lib_up "前のライブラリと順番を入れ替える" +say lib_down "次のライブラリと順番を入れ替える" +say lib_remove "リスト中の選択されたライブラリを外す" +say dir_add "ダイアログを使ってフォルダを加える" +say dir_up "前のフォルダと順番を入れ替える" +say dir_down "次のフォルダと順番を入れ替える" +say dir_remove "リスト中の選択されたフォルダを外す" +say client_class_tree "クライアント構成" +say clipboard_view "クリップボードを表示" +say command_history_view "操作履歴を表示" +say event_history_view "イベント履歴を表示" + +# during/after piksel: + +say auto_apply "自動的に適用する" +say font_preview "プレビュー:" +say font_preview_2 "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n0123456789" +say font_style "スタイル:" +say font_bold "太字" +say font_italic "斜体" +say font_family "名前:" +say font_size "大きさ:" +say damn "最低!" +say console_clear "コンソールをクリア" +say horizontal "水平方向" +say vertical "鉛直方向" +say language "言語" + +# 2007: + +say no_matches "(適合するものなし)" +say preset "プリセット" +say canvasgrid "グリッドの色" +say grid_size "グリッドの大きさ" +say gridstate "背景にグリッドを表示" +say snap_grid "グリッドにスナップ" +say viewfont "オブジェクトのフォント" +say consolefont "コンソールのフォント" +say keyboarddialogfont "仮想キーボードのフォント" +say keyboard_view "仮想キーボード" diff --git a/desiredata/src/locale/polski.tcl b/desiredata/src/locale/polski.tcl new file mode 100644 index 00000000..5be7cd48 --- /dev/null +++ b/desiredata/src/locale/polski.tcl @@ -0,0 +1,557 @@ +#!/usr/bin/env tclsh +# Polish (polski) translations for PureData +# $Id: polski.tcl,v 1.1.2.2 2007-08-01 04:24:43 matju Exp $ +# by Michal Seta, mis@artengine.ca + +say file "Plik" + say new_file "Nowy Plik" + say open_file "Otwórz Plik..." + say server_prefs "Preferencje servera..." + say client_prefs "Preferencje klienta..." + say send_message "Wysłać Wiadomość..." + say paths "Ścieżki..." + say close "Zamknąć" + say save "Zapisz" + say save_as "Zapisz Jako..." + say print "Wydrukuj..." + say quit "Zakończ" + + say canvasnew_file "Nowy Plik" + say canvasopen_file "Otwórz Plik..." + say canvassave "Zapisz" + say canvassave_as "Zapisz Jako..." + say clientpdrc_editor "Edycja .pdrc" + say clientddrc_editor "Edycja .ddrc" + say canvasclose "Zamknij" + say canvasquit "Zakończ" + +say edit "Edycja" + say undo "Cofnij" + say redo "Ponów" + say cut "Wytnij" + say copy "Skopiuj" + say paste "Wklei" + say duplicate "Powiel" + say select_all "Zaznacz wszystko" + say text_editor "Edytor tekstu..." + say font "Czcionka" + say tidy_up "Wyrównać" + say edit_mode "Tryb edycji" + say editmodeswitch "Tryb edycji/pracy" + + say canvascut "Wytnij" + say canvascopy "Skopiuj" + say canvasundo "Anuluj" + say canvasredo "Ponów" + say canvaspaste "Wklei" + say canvasduplicate "Powiel" + say canvasselect_all "Zaznacz wszystko" + say canvaseditmodeswitch "Tryb edycji/pracy" + +say view "Widok" + say reload "Załaduj ponownie" + say redraw "Odśwież" + + say canvasreload "Załaduj ponownie" + say canvasredraw "Odśwież" + + say find "Szukaj" + say find_again "Szukaj ponownie" + say find_last_error "Znajdź ostatni błąd" + say string "Znajdź ciąg znaków" + say canvasfind "Szukaj" + say canvasfind_again "Szukaj ponownie" + +say put "Połóż" + say Object "Objekt" + say Message "Komunikat" + say Number "Liczba" + say Symbol "Znak" + say Comment "Komentarz" + say Array "Tabela" + +say media "Media" + say audio_on "Włącz dźwięk" + say audio_off "Wyłącz dźwięk" + say test_audio_and_midi "Próba dżwięku i MIDI" + say load_meter "Miernik procesora" + + say canvasaudio_on "Włącz dźwięk" + say canvasaudio_off "Wyłącz dźwięk" + say clienttest_audio_and_midi "Próba dżwięku i MIDI" + say canvasload_meter "Miernik procesora" + +say window "Okno" + +say help "Pomoc" + say about "Na temat..." + say pure_documentation "Dokumentacja..." + say class_browser "Przeglądarka klas..." + + say canvasabout "Na temat..." + + say properties "Właściwości" +say open "Otwórz" + +### for key binding editor +say general "Ogólne" +say audio_settings "Ustawienia dżwięku" +say midi_settings "Ustawienia MIDI" +say latency_meter "Miernik opóźnienia" +say Pdwindow "Okno PD" + +say canvaspdwindow "Konsola PD" +say canvaslatency_meter "Miernik opóźnienia" +say clientaudio_settings "Ustawienia dżwięku" +say clientmidi_settings "Ustawienia MIDI" + +### for Properties Dialog (phase 5B) +say_category IEM +say w "Szerokość(px)" +say h "Wysokość(px)" +say hold "Czas utrzymania(ms)" +say break "Czas zatrzymania(ms)" +say min "Zmienna minimalna" +say max "Zmienna maxymalna" +say is_log "Tryb" +say linear "Liniowy" +say logarithmic "Logarytmiczny" +say isa "Uruchomienie" +say n "Ilość wyboru" +say steady "Równomierny" +say steady_no "Skok po kliknięciu" +say steady_yes "Stały po kliknięciu" +say snd "oznakowanie wysłania" +say rcv "oznakowanie pobrania" +say lab "etykieta" +say ldx "przesunięcie etykiety po x" +say ldy "przesunięcie etykiety po y" +say fstyle "Czcionka" +say fs "Rozmiar czcionki" +say bcol "Kolor tła" +say fcol "Kolor przedni" +say lcol "Kolor etykiety" +say yes "Tak" +say no "Nie" +say courier "courrier (typewriter)" +say helvetica "helvetique (sansserif)" +say times "times (serif)" +say coords "grafika na nadrzędnym" + +say_category GAtomProperties +say width "szerokość" +say lo "ograniczenie niskie" +say hi "ograniczenie wysole" +say label "etykieta" +say wherelabel "wyświetlić etykietę" +say symto "wyślij znak" +say symfrom "pobierz znak" + +say_category GraphProperties +say x1 "x od" +say x2 "x do" +say xpix "szerokość ekranu" +say y2 "y od" +say y1 "y do" +say ypix "wysokość ekranu" + +say_category CanvasProperties +#say xscale "X units/px" +#say yscale "Y units/px" +say gop "grafika na nadrzędnym" +say xmargin "margines x" +say ymargin "margines y" +say height "wysokość" +say_category ArrayProperties +say name "nazwa" +say n "rozmiar" +say xfrom "zakres x od" +say xto "zakres x do" +say yfrom "zakres y od" +say yto "zakres y do" + + +say_category MainWindow +say in "wejście" +say out "wyjście" +say audio "Dźwięk" +say meters "Pomiary" +say io_errors "Błędy I/O" +say tcl_console "Klient Tcl" +say pd_console "Server pd" + +### phase 2 + +say_category Other +say_namespace summary { + say_category IEMGUI + say bng "Bang" + say tgl "Przerzutnik" + say nbx "Liczba" + say hsl "Suwak (poziomy)" + say vsl "Suwak (Pionowy)" + say hradio "Pole wyboru (poziome)" + say vradio "Pole wyboru (pionowe)" + say cnv "Płótno" + say dropper "Drag-and-Drop Box" + say vu "Miernik VU" + + say_category GLUE + say bang "output a bang message" + say float "store and recall a number" + say symbol "store and recall a symbol" + say int "store and recall an integer" + say send "send a message to a named object" + say receive "catch sent messages" + say select "test for matching numbers or symbols" + say route "route messages according to first element" + say pack "make compound messages" + say unpack "get elements of compound messages" + say trigger "sequence and convert messagess" + say spigot "interruptible message connection" + say moses "part a numeric stream" + say until "looping mechanism" + say print "print out messages" + say makefilename "format a symbol with a variable field" + say change "remove repeated numbers from a stream" + say swap "swap two numbers" + say value "shared numeric value" + + say_category TIME + say delay "send a message after a time delay" + say metro "send a message periodically" + say line "send a series of linearly stepped numbers" + say timer "measure time intervals" + say cputime "measure CPU time" + say realtime "measure real time" + say pipe "dynamically growable delay line for numbers" + + say_category MATH + say + "add" + say - "substract" + say * "multiply" + say {/ div} "divide" + say {% mod} "division remainder" + say pow "exponentiate" + say == "equal?" + say != "not equal?" + say > "more than?" + say < "less than?" + say >= "not less than?" + say <= "not more than?" + say & "bitwise conjunction (and)" + say | "bitwise disjunction (or)" + say && "logical conjunction (and)" + say || "logical disjunction (or)" + say mtof "MIDI to Hertz" + say ftom "Hertz to MIDI" + say powtodb "Watts to dB" + say dbtopow "dB to Watts" + say rmstodb "Volts to dB" + say dbtorms "dB to Volts" + say {sin cos tan atan atan2 sqrt} "trigonometry" + say log "Euler logarithm" + say exp "Euler exponential" + say abs "absolute value" + say random "random" + say max "greater of two numbers" + say min "lesser of two numbers" + say clip "force a number into a range" + + say_category MIDI + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} "MIDI input" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} "MIDI output" + say makenote "schedule a delayed \"note off\" message corresponding to a note-on" + say stripnote "strip \"note off\" messages" + + say_category TABLES + say tabread "read a number from a table" + say tabread4 "read a number from a table, with 4 point interpolation" + say tabwrite "write a number to a table" + say soundfiler "read and write tables to soundfiles" + + say_category MISC + say loadbang "bang on load" + say serial "serial device control for NT only" + say netsend "send messages over the internet" + say netreceive "receive them" + say qlist "message sequencer" + say textfile "file to message converter" + say openpanel "\"Open\" dialog" + say savepanel "\"Save as\" dialog" + say bag "set of numbers" + say poly "polyphonic voice allocation" + say {key keyup} "numeric key values from keyboard" + say keyname "symbolic key name" + + say_category "AUDIO MATH" + foreach word {+ - * /} {say $word~ "[say $word] (for signals)"} + say max~ "supremum of signals" + say min~ "infimum of signals" + say clip~ "constrict signal to lie between two bounds" + say q8_rsqrt~ "cheap reciprocal square root (beware -- 8 bits!)" + say q8_sqrt~ "cheap square root (beware -- 8 bits!)" + say wrap~ "wraparound (fractional part, sort of)" + say fft~ "complex forward discrete Fourier transform" + say ifft~ "complex inverse discrete Fourier transform" + say rfft~ "real forward discrete Fourier transform" + say rifft~ "real inverse discrete Fourier transform" + say framp~ "output a ramp for each block" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (for signals)" + } +} + +### phase 3 + +say_namespace summary { + say_category "AUDIO GLUE" + say dac~ "audio output" + say adc~ "audio input" + say sig~ "convert numbers to audio signals" + say line~ "generate audio ramps" + say vline~ "deluxe line~" + say threshold~ "detect signal thresholds" + say snapshot~ "sample a signal (convert it back to a number)" + say vsnapshot~ "deluxe snapshot~" + say bang~ "send a bang message after each DSP block" + say samplerate~ "get the sample rate" + say send~ "nonlocal signal connection with fanout" + say receive~ "get signal from send~" + say throw~ "add to a summing bus" + say catch~ "define and read a summing bus" + say block~ "specify block size and overlap" + say switch~ "switch DSP computation on and off" + say readsf~ "soundfile playback from disk" + say writesf~ "record sound to disk" + + say_category "AUDIO OSCILLATORS AND TABLES" + say phasor~ "sawtooth oscillator" + say {cos~ osc~} "cosine oscillator" + say tabwrite~ "write to a table" + say tabplay~ "play back from a table (non-transposing)" + say tabread~ "non-interpolating table read" + say tabread4~ "four-point interpolating table read" + say tabosc4~ "wavetable oscillator" + say tabsend~ "write one block continuously to a table" + say tabreceive~ "read one block continuously from a table" + + say_category "AUDIO FILTERS" + say vcf~ "voltage controlled filter" + say noise~ "white noise generator" + say env~ "envelope follower" + say hip~ "high pass filter" + say lop~ "low pass filter" + say bp~ "band pass filter" + say biquad~ "raw filter" + say samphold~ "sample and hold unit" + say print~ "print out one or more \"blocks\"" + say rpole~ "raw real-valued one-pole filter" + say rzero~ "raw real-valued one-zero filter" + say rzero_rev~ "[say rzero~] (time-reversed)" + say cpole~ "[say rpole~] (complex-valued)" + say czero~ "[say rzero~] (complex-valued)" + say czero_rev~ "[say rzero_rev~] (complex-valued)" + + say_category "AUDIO DELAY" + say delwrite~ "write to a delay line" + say delread~ "read from a delay line" + say vd~ "read from a delay line at a variable delay time" + + say_category "SUBWINDOWS" + say pd "define a subwindow" + say table "array of numbers in a subwindow" + say inlet "add an inlet to a pd" + say outlet "add an outlet to a pd" + say inlet~ "[say inlet] (for signal)" + say outlet~ "[say outlet] (for signal)" + + say_category "DATA TEMPLATES" + say struct "define a data structure" + say {drawcurve filledcurve} "draw a curve" + say {drawpolygon filledpolygon} "draw a polygon" + say plot "plot an array field" + say drawnumber "print a numeric value" + + say_category "ACCESSING DATA" + say pointer "point to an object belonging to a template" + say get "get numeric fields" + say set "change numeric fields" + say element "get an array element" + say getsize "get the size of an array" + say setsize "change the size of an array" + say append "add an element to a list" + say sublist "get a pointer into a list which is an element of another scalar" + say scalar "draw a scalar on parent" + + say_category "OBSOLETE" + say scope~ "(use tabwrite~ now)" + say namecanvas "" ;# what was this anyway? + say template "(use struct now)" +} + +# phase 4 (pdrc & ddrc) + +say section_audio "Audio" + say -r "sample rate" + say -audioindev "audio in devices" + say -audiooutdev "audio out devices" + say -inchannels "audio input channels (by device, like \"2\" or \"16,8\")" + say -outchannels "number of audio out channels (same)" + say -audiobuf "specify size of audio buffer in msec" + say -blocksize "specify audio I/O block size in sample frames" + say -sleepgrain "specify number of milliseconds to sleep when idle" + say -nodac "suppress audio output" + say -noadc "suppress audio input" + say audio_api_choice "Audio API" + say default "default" + say -alsa "use ALSA audio API" + say -jack "use JACK audio API" + say -mmio "use MMIO audio API (default for Windows)" + say -portaudio "use ASIO audio driver (via Portaudio)" + say -oss "use OSS audio API" + say -32bit "allow 32 bit OSS audio (for RME Hammerfall)" + say {} "default" + +say section_midi "MIDI" + say -nomidiin "suppress MIDI input" + say -nomidiout "suppress MIDI output" + say -midiindev "midi in device list; e.g., \"1,3\" for first and third" + say -midioutdev "midi out device list, same format" + +say section_externals "Externals" + say -path "file search path" + say -helppath "help file search path" + say -lib "load object libraries" + +say section_gui "Gooey" + say -nogui "suppress starting the GUI (caution)" + say -guicmd "substitute another GUI program (e.g., rsh)" + say -look "buttonbar icons" + say -font "specify default font size in points" + +say section_other "Other" + say -open "open file(s) on startup" + say -verbose "extra printout on startup and when searching for files" + say -d "debug level" + say -noloadbang "disable the effect of \[loadbang\]" + say -send "send a message at startup (after patches are loaded)" + say -listdev "list audio and MIDI devices upon startup" + say -realtime "use real-time priority (needs root privilege)" + +say section_paths "Paths" + +# phase 4B: ddrc (keyword names not finalized!) +say console "console scrollback lines (0 = disable console)" +say lang "Language to use" +say pointer_sense "Mouse pointer sensitivity" +say section_color "colors" + say canvas_color "canvas" + say canvasbgedit "canvas background (edit mode)" + say canvasbgrun "canvas background (run mode)" + say object_color "object" + say viewframe1 "objectbox color" + say viewframe2 "objectbox color" + say viewframe3 "objectbox color" + say viewframe4 "objectbox highlight color" + say viewbg "object background" + say viewfg "object foreground" + say commentbg "comment background" + say commentfg "comment forground" + say commentframe1 "comment frame" + say commentframe2 "comment frame" + say commentframe3 "comment frame" + say viewselectframe "hilight box" + say wire_color "wire" + say wirefg "wire color" + say wirefg2 "wire highlight" + say wiredspfg "dsp wire color" + say futurewiredash "new (dashed) wire" + say others_color "others" + say boxinletfg "inlet color" + say boxoutletfg "outlet color" + say selrectrect "selection box" +say keys "keys" +say others "others" +say hairstate "Activate crosshair" +say hairsnap "Crosshair snap to object" +say statusbar "Activate statusbar" +say buttonbar "Activate buttonbar" +say menubar "Activate menubar" +say scrollbar "Active auto scrollbar" +say wirearrow "Wire Arrow" +say tooltip "ToolTip" +say insert_object "Insert object" +say chain_object "Chain object" +say clear_wires "Clear wires" +say auto_wire "Remove object" +say subpatcherize "Subpatcherize" +say keynav "keyboard navigation" +say key_nav_up "move up" +say key_nav_up_shift "plus select" +say key_nav_down "move down" +say key_nav_down_shift "plus select" +say key_nav_right "move right" +say key_nav_right_shift "plus select" +say key_nav_left "move left" +say key_nav_left_shift "plus select" +say key_nav_ioselect "select in/outlets" +# phase 5A + +say cannot "can't" +say cancel "Cancel" +say apply "Apply" +say ok "OK" +say popup_open "Open" +say popup_insert "Insert" +say popup_properties "Properties" +say popup_clear_wires "Clear wires" +say popup_remove_from_path "Remove object from path" +say popup_delete_from_path "Delete object from path" +say popup_help "Help" +say filter "Filter: " +say how_many_object_classes "%d of %d object classes" +say do_what_i_mean "Do What I Mean" +say ask_cool "This would be a cool feature, eh?" +say save_changes? "Save changes?" +say reset "Reset" +say Courier "Courier (monospaced)" +say Helvetica "Helvetica (sansserif)" +say Times "Times (serif)" +say add "Add" +say up "Up" +say down "Down" +say remove "Remove" +say lib_add "add the name you typed to the list" +say lib_up "swap order with previous library" +say lib_down "swap order with next library" +say lib_remove "remove library selected in the list" +say dir_add "add a folder using a file dialog" +say dir_up "swap order with previous folder" +say dir_down "swap order with next folder" +say dir_remove "remove folder selected in the list" +say client_class_tree "Client Class Tree" +say clipboard_view "Clipboard View" +say command_history_view "Command History View" +say event_history_view "Event History View" + +# during/after piksel: + +say auto_apply "Auto-Apply" +say font_preview "Preview:" +say font_preview_2 "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n0123456789" +say font_style "Style:" +say font_bold "Bold" +say font_italic "Italic" +say font_family "Name:" +say font_size "Size:" +say damn "Damn!" +say console_clear "Clear Console" +say horizontal "Horizontal" +say vertical "Vertical" +say language "Language" + +# 2007: + +say no_matches "(no matches)" +say preset "preset" diff --git a/desiredata/src/locale/portugues.tcl b/desiredata/src/locale/portugues.tcl new file mode 100644 index 00000000..64385356 --- /dev/null +++ b/desiredata/src/locale/portugues.tcl @@ -0,0 +1,119 @@ +#!/usr/bin/env tclsh +# Portuguese (Portugus) translations for PureData +# $Id: portugues.tcl,v 1.1.2.5 2006-10-13 16:00:56 matju Exp $ +# by Nuno Godinho + +# (waiting for a version that has 8859-1 accents) + +### Menus + +say file "Ficheiro" + say new_file "Novo Ficheiro" + say open_file "Abrir Ficheiro..." + say pdrc_editor "Editor .pdrc" + say send_message "Enviar Mensagem..." + say paths "Caminhos..." + say close "Fechar" + say save "Gravar" + say save_as "Gravar Como..." + say print "Imprimir..." + say quit "Sair" + +say edit "Editar" + say undo "Desfazer" + say redo "Refazer" + say cut "Cortar" + say copy "Copiar" + say paste "Colar" + say duplicate "Duplicar" + say select_all "Seleccionar Tudo" + say text_editor "Editor de Texto..." + say tidy_up "Arranjar" + say edit_mode "Modo Editar" + +say view "Vista" + say reload "Recarregar" + say redraw "Redesenhar" + +say find "Procurar" + say find_again "Procurar Novamente" + say find_last_error "Encontrar Ultimo Erro" + +say put "Colocar" + +say media "Media" + say audio_on "Audio ON" + say audio_off "Audio OFF" + say test_audio_and_midi "Testar Audio e MIDI" + say load_meter "Medidor de Carga" + +say window "Janela" + +say help "Ajuda" + say about "Acerca..." + say pure_documentation "Documentao do Pure..." + say class_browser "Listar Classes..." + + +### Main Window + +say in "entrada" +say out "saida" +say audio "Audio" +say meters "Medidores" +say io_errors "Erros de IO" + +### Other + +say cannot "impossivel" + +### phase 4 + +say section_audio "udio" + say -r "frequncia de amostragem" + say -audioindev "dispositivos de entradaa udio" + say -audiooutdev "dispositivos de sada udio" + say -inchannels "canais de entrada udio (por dispositivo, como \"2\" ou \"16,8\")" + say -outchannels "nmero de canais de sada udio (igual)" + say -audiobuf "especificar tamanho do buffer de udio em ms" + say -blocksize "especificar tamanho do bloco I/O udio em nmero de amostras" + say -sleepgrain "especificar nmero de milisegundos que dorme quando inactivo" + say -nodac "inibir sada de udio" + say -noadc "inibir entrada de udio" + say audio_api_choice "udio API" + say default "defeito" + say -alsa "usar ALSA audio API" + say -jack "usar JACK audio API" + say -mmio "usar MMIO audio API (por defeito para Windows)" + say -portaudio "usar ASIO audio driver (via Portaudio)" + say -oss "usar OSS audio API" + say -32bit "permitir OSS udio a 32 bit (para RME Hammerfall)" + +say section_midi "MIDI" + say -nomidiin "inibir entrada MIDI" + say -nomidiout "inibir sada MIDI" + say -midiindev "lista de dispositivos de entrada midi; ex., \"1,3\" para primeiro e terceiro" + say -midioutdev "lista de dispositivos de sada midi, mesmo formato" + +say section_externals "Externals" + say -path "adicionar a caminho de pesquisa de ficheiros" + say -helppath "adicionar a caminho de pesquisa de ficheiros de ajuda" + say -lib "carregar biblioteca(s) de objectos" + +say section_gui "Gooey" + say -nogui "inibir inicializao de GUI (cuidado)" + say -guicmd "substituir programa de GUI (ex., rsh)" + say -console "linhas armazenadas na consola (0 = inibir consola)" + say -look "pasta contendo icons para barra de botes" + say -statusbar "inibir barra de status" + say -font "especificar tamanho da fonte por defeito em pontos" + +say section_other "Outros" + say -open "abrir ficheiro(s) na inicializao" + say -verbose "impresses extra na inicializao e durante pesquisa de ficheiros" + say -d "nvel de depurao" + say -noloadbang "inibir efeito de \[loadbang\]" + say -send "enviar mensagem na inicializao (depois dos patches carregados)" + say -listdev "listar dispositivos udio e MIDI aps inicializao" + say -realtime "usar prioridade de tempo-real (necessrios privilgios de root)" + diff --git a/desiredata/src/locale/russkij.tcl b/desiredata/src/locale/russkij.tcl new file mode 100644 index 00000000..448c4bf5 --- /dev/null +++ b/desiredata/src/locale/russkij.tcl @@ -0,0 +1,574 @@ +#!/usr/bin/env tclsh +# русский перевод PureData (пьюр дата - англ., "чисто данные") +# by Ilya Dmitrichenko, 'errordeveloper"^at^"gmail"^dot^"com' +# $Id: russkij.tcl,v 1.1.2.1 2007-10-26 20:17:14 matju Exp $ + +### Menus + +say file "Файл" + say new_file "Новый файл" + say open_file "Открыть файл..." + say server_prefs "Настройки сервера..." + say client_prefs "Настройки клиента..." + say send_message "Послать сообщение..." + say paths "Расположение расширений..." + say close "Закрыть" + say save "Сохранить" + say save_as "Сохранить с именем..." + say print "Распечатать..." + say abort_server "О сервере" + say quit "Покинуть программу" + + say canvasnew_file "Новый файл" + say canvasopen_file "Отрыть файл..." + say canvassave "Сохранить" + say canvassave_as "Сохранить как..." + say clientpdrc_editor "отредактировать .pdrc" + say clientddrc_editor "отредактировать .ddrc" + say canvasclose "Закрыть" + say canvasquit "Выход" + +say edit "Правка" + say undo "Шаг назад" + say redo "Шаг вперёд" + say cut "Вырезать" + say copy "Копировать" + say paste "Вставить" + say duplicate "Дублировать" + say select_all "Выбрать всё" + say clear_selection "Отменить выбор" + say text_editor "Текстовый редактор..." + say font "Шрифт" + say tidy_up "Упорядочить" + say edit_mode "Режим редактирования" + say editmodeswitch "Режим исполнения/редактирования" + say subpatcherize "Превратить в суб-патч" + + say canvascut "Вырезать" + say canvascopy "Копировать" + say canvasundo "Шаг назад" + say canvasredo "Шаг вперёд" + say canvaspaste "Вставить" + say canvasduplicate "Дублировать" + say canvasselect_all "Выбрать всё" + say canvaseditmodeswitch "Режим исполнения/редактирования" + +say view "Вид" + say reload "Перезагрузить" + say redraw "Обновить" + + say canvasreload "Перезагрузить" + say canvasredraw "Обновить" + +say find "Поиск" + say find_again "Найти ещё" + say find_last_error "Найти последнею ошибку" + say string "Искать текст" +say canvasfind "Найти" + say canvasfind_again "Найти ещё" + +# contents of Put menu is Phase 5C +say put "Положить" + say Object "Обьект" + say Message "Сообщение" + say Number "Номер" + say Symbol "Символ" + say Comment "Комментарий" + say Graph "Пустая графа" + say Array "Графа с данными" + +say media "Мотор" + say audio_on "ВКЛ звук" + say audio_off "ВЫКЛ звук" + say test_audio_and_midi "Проверка аудио и MIDI" + say load_meter "Измеритель загруженности" + + say canvasaudio_on "ВКЛ звук" + say canvasaudio_off "ВЫКЛ звук" + say clienttest_audio_and_midi "Проверка звука и MIDI" + say canvasload_meter "загруз. метр" + +say window "Окна" + +say help "Помощь" + say about "О программе..." + say documentation "Документация..." + say class_browser "Браузер классов..." + + say canvasabout "О программе..." + +say properties "Свойства" +say open "Открыть" + +### for key binding editor +say general "Общее" +say audio_settings "Настройки звука" +say midi_settings "Настройки MIDI" +say latency_meter "Измеритель опозданий" +say Pdwindow "Основное окно" + +say canvaspdwindow "Основное окно" +say canvaslatency_meter "Измеритель задержек" +say clientaudio_settings "Настройки аудио" +say clientmidi_settings "Настройки MIDI" + +### for Properties Dialog (phase 5B) +say_category IEM +say w "ширина, px" +say h "высота, px" +say hold "время удержки, мс" +say break "break time, мс" +say min "наименьшее значение" +say max "наибольшее значение" +say is_log "режим" +say linear "линейный" +say logarithmic "логорифмичекий" +say isa "init" +say n "колличество вариантов" +say steady "стойкость" +say steady_no "сдвиг при клике" +say steady_yes "стойкий при клике" +say snd "отправлять с символом" +say rcv "получать по сиволу" +say lab "название" +say ldx "смещение названия Х" +say ldy "смещение названия У" +say fstyle "шрифт" +say fs "размер шрифта" +say bcol "цвет фона" +say fcol "основной цвет" +say lcol "цвет названия" +say yes "да" +say no "нет" +say courier "courier (typewriter)" +say helvetica "helvetica (sansserif)" +say times "times (serif)" +say coords "graph on parent" + +say_category GAtomProperties +say width "ширина" +say lo "нижний предел" +say hi "верхний предел" +say label "обозначение" +say wherelabel "отображать" +say symto "отправлять с символом" +say symfrom "получать по сиволу" + +say_category GraphProperties +say x1 "значения x с" +say x2 "x до" +say xpix "ширина" +say y2 "значения y с" +say y1 "y до" +say ypix "высота" + +say_category CanvasProperties +#say xscale "X units/px" +#say yscale "Y units/px" +say gop "graph on parent" +say xmargin "предел по оси X" +say ymargin "предел по оси Y" +say height "высота" +say_category ArrayProperties +say name "название" +say n "размер" +say xfrom "значения x с" +say xto "значения x до" +say yfrom "значения у с" +say yto "значения у до" + + +say_category MainWindow +say in "вход" +say out "выход" +say audio "Аудио" +say meters "Уровни" +say io_errors "Ошибки на входе и выходе" +say tcl_console "клиент Tcl" +say pd_console "pd сервер" + +### phase 2 + +say_category Other +say_namespace summary { + say_category IEMGUI + say bng "Bang Box" + say tgl "Toggle Box" + say nbx "Number Box (IEM)" + say hsl "Slider (Горизонтальный)" + say vsl "Slider (Вертикальный)" + say hradio "Choice Box (Горизонтальный)" + say vradio "Choice Box (Вертикальный)" + say cnv "Полотно (IEM)" + say dropper "Drag-and-Drop Box" + say vu "Уровень громкости (VU-meter)" + + say_category GLUE + say bang "выдать 'bang'" + say float "store and recall a number" + say symbol "store and recall a symbol" + say int "сохранить число" + say send "отсылка сообсчений с символы" + say receive "приём отосланных сообсчений по символу" + say select "test for matching numbers or symbols" + say route "route messages according to first element" + say pack "make compound messages" + say unpack "get elements of compound messages" + say trigger "sequence and convert messagess" + say spigot "прерываемый поток сообщений" + say moses "разбить поток чисел" + say until "механизм кругового действия" + say print "текстовый вывод" + say makefilename "создать текст с переменными полями" + say change "убрать повторяющееся число из потока" + say swap "поменять два числа местами" + say value "сохранить обсщедоступную переменную с заданным именем" + + say_category TIME + say delay "выдавать соовщение с заданной задержкой" + say metro "выдавать переодическое соовщение с заданным интервалом" + say line "выдать линейную прогрессию" + say timer "отмерить временной промеуток" + say cputime "отмерить промежуток по процесорному времени" + say realtime "отмерить реальный временной промежуток" + say pipe "динамически нарастающий накопитель чисел с задержкой вывода" + + say_category MATH + say + "сумма" + say - "разность" + say * "произведение" + say {/ div} "частное" + say {% mod} "деление с остатком" + say pow "возвести в стапань" + say == "равно?" + say != "не равно?" + say > "больше?" + say < "меньше?" + say >= "не меньше?" + say <= "не больше?" + say & "bitwise conjunction (and)" + say | "bitwise disjunction (or)" + say && "logical conjunction (and)" + say || "logical disjunction (or)" + say mtof "MIDI > Герцы" + say ftom "Герцы > MIDI" + say powtodb "Ватты > dB" + say dbtopow "dB > Ватты" + say rmstodb "Вольты > dB" + say dbtorms "dB > Вольты" + say {sin cos tan atan atan2 sqrt} "Тригонометрические функции" + say log "Натуральный логоритм" + say exp "Экспонента Эвлера" + say abs "Значение по модулю" + say random "Случайное число" + say max "Наибольшее из двух" + say min "Наименьшее из дцух" + say clip "Ограничить значения в потоке чисел" + + say_category MIDI + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} "MIDI вход" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} "MIDI выход" + say makenote "schedule a delayed \"note off\" message corresponding to a note-on" + say stripnote "strip \"note off\" messages" + + say_category TABLES + say tabread "получить значение из таблицы (по индексу)" + say tabread4 "read a number from a table, with 4 point interpolation" + say tabwrite "вписать значение в таблицу" + say soundfiler "считывать аудио-файл в/из таблиц" + + say_category MISC + say loadbang "выдать 'bang' при загрузке" + say serial "доступ к серийному порту (только в NT)" + say netsend "посылать сообщения по сети (по TCP или UDP)" + say netreceive "получать сообщения из сети (по TCP или UDP)" + say qlist "message sequencer" + say textfile "file to message converter" + say openpanel "диалоговое окно \"Открыть\"" + say savepanel "диалоговое окно \"Save as\"" + say bag "набор чисел" + say poly "полифонизатор" + say {key keyup} "числовые значения клавиш клавиатуры" + say keyname "именные значения клавиш" + + say_category "AUDIO MATH" + foreach word {+ - * /} {say $word~ "[say $word] (для сигналов)"} + say max~ "supremum of signals" + say min~ "infimum of signals" + say clip~ "ограничить амплитуду сигнала" + say q8_rsqrt~ "упрощенный алгоритм (8 бит) обратного квадратного корня" + say q8_sqrt~ "упрощенный алгоритм (8 бит) квадратного корня" + say wrap~ "wraparound (fractional part, sort of)" + say fft~ "complex forward discrete Fourier transform" + say ifft~ "complex inverse discrete Fourier transform" + say rfft~ "real forward discrete Fourier transform" + say rifft~ "вещественный обратный inverse discrete Fourier transform" + say framp~ "output a ramp for each block" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (for signals)" + } +} + +### phase 3 + +say_namespace summary { + say_category GLUE + say dac~ "аудио выход" + say adc~ "аудио вход" + say sig~ "превратить число в сигнал" + say line~ "generate audio ramps" + say vline~ "улучшенный line~" + say threshold~ "detect signal thresholds" + say snapshot~ "семплироавть сигнал (представить в виде числа)" + say vsnapshot~ "улучшенный snapshot~" + say bang~ "посылать 'bang' с каждым аудио-блоком" + say samplerate~ "узнать частоту дискретизации" + say send~ "отослать сигнал для удалённого приёма" + say receive~ "получить отосланный сигнал" + say throw~ "отослать сигнал в суммарный канал" + say catch~ "считывать с суммарного канала" + say block~ "размер аудио-блока и нахлестки" + say switch~ "switch DSP computation on and off" + say readsf~ "чтение с диска" + say writesf~ "запись на диск" + + say_category "AUDIO OSCILLATORS AND TABLES" + say phasor~ "генератор пилообразного сигнала" + say {cos~ osc~} "генератор синусодального сигнала" + say tabwrite~ "писать сигнал в таблицу" + say tabplay~ "play back from a table (non-transposing)" + say tabread~ "non-interpolating table read" + say tabread4~ "four-point interpolating table read" + say tabosc4~ "табличный вибратор" + say tabsend~ "постоянно вписывать блок в таблицу" + say tabreceive~ "постоянно считывать блок из таблицы" + + say_category "AUDIO FILTERS" + say vcf~ "фильтр контролирумый напряжением" + say noise~ "генератор \"белого\" шума" + say env~ "envelope follower" + say hip~ "high pass filter" + say lop~ "low pass filter" + say bp~ "band pass filter" + say biquad~ "raw filter" + say samphold~ "sample and hold unit" + say print~ "print out one or more \"blocks\"" + say rpole~ "raw real-valued one-pole filter" + say rzero~ "raw real-valued one-zero filter" + say rzero_rev~ "[say rzero~] (time-reversed)" + say cpole~ "[say rpole~] (комплексный)" + say czero~ "[say rzero~] (комплексный)" + say czero_rev~ "[say rzero_rev~] (комплексный)" + + say_category "AUDIO DELAY" + say delwrite~ "write to a delay line" + say delread~ "read from a delay line" + say vd~ "read from a delay line at a variable delay time" + + say_category "SUBWINDOWS" + say pd "новое окно" + say table "таблица в новом окне" + say inlet "добавляет впуск" + say outlet "добавляет выпуск" + say inlet~ "[say inlet] (для сигналов)" + say outlet~ "[say outlet] (для сигналов)" + + say_category "DATA TEMPLATES" + say struct "define a data structure" + say {drawcurve filledcurve} "начертить кривую" + say {drawpolygon filledpolygon} "начертить многоугольник" + say plot "plot an array field" + say drawnumber "print a numeric value" + + say_category "Доступ к данным" + say pointer "указать на объект в шаблоне" + say get "получить численное значение" + say set "изменить численное значение" + say element "получить значение из таблицы" + say getsize "получить размер таблицы" + say setsize "изменить размер таблицы" + say append "добавить к списку" + say sublist "get a pointer into a list which is an element of another scalar" + say scalar "draw a scalar on parent" + + say_category "Ныне отсутсвующее" + say scope~ "(теперь используется tabwrite~)" + say namecanvas "именовать полотно" ;# what was this anyway? + say template "(теперь используется struct)" +} + +# phase 4 (pdrc & ddrc) + +say section_audio "Audio" + say -r "частота дискретизации" + say -audioindev "аудио вход" + say -audiooutdev "аудио выход" + say -inchannels "кол-во входных каналов (в соответствии с устройствами, на пример: \"2\" или \"16,8\")" + say -outchannels "кол-во выходных каналов" + say -audiobuf "размер сигнального буфера (милисек)" + say -blocksize "указать размер аудио-блока в сэмпл-фреймах" + say -sleepgrain "указать время (милисек) для перехода в режим сна когда неактивен" + say -nodac "предотвратить звук на входе (при запуске)" + say -noadc "предотвратить звук на выходе (при запуске)" + say audio_api_choice "Выбор аудио API" + say default "по умолчанию" + say -alsa "использовать ALSA аудио API" + say -jack "использовать JACK аудио API" + say -mmio "использовать MMIO API (в Windows, по-умолчанию)" + say -portaudio "использовать дарйвера ASIO (при помощи Portaudio)" + say -oss "использовать OSS аудио API" + say -32bit "включить 32-битный режим аудио (OSS, для карт RME Hammerfall)" + say {} "по умолчанию" + +say section_midi "MIDI" + say -nomidiin "предотвратить MIDI вход" + say -nomidiout "предотвратить MIDI выход" + say -midiindev "midi in device list; e.g., \"1,3\" for first and third" + say -midioutdev "midi out device list, same format" + +say section_externals "Расширения" + say -path "путь поиска файлов" + say -helppath "путь поиска вспомогательных примеров" + say -lib "загрузить библиотеку (по имени)" + +say section_gui "Графический интерфейс" + say -nogui "отключит графический интерфейс" + say -guicmd "запуск с использованием иной программы (например: rsh или ssh)" + say -look "иконки на панельке" + say -font "размер шрифта в точках" + +say section_other "Остальное" + say -open "открыть файл(ы) при запуске" + say -verbose "вывод дополнительной информации" + say -d "уровень debug" + say -noloadbang "отключить действие \[loadbang\]" + say -send "послать сообсчение при запуске (как только все патчи загружены)" + say -listdev "вывод доступных аудио и MIDI устройств" + say -realtime "исполнение в \"реальном времени\" (требует привелегий спец-пользователя)" + +say section_paths "Путь" + +# phase 4B: ddrc (keyword names not finalized!) +say console "колличество полос памяти на консоли (0 = отключить консоль)" +say lang "язык" +say pointer_sense "чувствительность курсора" +say section_color "вид" + say canvas_color "полотно" + say canvasbgedit "цвет фона в режиме редактора" + say canvasbgrun "цвет фона в режиме исполнения" + say object_color "объект" + say viewframe1 "цвет рамки" + say viewframe2 "цвет рамки" + say viewframe3 "цвет рамки" + say viewframe4 "цвет выделенной рамки" + say viewbg "цвет фона объекта" + say viewfg "основной цвет объекта" + say commentbg "фон комментария" + say commentfg "цвет текста комментария" + say commentframe1 "рамка комментария" + say commentframe2 "рамка комментария" + say commentframe3 "рамка комментария" + say viewselectframe "выделенная рамка" + say wire_color "провод" + say wirefg "цвет провода" + say wirefg2 "выделенный провод" + say wiredspfg "цвет провода несущего сигнал" + say futurewiredash "новый провод (пунктиром)" + say others_color "другое" + say boxinletfg "цвет inlet" + say boxoutletfg "цвет outlet" + say selrectrect "выделение" +say keys "клавиши" +say others "другие" +say hairstate "включить крестик (crosshair)" +say hairsnap "crosshair snap to object" +say statusbar "включить строку статуса" +say buttonbar "включить панельку кнопок" +say menubar "включить менюшку" +say scrollbar "включить автопромотку" +say wirearrow "wire стрелка" +say tooltip "подсказки" +say insert_object "Вставить объект" +say chain_object "Цепочка оьектов" +say clear_wires "Прибрать провода" +say auto_wire "Удалить объект" +say subpatcherize "Превратить в суб-патч" +say keynav "Навигация припомощи клавиатуры" +say key_nav_up "вверх" +say key_nav_up_shift "выделение с добавкой" +say key_nav_down "вниз" +say key_nav_down_shift "выделение с добавкой" +say key_nav_right "направо" +say key_nav_right_shift "выделение с добавкой" +say key_nav_left "налево" +say key_nav_left_shift "выделение с добавкой" +say key_nav_ioselect "select in/outlets" +# phase 5A + +say cannot "Не получается .." +say cancel "Сброс" +say apply "Применить" +say ok "Окей" +say popup_open "Открыть" +say popup_insert "Вставить" +say popup_properties "Свойства" +say popup_clear_wires "Прибрать провода" +say popup_remove_from_path "Remove object from path" +say popup_delete_from_path "Delete object from path" +say popup_help "Помощь" +say filter "Фильтровать: " +say how_many_object_classes "%d из %d классов объектов" +say do_what_i_mean "Сделай всё!" +say ask_cool "Было бы круто добавить такой наворот! Не так ли?" +say save_changes? "Сохранить?" +say reset "Сброс" +say Courier "Courier (monospaced)" +say Helvetica "Helvetica (sansserif)" +say Times "Times (serif)" +say add "Добавить" +say up "Вверх" +say down "Вниз" +say remove "Удалить" +say lib_add "добавить к списку" +say lib_up "поменять местами с предыдущей библиотекой" +say lib_down "поменять местами со следующей библиотекой" +say lib_remove "удалить выбранную библиотеку" +say dir_add "указать местонахождения" +say dir_up "поменять местами с предыдущей папкой" +say dir_down "поменять местами со следующей папкой" +say dir_remove "удалить из списка" +say client_class_tree "Дерево классов" +say clipboard_view "Clipboard View" +say command_history_view "Просмотр истории команд" +say event_history_view "Просмотр истории событий" + +# during/after piksel: + +say auto_apply "Автопримение" +say font_preview "Просмотр:" +say font_preview_2 "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n0123456789" +say font_style "Стиль:" +say font_bold "Жирный" +say font_italic "Курсивом" +say font_family "Название:" +say font_size "Размер:" +say damn "Эх!" +# say damn "Нах!" +# say damn "Тьфу! Да ну тебя!" +say console_clear "Очистить консоль" +say horizontal "Горизонталь" +say vertical "Вертикаль" +say language "Язык" + +# 2007: + +say no_matches "(совпадений нет)" +say preset "preset" +say canvasgrid "цвет клеток" +say grid_size "размер клеточки" +say gridstate "показать клеточки" +say snap_grid "подгонка под клетки" +say viewfont "шрифт имён объектов" +say consolefont "консольный шрифт" +say keyboarddialogfont "шрифт разметки виртуальной клавиатуры" +say keyboard_view "Виртуальные клавиши" diff --git a/desiredata/src/locale/turkce.tcl b/desiredata/src/locale/turkce.tcl new file mode 100644 index 00000000..e33cb235 --- /dev/null +++ b/desiredata/src/locale/turkce.tcl @@ -0,0 +1,572 @@ +#!/usr/bin/env tclsh +# Turkish translations for PureData +# $Id: turkce.tcl,v 1.1.2.1 2007-10-05 23:14:23 matju Exp $ +# by Koray Tahiroglu + +### Menus + +say file "Dosya" + say new_file "Yeni" + say open_file "Aç..." + say server_prefs "Sunucu Tercihleri..." + say client_prefs "Kullanıcı Tercihleri..." + say send_message "Mesajı Gönder..." + say paths "Yollar..." + say close "Kapat" + say save "Kaydet" + say save_as "Farklı Kaydet..." + say print "Yazdır..." + say abort_server "Sunucuyu Durdur" + say quit "Çıkış" + + say canvasnew_file "Yeni" + say canvasopen_file "Aç..." + say canvassave "Kaydet" + say canvassave_as "Farklı Kaydet..." + say clientpdrc_editor ".pdrc Düzenleyicisi" + say clientddrc_editor ".ddrc Düzenleyicisi" + say canvasclose "Kapat" + say canvasquit "Çıkış" + +say edit "Düzen" + say undo "Geri Al" + say redo "Yeniden Yap" + say cut "Kes" + say copy "Kopyala" + say paste "Yapıştır" + say duplicate "Çoğalt" + say select_all "Tümünü Seç" + say clear_selection "Seçimi Kaldır" + say text_editor "Not Defteri..." + say font "Yazı Tipi" + say tidy_up "Derle Toparla" + say edit_mode "Düzenleme Durumu" + say editmodeswitch "Düzenleme/Çalıştırma Durumu" + say subpatcherize "Alt-Bağlantı" + + say canvascut "Kes" + say canvascopy "Kopyala" + say canvasundo "Geri Al" + say canvasredo "Yeniden Yap" + say canvaspaste "Yapıştır" + say canvasduplicate "Çoğalt" + say canvasselect_all "Tümünü Seç" + say canvaseditmodeswitch "Düzenleme/Çalıştırma Durumu" + +say view "Görünüm" + say reload "Yeniden Yükle" + say redraw "Yeniden Çiz" + + say canvasreload "Yeniden Yükle" + say canvasredraw "Yeniden Çiz" + +say find "Bul" + say find_again "Sonrakini Bul" + say find_last_error "En Son Hatayı Bul" + say string "Diziyi Bul" +say canvasfind "Bul" + say canvasfind_again "Sonrakini Bul" + +# contents of Put menu is Phase 5C +say put "Yerleştir" + say Object "Nesne" + say Message "Mesaj" + say Number "Sayı" + say Symbol "Simge" + say Comment "Not" + say Graph "Grafik" + say Array "Dizilim" + +say media "Ortam" + say audio_on "Ses Aç" + say audio_off "Ses Kapat" + say test_audio_and_midi "Ses ve MIDI Testi" + say load_meter "Ölçeri Yükle" + + say canvasaudio_on "Ses Aç" + say canvasaudio_off "Ses Kapat" + say clienttest_audio_and_midi "Ses ve MIDI Testi" + say canvasload_meter "Ölçeri Yükle" + +say window "Pencere" + +say help "Yardım" + say about "Hakkında..." + say documentation "Belgeleme..." + say class_browser "Sınıf Gözatıcısı..." + + say canvasabout "Hakkında..." + +say properties "Özellikler" +say open "Aç" + +### for key binding editor +say general "Genel" +say audio_settings "Ses Ayarları" +say midi_settings "Midi Ayarları" +say latency_meter "Gecikme Ölçeri" +say Pdwindow "Pd penceresi" + +say canvaspdwindow "Pd penceresi" +say canvaslatency_meter "Gecikme Ölçeri" +say clientaudio_settings "Ses Ayarları" +say clientmidi_settings "Midi Ayarları" + +### for Properties Dialog (phase 5B) +say_category IEM +say w "genişlik(px)" +say h "yükseklik(px)" +say hold "tutma zamanı(ms)" +say break "kesme zamanı(ms)" +say min "en az değer" +say max "en fazla değer" +say is_log "durum" +say linear "doğrusal" +say logarithmic "logaritmik" +say isa "init" +say n "seçenek sayısı" +say steady "kararlı" +say steady_no "tıklama ile sıçra" +say steady_yes "tıklama ile kararli ol" +say snd "simge-gönder" +say rcv "simge-al" +say lab "Etiket" +say ldx "Etiket x kaydır" +say ldy "Etiket y kaydır" +say fstyle "Yazı Biçimi" +say fs "Yazitippi boyutu" +say bcol "arkaplan rengi" +say fcol "önalan rengi" +say lcol "Etiket rengi" +say yes "evet" +say no "hayır" +say courier "courier (typewriter)" +say helvetica "helvetica (sansserif)" +say times "times (serif)" +say coords "üstteki grafik" + +say_category GAtomÖzellikleri +say width "genişlik" +say lo "alt sınır" +say hi "üst sınır" +say label "etiket" +say wherelabel "etiket göster" +say symto "simge gönder" +say symfrom "simge al" + +say_category GrafikÖzellikleri +say x1 "x'den" +say x2 "x'e" +say xpix "görüntü alanı genişliği" +say y2 "y'den" +say y1 "y'e" +say ypix "görüntü alanı yükselkiği" + +say_category KanavaÖzellikleri +#say xscale "X birim/px" +#say yscale "Y birim/px" +say gop "üstteki grafik" +say xmargin "x-kenar boşluğu" +say ymargin "y-kenar boşluğu" +say height "yükseklik" +say_category DizilimÖzellikleri +say name "adı" +say n "boyutu" +say xfrom "x aralığından" +say xto "x aralığına" +say yfrom "y aralığından" +say yto "y aralığına" + + +say_category AnaPencere +say in "giriş" +say out "çıkış" +say audio "Ses" +say meters "Ölçerler" +say io_errors "IO Hataları" +say tcl_console "Tcl Kullanıcısi" +say pd_console "Pd Sunucusu" + +### phase 2 + +say_category Diğer +say_namespace özet { + say_category IEMGUI + say bng "Patlama Kutusu" + say tgl "Düğme Kutusu" + say nbx "Sayı Kutusu (IEM)" + say hsl "Kaydırıcı (yatay)" + say vsl "Kaydırıcı (düşey)" + say hradio "Seçme Kutusu (yatay)" + say vradio "Seçme Kutusu (düşey)" + say cnv "Kanava (IEM)" + say dropper "Taşı-ve-Bırak Kutusu" + say vu "Vumeter" + + say_category GLUE + say bang "patlama mesajı ver" + say float "sayıyı sakla ve yeniden çağır" + say symbol "simgeyi sakla ve yeniden çağır" + say int "tamsayıyı sakla ve yeniden çağır" + say send "isimlendirilmiş bir nesneye mesaj gönder" + say receive "gönderilen mesajları al" + say select "eşleşen sayı ve simgeler için test" + say route "ilk öğelerine göre mesajları yönlendir" + say pack "make bileşik mesajlar oluştur" + say unpack "get elements of compound messages" + say trigger "mesajları sırala ve dönüştür" + say spigot "kesilebilir mesaj bağlantısı" + say moses "sayısal akımı ayır" + say until "döngü meaknizması" + say print "mesajları yazdır" + say makefilename "simgeyi bir değişken alanı ile biçimlendir" + say change "tekrar edilen sayıları akımdan ayır" + say swap "iki sayıyı takas et" + say value "paylaşılan sayısal değer" + + say_category ZAMAN + say delay "zaman geciktirmesi sonunda mesaj gönder" + say metro "belirli aralıklarla mesaj gönder" + say line "send a series of linearly stepped numbers" + say timer "zaman aralıklarını hesapla" + say cputime "CPU zamanını hesapla" + say realtime "gerçek zamanı hesapla" + say pipe "sayılar için dinamik olarak büyüyen gecikme hattı" + + say_category MATEMATİK + say + "topla" + say - "çıkart" + say * "çarp" + say {/ div} "böl" + say {% mod} "bölümden kalan" + say pow "üs al" + say == "eşit?" + say != "eşit değil?" + say > "büyük?" + say < "küçük?" + say >= "küçük değil?" + say <= "büyük değil?" + say & "bit olarak birleşme (and)" + say | "bit olarak ayırtım (or)" + say && "mantıksal birleşme (and)" + say || "mantıksal ayırtım (or)" + say mtof "MIDI'den Hertz'e" + say ftom "Hertz'den MIDI'ye" + say powtodb "Watts'dan dB'e" + say dbtopow "dB'den Watts'a" + say rmstodb "Gerilimden dB'e" + say dbtorms "dB'den Gerilime" + say {sin cos tan atan atan2 sqrt} "trigonometri" + say log "Euler logaritma" + say exp "Euler Üssel" + say abs "mutlak değer" + say random "rasgele" + say max "iki sayının en büyüğü" + say min "iki sayının en küçüğü" + say clip "sayıyı belirli bir değer aralığına indirge" + + say_category MIDI + say {notein ctlin pgmin bendin touchin polytouchin midiin sysexin} "MIDI giriş" + say {noteout ctlout pgmout bendout touchout polytouchout midiout} "MIDI çıkış" + say makenote " note-on a uygun olarak gecikmeli bir \"note off\" mesajı düzenle" + say stripnote "strip \"note off\" mesajı" + + say_category TABLOLAR + say tabread "tablodan bir sayı oku" + say tabread4 "tablodan 4 noktalı aradeğerleme ile sayı oku" + say tabwrite "tabloya bir sayı yaz" + say soundfiler "tabloları ses dosyalarına yaz ve oku" + + say_category MISC + say loadbang "yüklendiği zaman patlama gönder" + say serial "yalnızca NT için dizisel aygıt kontrolü" + say netsend "internet üzerinden mesaj gönder" + say netreceive "internet üzerinden mesaj al" + say qlist "mesaj ardıştırıcısı" + say textfile "dosyayı mesaja çevirici" + say openpanel "\"Aç\" diyalog" + say savepanel "\"Farklı Kaydet\" diyalog" + say bag "sayılar kümesi" + say poly "çoksesli ses ayırması" + say {key keyup} "klavyeden sayısal tuş değerleri" + say keyname "simgelsel tuş ismi" + + say_category "Ses Matematiği" + foreach word {+ - * /} {say $word~ "[say $word] (sinyaller için)"} + say max~ "sinyallerin enyükseği" + say min~ "sinyallerin en düşüğü" + say clip~ "sinyalin iki sınır arasında yer almasının sağlanması" + say q8_rsqrt~ "indirimli ters karekökü (dikkat -- 8 bits!)" + say q8_sqrt~ "ndirimli karekök (dikkat -- 8 bits!)" + say wrap~ "wraparound (fractional part, sort of)" + say fft~ "complex forward discrete Fourier transform" + say ifft~ "complex inverse discrete Fourier transform" + say rfft~ "real forward discrete Fourier transform" + say rifft~ "real inverse discrete Fourier transform" + say framp~ "output a ramp for each block" + foreach word {mtof ftom rmstodb dbtorms rmstopow powtorms} { + say $word~ "[say $word] (for signals)" + } +} + +### phase 3 + +say_namespace summary { + say_category "AUDIO GLUE" + say dac~ "audio output" + say adc~ "audio input" + say sig~ "convert numbers to audio signals" + say line~ "generate audio ramps" + say vline~ "deluxe line~" + say threshold~ "detect signal thresholds" + say snapshot~ "sample a signal (convert it back to a number)" + say vsnapshot~ "deluxe snapshot~" + say bang~ "send a bang message after each DSP block" + say samplerate~ "get the sample rate" + say send~ "nonlocal signal connection with fanout" + say receive~ "get signal from send~" + say throw~ "add to a summing bus" + say catch~ "define and read a summing bus" + say block~ "specify block size and overlap" + say switch~ "switch DSP computation on and off" + say readsf~ "soundfile playback from disk" + say writesf~ "record sound to disk" + + say_category "AUDIO OSCILLATORS AND TABLES" + say phasor~ "sawtooth oscillator" + say {cos~ osc~} "cosine oscillator" + say tabwrite~ "write to a table" + say tabplay~ "play back from a table (non-transposing)" + say tabread~ "non-interpolating table read" + say tabread4~ "four-point interpolating table read" + say tabosc4~ "wavetable oscillator" + say tabsend~ "write one block continuously to a table" + say tabreceive~ "read one block continuously from a table" + + say_category "AUDIO FILTERS" + say vcf~ "voltage controlled filter" + say noise~ "white noise generator" + say env~ "envelope follower" + say hip~ "high pass filter" + say lop~ "low pass filter" + say bp~ "band pass filter" + say biquad~ "raw filter" + say samphold~ "sample and hold unit" + say print~ "print out one or more \"blocks\"" + say rpole~ "raw real-valued one-pole filter" + say rzero~ "raw real-valued one-zero filter" + say rzero_rev~ "[say rzero~] (time-reversed)" + say cpole~ "[say rpole~] (complex-valued)" + say czero~ "[say rzero~] (complex-valued)" + say czero_rev~ "[say rzero_rev~] (complex-valued)" + + say_category "AUDIO DELAY" + say delwrite~ "write to a delay line" + say delread~ "read from a delay line" + say vd~ "read from a delay line at a variable delay time" + + say_category "SUBWINDOWS" + say pd "define a subwindow" + say table "array of numbers in a subwindow" + say inlet "add an inlet to a pd" + say outlet "add an outlet to a pd" + say inlet~ "[say inlet] (for signal)" + say outlet~ "[say outlet] (for signal)" + + say_category "DATA TEMPLATES" + say struct "define a data structure" + say {drawcurve filledcurve} "draw a curve" + say {drawpolygon filledpolygon} "draw a polygon" + say plot "plot an array field" + say drawnumber "print a numeric value" + + say_category "ACCESSING DATA" + say pointer "point to an object belonging to a template" + say get "get numeric fields" + say set "change numeric fields" + say element "get an array element" + say getsize "get the size of an array" + say setsize "change the size of an array" + say append "add an element to a list" + say sublist "get a pointer into a list which is an element of another scalar" + say scalar "draw a scalar on parent" + + say_category "OBSOLETE" + say scope~ "(use tabwrite~ now)" + say namecanvas "" ;# what was this anyway? + say template "(use struct now)" +} + +# phase 4 (pdrc & ddrc) + +say section_audio "Audio" + say -r "sample rate" + say -audioindev "audio in devices" + say -audiooutdev "audio out devices" + say -inchannels "audio input channels (by device, like \"2\" or \"16,8\")" + say -outchannels "number of audio out channels (same)" + say -audiobuf "specify size of audio buffer in msec" + say -blocksize "specify audio I/O block size in sample frames" + say -sleepgrain "specify number of milliseconds to sleep when idle" + say -nodac "suppress audio output" + say -noadc "suppress audio input" + say audio_api_choice "Audio API" + say default "default" + say -alsa "use ALSA audio API" + say -jack "use JACK audio API" + say -mmio "use MMIO audio API (default for Windows)" + say -portaudio "use ASIO audio driver (via Portaudio)" + say -oss "use OSS audio API" + say -32bit "allow 32 bit OSS audio (for RME Hammerfall)" + say {} "default" + +say section_midi "MIDI" + say -nomidiin "suppress MIDI input" + say -nomidiout "suppress MIDI output" + say -midiindev "midi in device list; e.g., \"1,3\" for first and third" + say -midioutdev "midi out device list, same format" + +say section_externals "Externals" + say -path "file search path" + say -helppath "help file search path" + say -lib "load object libraries" + +say section_gui "Gooey" + say -nogui "suppress starting the GUI (caution)" + say -guicmd "substitute another GUI program (e.g., rsh)" + say -look "buttonbar icons" + say -font "specify default font size in points" + +say section_other "Other" + say -open "open file(s) on startup" + say -verbose "extra printout on startup and when searching for files" + say -d "debug level" + say -noloadbang "disable the effect of \[loadbang\]" + say -send "send a message at startup (after patches are loaded)" + say -listdev "list audio and MIDI devices upon startup" + say -realtime "use real-time priority (needs root privilege)" + +say section_paths "Paths" + +# phase 4B: ddrc (keyword names not finalized!) +say console "console scrollback lines (0 = disable console)" +say lang "Language to use" +say pointer_sense "Mouse pointer sensitivity" +say section_color "Apparences" + say canvas_color "canvas" + say canvasbgedit "canvas background (edit mode)" + say canvasbgrun "canvas background (run mode)" + say object_color "object" + say viewframe1 "objectbox color" + say viewframe2 "objectbox color" + say viewframe3 "objectbox color" + say viewframe4 "objectbox highlight color" + say viewbg "object background" + say viewfg "object foreground" + say commentbg "comment background" + say commentfg "comment forground" + say commentframe1 "comment frame" + say commentframe2 "comment frame" + say commentframe3 "comment frame" + say viewselectframe "hilight box" + say wire_color "wire" + say wirefg "wire color" + say wirefg2 "wire highlight" + say wiredspfg "dsp wire color" + say futurewiredash "new (dashed) wire" + say others_color "others" + say boxinletfg "inlet color" + say boxoutletfg "outlet color" + say selrectrect "selection box" +say keys "keys" +say others "others" +say hairstate "Activate crosshair" +say hairsnap "Crosshair snap to object" +say statusbar "Activate statusbar" +say buttonbar "Activate buttonbar" +say menubar "Activate menubar" +say scrollbar "Active auto scrollbar" +say wirearrow "Wire Arrow" +say tooltip "ToolTip" +say insert_object "Insert object" +say chain_object "Chain object" +say clear_wires "Clear wires" +say auto_wire "Remove object" +say subpatcherize "Subpatcherize" +say keynav "keyboard navigation" +say key_nav_up "move up" +say key_nav_up_shift "plus select" +say key_nav_down "move down" +say key_nav_down_shift "plus select" +say key_nav_right "move right" +say key_nav_right_shift "plus select" +say key_nav_left "move left" +say key_nav_left_shift "plus select" +say key_nav_ioselect "select in/outlets" +# phase 5A + +say cannot "can't" +say cancel "Cancel" +say apply "Apply" +say ok "OK" +say popup_open "Open" +say popup_insert "Insert" +say popup_properties "Properties" +say popup_clear_wires "Clear wires" +say popup_remove_from_path "Remove object from path" +say popup_delete_from_path "Delete object from path" +say popup_help "Help" +say filter "Filter: " +say how_many_object_classes "%d of %d object classes" +say do_what_i_mean "Do What I Mean" +say ask_cool "This would be a cool feature, eh?" +say save_changes? "Save changes?" +say reset "Reset" +say Courier "Courier (monospaced)" +say Helvetica "Helvetica (sansserif)" +say Times "Times (serif)" +say add "Add" +say up "Up" +say down "Down" +say remove "Remove" +say lib_add "add the name you typed to the list" +say lib_up "swap order with previous library" +say lib_down "swap order with next library" +say lib_remove "remove library selected in the list" +say dir_add "add a folder using a file dialog" +say dir_up "swap order with previous folder" +say dir_down "swap order with next folder" +say dir_remove "remove folder selected in the list" +say client_class_tree "Client Class Tree" +say clipboard_view "Clipboard View" +say command_history_view "Command History View" +say event_history_view "Event History View" + +# during/after piksel: + +say auto_apply "Auto-Apply" +say font_preview "Preview:" +say font_preview_2 "ABCDEFGHIJKLMNOPQRSTUVWXYZ\nabcdefghijklmnopqrstuvwxyz\n0123456789" +say font_style "Style:" +say font_bold "Bold" +say font_italic "Italic" +say font_family "Name:" +say font_size "Size:" +say damn "Damn!" +say console_clear "Clear Console" +say horizontal "Horizontal" +say vertical "Vertical" +say language "Language" + +# 2007: + +say no_matches "(no matches)" +say preset "preset" +say canvasgrid "Grid color" +say grid_size "Grid size" +say gridstate "Activate background grid" +say snap_grid "Snap to grid" +say viewfont "object font" +say consolefont "console font" +say keyboarddialogfont "virtual keyboard font" +say keyboard_view "Virtual keyboard"
\ No newline at end of file diff --git a/desiredata/src/m_atomic.h b/desiredata/src/m_atomic.h new file mode 100644 index 00000000..f7314d11 --- /dev/null +++ b/desiredata/src/m_atomic.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2005, Tim Blechmann + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt" in this distribution. */ + + +#if defined(__GNUC__) && (defined(_X86_) || defined(__i386__) || defined(__i586__) || defined(__i686__)) + +/* gcc, x86 */ +#define ATOMIC_INC(X) \ + asm __volatile__("lock incl (%0) \n" \ + : :"r"(X) :"memory") + + +#define ATOMIC_DEC(X) \ + asm __volatile__("lock decl (%0) \n" \ + : :"r"(X) :"memory") + + + +#elif defined(NT) && defined(_MSC_VER) + +/* msvc */ +#include <windows.h> +#define ATOMIC_INC(X) InterlockedIncrement(X) +#define ATOMIC_DEC(X) InterlockedDecrement(X) + + +#elif defined(__GNUC__) && defined(__POWERPC__) + +/* ppc */ +#define ATOMIC_INC(X) { \ +int X##_i; \ +asm __volatile__( \ + "1: \n" \ + "lwarx %0, 0, %2 \n" \ + "addic %0, %0, 1 \n" \ + "stwcx. %0, 0, %2 \n" \ + "bne- 1b \n" \ + :"=&r"(X##_i), "=m"(X) \ + : "r" (&X), "m"(X) \ + : "cc"); } + + +#define ATOMIC_DEC(X) { \ +int X##_i; \ +asm __volatile__( \ + "1: \n" \ + "lwarx %0, 0, %2 \n" \ + "addic %0, %0, -1 \n" \ + "stwcx. %0, 0, %2 \n" \ + "bne- 1b \n" \ + :"=&r"(X##_i), "=m"(X) \ + : "r" (&X), "m"(X) \ + : "cc"); } +#endif diff --git a/desiredata/src/m_fifo.c b/desiredata/src/m_fifo.c new file mode 100644 index 00000000..34d19d1b --- /dev/null +++ b/desiredata/src/m_fifo.c @@ -0,0 +1,443 @@ +/* Copyright (c) 2004, Tim Blechmann + * supported by vibrez.net + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt" in this distribution. */ + + +#include <stddef.h> +#include <stdlib.h> +#include "m_pd.h" + +#ifndef LOCKFREE + +/* we always have the implementation for posix systems with threadlocks */ + +#include "pthread.h" +#include "errno.h" + +typedef struct _fifocell +{ + struct _fifocell* next; + void* data; /* pointer to our data */ +} t_fifocell; + +struct _fifo { + t_fifocell * head; + t_fifocell * tail; + pthread_mutex_t mutex; +}; + +t_fifo *fifo_init(void) { + t_fifo* ret = (t_fifo*)malloc(sizeof(t_fifo)); + t_fifocell * fifo_begin = (t_fifocell*)malloc(sizeof(t_fifocell)); + fifo_begin->data = 0; + fifo_begin->next = 0; + ret->head = fifo_begin; + ret->tail = fifo_begin; + pthread_mutex_init(&ret->mutex, 0); + pthread_mutex_unlock(&ret->mutex); + return ret; +} + +void fifo_destroy(t_fifo* fifo) { + void *data; + do data = fifo_get(fifo); while (data); + pthread_mutex_lock(&fifo->mutex); + pthread_mutex_destroy(&fifo->mutex); + free(fifo); + return; +} + +/* fifo_put and fifo_get are the only threadsafe functions!!! */ +void fifo_put(t_fifo* fifo, void* data) { + if (data) { + t_fifocell * cell = (t_fifocell*)malloc(sizeof(t_fifocell)); + cell->data = data; + cell->next = 0; + pthread_mutex_lock(&fifo->mutex); + fifo->tail->next = cell; + fifo->tail = cell; + pthread_mutex_unlock(&fifo->mutex); + } + return; +} + +/* this fifo_get returns 0 if the fifo is empty or locked by another thread */ +void* fifo_get(t_fifo* fifo) { + t_fifocell * cell; + void *data; + if(pthread_mutex_trylock(&fifo->mutex) != EBUSY) { + cell = fifo->head->next; + if (cell) { + fifo->head->next = cell->next; + if(cell == fifo->tail) + fifo->tail = fifo->head; + data = cell->data; + free(cell); + } else data = 0; + pthread_mutex_unlock(&fifo->mutex); + } else data = 0; + return data; +} + +#else /* LOCKFREE */ + +/* + lockfree fifo adapted from the midishare: Copyright � Grame 1999 + Grame Research Laboratory, 9, rue du Garet 69001 Lyon - France + grame@rd.grame.fr +*/ + +typedef struct _fifocell { + struct _fifocell *next; + void *data; /* pointer to our data */ +} t_fifocell; + +typedef struct _lifo { + unsigned long ic; /* operation counter */ + t_fifocell* top; /* stack pointer */ + unsigned long oc; /* operation counter */ +#ifdef __POWERPC__ + long unused [5]; /* lifo size must be at least 32 bytes */ + /* to avoid livelock in multiprocessor */ +#endif +} t_lifo; + +struct _fifo { + t_lifo in; + t_lifo out; +}; + +/* platform dependent code */ + +#ifdef __SMP__ +#define LOCK lock ; +#else +#define LOCK +#endif + +#if defined(__GNUC__) && (defined(__POWERPC__) || defined(__PPC__)) + +static void* lifo_pop(t_lifo* lifo) { + register void * data; + register volatile long a, b; + register long c=0; + asm volatile ( + "# LFPOP \n" + "0: \n" + " lwarx %4, %1, %2 \n" /* creates a reservation on lf */ + " cmpwi %4, 0 \n" /* test if the lifo is empty */ + " beq- 1f \n" + " lwz %5, 0(%4) \n" /* next cell in b */ + " sync \n" /* synchronize instructions */ + " stwcx. %5, %1, %2 \n" /* if the reservation is not altered */ + /* modify lifo top */ + " bne- 0b \n" /* otherwise: loop and try again */ + "0: \n" + " lwarx %5, %1, %3 \n" /* creates a reservation on lf->count */ + " addi %5, %5, -1 \n" /* dec count */ + " sync \n" /* synchronize instructions */ + " stwcx. %5, %1, %3 \n" /* conditionnal store */ + " bne- 0b \n" + "1: \n" + " mr %0, %4 \n" + :"=r" (data), "=r" (c) + : "r" (&lifo->top), "r" (&lifo->oc), "r" (a), "r" (b), "1" (c) + : "r0" /* prevents using r0 because of the ambiguity of 'addi' coding: */ + /* gcc version 2.95.3 20010315 (release - Linux-Mandrake 8.0 for PPC) */ + /* compiles the instruction "addi 0, 0, n" as li 0, n */ + ); + return data; +} + +static void* lifo_push(register t_lifo *lifo, register void *data) { + register volatile long t1; + register long t2=0; + asm volatile ( + "# LFPUSH \n" + "0: \n" + " lwarx %0, %3, %1 \n" + " stw %0, 0(%2) \n" + " sync \n" + " stwcx. %2, %3, %1 \n" + " bne- 0b \n" + "0: \n" + " lwarx %0, %3, %4 \n" + " addi %0, %0, 1 \n" + " sync \n" + " stwcx. %0, %3, %4 \n" + " bne- 0b \n" + : "=r" (t1) + : "r" (&lifo->top), "r" (data), "r" (t2), "r" (&lifo->oc), "0" (t1) + : "r0" /* prevents using r0 because of the ambiguity of 'addi' coding: */ + /* gcc version 2.95.3 20010315 (release - Linux-Mandrake 8.0 for PPC) */ + /* compiles the instruction "addi 0, 0, n" as li 0, n */ + ); +} + +#elif defined(__ppc__) && defined(__APPLE__) + +static void *lifo_pop(t_lifo *lifo) { + register void *data; + register long a, b; + asm { + addi lifo, lifo, 4 + loop: + lwarx a, 0, lifo /* creates a reservation on lifo */ + cmpwi a, 0 /* test if the lifo is empty */ + beq- empty + lwz b, 0(a) /* next cell in b */ + sync /* synchronize instructions */ + stwcx. b, 0, lifo /* if the reservation is not altered */ + /* modify lifo top */ + bne- loop /* otherwise: loop and try again */ + + addi lifo, lifo, 4 + dec: + lwarx b, 0, lifo /* creates a reservation on lifo->count */ + addi b, b, -1 /* dec count */ + sync /* synchronize instructions */ + stwcx. b, 0, lifo /* conditionnal store */ + bne- dec + + empty: + mr data, a + } + return data; +} + +static void lifo_push (register t_lifo * lifo, register void * data) +{ + register long tmp; + asm { + addi lifo, lifo, 4 + loop: + lwarx tmp, 0, lifo /* creates a reservation on lifo */ + stw tmp, 0(data) /* link the new cell to the lifo */ + sync /* synchronize instructions */ + stwcx. data, 0, lifo /* if the reservation is not altered */ + /* modify lifo top */ + bne- loop /* otherwise: loop and try again */ + + addi lifo, lifo, 4 + inc: + lwarx tmp, 0, lifo /* creates a reservation on lifo->count */ + addi tmp, tmp, 1 /* inc count */ + sync /* synchronize instructions */ + stwcx. tmp, 0, lifo /* conditionnal store */ + bne- inc + } +} + + + +#elif defined(__GNUC__) && (defined(_X86_) || defined(__i386__) || defined(__i586__) || defined(__i686__)) + +static void* lifo_pop(t_lifo* lifo) +{ + void * data = 0; + __asm__ __volatile__ ( + "# LFPOP \n\t" + "pushl %%ebx \n\t" + "pushl %%ecx \n\t" + "movl 4(%%esi), %%edx \n\t" + "movl (%%esi), %%eax \n\t" + "testl %%eax, %%eax \n\t" + "jz 2f \n" + "1:\t" + "movl (%%eax), %%ebx \n\t" + "movl %%edx, %%ecx \n\t" + "incl %%ecx \n\t" + LOCK "cmpxchg8b (%%esi) \n\t" + "jz 2f \n\t" + "testl %%eax, %%eax \n\t" + "jnz 1b \n" + "2:\t" + "popl %%ecx \n\t" + "popl %%ebx \n\t" + :"=a" (data) + :"S" (&lifo->top) + :"memory", "edx"); + return data; +} + +static void lifo_push(t_lifo * lifo, void * data) +{ + __asm__ __volatile__ ( + "# LFPUSH \n\t" + "pushl %%ebx \n\t" + "pushl %%ecx \n\t" + "movl 0(%%esi), %%eax \n\t" + "movl 4(%%esi), %%edx \n" + "1:\t" + "movl %%eax, %%ebx \n\t" + "incl %%ebx \n\t" + "movl %%edx, (%%ecx) \n\t" + LOCK "cmpxchg8b (%%esi) \n\t" + "jnz 1b \n\t" + "popl %%ecx \n\t" + "popl %%ebx \n\t" + :/* no output */ + :"S" (lifo), "c" (data) + :"memory", "eax", "edx"); +} + +#elif defined(__GNUC__) && defined(__x86_64__) + +/* this will not work for all revisions of the amd64 architecture ... */ + +static void* lifo_pop(t_lifo* lifo) +{ + void * data = 0; + __asm__ __volatile__ ( + "# LFPOP \n\t" + "push %%rbx \n\t" + "push %%rcx \n\t" + "mov 8(%%rdi), %%rdx \n\t" + "mov (%%rdi), %%rax \n\t" + "test %%rax, %%rax \n\t" + "jz 2f \n" + "1:\t" + "mov (%%rax), %%rbx \n\t" + "mov %%rdx, %%rcx \n\t" + "inc %%rcx \n\t" + LOCK "cmpxchg16b (%%rdi) \n\t" + "jz 2f \n\t" + "test %%rax, %%rax \n\t" + "jnz 1b \n" + "2:\t" + "pop %%rcx \n\t" + "pop %%rbx \n\t" + :"=a" (data) + :"D" (&lifo->top) + :"memory", "rdx"); + return data; +} + +static void lifo_push(t_lifo * lifo, void * data) +{ + __asm__ __volatile__ ( + "# LFPUSH \n\t" + "push %%rbx \n\t" + "push %%rcx \n\t" + "mov 0(%%rdi), %%rax \n\t" + "mov 8(%%rdi), %%rdx \n" + "1:\t" + "mov %%rax, %%rbx \n\t" + "inc %%rbx \n\t" + "mov %%rdx, (%%rcx) \n\t" + LOCK "cmpxchg16b (%%rdi) \n\t" + "jnz 1b \n\t" + "pop %%rcx \n\t" + "pop %%rbx \n\t" + :/* no output */ + :"D" (lifo), "c" (data) + :"memory", "rax", "rdx"); +} + +#elif defined(_WIN32) && defined(_MSC_VER) + +static void* lifo_pop(t_lifo* lifo) +{ + __asm + { + push ebx + push ecx + push edx + push esi + mov esi, lifo + add esi, 4 + mov edx, dword ptr [esi+4] + mov eax, dword ptr [esi] + test eax, eax + jz _end + _loop: + mov ebx, dword ptr [eax] + mov ecx, edx + inc ecx + LOCK cmpxchg8b qword ptr [esi] + jz _end + test eax, eax + jnz _loop + _end: + pop esi + pop edx + pop ecx + pop ebx + } +} + +static void lifo_push(t_lifo * lifo, void * data) +{ + __asm + { + push eax + push ebx + push ecx + push edx + push esi + mov esi, lifo + mov eax, dword ptr [esi] + mov ecx, data + mov edx, dword ptr 4[esi] + _loop: + mov ebx, eax + inc ebx + mov [ecx], edx + LOCK cmpxchg8b qword ptr [esi] + jnz _loop + pop esi + pop edx + pop ecx + pop ebx + pop eax + } +} + + +#else +#error lockfree fifos not available on this platform +#endif + +static void lifo_init(t_lifo* lifo) { + lifo->ic = 0; + lifo->top = 0; + lifo->oc = 0; +} + +t_fifo *fifo_init(void) { + t_fifo *ret = (t_fifo *)malloc(sizeof(t_fifo)); + lifo_init(&ret->in); + lifo_init(&ret->out); + return ret; +} + +void fifo_destroy(t_fifo *fifo) { + void *data; + do data = fifo_get(fifo); while (data); + free(fifo); + return; +} + +void fifo_put(t_fifo *fifo, void *data) {lifo_push(&fifo->in, data);} + +void* fifo_get(t_fifo *fifo) { + void *data; + t_lifo *out = &fifo->out; + data = lifo_pop(out); + if (!data) { + void *tmp; + t_lifo *in = &fifo->in; + data = lifo_pop(in); + if (data) { + while((tmp = lifo_pop(in))) { + lifo_push(out, data); + data = tmp; + } + } + + } + return data; +} + +#endif diff --git a/desiredata/src/m_pd.h b/desiredata/src/m_pd.h new file mode 100644 index 00000000..cb1a7c72 --- /dev/null +++ b/desiredata/src/m_pd.h @@ -0,0 +1,936 @@ +/* Copyright (c) 2006-2007 Mathieu Bouchard. + 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_PLUSPLUS_FACE is not considered as part of the main interface for externals, + even though it has become the main interface for internals. please don't rely on + it outside of the desiredata source code */ + +#ifndef __m_pd_h_ + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +#include <iostream> + +#ifdef __cplusplus +template <class K, class V> class t_hash /* : t_pd */ { + struct entry { + K k; + V v; + struct entry *next; + }; + long capa; + long n; + entry **tab; +/* when iterating: */ + long i; + entry *m_next; +public: + t_hash(long capa_) : capa(capa_), n(0), i(0) { + tab = new entry *[capa]; + for (long j=0; j<capa; j++) tab[j]=0; + } + ~t_hash() { + for (long j=0; j<capa; j++) while (tab[j]) del(tab[j]->k); + delete[] tab; + } + size_t size() {return n;} + V get(K k) { + long h = hash(k); + for (entry *e=tab[h]; e; e=e->next) {if (e->k==k) return e->v;} + return 0; + } + void set(K k, V v) { + long h = hash(k); + for (entry *e=tab[h]; e; e=e->next) {if (e->k==k) {e->v=v; return;}} + entry *nu = new entry; nu->k=k; nu->v=v; nu->next=tab[h]; + n++; + tab[h] = nu; + } + V del(K k) { + long h = hash(k); + for (entry **ep=&tab[h]; *ep; ep=&(*ep)->next) { + if ((*ep)->k==k) { + V v=(*ep)->v; + entry *next=(*ep)->next; + delete *ep; n--; + *ep = next; + return v; + } + } + return 0; + } + int exists(K k) { + long h = hash(k); + for (entry *e=tab[h]; e; e=e->next) {if (e->k==k) return 1;} + return 0; + } + void start() {i=0; m_next=0;} + int next(K *kp, V *vp) { + while (!m_next && i<capa) m_next = tab[i++]; + if (m_next) { + *kp = m_next->k; + *vp = m_next->v; + m_next = m_next->next; + return 1; + } + return 0; + } + #define hash_foreach(k,v,h) for (h->start(); h->next(&k,&v); ) + long hash(K k) {return (((long)k*0x54321) & 0x7FFFFFFF)%capa;} +}; +#endif + +extern "C" { +#endif + +#define PD_MAJOR_VERSION 1 +#define PD_MINOR_VERSION 0 +#define PD_DEVEL_VERSION 0 +#define PD_TEST_VERSION "" + +/* old name for "MSW" flag -- we have to take it for the sake of many old +"nmakefiles" for externs, which will define NT and not MSW */ +#if (defined(_WIN32) || defined(NT)) && !defined(MSW) +#define MSW +#endif + +#ifdef __CYGWIN__ +#define UNISTD +#endif + +#ifdef _MSC_VER +/* #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 /* _MSC_VER */ + + /* the external storage class is "extern" in GCC; in MS-C it's ugly. */ +#if defined(MSW) && !defined (__GNUC__) +#ifdef PD_INTERNAL +#define EXTERN __declspec(dllexport) extern +#else +#define EXTERN __declspec(dllimport) extern +#endif /* PD_INTERNAL */ +#else +#define EXTERN extern +#endif /* MSW */ + +#if !defined(_SIZE_T) && !defined(_SIZE_T_) +#include <stddef.h> +#endif + +#include <stdarg.h> + +#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: */ +/* GG: long is the size of a pointer */ +typedef long t_int; + +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 _class t_class; +typedef struct _outlet t_outlet; +typedef struct _inlet t_inlet; +typedef struct _clock t_clock; +typedef struct _glist t_glist, t_canvas; + +#ifdef PD_PLUSPLUS_FACE +struct t_pd { + t_class *_class; +}; +#else +typedef t_class *t_pd; /* pure datum: nothing but a class pointer */ +#endif + +typedef struct _symbol { + char *name; /* the const string that represents this symbol */ + t_pd *thing; /* pointer to the target of a receive-symbol or to the bindlist of several targets */ + struct _symbol *next; /* brochette of all symbols (only for permanent symbols) */ + size_t refcount; /* refcount<0 means that the symbol is permanent */ + size_t n; /* size of name (support for NUL characters) */ +} t_symbol; +#define s_name name +#define s_thing thing +#define s_next next + +#ifdef PD_PLUSPLUS_FACE +typedef struct t_list : t_pd { +#else +typedef struct t_list { + t_pd *pd; +#endif + struct _atom *v; + size_t capa; + size_t refcount; + size_t n; +} t_list, t_binbuf; + +typedef struct _array t_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. */ + +#ifdef PD_PLUSPLUS_FACE +typedef struct _gpointer { + union { + struct _scalar *scalar; /* scalar we're in (if glist) */ + union word *w; /* raw data (if array) */ + }; + union { + struct _glist *canvas; + struct _array *array; + struct t_text *o; + }; + size_t dummy; + size_t refcount; +//#define gs_refcount refcount +#define gs_refcount canvas->refcount +} t_gpointer; +#else +typedef struct _gpointer { + union { + struct _scalar *gp_scalar; /* scalar we're in (if glist) */ + union word *gp_w; /* raw data (if array) */ + } gp_un; + union { + struct _glist *canvas; + struct _array *array; + struct _text *o; + } un; + size_t dummy; + size_t refcount; +//#define gs_refcount un.canvas->gl_obj.refcount +#define gs_refcount refcount +} t_gpointer; +#endif + +#define w_list w_canvas +typedef union word { + t_float w_float; /* A_FLOAT */ + t_symbol *w_symbol; /* A_SYMBOL or A_DOLLSYM */ + t_gpointer *w_gpointer; /* A_POINTER */ + t_array *w_array; /* DT_ARRAY */ + struct _glist *w_canvas; /* DT_LIST */ + t_int w_index; /* A_SEMI or A_COMMA or A_DOLLAR */ +} t_word; + +typedef enum { + A_NULL, /* non-type: represents end of typelist */ + A_FLOAT, A_SYMBOL, A_POINTER, /* stable elements */ + A_SEMI, A_COMMA, /* radioactive elements of the first kind */ + A_DEFFLOAT, A_DEFSYM, /* pseudo-types for optional (DEFault) arguments */ + A_DOLLAR, A_DOLLSYM, /* radioactive elements of the second kind */ + A_GIMME, /* non-type: represents varargs */ + A_CANT, /* bottom type: type constraint is impossible */ + A_ATOM /* top type: type constraint doesn't constrain */ +} t_atomtype; + +#define A_DEFSYMBOL A_DEFSYM /* better name for this */ + +typedef struct _atom { + t_atomtype a_type; + union word a_w; +#ifdef __cplusplus + operator float () {return a_w.w_float;} + operator t_symbol *() {return a_w.w_symbol;} + //bool operator == (_atom &o) {return a_type==o.a_type && a_w.w_index==o.a_w.w_index;} + //bool operator != (_atom &o) {return a_type!=o.a_type || a_w.w_index!=o.a_w.w_index;} +#endif +} t_atom; + +struct t_arglist {long c; t_atom v[0];}; + +typedef unsigned long long uint64; + +/* t_appendix is made of the things that logically ought to be in t_gobj but have been put in a + separate memory space because this allows most externals to work unmodified on both DesireData + and non-DesireData systems. The equivalent in the Tcl side is really part of every view object. */ +typedef struct t_appendix { + t_canvas *canvas; /* the holder of this object */ +/* actual observable */ + size_t nobs; /* number of spies */ + struct _gobj **obs; /* I spy with my little I */ +/* miscellaneous */ +#ifdef __cplusplus + t_hash<t_symbol *, t_arglist *> *visual; +#else + void *visual; +#endif + long index; /* index of an object within its canvas scope. */ + uint64 elapsed; +} t_appendix; +t_appendix *appendix_new (struct _gobj *master); +void appendix_free(struct _gobj *self); +void appendix_save(struct _gobj *master, t_binbuf *b); + +#ifdef PD_PLUSPLUS_FACE +#define g_pd _class +typedef struct _gobj : t_pd { +#else +typedef struct _gobj /* a graphical object */ +{ + t_pd g_pd; /* pure datum header (class) */ +#endif + t_appendix *dix; +#define g_adix dix +#ifdef PD_PLUSPLUS_FACE + _gobj *next(); +#endif +} t_gobj; + +#ifdef PD_PLUSPLUS_FACE +typedef struct _outconnect : _gobj { + struct _outconnect *next; + t_pd *oc_to; + struct t_text *from; + struct t_text *to; + short outlet, inlet; +} t_outconnect, t_wire; +#define oc_next next +#else +typedef struct _outconnect t_outconnect; +#endif + +#ifdef PD_PLUSPLUS_FACE +typedef struct _scalar : t_gobj { +#else +typedef struct _scalar /* a graphical object holding data */ +{ + t_gobj sc_gobj; /* header for graphical object */ +#endif + t_symbol *t; /* template name (LATER replace with pointer) */ + t_word v[1]; /* indeterminate-length array of words */ +} t_scalar; + +#define sc_template t +#define sc_vec v + +#ifdef PD_PLUSPLUS_FACE +typedef struct t_text : t_gobj { +#else +typedef struct _text /* patchable object - graphical, with text */ +{ + t_gobj te_g; /* header for graphical object */ +#endif + t_binbuf *binbuf; /* holder for the text */ + t_outlet *outlet; /* linked list of outlets */ + t_inlet *inlet; /* linked list of inlets */ + short x,y; /* x&y location (within the toplevel) */ + int refcount; /* there used to be a bitfield here, which may be a problem with ms-bitfields (?) */ +#ifdef PD_PLUSPLUS_FACE + t_inlet * in(int n); + t_outlet *out(int n); +#endif +} t_text, t_object; + +#define te_binbuf binbuf +#define te_outlet outlet +#define te_inlet inlet +#define te_xpix x +#define te_ypix y +#define te_width width +#define te_type type + +#define ob_outlet te_outlet +#define ob_inlet te_inlet +#define ob_binbuf te_binbuf +#ifdef PD_PLUSPLUS_FACE +#define te_pd g_pd +#define ob_pd g_pd +#else +#define te_pd te_g.g_pd +#define ob_pd te_g.g_pd +#endif +#define ob_g te_g + +/* don't take those three types for cash (on average). they're lying because the type system can't express what there is to be expressed. */ +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, s_float, s_symbol; +EXTERN t_symbol s_bang, s_list, s_anything, s_signal; +EXTERN t_symbol s__N, s__X, s_x, s_y, s_; + +/* --------- 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(const char *s); +EXTERN t_symbol *gensym2(const char *s, size_t n); +EXTERN t_symbol *symprintf(const char *s, ...); +EXTERN t_gotfn getfn(t_pd *x, t_symbol *s); +EXTERN t_gotfn zgetfn(t_pd *x, t_symbol *s); +EXTERN void nullfn(void); +EXTERN void pd_vmess(t_pd *x, t_symbol *s, char *fmt, ...); +#define mess0(x,s) (getfn((x),(s))((x))) +#define mess1(x,s,a) (getfn((x),(s))((x),(a))) +#define mess2(x,s,a,b) (getfn((x),(s))((x),(a),(b))) +#define mess3(x,s,a,b,c) (getfn((x),(s))((x),(a),(b),(c))) +#define mess4(x,s,a,b,c,d) (getfn((x),(s))((x),(a),(b),(c),(d))) +#define mess5(x,s,a,b,c,d,e) (getfn((x),(s))((x),(a),(b),(c),(d),(e))) +EXTERN void obj_list(t_object *x, t_symbol *s, int argc, t_atom *argv); +EXTERN t_pd *pd_newest(void); /* multiclient race conditions? */ + +/* --------------- memory management -------------------- */ +EXTERN void *getbytes(size_t nbytes); /* deprecated, use malloc() */ +EXTERN void *copybytes(void *src, size_t nbytes); +EXTERN void freebytes(void *x, size_t nbytes); /* deprecated, use free() */ +EXTERN void *resizebytes(void *x, size_t oldsize, size_t newsize); /* deprecated, use realloc() */ + +/* T.Grill - functions for aligned memory (according to CPU SIMD architecture) */ +EXTERN void *getalignedbytes(size_t nbytes); +EXTERN void *copyalignedbytes(void *src, size_t nbytes); +EXTERN void freealignedbytes(void *x,size_t nbytes); +EXTERN void *resizealignedbytes(void *x,size_t oldsize, size_t newsize); +/* -------------------- atoms ----------------------------- */ + +#define atom_decref(atom) (void)42 +#define atom_incref(atom) (void)42 +#define SETATOM(atom,type,field,value) (atom_decref(atom), (atom)->a_type=type, (atom)->a_w.field=value, atom_incref(atom)) +#define SETSEMI(atom) SETATOM(atom,A_SEMI, w_index,0) +#define SETCOMMA(atom) SETATOM(atom,A_COMMA, w_index,0) +#define SETPOINTER(atom, gp) SETATOM(atom,A_POINTER,w_gpointer,(gp)) /* looks unsafe... */ +#define SETFLOAT(atom, f) SETATOM(atom,A_FLOAT, w_float,(f)) +#define SETSYMBOL(atom, s) SETATOM(atom,A_SYMBOL, w_symbol,(s)) +#define SETSTRING(atom, s) SETATOM(atom,A_SYMBOL, w_symbol,gensym(s)) +#define SETDOLLAR(atom, n) SETATOM(atom,A_DOLLAR, w_index,(n)) +#define SETDOLLSYM(atom, s) SETATOM(atom,A_DOLLSYM,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); /* this call causes a t_symbol to become ternal */ +EXTERN const char *atom_getstring(t_atom *a); /* the return value should be used only immediately */ +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 const char *atom_getstringarg(int which, int argc, t_atom *argv); /* see above */ + +EXTERN t_symbol *atom_gensym( t_atom *a); + +/* this function should produce a literal, whereas getstring gives the exact string */ +EXTERN void atom_string(t_atom *a, char *buf, unsigned int bufsize); +#ifdef __cplusplus +EXTERN void atom_ostream(t_atom *a, std::ostream &buf); +inline std::ostream &operator <<(std::ostream &buf, t_atom *a) {atom_ostream(a,buf); return buf;} +#endif + +/* goes with desiredata's CLASS_NEWATOM */ +EXTERN void atom_init(t_atom *a, size_t n); +EXTERN void atom_copy(t_atom *a, t_atom *b, size_t n); +EXTERN void atom_delete(t_atom *a, size_t n); + +/* ------------------ binbufs --------------- */ + +EXTERN t_binbuf *binbuf_new(void); +EXTERN void binbuf_free(t_binbuf *x); +EXTERN t_binbuf *binbuf_duplicate(t_binbuf *y); + +EXTERN void binbuf_text(t_binbuf *x, char *text, size_t size); +EXTERN void binbuf_gettext(t_binbuf *x, char **bufp, int *lengthp); +EXTERN char *binbuf_gettext2(t_binbuf *x); +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 flags); +EXTERN int binbuf_read_via_canvas(t_binbuf *b, char *filename, t_canvas *canvas, int flags); +EXTERN int binbuf_read_via_path( t_binbuf *b, char *filename, char *dirname, int flags); +EXTERN int binbuf_write( t_binbuf *x, char *filename, char *dir, int flags); +EXTERN void binbuf_evalfile(t_symbol *name, t_symbol *dir); +EXTERN t_symbol *binbuf_realizedollsym(t_symbol *s, int ac, t_atom *av, int tonew); + +/* ------------------ clocks --------------- */ + +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_string( t_pd *x, const char *s); /* makes a refcounted symbol (copying s) */ +EXTERN void pd_list( t_pd *x, t_symbol *s, int argc, t_atom *argv); + +#ifdef PD_PLUSPLUS_FACE +#define pd_class(x) ((x)->_class) +#else +#define pd_class(x) (*(x)) +#endif + +EXTERN void gobj_subscribe (t_gobj *self, t_gobj *observer); +EXTERN void gobj_unsubscribe (t_gobj *self, t_gobj *observer); +EXTERN void gobj_changed (t_gobj *self, const char *k); +EXTERN void gobj_changed2 (t_gobj *self, int argc, t_atom *argv); +EXTERN void gobj_changed3 (t_gobj *self, t_gobj *origin, int argc, t_atom *argv); + +/* ----------------- 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" -------------- */ +/* already defined +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 t_inlet *stringinlet_new(t_object *owner, t_symbol **sp); /* for future use */ +EXTERN t_inlet *signalinlet_new(t_object *owner, t_float f); +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_string( t_outlet *x, const char *s); /* makes a refcounted symbol (copying s) */ +EXTERN void outlet_atom( t_outlet *x, t_atom *a); +EXTERN void outlet_list( t_outlet *x, t_symbol *s, int argc, t_atom *argv); +EXTERN void outlet_anything(t_outlet *x, t_symbol *s, int argc, t_atom *argv); +EXTERN t_symbol *outlet_getsymbol(t_outlet *x); +EXTERN void outlet_free(t_outlet *x); +EXTERN t_object *pd_checkobject(t_pd *x); + +/* -------------------- canvases -------------- */ + +EXTERN void glob_setfilename(void *dummy, t_symbol *name, t_symbol *dir); +EXTERN void canvas_setargs(int argc, t_atom * argv ); +EXTERN void canvas_getargs(int *argcp, t_atom **argvp); +EXTERN t_symbol *canvas_getcurrentdir(void); +EXTERN t_glist *canvas_getcurrent(void); +/* if result==0 then it will allocate a result and return it */ +EXTERN char *canvas_makefilename(t_glist *c, char *file, char *result, int resultsize); +EXTERN t_symbol *canvas_getdir(t_glist *x); +EXTERN void canvas_dataproperties(t_glist *x, t_scalar *sc, t_binbuf *b); +EXTERN int canvas_open( t_canvas *x, const char *name, const char *ext, char * dirresult, char **nameresult, unsigned int size, int bin); +EXTERN int canvas_open2(t_canvas *x, const char *name, const char *ext, char **dirresult, char **nameresult, int bin); + +/* -------------------- 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_NEWATOMS 16 +#define CLASS_TYPEMASK 3 + +#ifdef __cplusplus +typedef int t_atomtypearg; +#else +typedef t_atomtype t_atomtypearg; +#endif + +EXTERN t_class *class_find (t_symbol *s); +EXTERN t_class *class_new( t_symbol *name,t_newmethod nu,t_method freem,size_t size,int flags,t_atomtypearg arg1, ...); +EXTERN t_class *class_new2(const char *name,t_newmethod nu,t_method freem,size_t size,int flags,const char *sig); +EXTERN void class_addcreator( t_newmethod nu, t_symbol *sel, t_atomtypearg arg1, ...); +EXTERN void class_addcreator2(const char *name, t_newmethod nu, const char *sig); +EXTERN void class_addmethod( t_class *c, t_method fn, t_symbol *sel, t_atomtypearg arg1, ...); +EXTERN void class_addmethod2( t_class *c, t_method fn, const char *sel, const char *sig); +#define class_new2(NAME,NU,FREE,SIZE,FLAGS,SIG) class_new2(NAME,(t_newmethod)NU,(t_method)FREE,SIZE,FLAGS,SIG) +#define class_addcreator2(NAME,NU,SIG) class_addcreator2(NAME,(t_newmethod)NU,SIG) +#define class_addmethod2(NAME,METH,SEL,SIG) class_addmethod2(NAME,(t_method)METH,SEL,SIG) + +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_addstring( t_class *c, t_method fn); /* for future use */ +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 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) + +/* in "class" observer: observable calls this to notify of a change */ +typedef void (*t_notice)( t_gobj *x, t_gobj *origin, int argc, t_atom *argv); +EXTERN void class_setnotice(t_class *c, t_notice notice); + +/* in "class" observable: observable sends initial data to observer */ +/* this is called by gobj_subscribe to find out all the indirect subscriptions of aggregators. */ +/* every class that redefines this is an aggregator; every aggregator should redefine this. */ +/* "calling super" is done by calling gobj_onsubscribe with same args */ +typedef void (*t_onsubscribe)(t_gobj *x, t_gobj *observer); +EXTERN void class_setonsubscribe(t_class *c, t_onsubscribe onsubscribe); +EXTERN void gobj_onsubscribe(t_gobj *x, t_gobj *observer); /* default handler, that you may inherit from */ + +/* prototype for functions to save Pd's to a binbuf */ +typedef void (*t_savefn)(t_gobj *x, t_binbuf *b); +EXTERN void class_setsavefn(t_class *c, t_savefn f); +EXTERN t_savefn class_getsavefn(t_class *c); + +#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_addstring(x, y) class_addstring( (x), (t_method)(y)) /* for future use */ +#define class_addlist(x, y) class_addlist( (x), (t_method)(y)) +#define class_addanything(x, y) class_addanything((x), (t_method)(y)) +#endif + +EXTERN void class_settip(t_class *x,t_symbol* s); +EXTERN void inlet_settip(t_inlet* i,t_symbol* s); +EXTERN void class_setfieldnames(t_class *x, const char *s); /* where s is split on spaces and tokenized */ +EXTERN int class_getfieldindex(t_class *x, const char *s); +typedef int (*t_loader)(t_canvas *canvas, char *classname); +EXTERN void sys_register_loader(t_loader loader); + +/* ------------ printing --------------------------------- */ +EXTERN void post(const char *fmt, ...); +EXTERN void startpost(const char *fmt, ...); +EXTERN void poststring(const char *s); +EXTERN void postfloat(float f); +EXTERN void postatom(int argc, t_atom *argv); +EXTERN void endpost(void); +EXTERN void error(const char *fmt, ...); +EXTERN void verror(const char *fmt, va_list args); +EXTERN void verbose(int level, const char *fmt, ...); +EXTERN void bug(const char *fmt, ...); +EXTERN void pd_error(void *object, const char *fmt, ...) /*__attribute__ ((deprecated))*/; +EXTERN void sys_logerror(const char *object, const char *s); +EXTERN void sys_unixerror(const char *object); +EXTERN void sys_ouch(void); + +/* ------------ 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 open_via_path2(const char *dir, const char *name, const char *ext, char **dirresult, char **nameresult, int bin); + +EXTERN int sched_geteventno(void); +EXTERN double sys_getrealtime(void); + + +/* ------------ threading ------------------- */ +/* T.Grill - see m_sched.c */ +EXTERN void sys_lock(void); +EXTERN void sys_unlock(void); +EXTERN int sys_trylock(void); +EXTERN int sys_timedlock(int microsec); +/* tb: to be called at idle time */ +EXTERN void sys_callback(t_int (*callback) (t_int* argv), t_int* argv, t_int argc); + +/* --------------- signals ----------------------------------- */ + +typedef float t_sample; +#define MAXLOGSIG 32 +#define MAXSIGSIZE (1 << MAXLOGSIG) + +/* this doesn't really have to do with C++, just with getting rid of prefixes */ +#ifdef PD_PLUSPLUS_FACE +struct t_signal { + int n; + t_sample *v; + float sr; + int refcount; + int isborrowed; + t_signal *borrowedfrom; + t_signal *nextfree; + t_signal *nextused; + int vecsize; +}; +#else +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 */ + int s_vecsize; /* allocated size of array in points */ +} t_signal; +#endif + +typedef t_int *(*t_perfroutine)(t_int *args); + +/* tb: exporting basic arithmetic dsp functions { + * for (n % 8) != 0 */ +EXTERN t_int *zero_perform(t_int *args); +EXTERN t_int *copy_perform(t_int *args); +EXTERN t_int *plus_perform( t_int *args); EXTERN t_int *scalarplus_perform( t_int *args); +EXTERN t_int *minus_perform(t_int *args); EXTERN t_int *scalarminus_perform(t_int *args); +EXTERN t_int *times_perform(t_int *args); EXTERN t_int *scalartimes_perform(t_int *args); +EXTERN t_int *over_perform( t_int *args); EXTERN t_int *scalarover_perform( t_int *args); +EXTERN t_int *max_perform( t_int *args); EXTERN t_int *scalarmax_perform( t_int *args); +EXTERN t_int *min_perform( t_int *args); EXTERN t_int *scalarmin_perform( t_int *args); +EXTERN t_int *sig_tilde_perform(t_int *args); +EXTERN t_int *clip_perform(t_int *args); + +/* for (n % 8) == 0 */ +EXTERN t_int *zero_perf8(t_int *args); +EXTERN t_int *copy_perf8(t_int *args); +EXTERN t_int *plus_perf8( t_int *args); EXTERN t_int *scalarplus_perf8( t_int *args); +EXTERN t_int *minus_perf8(t_int *args); EXTERN t_int *scalarminus_perf8(t_int *args); +EXTERN t_int *times_perf8(t_int *args); EXTERN t_int *scalartimes_perf8(t_int *args); +EXTERN t_int *over_perf8( t_int *args); EXTERN t_int *scalarover_perf8( t_int *args); +EXTERN t_int *max_perf8( t_int *args); EXTERN t_int *scalarmax_perf8( t_int *args); +EXTERN t_int *min_perf8( t_int *args); EXTERN t_int *scalarmin_perf8( t_int *args); +EXTERN t_int *sqr_perf8(t_int *args); +EXTERN t_int *sig_tilde_perf8(t_int *args); + +/* for (n % 8) == 0 && aligned signal vectors + * check with simd_checkX functions !!! */ +EXTERN t_int *zero_perf_simd(t_int *args); +EXTERN t_int *copy_perf_simd(t_int *args); +EXTERN t_int *plus_perf_simd( t_int *args); EXTERN t_int *scalarplus_perf_simd( t_int *args); +EXTERN t_int *minus_perf_simd(t_int *args); EXTERN t_int *scalarminus_perf_simd(t_int *args); +EXTERN t_int *times_perf_simd(t_int *args); EXTERN t_int *scalartimes_perf_simd(t_int *args); +EXTERN t_int *over_perf_simd( t_int *args); EXTERN t_int *scalarover_perf_simd( t_int *args); +EXTERN t_int *max_perf_simd( t_int *args); EXTERN t_int *scalarmax_perf_simd( t_int *args); +EXTERN t_int *min_perf_simd( t_int *args); EXTERN t_int *scalarmin_perf_simd( t_int *args); +EXTERN t_int *sqr_perf_simd(t_int *args); +EXTERN t_int *sig_tilde_perf_simd(t_int *args); +EXTERN t_int *clip_perf_simd(t_int *args); +/* } tb */ + +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<<LOGCOSTABSIZE) + +EXTERN int canvas_suspend_dsp(void); +EXTERN void canvas_resume_dsp(int oldstate); +EXTERN void canvas_update_dsp(void); +EXTERN int canvas_dspstate; + +/* IOhannes { (up/downsampling) */ +/* use zero-padding to generate samples inbetween */ +#define RESAMPLE_ZERO 0 +/* use sample-and-hold to generate samples inbetween */ +#define RESAMPLE_HOLD 1 +/* use linear interpolation to generate samples inbetween */ +#define RESAMPLE_LINEAR 2 +/* not a real up/downsampling: + * upsampling: copy the original vector to the first part of the upsampled vector; the rest is zero + * downsampling: take the first part of the original vector as the downsampled vector + * WHAT FOR: we often want to process only the first half of an FFT-signal (the rest is redundant) + */ +#define RESAMPLE_BLOCK 3 + +#define RESAMPLE_DEFAULT RESAMPLE_ZERO + +typedef struct _resample { + int method; /* up/downsampling method ID */ + t_int downsample; /* downsampling factor */ + t_int upsample; /* upsampling factor */ +#ifdef PD_PLUSPLUS_FACE + t_float *v; /* here we hold the resampled data */ + int n; +#else + t_float *vec; + int s_n; +#endif + t_float *coeffs; /* coefficients for filtering... */ + int coefsize; + t_float *buffer; /* buffer for filtering */ + int bufsize; +} t_resample; + +EXTERN void resample_init(t_resample *x); +EXTERN void resample_free(t_resample *x); +EXTERN void resample_dsp(t_resample *x, t_sample *in, int insize, t_sample *out, int outsize, int method); +EXTERN void resamplefrom_dsp(t_resample *x, t_sample *in, int insize, int outsize, int method); +EXTERN void resampleto_dsp(t_resample *x, t_sample *out, int insize, int outsize, int method); +/* } IOhannes */ + +/* tb: exporting basic simd coded dsp functions { */ + +/* vectorized, not simd functions*/ +EXTERN void zerovec_8(t_float *dst,int n); +EXTERN void setvec_8(t_float *dst,t_float v,int n); +EXTERN void copyvec_8(t_float *dst,const t_float *src,int n); +EXTERN void addvec_8(t_float *dst,const t_float *src,int n); +EXTERN void testcopyvec_8(t_float *dst,const t_float *src,int n); +EXTERN void testaddvec_8(t_float *dst,const t_float *src,int n); +/* EXTERN float sumvec_8(t_float* in, t_int n); */ + +/* vectorized, simd functions * + * dst and src are assumed to be aligned */ +EXTERN void zerovec_simd(t_float *dst,int n); +EXTERN void setvec_simd(t_float *dst,t_float v,int n); +EXTERN void copyvec_simd(t_float *dst,const t_float *src,int n); +EXTERN void addvec_simd(t_float *dst,const t_float *src,int n); +EXTERN void testcopyvec_simd(t_float *dst,const t_float *src,int n); +EXTERN void testaddvec_simd(t_float *dst,const t_float *src,int n); +/* EXTERN float sumvec_simd(t_float* in, t_int n); */ +EXTERN void copyvec_simd_unalignedsrc(t_float *dst,const t_float *src,int n); + +/* not vectorized, not simd functions */ +EXTERN void copyvec(t_float *dst,const t_float *src,int n); +EXTERN void addvec(t_float *dst,const t_float *src,int n); +EXTERN void zerovec(t_float *dst, int n); + +EXTERN int simd_runtime_check(void); +EXTERN int simd_check1(t_int n, t_float* ptr1); +EXTERN int simd_check2(t_int n, t_float* ptr1, t_float* ptr2); +EXTERN int simd_check3(t_int n, t_float* ptr1, t_float* ptr2, t_float* ptr3); + +/* } tb */ + + +/* ----------------------- utility functions for signals -------------- */ +EXTERN float mtof(float); +EXTERN float ftom(float); +EXTERN float rmstodb(float); +EXTERN float powtodb(float); +EXTERN float dbtorms(float); +EXTERN float dbtopow(float); + +EXTERN float q8_sqrt(float); +EXTERN float q8_rsqrt(float); +#ifndef N32 +EXTERN float qsqrt(float); /* old names kept for extern compatibility */ +EXTERN float qrsqrt(float); +#endif +/* --------------------- data --------------------------------- */ + +/* graphical arrays */ +typedef struct _garray t_garray; + +EXTERN t_class *garray_class; +EXTERN int garray_getfloatarray(t_garray *x, int *size, t_float **vec); +EXTERN float garray_get(t_garray *x, t_symbol *s, t_int indx); +EXTERN void garray_redraw(t_garray *x); +EXTERN int garray_npoints(t_garray *x); +EXTERN char *garray_vec(t_garray *x); +EXTERN void garray_resize(t_garray *x, t_floatarg f); +EXTERN void garray_usedindsp(t_garray *x); +EXTERN void garray_setsaveit(t_garray *x, int saveit); +EXTERN double garray_updatetime(t_garray *x); + +EXTERN t_class *scalar_class; + +EXTERN t_float *value_get(t_symbol *s); +EXTERN void value_release(t_symbol *s); +EXTERN int value_getfloat(t_symbol *s, t_float *f); +EXTERN int value_setfloat(t_symbol *s, t_float f); + +/* ------- GUI interface - functions to send strings to TK --------- */ +EXTERN void sys_vgui(char *fmt, ...); +EXTERN void sys_gui(char *s); + +extern t_class *glob_pdobject; /* object to send "pd" messages */ + +/*------------- Max 0.26 compatibility --------------------*/ + +#define t_getbytes getbytes +#define t_freebytes freebytes +#define t_resizebytes resizebytes +#define typedmess pd_typedmess +#define vmess pd_vmess + +/* A definition to help gui objects straddle 0.34-0.35 changes. If this is +defined, there is a "te_xpix" field in objects, not a "te_xpos" as before: */ + +#define PD_USE_TE_XPIX + +#ifdef __i386__ +/* a test for NANs and denormals. Should only be necessary on i386. */ +#define PD_BADFLOAT(f) ((((*(unsigned int*)&(f))&0x7f800000)==0) || \ + (((*(unsigned int*)&(f))&0x7f800000)==0x7f800000)) +/* more stringent test: anything not between 1e-19 and 1e19 in absolute val */ +#define PD_BIGORSMALL(f) ((((*(unsigned int*)&(f))&0x60000000)==0) || \ + (((*(unsigned int*)&(f))&0x60000000)==0x60000000)) +#else +#define PD_BADFLOAT(f) 0 +#define PD_BIGORSMALL(f) 0 +#endif + +/* tb: wrapper for PD_BIGORSMALL macro */ +EXTERN void testcopyvec(t_float *dst,const t_float *src,int n); +EXTERN void testaddvec(t_float *dst,const t_float *src,int n); + +/* tb's fifos */ +typedef struct _fifo t_fifo; +/* function prototypes */ +EXTERN t_fifo * fifo_init(void); +EXTERN void fifo_destroy(t_fifo*); +/* fifo_put() and fifo_get are the only threadsafe functions!!! */ +EXTERN void fifo_put(t_fifo*, void*); +EXTERN void* fifo_get(t_fifo*); + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +} +#endif + +#define __m_pd_h_ +#endif /* __m_pd_h_ */ + +/* removed functions: + sys_fontwidth, sys_fontheight, t_widgetbehavior, class_setproperties, + class_setwidget, class_setparentwidget, class_parentwidget, pd_getparentwidget, + sys_pretendguibytes, sys_queuegui, sys_unqueuegui, getzbytes, + gfxstub_new, gfxstub_deleteforkey + removed fields: te_width, te_type. + */ +/* externs that use g_next directly can't work with desiredata: gui/state, clone, dyn, dynext, toxy, cyclone */ diff --git a/desiredata/src/m_sched.c b/desiredata/src/m_sched.c new file mode 100644 index 00000000..b4e0fc1a --- /dev/null +++ b/desiredata/src/m_sched.c @@ -0,0 +1,654 @@ +/* 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. */ + +/* scheduling stuff */ + +#include "desire.h" +#include <stdlib.h> + +/* for timeval */ +#ifdef UNISTD +#include <sys/time.h> +#endif +#ifdef __CYGWIN__ +#include <sys/time.h> +#endif +#ifdef MSW +#include "winsock.h" +#endif + +#include "assert.h" + +/* LATER consider making this variable. It's now the LCM of all sample + rates we expect to see: 32000, 44100, 48000, 88200, 96000. */ +#define TIMEUNITPERSEC (32.*441000.) + +/* T.Grill - enable PD global thread locking - sys_lock, sys_unlock, sys_trylock functions */ +#define THREAD_LOCKING +#include "pthread.h" +#include "time.h" + +static int sys_quit; +double sys_time; +static double sys_time_per_msec = TIMEUNITPERSEC / 1000.; + +/* tb: { */ +int sys_keepsched = 1; /* if 0: change scheduler mode */ +int sys_callbackscheduler = 0; /* if 1: change scheduler to callback based dsp */ +static void run_idle_callbacks(int microsec); +t_fifo * callback_fifo = NULL; +/* tb: }*/ + +int sys_usecsincelastsleep (); +int sys_sleepgrain; + +typedef void (*t_clockmethod)(void *client); + +#ifdef UNISTD +#include <unistd.h> +#endif + +struct _clock { + double settime; + void *owner; + t_clockmethod fn; + struct _clock *next; +}; + +t_clock *clock_setlist; +t_clock *clock_new(void *owner, t_method fn) { + t_clock *x = (t_clock *)malloc(sizeof(*x)); + x->settime = -1; + x->owner = owner; + x->fn = (t_clockmethod)fn; + x->next = 0; + return x; +} + +void clock_unset(t_clock *x) { + if (x->settime >= 0) { + if (x == clock_setlist) clock_setlist = x->next; + else { + t_clock *x2 = clock_setlist; + while (x2->next != x) x2 = x2->next; + x2->next = x->next; + } + x->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->settime = setticks; + if (clock_setlist && clock_setlist->settime <= setticks) { + t_clock *cbefore, *cafter; + for (cbefore = clock_setlist, cafter = clock_setlist->next; cbefore; cbefore = cafter, cafter = cbefore->next) { + if (!cafter || cafter->settime > setticks) { + cbefore->next = x; + x->next = cafter; + return; + } + } + } else x->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 () {return sys_time;} + +/* OBSOLETE NAME */ +double clock_getsystime () {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); + free(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_didpoll, sched_didnothing; + +void sys_clearhist () { + unsigned 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_didpoll = sched_didnothing = 0; +} + +void sys_printhist () { + for (int i=0; i<NHIST; i++) { + int doit = 0; + for (unsigned int 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, pollgui %d, nothing %d", sched_diddsp, sched_didpoll, sched_didnothing); +} + +static int sys_histphase; + +int sys_addhist(int phase) { + int phasewas = sys_histphase; + double newtime = sys_getrealtime(); + int msec = int((newtime-sys_histtime)*1000.); + for (int 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 + +struct t_resync { + int ntick; + int error; +}; + +static int oss_resyncphase = 0; +static int oss_nresync = 0; +static t_resync oss_resync[NRESYNC]; + +static char *(oss_errornames[]) = { +"unknown", +"ADC blocked", +"DAC blocked", +"A/D/A sync", +"data late", +"xrun", +"sys_lock timeout" +}; + +void glob_audiostatus (void *dummy) { + int nresync, nresyncphase, i; + 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].error; + if (errtype < 0 || errtype > 4) errtype = 0; + post("%9.2f\t%s", (sched_diddsp - oss_resync[nresyncphase].ntick) + * ((double)sys_schedblocksize) / 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].ntick = sched_diddsp; + oss_resync[oss_resyncphase].error = type; + oss_nresync++; + if (++oss_resyncphase == NRESYNC) oss_resyncphase = 0; + if (type != ERR_NOTHING && !sched_diored && (sched_diddsp >= sched_dioredtime)) { + sys_vgui("pdtk_pd_dio 1\n"); + sched_diored = 1; + } + sched_dioredtime = sched_diddsp + (int)(sys_dacsr /(double)sys_schedblocksize); +} + +static int sched_lastinclip, sched_lastoutclip, sched_lastindb, sched_lastoutdb; + +static void sched_pollformeters () { + 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 __linux__ + if (sys_hipriority && (sched_diddsp - sched_nextpingtime > 0)) { + glob_watchdog(0); + /* ping every 2 seconds */ + sched_nextpingtime = sched_diddsp + 2*(int)(sys_dacsr /(double)sys_schedblocksize); + } +#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 = int(0.5 + rmstodb(inmax)); + outdb = int(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)sys_schedblocksize); +} + +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 0 +void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) { + if (argc) sys_clearhist(); + else sys_printhist(); +} +#endif + +extern void dsp_tick (); + +static int sched_usedacs = 0; +static double sched_referencerealtime, sched_referencelogicaltime; +double sys_time_per_dsp_tick; + +void sched_set_using_dacs(int flag) { + sched_usedacs = flag; + if (!flag) { + sched_referencerealtime = sys_getrealtime(); + sched_referencelogicaltime = clock_getlogicaltime(); + } + sys_time_per_dsp_tick = (TIMEUNITPERSEC) * ((double)sys_schedblocksize) / sys_dacsr; +} + +static void run_clock_callbacks(double next_sys_time) { + if (clock_setlist && clock_setlist->settime <= next_sys_time) { + do { + t_clock *c = clock_setlist; + sys_time = c->settime; + clock_unset(c); /* the compiler should easily inline this */ + outlet_setstacklim(); + c->fn(c->owner); + } while (clock_setlist && clock_setlist->settime <= next_sys_time); + } +} + +/* take the scheduler forward one DSP tick, also handling clock timeouts */ +void sched_tick(double next_sys_time) { + run_clock_callbacks(next_sys_time); + sys_time = next_sys_time; + sched_diddsp++; /* rethink: how to get rid of this stupid histogram??? */ + dsp_tick(); + /* rethink: should we really do all this midi messaging in the realtime thread ? */ + sys_pollmidiqueue(); + sys_setmiditimediff(0, 1e-6 * sys_schedadvance); +} + + +/* +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 busy with previous transfers. +*/ + +void sys_pollmidiqueue (); +void sys_initmidiqueue (); + +void canvas_stop_dsp (); + +int m_scheduler () { + int idlecount = 0; + sys_time_per_dsp_tick = (TIMEUNITPERSEC) * ((double)sys_schedblocksize) / sys_dacsr; + /* T.Grill - lock mutex */ + sys_lock(); + sys_clearhist(); + /* tb: adapt sleepgrain with advance */ + sys_update_sleepgrain(); + sched_set_using_dacs(0); /* tb: dsp is switched off */ + sys_initmidiqueue(); + while (!sys_quit) { + if (!sys_callbackscheduler || !sched_usedacs) + while (sys_keepsched) { + int didsomething = 0; + int timeforward; + waitfortick: + if (sched_usedacs) { + 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"); + sys_close_audio(); + sched_set_using_dacs(0); + canvas_stop_dsp(); /* added by matju 2007.06.30 */ + goto waitfortick; + } + } + } + } else { + if (1000. * (sys_getrealtime() - sched_referencerealtime) > clock_gettimesince(sched_referencelogicaltime)) + timeforward = SENDDACS_YES; + else timeforward = SENDDACS_NO; + } + sys_setmiditimediff(0, 1e-6 * sys_schedadvance); + if (timeforward != SENDDACS_NO) sched_tick(sys_time + sys_time_per_dsp_tick); + if (timeforward == SENDDACS_YES) didsomething = 1; + sys_pollmidiqueue(); + if (sys_pollgui()) didsomething = 1; + /* test for idle; if so, do graphics updates. */ + if (!didsomething) { + sched_pollformeters(); + /* tb: call idle callbacks */ + if (timeforward != SENDDACS_SLEPT) run_idle_callbacks(sys_sleepgrain); + } + } + else /* tb: scheduler for callback-based dsp scheduling */ + while(sys_keepsched) { + /* tb: allow the audio callback to run */ + sys_unlock(); + sys_microsleep(sys_sleepgrain); + sys_lock(); + sys_pollmidiqueue(); + sys_setmiditimediff(0, 1e-6 * sys_schedadvance); + if (sys_pollgui()) continue; + /* do graphics updates and run idle callbacks */ + sched_pollformeters(); + } + sys_keepsched = 1; + } + sys_close_audio(); + sys_unlock(); + return 0; +} + +/* ------------ thread locking ------------------- */ +/* added by Thomas Grill */ + +#ifdef THREAD_LOCKING +static pthread_mutex_t sys_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t sys_cond = PTHREAD_COND_INITIALIZER; + +void sys_lock () { + pthread_mutex_lock(&sys_mutex); +} +void sys_unlock () { + pthread_mutex_unlock(&sys_mutex); + pthread_cond_signal(&sys_cond); +} +int sys_trylock () { + return pthread_mutex_trylock(&sys_mutex); +} + +/* tb { */ +#include <errno.h> + +#ifdef MSW +/* gettimeofday isn't available on windoze ... */ +int gettimeofday (struct timeval *tv, void* tz) { + __int64 now; /* time since 1 Jan 1601 in 100ns */ + GetSystemTimeAsFileTime ((FILETIME*) &now); + tv->tv_usec = (long) ((now / 10LL) % 1000000LL); + tv->tv_sec = (long) ((now - 116444736000000000LL) / 10000000LL); + return 0; +} +#endif + +#if 0 +/* osx doesn't define a pthread_mutex_timedlock ... maybe someday + it will ... */ +int sys_timedlock(int microsec) { + struct timespec timeout; + struct timeval now; + /* timedlock seems to have a resolution of 1ms */ + if (microsec < 1000) microsec = 1000; + gettimeofday(&now,0); + timeout.tv_sec = now.tv_sec; + timeout.tv_nsec = (now.tv_usec + microsec) * 1000; + while (timeout.tv_nsec > 1e9) { + timeout.tv_sec += 1; + timeout.tv_nsec -= 1e9; + } + int ret = pthread_mutex_timedlock(&sys_mutex, &timeout); + if (ret) post("timeout, %d", ret); + return ret; +} +#else + +int sys_timedlock(int microsec) { + struct timespec timeout; + struct timeval now; + if (sys_trylock() == 0) return 0; + if (microsec < 1000) microsec = 1000; + gettimeofday(&now,0); + timeout.tv_sec = now.tv_sec; + timeout.tv_nsec = (now.tv_usec + microsec) * 1000; + while (timeout.tv_nsec > 1000000000) { + timeout.tv_sec += 1; + timeout.tv_nsec -= 1000000000; + } + /* in case the lock has been released during the system call, try + again before waiting for the signal */ + if (sys_trylock() == 0) return 0; + return pthread_cond_timedwait(&sys_cond, &sys_mutex, &timeout); +} +#endif +/* tb } */ + +#else +void sys_lock () {} +void sys_unlock () {} +int sys_trylock () { return 0; } +int sys_timedlock (int microsec) { return 0; } +#endif + +/* ------------ soft quit ------------------- */ +/* added by Thomas Grill - + just set the quit flag for the scheduler loop + this is useful for applications using the PD shared library to signal the scheduler to terminate +*/ + +void sys_exit () { + sys_keepsched = 0; + sys_quit = 1; +} + +/* tb: place callbacks in scheduler + * { */ +/* linked list of callbacks; callback will be freed after returning 0 */ +struct t_sched_callback { + struct t_sched_callback *next; /* next callback in ringbuffer / in fifo */ + t_int (*function)(t_int *argv); + t_int *argv; + t_int argc; +}; + +void sys_callback(t_int (*callback)(t_int* argv), t_int* argv, t_int argc) { + t_sched_callback* noo = (t_sched_callback *)malloc(sizeof(t_sched_callback)); + noo->function = callback; + if (argv && argc) { + noo->argv = (t_int*) copybytes (argv, argc * sizeof (t_int)); + noo->argc = argc; + } else { + noo->argc = 0; + noo->argv = 0; + } + noo->next = 0; + if (!callback_fifo) callback_fifo = fifo_init(); + fifo_put(callback_fifo, noo); +} + +void sys_init_idle_callbacks () { + callback_fifo = fifo_init(); /* tb: initialize fifo for idle callbacks */ +} + +static t_sched_callback *ringbuffer_head = NULL; + +void run_all_idle_callbacks () { + t_sched_callback *new_callback; + /* append idle callback to ringbuffer */ + while ((new_callback = (t_sched_callback*) fifo_get(callback_fifo))) { + t_sched_callback *next; + /* set the next field to 0 ... it might be set in the fifo */ + new_callback->next = 0; + if (!ringbuffer_head) { + ringbuffer_head = new_callback; + } else { + next = ringbuffer_head; + while (next->next) next = next->next; + next->next = new_callback; + } + } + if (ringbuffer_head) { + t_sched_callback *idle_callback = ringbuffer_head; + t_sched_callback *last = 0; + t_sched_callback *next; + do { + int status; + status = (idle_callback->function)(idle_callback->argv); + switch (status) { + /* callbacks returning 0 will be deleted */ + case 0: + next = idle_callback->next; + if (idle_callback->argv) free(idle_callback->argv); + free((void*)idle_callback); + if (!last) ringbuffer_head = next; else last->next = next; + idle_callback = next; + /* callbacks returning 1 will be run again */ + case 1: + break; + /* callbacks returning 2 will be run during the next idle callback */ + case 2: + last = idle_callback; + idle_callback = idle_callback->next; + } + } while (idle_callback); + } +} + +static void run_idle_callbacks(int microsec) { + t_sched_callback *new_callback; + double stop = sys_getrealtime()*1.e6 + (double)microsec; + /* append idle callback to ringbuffer */ + while ((new_callback = (t_sched_callback*) fifo_get(callback_fifo))) { + /* set the next field to NULL ... it might be set in the fifo */ + new_callback->next = 0; + if (!ringbuffer_head) { + ringbuffer_head = new_callback; + } else { + t_sched_callback *next = ringbuffer_head; + while (next->next != 0) + next = next->next; + next->next = new_callback; + } + } + if (ringbuffer_head) { + double remain = stop - sys_getrealtime() * 1.e6; + t_sched_callback *idle_callback = ringbuffer_head; + t_sched_callback *last = 0; + t_sched_callback *next; + do { +// sys_lock(); + int status = idle_callback->function(idle_callback->argv); +// sys_unlock(); + switch (status) { + /* callbacks returning 0 will be deleted */ + case 0: + next = idle_callback->next; + if (idle_callback->argc) free(idle_callback->argv); + free((void*)idle_callback); + if (!last) ringbuffer_head = next; else last->next = next; + idle_callback = next; + /* callbacks returning 1 will be run again */ + case 1: + break; + /* callbacks returning 2 will be run during the next idle callback */ + case 2: + last = idle_callback; + idle_callback = idle_callback->next; + } + remain = stop-sys_getrealtime()*1.e6; + } while (idle_callback && remain>0); + /* sleep for the rest of the time */ + if(remain > 0) { + sys_unlock(); + sys_microsleep(int(remain)); + sys_lock(); + } + } else { + sys_unlock(); + sys_microsleep(microsec); + sys_lock(); + } +} +/* } tb */ + +void sys_setscheduler(int scheduler) { + sys_keepsched = 0; + sys_callbackscheduler = scheduler; + return; +} + +int sys_getscheduler () {return sys_callbackscheduler;} + +static t_int sys_xrun_notification_callback(t_int *dummy) { + t_symbol *pd = gensym("pd"); + t_symbol *xrun = gensym("xrun"); + typedmess(pd->s_thing, xrun, 0, 0); + return 0; +} + +void sys_xrun_notification () {sys_callback(sys_xrun_notification_callback, 0, 0);} + +static t_int sys_lock_timeout_notification_callback(t_int *dummy) { + t_symbol *pd = gensym("pd"); + t_symbol *timeout = gensym("sys_lock_timeout"); + typedmess(pd->s_thing, timeout, 0, 0); + return 0; +} + +void sys_lock_timeout_notification () {sys_callback(sys_lock_timeout_notification_callback, 0, 0);} diff --git a/desiredata/src/m_simd.c b/desiredata/src/m_simd.c new file mode 100644 index 00000000..1c0cb021 --- /dev/null +++ b/desiredata/src/m_simd.c @@ -0,0 +1,145 @@ +/* + Implementation of general vectorized functions + added by T.Grill +*/ + +#include "m_pd.h" +#include "m_simd.h" + +void zerovec_8(t_float *dst,int n) +{ + for(n >>= 3; n--; dst += 8) { + dst[0] = dst[1] = dst[2] = dst[3] = dst[4] = dst[5] = dst[6] = dst[7] = 0; + } +} + +void setvec_8(t_float *dst,t_float v,int n) +{ + for(n >>= 3; n--; dst += 8) { + dst[0] = dst[1] = dst[2] = dst[3] = dst[4] = dst[5] = dst[6] = dst[7] = v; + } +} + +void copyvec_8(t_float *dst,const t_float *src,int n) +{ + for(n >>= 3; n--; src += 8,dst += 8) { + dst[0] = src[0],dst[1] = src[1],dst[2] = src[2],dst[3] = src[3]; + dst[4] = src[4],dst[5] = src[5],dst[6] = src[6],dst[7] = src[7]; + } +} + +void addvec_8(t_float *dst,const t_float *src,int n) +{ + for(n >>= 3; n--; src += 8,dst += 8) { + dst[0] += src[0],dst[1] += src[1],dst[2] += src[2],dst[3] += src[3]; + dst[4] += src[4],dst[5] += src[5],dst[6] += src[6],dst[7] += src[7]; + } +} + +void copyvec(t_float *dst,const t_float *src,int n) +{ + while(n--) + *dst++ = *src++; +} + +void zerovec(t_float *dst, int n) +{ + while(n--) + *dst++ = 0; +} + + +void addvec(t_float *dst,const t_float *src,int n) +{ + while(n--) + *dst++ += *src++; +} + + +void testcopyvec_8(t_float *dst,const t_float *src,int n) +{ + while(n--) { + *(dst++) = (PD_BIGORSMALL(*src) ? 0 : *src); src++; + } +} + +void testcopyvec(t_float *dst,const t_float *src,int n) +{ + testcopyvec_8(dst, src, n); +} + +void testaddvec_8(t_float *dst,const t_float *src,int n) +{ + while(n--) { + *(dst++) += (PD_BIGORSMALL(*src) ? 0 : *src); src++; + } +} + +void testaddvec(t_float *dst,const t_float *src,int n) +{ + testaddvec_8(dst, src, n); +} + + +int simd_check1(t_int n, t_float* ptr1) +{ + return SIMD_CHECK1(n,ptr1); +} + +int simd_check2(t_int n, t_float* ptr1, t_float* ptr2) +{ + return SIMD_CHECK2(n,ptr1,ptr2); +} + +int simd_check3(t_int n, t_float* ptr1, t_float* ptr2, t_float* ptr3) +{ + return SIMD_CHECK3(n,ptr1,ptr2,ptr3); +} + + + +#ifdef DONTUSESIMD +int simd_runtime_check() +{ + return 0; +} + +/* tb: wrapper for simd functions */ +void zerovec_simd(t_float *dst,int n) +{ + zerovec_8(dst,n); +} + +void setvec_simd(t_float *dst,t_float v,int n) +{ + setvec_8(dst,v,n); +} + +void copyvec_simd(t_float *dst,const t_float *src,int n) +{ + copyvec_8(dst,src,n); +} + +void copyvec_simd_unalignedsrc(t_float *dst,const t_float *src,int n) +{ + copyvec_8(dst,src,n); +} + +void addvec_simd(t_float *dst,const t_float *src,int n) +{ + addvec_8(dst,src,n); +} + +void testcopyvec_simd(t_float *dst,const t_float *src,int n) +{ + testcopyvec_8(dst,src,n); +} + +void testaddvec_simd(t_float *dst,const t_float *src,int n) +{ + testaddvec_8(dst,src,n); +} + + +#endif /* DONTUSESIMD */ + diff --git a/desiredata/src/m_simd.h b/desiredata/src/m_simd.h new file mode 100644 index 00000000..feeb78e0 --- /dev/null +++ b/desiredata/src/m_simd.h @@ -0,0 +1,82 @@ +/* Definitions for SIMD functionality; added by T.Grill */ +#ifndef __M_SIMD_H +#define __M_SIMD_H + +/* general vector functions */ +void zerovec_8(t_float *dst,int n); +void setvec_8(t_float *dst,t_float v,int n); +void copyvec_8(t_float *dst,const t_float *src,int n); +void addvec_8(t_float *dst,const t_float *src,int n); +void testcopyvec_8(t_float *dst,const t_float *src,int n); +void testaddvec_8(t_float *dst,const t_float *src,int n); + +#define SIMD_BYTEALIGN (128/8) + +/* how many floats do we calculate in the loop of a SIMD codelet? */ +#define SIMD_BLOCK 16 /* must be a power of 2 */ + +//#if defined(__GNUC__) && (defined(_X86_) || defined(__i386__) || defined(__i586__) || defined(__i686__)) +#ifdef SIMD_SSE /* Intel SSE with GNU C */ /* ought to add this in configure.in */ +t_int *sigwrap_perf_simd(t_int *w); +void line_tilde_slope_simd(t_float* out, t_int n, t_float* value, t_float* slopes, t_float* slopestep); +float env_tilde_accum_simd(t_float* in, t_float* hp, t_int n); +t_int* sigsqrt_perf_simd(t_int *w); +t_int* sigrsqrt_perf_simd(t_int *w); +float peakvec_simd(t_float* vec, t_int n, t_float cur_max); +#endif + +//#if defined(__GNUC__) && defined(__POWERPC__) && defined(__ALTIVEC__) +#ifdef SIMD_ALTIVEC /* Altivec with GNU C ( -faltivec must be given as a compiler option! ) */ /* ought to add this in configure.in */ + #include "m_simd_ve_gcc.h" +#endif + +#ifdef DONTUSESIMD + /* This is used when there's no implementation of SIMD code for the current platform and/or compiler */ + /* These are the functions that can be coded for SIMD */ + #define zero_perf_simd zero_perf8 + #define copy_perf_simd copy_perf8 + #define sig_tilde_perf_simd sig_tilde_perf8 + #define sigwrap_perf_simd sigwrap_perform + #define line_tilde_slope_simd line_tilde_slope + #define env_tilde_accum_simd env_tilde_accum_8 /* it's a bad place to set that here since there's no public prototype */ + #define plus_perf_simd plus_perf8 + #define scalarplus_perf_simd scalarplus_perf8 + #define minus_perf_simd minus_perf8 + #define scalarminus_perf_simd scalarminus_perf8 + #define times_perf_simd times_perf8 + #define scalartimes_perf_simd scalartimes_perf8 + #define sqr_perf_simd sqr_perf8 + #define over_perf_simd over_perf8 + #define scalarover_perf_simd scalarover_perf8 + #define min_perf_simd min_perf8 + #define scalarmin_perf_simd scalarmin_perf8 + #define max_perf_simd max_perf8 + #define scalarmax_perf_simd scalarmax_perf8 + #define clip_perf_simd clip_perform /* SIMD not implemented */ + #define sigwrap_perf_simd sigwrap_perform /* SIMD not implemented */ + #define sigsqrt_perf_simd sigsqrt_perform /* SIMD not implemented */ + #define sigrsqrt_perf_simd sigrsqrt_perform /* SIMD not implemented */ + #define peakvec_simd peakvec + /* #define sum_vecsimd sumvec_8 */ +#endif + +/* check if n meets the requirements for SIMD codelets */ +#define SIMD_CHKCNT(n) ( ((n)&(SIMD_BLOCK-1)) == 0 ) +/* check if a pointer is correctly aligned for SIMD codelets */ +#define SIMD_CHKALIGN(ptr) ( ((size_t)(ptr) & (SIMD_BYTEALIGN-1)) == 0 ) +/* check n and 1 pointer at once */ +#define SIMD_CHECK1(n,ptr1) (SIMD_CHKCNT(n) && SIMD_CHKALIGN(ptr1) && simd_runtime_check()) +/* check n and 2 pointers at once */ +#define SIMD_CHECK2(n,ptr1,ptr2) (SIMD_CHKCNT(n) && SIMD_CHKALIGN(ptr1) && SIMD_CHKALIGN(ptr2) && simd_runtime_check()) +/* check n and 3 pointers at once */ +#define SIMD_CHECK3(n,ptr1,ptr2,ptr3) (SIMD_CHKCNT(n) && SIMD_CHKALIGN(ptr1) && SIMD_CHKALIGN(ptr2) && SIMD_CHKALIGN(ptr3) && simd_runtime_check()) + +/* T.Grill - bit alignment for signal vectors (must be a multiple of 8!) */ +/* if undefined no alignment occurs */ +#ifdef SIMD_BYTEALIGN + #define VECTORALIGNMENT (SIMD_BYTEALIGN*8) +#else + #define VECTORALIGNMENT 128 +#endif + +#endif /* __M_SIMD_H */ diff --git a/desiredata/src/m_simd_sse_gcc.c b/desiredata/src/m_simd_sse_gcc.c new file mode 100644 index 00000000..17182a59 --- /dev/null +++ b/desiredata/src/m_simd_sse_gcc.c @@ -0,0 +1,1131 @@ +/* + Implementation of SIMD functionality for Intel SSE with GCC compiler + added by T.Grill +*/ + +#include "m_pd.h" +#include "m_simd.h" + +#if defined(__GNUC__) && (defined(_X86_) || defined(__i386__) || defined(__i586__) || defined(__i686__)) && !(defined DONTUSESIMD) + + +/* TB: adapted from thomas' vc routines */ + +/* dst is assumed to be aligned */ +void zerovec_simd(t_float *dst,int n) +{ + asm( + ".set T_FLOAT,4 \n" /* sizeof(t_float) */ + "xorps %%xmm0, %%xmm0 \n" /* zero value */ + "shr $4, %0 \n" + + /* should we do more loop unrolling? */ + /* *dst = 0 */ + "1: \n" + "movaps %%xmm0, (%1) \n" + "movaps %%xmm0, 4*T_FLOAT(%1) \n" + "movaps %%xmm0, 8*T_FLOAT(%1) \n" + "movaps %%xmm0, 12*T_FLOAT(%1) \n" + + "addl $16*T_FLOAT,%1 \n" + "loop 1b \n" + : + :"c"(n),"r"(dst) + :"%xmm0"); +} + +/* dst is assumed to be aligned */ +void setvec_simd(t_float *dst,t_float v,int n) +{ + asm( + ".set T_FLOAT,4 \n" /* sizeof(t_float) */ + "movss (%2),%%xmm0 \n" + "shufps $0,%%xmm0,%%xmm0 \n" /* load value */ + "shr $4,%0 \n" + + /* should we do more loop unrolling? */ + /* *dst = v */ + "1: \n" + "movaps %%xmm0, (%1) \n" + "movaps %%xmm0, 4*T_FLOAT(%1) \n" + "movaps %%xmm0, 8*T_FLOAT(%1) \n" + "movaps %%xmm0, 12*T_FLOAT(%1) \n" + + "addl $16*T_FLOAT,%1 \n" + "loop 1b \n" + : + :"c"(n),"r"(dst),"r"(&v) + :"%xmm0"); +} + + +/* dst and src are assumed to be aligned */ +void copyvec_simd(t_float *dst,const t_float *src,int n) +{ + asm( + ".set T_FLOAT,4 \n" /* sizeof(t_float) */ + "shr $4, %0 \n" + + /* loop: *dst = *src */ + "1: \n" + "movaps (%1), %%xmm0 \n" + "movaps 4*T_FLOAT(%1), %%xmm1 \n" + "movaps 8*T_FLOAT(%1), %%xmm2 \n" + "movaps 12*T_FLOAT(%1), %%xmm3 \n" + "movaps %%xmm0, (%2) \n" + "movaps %%xmm1, 4*T_FLOAT(%2) \n" + "movaps %%xmm2, 8*T_FLOAT(%2) \n" + "movaps %%xmm3, 12*T_FLOAT(%2) \n" + + + "addl $16*T_FLOAT,%1 \n" + "addl $16*T_FLOAT,%2 \n" + "loop 1b \n" + : + :"c"(n),"r"(src),"r"(dst) + :"%xmm0","%xmm1","%xmm2","%xmm3"); +} + +/* dst is assumed to be aligned */ +void copyvec_simd_unalignedsrc(t_float *dst,const t_float *src,int n) +{ + asm( + ".set T_FLOAT,4 \n" /* sizeof(t_float) */ + "shr $4, %0 \n" + + /* loop: *dst = *src */ + "1: \n" + "movups (%1), %%xmm0 \n" + "movups 4*T_FLOAT(%1), %%xmm1 \n" + "movups 8*T_FLOAT(%1), %%xmm2 \n" + "movups 12*T_FLOAT(%1), %%xmm3 \n" + "movaps %%xmm0, (%2) \n" + "movaps %%xmm1, 4*T_FLOAT(%2) \n" + "movaps %%xmm2, 8*T_FLOAT(%2) \n" + "movaps %%xmm3, 12*T_FLOAT(%2) \n" + + + "addl $16*T_FLOAT,%1 \n" + "addl $16*T_FLOAT,%2 \n" + "loop 1b \n" + : + :"c"(n),"r"(src),"r"(dst) + :"%xmm0","%xmm1","%xmm2","%xmm3"); +} + + +/* dst and src are assumed to be aligned */ +void addvec_simd(t_float *dst,const t_float *src,int n) +{ + asm( + ".set T_FLOAT,4 \n" /* sizeof(t_float) */ + "shr $4, %0 \n" + + /* loop: *dst += *src */ + "1: \n" + "movaps (%2,%3),%%xmm0 \n" + "movaps (%1,%3),%%xmm1 \n" + "addps %%xmm0,%%xmm1 \n" + "movaps %%xmm1,(%2,%3) \n" + + "movaps 4*T_FLOAT(%2,%3),%%xmm0 \n" + "movaps 4*T_FLOAT(%1,%3),%%xmm1 \n" + "addps %%xmm0,%%xmm1 \n" + "movaps %%xmm1,4*T_FLOAT(%2,%3) \n" + + "movaps 8*T_FLOAT(%2,%3),%%xmm0 \n" + "movaps 8*T_FLOAT(%1,%3),%%xmm1 \n" + "addps %%xmm0,%%xmm1 \n" + "movaps %%xmm1,8*T_FLOAT(%2,%3) \n" + + "movaps 12*T_FLOAT(%2,%3),%%xmm0 \n" + "movaps 12*T_FLOAT(%1,%3),%%xmm1 \n" + "addps %%xmm0,%%xmm1 \n" + "movaps %%xmm1,12*T_FLOAT(%2,%3) \n" + + "addl $16*T_FLOAT,%3 \n" + "loop 1b \n" + : + : "c"(n),"r"(src),"r"(dst),"r"(0) + : "%xmm0","%xmm1","%xmm2","%xmm3","%xmm4","%xmm5","%xmm6","%xmm7"); +} + + +void testcopyvec_simd(t_float *dst,const t_float *src,int n) +{ + + asm( + ".section .rodata \n" + ".align 16 \n" + "2: \n" + ".long 1610612736 \n" /* bitmask */ + ".long 1610612736 \n" /* 0x60000000 */ + ".long 1610612736 \n" + ".long 1610612736 \n" + + ".set T_FLOAT,4 \n" + ".text \n" + + "shr $4, %0 \n" + "movaps (2b), %%xmm0 \n" /* xmm0 = bitmask */ + "xorps %%xmm1, %%xmm1 \n" /* xmm1 = 0x0 */ + + + "1: \n" + "movaps (%1), %%xmm2 \n" + "movaps %%xmm2, %%xmm3 \n" + "andps %%xmm0, %%xmm3 \n" + "movaps %%xmm3, %%xmm4 \n" + "cmpneqps %%xmm0, %%xmm3 \n" + "cmpneqps %%xmm1, %%xmm4 \n" + "andps %%xmm4, %%xmm3 \n" + "andps %%xmm3, %%xmm2 \n" + "movaps %%xmm2, (%2) \n" + + "movaps 4*T_FLOAT(%1), %%xmm2 \n" + "movaps %%xmm2, %%xmm3 \n" + "andps %%xmm0, %%xmm3 \n" + "movaps %%xmm3, %%xmm4 \n" + "cmpneqps %%xmm0, %%xmm3 \n" + "cmpneqps %%xmm1, %%xmm4 \n" + "andps %%xmm4, %%xmm3 \n" + "andps %%xmm3, %%xmm2 \n" + "movaps %%xmm2, 4*T_FLOAT(%2) \n" + + "movaps 8*T_FLOAT(%1), %%xmm2 \n" + "movaps %%xmm2, %%xmm3 \n" + "andps %%xmm0, %%xmm3 \n" + "movaps %%xmm3, %%xmm4 \n" + "cmpneqps %%xmm0, %%xmm3 \n" + "cmpneqps %%xmm1, %%xmm4 \n" + "andps %%xmm4, %%xmm3 \n" + "andps %%xmm3, %%xmm2 \n" + "movaps %%xmm2, 8*T_FLOAT(%2) \n" + + "movaps 12*T_FLOAT(%1), %%xmm2 \n" + "movaps %%xmm2, %%xmm3 \n" + "andps %%xmm0, %%xmm3 \n" + "movaps %%xmm3, %%xmm4 \n" + "cmpneqps %%xmm0, %%xmm3 \n" + "cmpneqps %%xmm1, %%xmm4 \n" + "andps %%xmm4, %%xmm3 \n" + "andps %%xmm3, %%xmm2 \n" + "movaps %%xmm2, 12*T_FLOAT(%2) \n" + + + "addl $16*T_FLOAT,%1 \n" + "addl $16*T_FLOAT,%2 \n" + "decl %0 \n" + "jne 1b \n" + : + :"c"(n),"r"(src),"r"(dst) + :"%xmm0","%xmm1","%xmm2","%xmm3", "%xmm4"); +} + + +void testaddvec_simd(t_float *dst,const t_float *src,int n) +{ + asm( + ".section .rodata \n" + ".align 16 \n" + "2: \n" + ".long 1610612736 \n" /* bitmask */ + ".long 1610612736 \n" /* 0x60000000 */ + ".long 1610612736 \n" + ".long 1610612736 \n" + + ".set T_FLOAT,4 \n" + ".text \n" + + "shr $4, %0 \n" + "movaps (2b), %%xmm0 \n" /* xmm0 = bitmask */ + "xorps %%xmm1, %%xmm1 \n" /* xmm1 = 0x0 */ + + + "1: \n" + "movaps (%1), %%xmm2 \n" + "movaps (%2), %%xmm5 \n" + "movaps %%xmm2, %%xmm3 \n" + "andps %%xmm0, %%xmm3 \n" + "movaps %%xmm3, %%xmm4 \n" + "cmpneqps %%xmm0, %%xmm3 \n" + "cmpneqps %%xmm1, %%xmm4 \n" + "andps %%xmm4, %%xmm3 \n" + "andps %%xmm3, %%xmm2 \n" + "addps %%xmm2, %%xmm5 \n" + "movaps %%xmm5, (%2) \n" + + "movaps 4*T_FLOAT(%1), %%xmm2 \n" + "movaps 4*T_FLOAT(%2), %%xmm5 \n" + "movaps %%xmm2, %%xmm3 \n" + "andps %%xmm0, %%xmm3 \n" + "movaps %%xmm3, %%xmm4 \n" + "cmpneqps %%xmm0, %%xmm3 \n" + "cmpneqps %%xmm1, %%xmm4 \n" + "andps %%xmm4, %%xmm3 \n" + "andps %%xmm3, %%xmm2 \n" + "addps %%xmm2, %%xmm5 \n" + "movaps %%xmm5, 4*T_FLOAT(%2) \n" + + "movaps 8*T_FLOAT(%1), %%xmm2 \n" + "movaps 8*T_FLOAT(%2), %%xmm5 \n" + "movaps %%xmm2, %%xmm3 \n" + "andps %%xmm0, %%xmm3 \n" + "movaps %%xmm3, %%xmm4 \n" + "cmpneqps %%xmm0, %%xmm3 \n" + "cmpneqps %%xmm1, %%xmm4 \n" + "andps %%xmm4, %%xmm3 \n" + "andps %%xmm3, %%xmm2 \n" + "addps %%xmm2, %%xmm5 \n" + "movaps %%xmm5, 8*T_FLOAT(%2) \n" + + "movaps 12*T_FLOAT(%1), %%xmm2 \n" + "movaps 12*T_FLOAT(%2), %%xmm5 \n" + "movaps %%xmm2, %%xmm3 \n" + "andps %%xmm0, %%xmm3 \n" + "movaps %%xmm3, %%xmm4 \n" + "cmpneqps %%xmm0, %%xmm3 \n" + "cmpneqps %%xmm1, %%xmm4 \n" + "andps %%xmm4, %%xmm3 \n" + "andps %%xmm3, %%xmm2 \n" + "addps %%xmm2, %%xmm5 \n" + "movaps %%xmm5, 12*T_FLOAT(%2) \n" + + + "addl $16*T_FLOAT,%1 \n" + "addl $16*T_FLOAT,%2 \n" + "decl %0 \n" + "jne 1b \n" + : + :"c"(n),"r"(src),"r"(dst) + :"%xmm0","%xmm1","%xmm2","%xmm3", "%xmm4", "%xmm5"); +} + + +t_int *zero_perf_simd(t_int *w) +{ + zerovec_simd((t_float *)w[1],w[2]); + return w+3; +} + +t_int *copy_perf_simd(t_int *w) +{ + copyvec_simd((t_float *)w[2],(const t_float *)w[1],w[3]); + return w+4; +} + +t_int *sig_tilde_perf_simd(t_int *w) +{ + setvec_simd((t_float *)w[2],*(const t_float *)w[1],w[3]); + return w+4; +} + + +t_int *plus_perf_simd (t_int * w) +{ + asm( + ".set T_FLOAT,4 \n" + + "shrl $4, %3 \n" /* divide by 16 */ + + /* loop: *out = *in1 + *in2 */ + "1: \n" + "movaps (%0,%4), %%xmm0 \n" + "movaps (%1,%4), %%xmm1 \n" + "addps %%xmm1, %%xmm0 \n" + "movaps %%xmm0, (%2,%4) \n" + + "movaps 4*T_FLOAT(%0,%4), %%xmm2 \n" + "movaps 4*T_FLOAT(%1,%4), %%xmm3 \n" + "addps %%xmm3, %%xmm2 \n" + "movaps %%xmm2, 4*T_FLOAT(%2,%4) \n" + + "movaps 8*T_FLOAT(%0,%4), %%xmm4 \n" + "movaps 8*T_FLOAT(%1,%4), %%xmm5 \n" + "addps %%xmm5, %%xmm4 \n" + "movaps %%xmm4, 8*T_FLOAT(%2,%4) \n" + + "movaps 12*T_FLOAT(%0,%4), %%xmm6 \n" + "movaps 12*T_FLOAT(%1,%4), %%xmm7 \n" + "addps %%xmm7, %%xmm6 \n" + "movaps %%xmm6, 12*T_FLOAT(%2,%4) \n" + + "addl $16*T_FLOAT, %4 \n" + "loop 1b \n" + : + /* in1, in2, out, n */ + :"r"(w[1]),"r"(w[2]),"r"(w[3]),"c"(w[4]),"r"(0) + :"%xmm0","%xmm1","%xmm2","%xmm3","%xmm4","%xmm5","%xmm6","%xmm7" + ); + return w+5; +} + + +t_int *scalarplus_perf_simd(t_int *w) +{ + asm( + ".set T_FLOAT,4 \n" + + "movss (%1), %%xmm0 \n" + "shufps $0, %%xmm0, %%xmm0 \n" + "shrl $4, %3 \n" /* divide by 16 */ + + /* loop: *out = *in + value */ + "1: \n" + "movaps (%0), %%xmm1 \n" + "addps %%xmm0, %%xmm1 \n" + "movaps %%xmm1, (%2) \n" + + "movaps 4*T_FLOAT(%0), %%xmm2 \n" + "addps %%xmm0, %%xmm2 \n" + "movaps %%xmm2, 4*T_FLOAT(%2) \n" + + "movaps 8*T_FLOAT(%0), %%xmm3 \n" + "addps %%xmm0, %%xmm3 \n" + "movaps %%xmm3, 8*T_FLOAT(%2) \n" + + "movaps 12*T_FLOAT(%0), %%xmm4 \n" + "addps %%xmm0, %%xmm4 \n" + "movaps %%xmm4, 12*T_FLOAT(%2) \n" + + "addl $16*T_FLOAT, %0 \n" + "addl $16*T_FLOAT, %2 \n" + "loop 1b \n" + : + /* in, value, out, n */ + :"r"(w[1]),"r"(w[2]),"r"(w[3]),"c"(w[4]) + :"%xmm0", "%xmm1","%xmm2","%xmm3","%xmm4" + ); + return w+5; +} + +t_int *minus_perf_simd(t_int *w) +{ + asm( + ".set T_FLOAT,4 \n" + + "shrl $4, %3 \n" /* divide by 16 */ + + /* loop: *out = *in1 - *in2 */ + "1: \n" + "movaps (%0,%4), %%xmm0 \n" + "movaps (%1,%4), %%xmm1 \n" + "subps %%xmm1, %%xmm0 \n" + "movaps %%xmm0, (%2,%4) \n" + + "movaps 4*T_FLOAT(%0,%4), %%xmm2 \n" + "movaps 4*T_FLOAT(%1,%4), %%xmm3 \n" + "subps %%xmm3, %%xmm2 \n" + "movaps %%xmm2, 4*T_FLOAT(%2,%4) \n" + + "movaps 8*T_FLOAT(%0,%4), %%xmm4 \n" + "movaps 8*T_FLOAT(%1,%4), %%xmm5 \n" + "subps %%xmm5, %%xmm4 \n" + "movaps %%xmm4, 8*T_FLOAT(%2,%4) \n" + + "movaps 12*T_FLOAT(%0,%4), %%xmm6 \n" + "movaps 12*T_FLOAT(%1,%4), %%xmm7 \n" + "subps %%xmm7, %%xmm6 \n" + "movaps %%xmm6, 12*T_FLOAT(%2,%4) \n" + + "addl $16*T_FLOAT, %4 \n" + "loop 1b \n" + : + /* in1, in2, out, n */ + :"r"(w[1]),"r"(w[2]),"r"(w[3]),"c"(w[4]),"r"(0) + :"%xmm0","%xmm1","%xmm2","%xmm3","%xmm4","%xmm5","%xmm6","%xmm7" + ); + return w+5; +} + +t_int* scalarminus_perf_simd(t_int *w) +{ + asm( + ".set T_FLOAT,4 \n" + + "movss (%1), %%xmm0 \n" + "shufps $0, %%xmm0, %%xmm0 \n" + "shrl $4, %3 \n" /* divide by 16 */ + + /* loop: *out = *in - value */ + "1: \n" + "movaps (%0), %%xmm1 \n" + "subps %%xmm0, %%xmm1 \n" + "movaps %%xmm1, (%2) \n" + + "movaps 4*T_FLOAT(%0), %%xmm2 \n" + "subps %%xmm0, %%xmm2 \n" + "movaps %%xmm2, 4*T_FLOAT(%2) \n" + + "movaps 8*T_FLOAT(%0), %%xmm3 \n" + "subps %%xmm0, %%xmm3 \n" + "movaps %%xmm3, 8*T_FLOAT(%2) \n" + + "movaps 12*T_FLOAT(%0), %%xmm4 \n" + "subps %%xmm0, %%xmm4 \n" + "movaps %%xmm4, 12*T_FLOAT(%2) \n" + + "addl $16*T_FLOAT, %0 \n" + "addl $16*T_FLOAT, %2 \n" + "loop 1b \n" + : + /* in, value, out, n */ + :"r"(w[1]),"r"(w[2]),"r"(w[3]),"c"(w[4]) + :"%xmm0","%xmm1","%xmm2","%xmm3","%xmm4" + ); + return w+5; +} + + +t_int *times_perf_simd(t_int *w) +{ + asm( + ".set T_FLOAT,4 \n" + + "shrl $4, %3 \n" /* divide by 16 */ + + /* loop: *out = *in1 * *in2 */ + "1: \n" + "movaps (%0,%4), %%xmm0 \n" + "movaps (%1,%4), %%xmm1 \n" + "mulps %%xmm1, %%xmm0 \n" + "movaps %%xmm0, (%2,%4) \n" + + "movaps 4*T_FLOAT(%0,%4), %%xmm2 \n" + "movaps 4*T_FLOAT(%1,%4), %%xmm3 \n" + "mulps %%xmm3, %%xmm2 \n" + "movaps %%xmm2, 4*T_FLOAT(%2,%4) \n" + + "movaps 8*T_FLOAT(%0,%4), %%xmm4 \n" + "movaps 8*T_FLOAT(%1,%4), %%xmm5 \n" + "mulps %%xmm5, %%xmm4 \n" + "movaps %%xmm4, 8*T_FLOAT(%2,%4) \n" + + "movaps 12*T_FLOAT(%0,%4), %%xmm6 \n" + "movaps 12*T_FLOAT(%1,%4), %%xmm7 \n" + "mulps %%xmm7, %%xmm6 \n" + "movaps %%xmm6, 12*T_FLOAT(%2,%4) \n" + + "addl $16*T_FLOAT, %4 \n" + "loop 1b \n" + : + /* in1, in2, out, n */ + :"r"(w[1]),"r"(w[2]),"r"(w[3]),"c"(w[4]),"r"(0) + :"%xmm0","%xmm1","%xmm2","%xmm3","%xmm4","%xmm5","%xmm6","%xmm7" + ); + return w+5; +} + +t_int* scalartimes_perf_simd(t_int *w) +{ + asm( + ".set T_FLOAT,4 \n" + + "movss (%1), %%xmm0 \n" + "shufps $0, %%xmm0, %%xmm0 \n" + "shrl $4, %3 \n" /* divide by 16 */ + + /* loop: *out = *in * value */ + "1: \n" + "movaps (%0), %%xmm1 \n" + "mulps %%xmm0, %%xmm1 \n" + "movaps %%xmm1, (%2) \n" + + "movaps 4*T_FLOAT(%0), %%xmm2 \n" + "mulps %%xmm0, %%xmm2 \n" + "movaps %%xmm2, 4*T_FLOAT(%2) \n" + + "movaps 8*T_FLOAT(%0), %%xmm3 \n" + "mulps %%xmm0, %%xmm3 \n" + "movaps %%xmm3, 8*T_FLOAT(%2) \n" + + "movaps 12*T_FLOAT(%0), %%xmm4 \n" + "mulps %%xmm0, %%xmm4 \n" + "movaps %%xmm4, 12*T_FLOAT(%2) \n" + + "addl $16*T_FLOAT, %0 \n" + "addl $16*T_FLOAT, %2 \n" + "loop 1b \n" + : + /* in, value, out, n */ + :"r"(w[1]),"r"(w[2]),"r"(w[3]),"c"(w[4]) + :"%xmm0","%xmm1","%xmm2","%xmm3","%xmm4" + ); + return w+5; +} + +t_int *sqr_perf_simd(t_int *w) +{ + asm( + ".set T_FLOAT,4 \n" + + "shrl $4, %2 \n" /* divide by 16 */ + + /* loop: *out = *in * *in */ + "1: \n" + "movaps (%0,%3), %%xmm0 \n" + "mulps %%xmm0, %%xmm0 \n" + "movaps %%xmm0, (%1) \n" + + "movaps 4*T_FLOAT(%0,%3), %%xmm1 \n" + "mulps %%xmm1, %%xmm1 \n" + "movaps %%xmm1, 4*T_FLOAT(%1) \n" + + "movaps 8*T_FLOAT(%0,%3), %%xmm2 \n" + "mulps %%xmm2, %%xmm2 \n" + "movaps %%xmm2, 8*T_FLOAT(%1) \n" + + "movaps 12*T_FLOAT(%0,%3), %%xmm3 \n" + "mulps %%xmm3, %%xmm3 \n" + "movaps %%xmm3, 12*T_FLOAT(%1) \n" + + "addl $16*T_FLOAT, %1 \n" + "addl $16*T_FLOAT, %3 \n" + "loop 1b \n" + : + /* in, out, n */ + :"r"(w[1]),"r"(w[2]),"c"(w[3]),"r"(0) + :"%xmm0","%xmm1","%xmm2","%xmm3" + ); + return w+4; +} + + +t_int* over_perf_simd(t_int * w) +{ + asm( + ".set T_FLOAT,4 \n" + + "shrl $4, %3 \n" /* divide by 16 */ + + /* loop: *out = *in1 / *in2 */ + "1: \n" + "movaps (%0,%4), %%xmm0 \n" + "movaps (%1,%4), %%xmm1 \n" + "divps %%xmm1, %%xmm0 \n" + "movaps %%xmm0, (%2,%4) \n" + + "movaps 4*T_FLOAT(%0,%4), %%xmm2 \n" + "movaps 4*T_FLOAT(%1,%4), %%xmm3 \n" + "divps %%xmm3, %%xmm2 \n" + "movaps %%xmm2, 4*T_FLOAT(%2,%4) \n" + + "movaps 8*T_FLOAT(%0,%4), %%xmm4 \n" + "movaps 8*T_FLOAT(%1,%4), %%xmm5 \n" + "divps %%xmm5, %%xmm4 \n" + "movaps %%xmm4, 8*T_FLOAT(%2,%4) \n" + + "movaps 12*T_FLOAT(%0,%4), %%xmm6 \n" + "movaps 12*T_FLOAT(%1,%4), %%xmm7 \n" + "divps %%xmm7, %%xmm6 \n" + "movaps %%xmm6, 12*T_FLOAT(%2,%4) \n" + + "addl $16*T_FLOAT, %4 \n" + "loop 1b \n" + : + /* in1, in2, out, n */ + :"r"(w[1]),"r"(w[2]),"r"(w[3]),"c"(w[4]),"r"(0) + :"%xmm0","%xmm1","%xmm2","%xmm3","%xmm4","%xmm5","%xmm6","%xmm7" + ); + return w+5; +} + +t_int* scalarover_perf_simd(t_int *w) +{ + asm( + ".set T_FLOAT,4 \n" + + "movss (%1), %%xmm0 \n" + "shufps $0, %%xmm0, %%xmm0 \n" + "shrl $4, %3 \n" /* divide by 16 */ + + /* loop: *out = *in / value */ + "1: \n" + "movaps (%0), %%xmm1 \n" + "divps %%xmm0, %%xmm1 \n" + "movaps %%xmm1, (%2) \n" + + "movaps 4*T_FLOAT(%0), %%xmm2 \n" + "divps %%xmm0, %%xmm2 \n" + "movaps %%xmm2, 4*T_FLOAT(%2) \n" + + "movaps 8*T_FLOAT(%0), %%xmm3 \n" + "divps %%xmm0, %%xmm3 \n" + "movaps %%xmm3, 8*T_FLOAT(%2) \n" + + "movaps 12*T_FLOAT(%0), %%xmm4 \n" + "divps %%xmm0, %%xmm4 \n" + "movaps %%xmm4, 12*T_FLOAT(%2) \n" + + "addl $16*T_FLOAT, %0 \n" + "addl $16*T_FLOAT, %2 \n" + "loop 1b \n" + : + /* in, value, out, n */ + :"r"(w[1]),"r"(w[2]),"r"(w[3]),"c"(w[4]) + :"%xmm0","%xmm1","%xmm2","%xmm3","%xmm4" + ); + return w+5; +} + + +t_int* min_perf_simd(t_int * w) +{ + asm( + ".set T_FLOAT,4 \n" + + "shrl $4, %3 \n" /* divide by 16 */ + + /* loop: *out = min (*in1, *in2) */ + "1: \n" + "movaps (%0,%4), %%xmm0 \n" + "movaps (%1,%4), %%xmm1 \n" + "minps %%xmm1, %%xmm0 \n" + "movaps %%xmm0, (%2,%4) \n" + + "movaps 4*T_FLOAT(%0,%4), %%xmm2 \n" + "movaps 4*T_FLOAT(%1,%4), %%xmm3 \n" + "minps %%xmm3, %%xmm2 \n" + "movaps %%xmm2, 4*T_FLOAT(%2,%4) \n" + + "movaps 8*T_FLOAT(%0,%4), %%xmm4 \n" + "movaps 8*T_FLOAT(%1,%4), %%xmm5 \n" + "minps %%xmm5, %%xmm4 \n" + "movaps %%xmm4, 8*T_FLOAT(%2,%4) \n" + + "movaps 12*T_FLOAT(%0,%4), %%xmm6 \n" + "movaps 12*T_FLOAT(%1,%4), %%xmm7 \n" + "minps %%xmm7, %%xmm6 \n" + "movaps %%xmm6, 12*T_FLOAT(%2,%4) \n" + + "addl $16*T_FLOAT, %4 \n" + "loop 1b \n" + : + /* in1, in2, out, n */ + :"r"(w[1]),"r"(w[2]),"r"(w[3]),"c"(w[4]),"r"(0) + :"%xmm0","%xmm1","%xmm2","%xmm3","%xmm4","%xmm5","%xmm6","%xmm7" + ); + return w+5; +} + + +t_int* scalarmin_perf_simd(t_int *w) +{ + asm( + ".set T_FLOAT,4 \n" + + "movss (%1), %%xmm0 \n" + "shufps $0, %%xmm0, %%xmm0 \n" + "shrl $4, %3 \n" /* divide by 16 */ + + /* loop: *out = min(*in, value) */ + "1: \n" + "movaps (%0), %%xmm1 \n" + "minps %%xmm0, %%xmm1 \n" + "movaps %%xmm1, (%2) \n" + + "movaps 4*T_FLOAT(%0), %%xmm2 \n" + "minps %%xmm0, %%xmm2 \n" + "movaps %%xmm2, 4*T_FLOAT(%2) \n" + + "movaps 8*T_FLOAT(%0), %%xmm3 \n" + "minps %%xmm0, %%xmm3 \n" + "movaps %%xmm3, 8*T_FLOAT(%2) \n" + + "movaps 12*T_FLOAT(%0), %%xmm4 \n" + "minps %%xmm0, %%xmm4 \n" + "movaps %%xmm4, 12*T_FLOAT(%2) \n" + + "addl $16*T_FLOAT, %0 \n" + "addl $16*T_FLOAT, %2 \n" + "loop 1b \n" + : + /* in, value, out, n */ + :"r"(w[1]),"r"(w[2]),"r"(w[3]),"c"(w[4]) + :"%xmm0","%xmm1","%xmm2","%xmm3","%xmm4" + ); + return w+5; +} + + +t_int* max_perf_simd(t_int * w) +{ + asm( + ".set T_FLOAT,4 \n" + + "shrl $4, %3 \n" /* divide by 16 */ + + /* loop: *out = max (*in1, *in2) */ + "1: \n" + "movaps (%0,%4), %%xmm0 \n" + "movaps (%1,%4), %%xmm1 \n" + "maxps %%xmm1, %%xmm0 \n" + "movaps %%xmm0, (%2,%4) \n" + + "movaps 4*T_FLOAT(%0,%4), %%xmm2 \n" + "movaps 4*T_FLOAT(%1,%4), %%xmm3 \n" + "maxps %%xmm3, %%xmm2 \n" + "movaps %%xmm2, 4*T_FLOAT(%2,%4) \n" + + "movaps 8*T_FLOAT(%0,%4), %%xmm4 \n" + "movaps 8*T_FLOAT(%1,%4), %%xmm5 \n" + "maxps %%xmm5, %%xmm4 \n" + "movaps %%xmm4, 8*T_FLOAT(%2,%4) \n" + + "movaps 12*T_FLOAT(%0,%4), %%xmm6 \n" + "movaps 12*T_FLOAT(%1,%4), %%xmm7 \n" + "maxps %%xmm7, %%xmm6 \n" + "movaps %%xmm6, 12*T_FLOAT(%2,%4) \n" + + "addl $16*T_FLOAT, %4 \n" + "loop 1b \n" + : + /* in1, in2, out, n */ + :"r"(w[1]),"r"(w[2]),"r"(w[3]),"c"(w[4]),"r"(0) + :"%xmm0","%xmm1","%xmm2","%xmm3","%xmm4","%xmm5","%xmm6","%xmm7" + ); + return w+5; +} + + +t_int* scalarmax_perf_simd(t_int *w) +{ + asm( + ".set T_FLOAT,4 \n" + + "movss (%1), %%xmm0 \n" + "shufps $0, %%xmm0, %%xmm0 \n" + "shrl $4, %3 \n" /* divide by 16 */ + + /* loop: *out = max(*in, value) */ + "1: \n" + "movaps (%0), %%xmm1 \n" + "maxps %%xmm0, %%xmm1 \n" + "movaps %%xmm1, (%2) \n" + + "movaps 4*T_FLOAT(%0), %%xmm2 \n" + "maxps %%xmm0, %%xmm2 \n" + "movaps %%xmm2, 4*T_FLOAT(%2) \n" + + "movaps 8*T_FLOAT(%0), %%xmm3 \n" + "maxps %%xmm0, %%xmm3 \n" + "movaps %%xmm3, 8*T_FLOAT(%2) \n" + + "movaps 12*T_FLOAT(%0), %%xmm4 \n" + "maxps %%xmm0, %%xmm4 \n" + "movaps %%xmm4, 12*T_FLOAT(%2) \n" + + "addl $16*T_FLOAT, %0 \n" + "addl $16*T_FLOAT, %2 \n" + "loop 1b \n" + : + /* in, value, out, n */ + :"r"(w[1]),"r"(w[2]),"r"(w[3]),"c"(w[4]) + :"%xmm0","%xmm1","%xmm2","%xmm3","%xmm4" + ); + return w+5; +} + +t_int* clip_perf_simd(t_int *w) +{ + asm( + ".set T_FLOAT,4 \n" + + "movss (%2), %%xmm0 \n" /* lo */ + "shufps $0, %%xmm0, %%xmm0 \n" + "movss (%3), %%xmm1 \n" /* hi */ + "shufps $0, %%xmm1, %%xmm1 \n" + + "shrl $4, %4 \n" /* divide by 16 */ + + /* loop: *out = min ( max (lo, *in), hi )*/ + "1: \n" + "movaps (%0), %%xmm2 \n" + "maxps %%xmm0, %%xmm2 \n" + "minps %%xmm1, %%xmm2 \n" + "movaps %%xmm2, (%1) \n" + + "movaps 4*T_FLOAT(%0), %%xmm3 \n" + "maxps %%xmm0, %%xmm3 \n" + "minps %%xmm1, %%xmm3 \n" + "movaps %%xmm3, 4*T_FLOAT(%1) \n" + + "movaps 8*T_FLOAT(%0), %%xmm4 \n" + "maxps %%xmm0, %%xmm4 \n" + "minps %%xmm1, %%xmm4 \n" + "movaps %%xmm4, 8*T_FLOAT(%1) \n" + + "movaps 12*T_FLOAT(%0), %%xmm5 \n" + "maxps %%xmm0, %%xmm5 \n" + "minps %%xmm1, %%xmm5 \n" + "movaps %%xmm5, 12*T_FLOAT(%1) \n" + + "addl $16*T_FLOAT, %0 \n" + "addl $16*T_FLOAT, %1 \n" + "loop 1b \n" + : + /* in, out, lo, hi, n */ + :"r"(w[1]),"r"(w[2]),"r"(w[3]),"r"(w[4]),"c"(w[5]) + :"%xmm0","%xmm1","%xmm2","%xmm3","%xmm4","%xmm5" + ); + return w+6; +} + + +t_int* sigsqrt_perf_simd(t_int *w) +{ + asm( + ".set T_FLOAT,4 \n" + + "shrl $4, %2 \n" /* divide by 16 */ + + /* loop: *out = sqrt(*in) */ + "1: \n" + "movaps (%0), %%xmm0 \n" + "sqrtps %%xmm0, %%xmm0 \n" + "movaps %%xmm0, (%1) \n" + + "movaps 4*T_FLOAT(%0), %%xmm1 \n" + "sqrtps %%xmm1, %%xmm1 \n" + "movaps %%xmm1, 4*T_FLOAT(%1) \n" + + "movaps 8*T_FLOAT(%0), %%xmm2 \n" + "sqrtps %%xmm2, %%xmm2 \n" + "movaps %%xmm2, 8*T_FLOAT(%1) \n" + + "movaps 12*T_FLOAT(%0), %%xmm3 \n" + "sqrtps %%xmm3, %%xmm3 \n" + "movaps %%xmm3, 12*T_FLOAT(%1) \n" + + "addl $16*T_FLOAT, %0 \n" + "addl $16*T_FLOAT, %1 \n" + "loop 1b \n" + : + /* in, out, n */ + :"r"(w[1]),"r"(w[2]),"c"(w[3]) + :"%xmm0","%xmm1","%xmm2","%xmm3" + ); + return w+4; +} + + +t_int* sigrsqrt_perf_simd(t_int *w) +{ + asm( + ".set T_FLOAT,4 \n" + + "shrl $4, %2 \n" /* divide by 16 */ + + /* loop: *out = sqrt(*in) */ + "1: \n" + "movaps (%0), %%xmm0 \n" + "rsqrtps %%xmm0, %%xmm0 \n" + "movaps %%xmm0, (%1) \n" + + "movaps 4*T_FLOAT(%0), %%xmm1 \n" + "rsqrtps %%xmm1, %%xmm1 \n" + "movaps %%xmm1, 4*T_FLOAT(%1) \n" + + "movaps 8*T_FLOAT(%0), %%xmm2 \n" + "rsqrtps %%xmm2, %%xmm2 \n" + "movaps %%xmm2, 8*T_FLOAT(%1) \n" + + "movaps 12*T_FLOAT(%0), %%xmm3 \n" + "rsqrtps %%xmm3, %%xmm3 \n" + "movaps %%xmm3, 12*T_FLOAT(%1) \n" + + "addl $16*T_FLOAT, %0 \n" + "addl $16*T_FLOAT, %1 \n" + "loop 1b \n" + : + /* in, out, n */ + :"r"(w[1]),"r"(w[2]),"c"(w[3]) + :"%xmm0","%xmm1","%xmm2","%xmm3" + ); + return w+4; +} + + +/* TB: runtime check */ +int simd_runtime_check() +{ + unsigned int eax, edx, ret; + __asm__("push %%ebx \n" /* ebx might be used as PIC register :-( */ + "cpuid \n" + "pop %%ebx \n" + : "=a"(eax),"=d"(edx) : "a" (1): "cx"); + ret = 0x2000000 & edx; + return (ret); +} + +float env_tilde_accum_simd(t_float* in, t_float* hp, t_int n) +{ + float ret; + asm( + ".set T_FLOAT,4 \n" + + "shrl $4, %2 \n" /* divide by 16 */ + "xorps %%xmm2, %%xmm2 \n" /* zero values */ + "xorps %%xmm3, %%xmm3 \n" + "xorps %%xmm4, %%xmm4 \n" + "xorps %%xmm5, %%xmm5 \n" + + + "1: \n" + "movaps -4*T_FLOAT(%1), %%xmm0 \n" + "movhlps %%xmm0, %%xmm1 \n" /* reversing xmm0 CHECK!!!*/ + "shufps $68, %%xmm1, %%xmm0 \n" + "movaps (%3), %%xmm1 \n" + "mulps %%xmm0, %%xmm0 \n" + "mulps %%xmm1, %%xmm0 \n" + "addps %%xmm0, %%xmm2 \n" + + "movaps -8*T_FLOAT(%1), %%xmm0 \n" + "movhlps %%xmm0, %%xmm1 \n" /* reversing xmm0 */ + "shufps $68, %%xmm1, %%xmm0 \n" + "movaps 4*T_FLOAT(%3), %%xmm1 \n" + "mulps %%xmm0, %%xmm0 \n" + "mulps %%xmm1, %%xmm0 \n" + "addps %%xmm0, %%xmm2 \n" + + "movaps -12*T_FLOAT(%1), %%xmm0 \n" + "movhlps %%xmm0, %%xmm1 \n" /* reversing xmm0 */ + "shufps $68, %%xmm1, %%xmm0 \n" + "movaps 8*T_FLOAT(%3), %%xmm1 \n" + "mulps %%xmm0, %%xmm0 \n" + "mulps %%xmm1, %%xmm0 \n" + "addps %%xmm0, %%xmm2 \n" + + "movaps -16*T_FLOAT(%1), %%xmm0 \n" + "movhlps %%xmm0, %%xmm1 \n" /* reversing xmm0 */ + "shufps $68, %%xmm1, %%xmm0 \n" + "movaps 12*T_FLOAT(%3), %%xmm1 \n" + "mulps %%xmm0, %%xmm0 \n" + "mulps %%xmm1, %%xmm0 \n" + "addps %%xmm0, %%xmm2 \n" + + "addl $-16*T_FLOAT,%1 \n" + "addl $16*T_FLOAT,%3 \n" + "loop 1b \n" + + "movhlps %%xmm2, %%xmm3 \n" /* unpack xmm0 */ + "movups %%xmm2, %%xmm4 \n" + "movups %%xmm3, %%xmm5 \n" + "shufps $81, %%xmm4, %%xmm4 \n" + "shufps $81, %%xmm5, %%xmm5 \n" + + "addss %%xmm2, %%xmm3 \n" + "addss %%xmm3, %%xmm4 \n" + "addss %%xmm4, %%xmm5 \n" + + "movss %%xmm5, (%0) \n" + + : + :"r"(&ret),"r"(in),"c"(n), "r"(hp) + :"%xmm0","%xmm1","%xmm2","%xmm3", "%xmm4", "%xmm5"); + return ret; +} + + +float peakvec_simd(t_float* vec, t_int n, t_float cur_max) +{ + asm( + ".section .rodata \n" + ".align 16 \n" + "2: \n" + ".long 2147483647 \n" /* bitmask for abs */ + ".long 2147483647 \n" /* 0x7fffffff */ + ".long 2147483647 \n" + ".long 2147483647 \n" + + ".set T_FLOAT,4 \n" + ".text \n" + + "shrl $4, %2 \n" /* divide by 16 */ + "movaps (2b), %%xmm0 \n" + + "movss (%0), %%xmm5 \n" /* cur_max */ + "shufps $0, %%xmm5, %%xmm5 \n" + + "1: \n" + "movaps (%1), %%xmm1 \n" + "andps %%xmm0, %%xmm1 \n" + "maxps %%xmm1, %%xmm5 \n" + + "movaps 4*T_FLOAT(%1), %%xmm1 \n" + "andps %%xmm0, %%xmm1 \n" + "maxps %%xmm1, %%xmm5 \n" + + "movaps 8*T_FLOAT(%1), %%xmm1 \n" + "andps %%xmm0, %%xmm1 \n" + "maxps %%xmm1, %%xmm5 \n" + + "movaps 12*T_FLOAT(%1), %%xmm1 \n" + "andps %%xmm0, %%xmm1 \n" + "maxps %%xmm1, %%xmm5 \n" + + "addl $16*T_FLOAT, %1 \n" + "loop 1b \n" + + "movhlps %%xmm5, %%xmm2 \n" + "movaps %%xmm5, %%xmm3 \n" + "movaps %%xmm2, %%xmm4 \n" + "shufps $81, %%xmm3, %%xmm3 \n" + "shufps $81, %%xmm4, %%xmm4 \n" + + "maxss %%xmm2, %%xmm3 \n" + "maxss %%xmm3, %%xmm4 \n" + "maxss %%xmm4, %%xmm5 \n" + + "movss %%xmm5, (%0) \n" + + : + :"r"(&cur_max), "r"(vec),"c"(n) + :"%xmm0","%xmm1","%xmm2","%xmm3", "%xmm4", "%xmm5"); + + return cur_max; +} + +void line_tilde_slope_simd(t_float* out, t_int n, t_float* value, + t_float* slopes, t_float* slopestep) +{ + asm( + ".set T_FLOAT,4 \n" + "movss (%2),%%xmm0 \n" /* value */ + "shufps $0, %%xmm0, %%xmm0 \n" + "movaps (%3), %%xmm1 \n" /* slopes */ + + "addps %%xmm1, %%xmm0 \n" /* compute first output */ + + "movss (%4), %%xmm2 \n" /* slopestep */ + "shufps $0, %%xmm2, %%xmm2 \n" + + "shrl $4, %1 \n" /* n>>4 */ + + "1: \n" + "movaps %%xmm0, (%0) \n" + "addps %%xmm2, %%xmm0 \n" + + "movaps %%xmm0, 4*T_FLOAT(%0) \n" + "addps %%xmm2, %%xmm0 \n" + + "movaps %%xmm0, 8*T_FLOAT(%0) \n" + "addps %%xmm2, %%xmm0 \n" + + "movaps %%xmm0, 12*T_FLOAT(%0) \n" + "addps %%xmm2, %%xmm0 \n" + + + "addl $16*T_FLOAT, %0 \n" + "loop 1b \n" + + + : + :"r"(out),"c"(n), "r"(value), "r"(slopes), + "r"(slopestep) + :"%xmm0", "%xmm1", "%xmm2"); + +} + +#endif + diff --git a/desiredata/src/m_simd_ve_gcc.c b/desiredata/src/m_simd_ve_gcc.c new file mode 100644 index 00000000..1f08d748 --- /dev/null +++ b/desiredata/src/m_simd_ve_gcc.c @@ -0,0 +1,748 @@ +/* + Implementation of SIMD functionality for Apple Velocity Engine (AltiVec) with GCC compiler + added by T.Grill +*/ + +#include "m_pd.h" +#include "m_simd.h" + +#if defined(__GNUC__) && defined(__POWERPC__) && defined(__ALTIVEC__) + +//#define USEVECLIB + +#ifdef USEVECLIB +#include <vecLib/vDSP.h> +#include <vecLib/vfp.h> +#endif + +/* functions for unaligned vector data - taken from http://developer.apple.com/hardware/ve/alignment.html */ + +/* T.Grill - this first version _should_ work! but it doesn't... */ +#if 0 +#define LoadUnaligned(v) (vec_perm( vec_ld( 0, (const vector float *)(v) ), vec_ld( 16, (const vector float *)(v) ), vec_lvsl( 0, (float *) (v) ) )) +#else +/* instead take the slower second one */ +static vector float LoadUnaligned(const float *v) +{ + union tmpstruct { float f[4]; vector float vec; } tmp; + tmp.f[0] = *(float *)v; + return vec_splat(vec_ld(0,&tmp.vec),0); +} +#endif + + +#define IsVectorAligned(where) ((unsigned long)(where)&(sizeof(vector float)-1) == 0) +/* +#define LoadValue(where) (IsVectorAligned((void *)(where))?vec_splat(vec_ld(0,(vector float *)(where)),0):LoadUnaligned((vector float *)(where))) +*/ +/* always assume unaligned */ +#define LoadValue(where) LoadUnaligned((const float *)(where)) + +void zerovec_simd(t_float *dst,int n) +{ + const vector float zero = (vector float)(0); + for(n >>= 4; n--; dst += 16) { + vec_st(zero, 0,dst); + vec_st(zero,16,dst); + vec_st(zero,32,dst); + vec_st(zero,48,dst); + } +} + +void setvec_simd(t_float *dst,t_float v,int n) +{ + const vector float arg = LoadValue(&v); + for(n >>= 4; n--; dst += 16) { + vec_st(arg, 0,dst); + vec_st(arg,16,dst); + vec_st(arg,32,dst); + vec_st(arg,48,dst); + } +} + +void copyvec_simd(t_float *dst,const t_float *src,int n) +{ + for(n >>= 4; n--; src += 16,dst += 16) { + vector float a1 = vec_ld( 0,src); + vector float a2 = vec_ld(16,src); + vector float a3 = vec_ld(32,src); + vector float a4 = vec_ld(48,src); + vec_st(a1, 0,dst); + vec_st(a2,16,dst); + vec_st(a3,32,dst); + vec_st(a4,48,dst); + } +} + +void addvec_simd(t_float *dst,const t_float *src,int n) +{ +#ifdef USEVECLIB + vadd(dst,1,src,1,dst,1,n); +#else + for(n >>= 4; n--; src += 16,dst += 16) { + vector float a1 = vec_ld( 0,dst),b1 = vec_ld( 0,src); + vector float a2 = vec_ld(16,dst),b2 = vec_ld(16,src); + vector float a3 = vec_ld(32,dst),b3 = vec_ld(32,src); + vector float a4 = vec_ld(48,dst),b4 = vec_ld(48,src); + + a1 = vec_add(a1,b1); + a2 = vec_add(a2,b2); + a3 = vec_add(a3,b3); + a4 = vec_add(a4,b4); + + vec_st(a1, 0,dst); + vec_st(a2,16,dst); + vec_st(a3,32,dst); + vec_st(a4,48,dst); + } +#endif +} + +/* no bad float testing for PPC! */ +void testcopyvec_simd(t_float *dst,const t_float *src,int n) +{ + copyvec_simd(dst,src,n); +} + +void testaddvec_simd(t_float *dst,const t_float *src,int n) +{ + addvec_simd(dst,src,n); +} + + +t_int *zero_perf_simd(t_int *w) +{ + zerovec_simd((t_float *)w[1],w[2]); + return w+3; +} + +t_int *copy_perf_simd(t_int *w) +{ + copyvec_simd((t_float *)w[2],(const t_float *)w[1],w[3]); + return w+4; +} + +t_int *sig_tilde_perf_simd(t_int *w) +{ + setvec_simd((t_float *)w[2],*(const t_float *)w[1],w[3]); + return w+4; +} + +t_int *plus_perf_simd(t_int *w) +{ +#ifdef USEVECLIB + vadd((const t_float *)w[1],1,(const t_float *)w[2],1,(t_float *)w[3],1,w[4]); +#else + const t_float *src1 = (const t_float *)w[1]; + const t_float *src2 = (const t_float *)w[2]; + t_float *dst = (t_float *)w[3]; + int n = w[4]>>4; + + for(; n--; src1 += 16,src2 += 16,dst += 16) { + vector float a1 = vec_ld( 0,src1),b1 = vec_ld( 0,src2); + vector float a2 = vec_ld(16,src1),b2 = vec_ld(16,src2); + vector float a3 = vec_ld(32,src1),b3 = vec_ld(32,src2); + vector float a4 = vec_ld(48,src1),b4 = vec_ld(48,src2); + + a1 = vec_add(a1,b1); + a2 = vec_add(a2,b2); + a3 = vec_add(a3,b3); + a4 = vec_add(a4,b4); + + vec_st(a1, 0,dst); + vec_st(a2,16,dst); + vec_st(a3,32,dst); + vec_st(a4,48,dst); + } +#endif + return w+5; +} + +t_int *scalarplus_perf_simd(t_int *w) +{ + const t_float *src = (const t_float *)w[1]; + const vector float arg = LoadValue(w[2]); + t_float *dst = (t_float *)w[3]; + int n = w[4]>>4; + + for(; n--; src += 16,dst += 16) { + vector float a1 = vec_ld( 0,src); + vector float a2 = vec_ld(16,src); + vector float a3 = vec_ld(32,src); + vector float a4 = vec_ld(48,src); + + a1 = vec_add(a1,arg); + a2 = vec_add(a2,arg); + a3 = vec_add(a3,arg); + a4 = vec_add(a4,arg); + + vec_st(a1, 0,dst); + vec_st(a2,16,dst); + vec_st(a3,32,dst); + vec_st(a4,48,dst); + } + return w+5; +} + +t_int *minus_perf_simd(t_int *w) +{ +#if 0 //def USEVECLIB + /* vsub is buggy for some OSX versions! */ + vsub((const t_float *)w[1],1,(const t_float *)w[2],1,(t_float *)w[3],1,w[4]); +#else + const t_float *src1 = (const t_float *)w[1]; + const t_float *src2 = (const t_float *)w[2]; + t_float *dst = (t_float *)w[3]; + int n = w[4]>>4; + + for(; n--; src1 += 16,src2 += 16,dst += 16) { + vector float a1 = vec_ld( 0,src1),b1 = vec_ld( 0,src2); + vector float a2 = vec_ld(16,src1),b2 = vec_ld(16,src2); + vector float a3 = vec_ld(32,src1),b3 = vec_ld(32,src2); + vector float a4 = vec_ld(48,src1),b4 = vec_ld(48,src2); + + a1 = vec_sub(a1,b1); + a2 = vec_sub(a2,b2); + a3 = vec_sub(a3,b3); + a4 = vec_sub(a4,b4); + + vec_st(a1, 0,dst); + vec_st(a2,16,dst); + vec_st(a3,32,dst); + vec_st(a4,48,dst); + } +#endif + return w+5; +} + +t_int *scalarminus_perf_simd(t_int *w) +{ + const t_float *src = (const t_float *)w[1]; + const vector float arg = LoadValue(w[2]); + t_float *dst = (t_float *)w[3]; + int n = w[4]>>4; + + for(; n--; src += 16,dst += 16) { + vector float a1 = vec_ld( 0,src); + vector float a2 = vec_ld(16,src); + vector float a3 = vec_ld(32,src); + vector float a4 = vec_ld(48,src); + + a1 = vec_sub(a1,arg); + a2 = vec_sub(a2,arg); + a3 = vec_sub(a3,arg); + a4 = vec_sub(a4,arg); + + vec_st(a1, 0,dst); + vec_st(a2,16,dst); + vec_st(a3,32,dst); + vec_st(a4,48,dst); + } + return w+5; +} + +t_int *times_perf_simd(t_int *w) +{ +#ifdef USEVECLIB + vmul((const t_float *)w[1],1,(const t_float *)w[2],1,(t_float *)w[3],1,w[4]); +#else + const t_float *src1 = (const t_float *)w[1]; + const t_float *src2 = (const t_float *)w[2]; + t_float *dst = (t_float *)w[3]; + const vector float zero = (vector float)(0); + int n = w[4]>>4; + + for(; n--; src1 += 16,src2 += 16,dst += 16) { + vector float a1 = vec_ld( 0,src1),b1 = vec_ld( 0,src2); + vector float a2 = vec_ld(16,src1),b2 = vec_ld(16,src2); + vector float a3 = vec_ld(32,src1),b3 = vec_ld(32,src2); + vector float a4 = vec_ld(48,src1),b4 = vec_ld(48,src2); + + a1 = vec_madd(a1,b1,zero); + a2 = vec_madd(a2,b2,zero); + a3 = vec_madd(a3,b3,zero); + a4 = vec_madd(a4,b4,zero); + + vec_st(a1, 0,dst); + vec_st(a2,16,dst); + vec_st(a3,32,dst); + vec_st(a4,48,dst); + } +#endif + return w+5; +} + +t_int *scalartimes_perf_simd(t_int *w) +{ +#ifdef USEVECLIB + vsmul((const t_float *)w[1],1,(t_float *)w[2],(t_float *)w[3],1,w[4]); +#else + const t_float *src = (const t_float *)w[1]; + const vector float arg = LoadValue(w[2]); + t_float *dst = (t_float *)w[3]; + const vector float zero = (vector float)(0); + int n = w[4]>>4; + + for(; n--; src += 16,dst += 16) { + vector float a1 = vec_ld( 0,src); + vector float a2 = vec_ld(16,src); + vector float a3 = vec_ld(32,src); + vector float a4 = vec_ld(48,src); + + a1 = vec_madd(a1,arg,zero); + a2 = vec_madd(a2,arg,zero); + a3 = vec_madd(a3,arg,zero); + a4 = vec_madd(a4,arg,zero); + + vec_st(a1, 0,dst); + vec_st(a2,16,dst); + vec_st(a3,32,dst); + vec_st(a4,48,dst); + } +#endif + return w+5; +} + +t_int *sqr_perf_simd(t_int *w) +{ +#ifdef USEVECLIB + vsq((const t_float *)w[1],1,(t_float *)w[2],1,w[3]); +#else + const t_float *src = (const t_float *)w[1]; + t_float *dst = (t_float *)w[2]; + const vector float zero = (vector float)(0); + int n = w[3]>>4; + + for(; n--; src += 16,dst += 16) { + vector float a1 = vec_ld( 0,src); + vector float a2 = vec_ld(16,src); + vector float a3 = vec_ld(32,src); + vector float a4 = vec_ld(48,src); + + a1 = vec_madd(a1,a1,zero); + a2 = vec_madd(a2,a2,zero); + a3 = vec_madd(a3,a3,zero); + a4 = vec_madd(a4,a4,zero); + + vec_st(a1, 0,dst); + vec_st(a2,16,dst); + vec_st(a3,32,dst); + vec_st(a4,48,dst); + } +#endif + return w+4; +} + +t_int *over_perf_simd(t_int *w) +{ + const t_float *src1 = (const t_float *)w[1]; + const t_float *src2 = (const t_float *)w[2]; + t_float *dst = (t_float *)w[3]; + const vector float zero = (vector float)(0); + const vector float one = (vector float)(1); + int n = w[4]>>4; + + for(; n--; src1 += 16,src2 += 16,dst += 16) { +#ifdef USEVECLIB + /* no zero checking here */ + vec_st(vdivf(vec_ld( 0,src1),vec_ld( 0,src2)), 0,dst); + vec_st(vdivf(vec_ld(16,src1),vec_ld(16,src2)),16,dst); + vec_st(vdivf(vec_ld(32,src1),vec_ld(32,src2)),32,dst); + vec_st(vdivf(vec_ld(48,src1),vec_ld(48,src2)),48,dst); +#else + vector float data1 = vec_ld( 0,src2); + vector float data2 = vec_ld(16,src2); + vector float data3 = vec_ld(32,src2); + vector float data4 = vec_ld(48,src2); + + vector unsigned char mask1 = vec_nor((vector unsigned char)vec_cmpeq(data1,zero),(vector unsigned char)zero); /* bit mask... all 0 for data = 0., all 1 else */ + vector unsigned char mask2 = vec_nor((vector unsigned char)vec_cmpeq(data2,zero),(vector unsigned char)zero); /* bit mask... all 0 for data = 0., all 1 else */ + vector unsigned char mask3 = vec_nor((vector unsigned char)vec_cmpeq(data3,zero),(vector unsigned char)zero); /* bit mask... all 0 for data = 0., all 1 else */ + vector unsigned char mask4 = vec_nor((vector unsigned char)vec_cmpeq(data4,zero),(vector unsigned char)zero); /* bit mask... all 0 for data = 0., all 1 else */ + + /* make estimated reciprocal and zero out NANs */ + vector float tmp1 = vec_re(data1); + vector float tmp2 = vec_re(data2); + vector float tmp3 = vec_re(data3); + vector float tmp4 = vec_re(data4); + + tmp1 = (vector float)vec_and((vector unsigned char)tmp1,mask1); + tmp2 = (vector float)vec_and((vector unsigned char)tmp2,mask2); + tmp3 = (vector float)vec_and((vector unsigned char)tmp3,mask3); + tmp4 = (vector float)vec_and((vector unsigned char)tmp4,mask4); + + data1 = vec_madd( vec_nmsub( tmp1, data1, one ), tmp1, tmp1 ); + data2 = vec_madd( vec_nmsub( tmp2, data2, one ), tmp2, tmp2 ); + data3 = vec_madd( vec_nmsub( tmp3, data3, one ), tmp3, tmp3 ); + data4 = vec_madd( vec_nmsub( tmp4, data4, one ), tmp4, tmp4 ); + + tmp1 = vec_ld( 0,src1); + tmp2 = vec_ld(16,src1); + tmp3 = vec_ld(32,src1); + tmp4 = vec_ld(48,src1); + + data1 = vec_madd(tmp1,data1,zero); + data2 = vec_madd(tmp2,data2,zero); + data3 = vec_madd(tmp3,data3,zero); + data4 = vec_madd(tmp4,data4,zero); + + vec_st(data1, 0,dst); + vec_st(data2,16,dst); + vec_st(data3,32,dst); + vec_st(data4,48,dst); +#endif + } + return w+5; +} + +t_int *scalarover_perf_simd(t_int *w) +{ + t_float *dst = (t_float *)w[3]; + const vector float zero = (vector float)(0); + int n = w[4]>>4; + + if(*(t_float *)w[2]) { + const t_float *src = (const t_float *)w[1]; +#ifdef USEVECLIB + float arg = *(t_float *)w[2]?1./ *(t_float *)w[2]: 0; + vsmul(src,1,&arg,dst,1,w[4]); +#else + const vector float v = LoadValue(w[2]); + const vector float one = (vector float)(1); + + vector float estimate = vec_re(v); + vector float arg = vec_madd( vec_nmsub( estimate, v, one ), estimate, estimate ); + + for(; n--; src += 16,dst += 16) { + vector float a1 = vec_ld( 0,src); + vector float a2 = vec_ld(16,src); + vector float a3 = vec_ld(32,src); + vector float a4 = vec_ld(48,src); + + a1 = vec_madd(a1,arg,zero); + a2 = vec_madd(a2,arg,zero); + a3 = vec_madd(a3,arg,zero); + a4 = vec_madd(a4,arg,zero); + + vec_st(a1, 0,dst); + vec_st(a2,16,dst); + vec_st(a3,32,dst); + vec_st(a4,48,dst); + } +#endif + } + else { + /* zero all output */ + for(; n--; dst += 16) { + vec_st(zero, 0,dst); + vec_st(zero,16,dst); + vec_st(zero,32,dst); + vec_st(zero,48,dst); + } + } + return w+5; +} + +t_int *min_perf_simd(t_int *w) +{ + const t_float *src1 = (const t_float *)w[1]; + const t_float *src2 = (const t_float *)w[2]; + t_float *dst = (t_float *)w[3]; + int n = w[4]>>4; + + for(; n--; src1 += 16,src2 += 16,dst += 16) { + vector float a1 = vec_ld( 0,src1),b1 = vec_ld( 0,src2); + vector float a2 = vec_ld(16,src1),b2 = vec_ld(16,src2); + vector float a3 = vec_ld(32,src1),b3 = vec_ld(32,src2); + vector float a4 = vec_ld(48,src1),b4 = vec_ld(48,src2); + + a1 = vec_min(a1,b1); + a2 = vec_min(a2,b2); + a3 = vec_min(a3,b3); + a4 = vec_min(a4,b4); + + vec_st(a1, 0,dst); + vec_st(a2,16,dst); + vec_st(a3,32,dst); + vec_st(a4,48,dst); + } + return w+5; +} + +t_int *scalarmin_perf_simd(t_int *w) +{ + const t_float *src = (const t_float *)w[1]; + const vector float arg = LoadValue(w[2]); + t_float *dst = (t_float *)w[3]; + int n = w[4]>>4; + + for(; n--; src += 16,dst += 16) { + vector float a1 = vec_ld( 0,src); + vector float a2 = vec_ld(16,src); + vector float a3 = vec_ld(32,src); + vector float a4 = vec_ld(48,src); + + a1 = vec_min(a1,arg); + a2 = vec_min(a2,arg); + a3 = vec_min(a3,arg); + a4 = vec_min(a4,arg); + + vec_st(a1, 0,dst); + vec_st(a2,16,dst); + vec_st(a3,32,dst); + vec_st(a4,48,dst); + } + return w+5; +} + +t_int *max_perf_simd(t_int *w) +{ + const t_float *src1 = (const t_float *)w[1]; + const t_float *src2 = (const t_float *)w[2]; + t_float *dst = (t_float *)w[3]; + int n = w[4]>>4; + + for(; n--; src1 += 16,src2 += 16,dst += 16) { + vector float a1 = vec_ld( 0,src1),b1 = vec_ld( 0,src2); + vector float a2 = vec_ld(16,src1),b2 = vec_ld(16,src2); + vector float a3 = vec_ld(32,src1),b3 = vec_ld(32,src2); + vector float a4 = vec_ld(48,src1),b4 = vec_ld(48,src2); + + a1 = vec_max(a1,b1); + a2 = vec_max(a2,b2); + a3 = vec_max(a3,b3); + a4 = vec_max(a4,b4); + + vec_st(a1, 0,dst); + vec_st(a2,16,dst); + vec_st(a3,32,dst); + vec_st(a4,48,dst); + } + return w+5; +} + +t_int *scalarmax_perf_simd(t_int *w) +{ + const t_float *src = (const t_float *)w[1]; + const vector float arg = LoadValue(w[2]); + t_float *dst = (t_float *)w[3]; + int n = w[4]>>4; + + for(; n--; src += 16,dst += 16) { + vector float a1 = vec_ld( 0,src); + vector float a2 = vec_ld(16,src); + vector float a3 = vec_ld(32,src); + vector float a4 = vec_ld(48,src); + + a1 = vec_max(a1,arg); + a2 = vec_max(a2,arg); + a3 = vec_max(a3,arg); + a4 = vec_max(a4,arg); + + vec_st(a1, 0,dst); + vec_st(a2,16,dst); + vec_st(a3,32,dst); + vec_st(a4,48,dst); + } + return w+5; +} + +t_int *clip_perf_simd(t_int *w) +{ + const t_float *src = (const t_float *)w[1]; + t_float *dst = (t_float *)w[2]; + const vector float lo = LoadValue(w[3]); + const vector float hi = LoadValue(w[4]); + int n = w[5]>>4; + + for(; n--; src += 16,dst += 16) { + vector float data1 = vec_ld( 0,src); + vector float data2 = vec_ld(16,src); + vector float data3 = vec_ld(32,src); + vector float data4 = vec_ld(48,src); + + vector unsigned char mlo1 = (vector unsigned char)vec_cmple(data1,lo); /* bit mask data <= lo */ + vector unsigned char mlo2 = (vector unsigned char)vec_cmple(data2,lo); /* bit mask data <= lo */ + vector unsigned char mlo3 = (vector unsigned char)vec_cmple(data3,lo); /* bit mask data <= lo */ + vector unsigned char mlo4 = (vector unsigned char)vec_cmple(data4,lo); /* bit mask data <= lo */ + + vector unsigned char mhi1 = (vector unsigned char)vec_cmpge(data1,hi); /* bit mask data >= hi */ + vector unsigned char mhi2 = (vector unsigned char)vec_cmpge(data2,hi); /* bit mask data >= hi */ + vector unsigned char mhi3 = (vector unsigned char)vec_cmpge(data3,hi); /* bit mask data >= hi */ + vector unsigned char mhi4 = (vector unsigned char)vec_cmpge(data4,hi); /* bit mask data >= hi */ + + data1 = (vector float)vec_and((vector unsigned char)data1,vec_nor(mlo1,mhi1)); + data2 = (vector float)vec_and((vector unsigned char)data2,vec_nor(mlo2,mhi2)); + data3 = (vector float)vec_and((vector unsigned char)data3,vec_nor(mlo3,mhi3)); + data4 = (vector float)vec_and((vector unsigned char)data4,vec_nor(mlo4,mhi4)); + + mlo1 = vec_and((vector unsigned char)lo,mlo1); + mlo2 = vec_and((vector unsigned char)lo,mlo2); + mlo3 = vec_and((vector unsigned char)lo,mlo3); + mlo4 = vec_and((vector unsigned char)lo,mlo4); + + mhi1 = vec_and((vector unsigned char)hi,mhi1); + mhi2 = vec_and((vector unsigned char)hi,mhi2); + mhi3 = vec_and((vector unsigned char)hi,mhi3); + mhi4 = vec_and((vector unsigned char)hi,mhi4); + + data1 = (vector float)vec_or(vec_or(mlo1,mhi1),(vector unsigned char)data1); + data2 = (vector float)vec_or(vec_or(mlo2,mhi2),(vector unsigned char)data2); + data3 = (vector float)vec_or(vec_or(mlo3,mhi3),(vector unsigned char)data3); + data4 = (vector float)vec_or(vec_or(mlo4,mhi4),(vector unsigned char)data4); + + vec_st(data1, 0,dst); + vec_st(data2,16,dst); + vec_st(data3,32,dst); + vec_st(data4,48,dst); + } + return w+6; +} + +t_int *sigwrap_perf_simd(t_int *w) +{ + const t_float *src = (const t_float *)w[1]; + t_float *dst = (t_float *)w[2]; + int n = w[3]>>4; + + for(; n--; src += 16,dst += 16) { + vector float data1 = vec_ld( 0,src); + vector float data2 = vec_ld(16,src); + vector float data3 = vec_ld(32,src); + vector float data4 = vec_ld(48,src); + + data1 = vec_sub(data1,vec_floor(data1)); + data2 = vec_sub(data2,vec_floor(data2)); + data3 = vec_sub(data3,vec_floor(data3)); + data4 = vec_sub(data4,vec_floor(data4)); + + vec_st(data1, 0,dst); + vec_st(data2,16,dst); + vec_st(data3,32,dst); + vec_st(data4,48,dst); + } + return w+4; +} + +t_int *sigsqrt_perf_simd(t_int *w) +{ + const t_float *src = (const t_float *)w[1]; + t_float *dst = (t_float *)w[2]; + int n = w[3]>>4; + + const vector float zero = (vector float)(0); + const vector float oneHalf = (vector float)(0.5); + const vector float one = (vector float)(1.0); + + for(; n--; src += 16,dst += 16) { + /* http://developer.apple.com/hardware/ve/algorithms.html + + Just as in Miller's scalar sigsqrt_perform, + first a rsqrt estimate is calculated which is then refined by one round of Newton-Raphson. + Here, to avoid branching a mask is generated which zeroes out eventual resulting NANs. + */ + +#ifdef USEVECLIB + /* no zero checking here */ + vec_st(vsqrtf(vec_ld( 0,src)), 0,dst); + vec_st(vsqrtf(vec_ld(16,src)),16,dst); + vec_st(vsqrtf(vec_ld(32,src)),32,dst); + vec_st(vsqrtf(vec_ld(48,src)),48,dst); +#else + vector float data1 = vec_ld( 0,src); + vector float data2 = vec_ld(16,src); + vector float data3 = vec_ld(32,src); + vector float data4 = vec_ld(48,src); + + const vector unsigned char mask1 = vec_nor((vector unsigned char)vec_cmple(data1,zero),(vector unsigned char)zero); /* bit mask... all 0 for data <= 0., all 1 else */ + const vector unsigned char mask2 = vec_nor((vector unsigned char)vec_cmple(data2,zero),(vector unsigned char)zero); /* bit mask... all 0 for data <= 0., all 1 else */ + const vector unsigned char mask3 = vec_nor((vector unsigned char)vec_cmple(data3,zero),(vector unsigned char)zero); /* bit mask... all 0 for data <= 0., all 1 else */ + const vector unsigned char mask4 = vec_nor((vector unsigned char)vec_cmple(data4,zero),(vector unsigned char)zero); /* bit mask... all 0 for data <= 0., all 1 else */ + + const vector float estimate1 = (vector float)vec_and((vector unsigned char)vec_rsqrte(data1),mask1); + const vector float estimate2 = (vector float)vec_and((vector unsigned char)vec_rsqrte(data2),mask2); + const vector float estimate3 = (vector float)vec_and((vector unsigned char)vec_rsqrte(data3),mask3); + const vector float estimate4 = (vector float)vec_and((vector unsigned char)vec_rsqrte(data4),mask4); + + /* this can still be improved.... */ + data1 = vec_madd(data1,vec_madd( vec_nmsub( data1, vec_madd( estimate1, estimate1, zero ), one ), vec_madd( estimate1, oneHalf, zero ), estimate1 ), zero); + data2 = vec_madd(data2,vec_madd( vec_nmsub( data2, vec_madd( estimate2, estimate2, zero ), one ), vec_madd( estimate2, oneHalf, zero ), estimate2 ), zero); + data3 = vec_madd(data3,vec_madd( vec_nmsub( data3, vec_madd( estimate3, estimate3, zero ), one ), vec_madd( estimate3, oneHalf, zero ), estimate3 ), zero); + data4 = vec_madd(data4,vec_madd( vec_nmsub( data4, vec_madd( estimate4, estimate4, zero ), one ), vec_madd( estimate4, oneHalf, zero ), estimate4 ), zero); + + vec_st(data1, 0,dst); + vec_st(data2,16,dst); + vec_st(data3,32,dst); + vec_st(data4,48,dst); +#endif + } + return w+4; +} + +/* Attention: there's a difference to sigsqrt_perform which delivers non-zero for a zero input... i don't think the latter is intended... */ +t_int *sigrsqrt_perf_simd(t_int *w) +{ + const t_float *src = (const t_float *)w[1]; + t_float *dst = (t_float *)w[2]; + int n = w[3]>>4; + + const vector float zero = (vector float)(0); + const vector float oneHalf = (vector float)(0.5); + const vector float one = (vector float)(1.0); + + for(; n--; src += 16,dst += 16) { + /* http://developer.apple.com/hardware/ve/algorithms.html + + Just as in Miller's scalar sigrsqrt_perform, + first a rsqrt estimate is calculated which is then refined by one round of Newton-Raphson. + Here, to avoid branching a mask is generated which zeroes out eventual resulting NANs. + */ + +#ifdef USEVECLIB + /* no zero checking here */ + vec_st(vrsqrtf(vec_ld( 0,src)), 0,dst); + vec_st(vrsqrtf(vec_ld(16,src)),16,dst); + vec_st(vrsqrtf(vec_ld(32,src)),32,dst); + vec_st(vrsqrtf(vec_ld(48,src)),48,dst); +#else + vector float data1 = vec_ld( 0,src); + vector float data2 = vec_ld(16,src); + vector float data3 = vec_ld(32,src); + vector float data4 = vec_ld(48,src); + + const vector unsigned char mask1 = vec_nor((vector unsigned char)vec_cmple(data1,zero),(vector unsigned char)zero); /* bit mask... all 0 for data <= 0., all 1 else */ + const vector unsigned char mask2 = vec_nor((vector unsigned char)vec_cmple(data2,zero),(vector unsigned char)zero); /* bit mask... all 0 for data <= 0., all 1 else */ + const vector unsigned char mask3 = vec_nor((vector unsigned char)vec_cmple(data3,zero),(vector unsigned char)zero); /* bit mask... all 0 for data <= 0., all 1 else */ + const vector unsigned char mask4 = vec_nor((vector unsigned char)vec_cmple(data4,zero),(vector unsigned char)zero); /* bit mask... all 0 for data <= 0., all 1 else */ + + const vector float estimate1 = (vector float)vec_and((vector unsigned char)vec_rsqrte(data1),mask1); + const vector float estimate2 = (vector float)vec_and((vector unsigned char)vec_rsqrte(data2),mask2); + const vector float estimate3 = (vector float)vec_and((vector unsigned char)vec_rsqrte(data3),mask3); + const vector float estimate4 = (vector float)vec_and((vector unsigned char)vec_rsqrte(data4),mask4); + + data1 = vec_nmsub( data1, vec_madd( estimate1, estimate1, zero ), one ); + data2 = vec_nmsub( data2, vec_madd( estimate2, estimate2, zero ), one ); + data3 = vec_nmsub( data3, vec_madd( estimate3, estimate3, zero ), one ); + data4 = vec_nmsub( data4, vec_madd( estimate4, estimate4, zero ), one ); + + data1 = vec_madd( data1, vec_madd( estimate1, oneHalf, zero ), estimate1 ); + data2 = vec_madd( data2, vec_madd( estimate2, oneHalf, zero ), estimate2 ); + data3 = vec_madd( data3, vec_madd( estimate3, oneHalf, zero ), estimate3 ); + data4 = vec_madd( data4, vec_madd( estimate4, oneHalf, zero ), estimate4 ); + + vec_st(data1, 0,dst); + vec_st(data2,16,dst); + vec_st(data3,32,dst); + vec_st(data4,48,dst); +#endif + } + return w+4; +} + +int simd_runtime_check() +{ + return 1; +} + + +#endif diff --git a/desiredata/src/m_simd_ve_gcc.h b/desiredata/src/m_simd_ve_gcc.h new file mode 100644 index 00000000..f58ca42f --- /dev/null +++ b/desiredata/src/m_simd_ve_gcc.h @@ -0,0 +1,18 @@ +/* SIMD functionality for Apple Velocity Engine (AltiVec) with GCC compiler; added by T.Grill */ +#ifndef __M_SIMD_VE_GCC_H +#define __M_SIMD_VE_GCC_H +#include "m_pd.h" + +/* SIMD functions for VE with GCC */ +t_int *sigwrap_perf_simd(t_int *w); +t_int *sigsqrt_perf_simd(t_int *w); +t_int *sigrsqrt_perf_simd(t_int *w); + +/* SIMD not implemented */ +#define env_tilde_accum_simd env_tilde_accum_8 +#define copyvec_simd_unalignedsrc copyvec_8 +/* #define sum_vecsimd sumvec_8 */ +#define line_tilde_slope_simd line_tilde_slope +#define peakvec_simd peakvec + +#endif /* __M_SIMD_VE_GCC_H */ diff --git a/desiredata/src/main.c b/desiredata/src/main.c new file mode 100644 index 00000000..cf4a6593 --- /dev/null +++ b/desiredata/src/main.c @@ -0,0 +1,17 @@ +/* this file is separate because it is outside of libpd.so */ + +extern "C" int sys_main(int argc, char **argv); +#if _MSC_VER +#include <windows.h> +#include <stdio.h> +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { + __try { + sys_main(__argc,__argv); + } __finally { + printf("caught an exception; stopping\n"); + } +} +#else /* not MSVC */ +int main(int argc, char **argv) {return sys_main(argc, argv);} +#endif + diff --git a/desiredata/src/makefile.in b/desiredata/src/makefile.in new file mode 100644 index 00000000..69acc76d --- /dev/null +++ b/desiredata/src/makefile.in @@ -0,0 +1,122 @@ +EXT= @EXT@ +LIBSUFFIX = @LIBSUFFIX@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +includedir = @includedir@ +libdir = @libdir@ +mandir = @mandir@ +# varibles to match packages/Makefile.buildlayout so that they can be easily +# overridden when building Pd-extended builds. <hans@at.or.at> +libpddir = $(libdir)/pd +pddocdir = $(libpddir)/doc +libpdbindir = $(libpddir)/bin + +MORECFLAGS = @MORECFLAGS@ +LDFLAGS = @LDFLAGS@ +LDSOFLAGS = @LDSOFLAGS@ +CPPFLAGS = -DDESIRE -DDONTUSESIMD -DPD @CPPFLAGS@ +CFLAGS = $(CPPFLAGS) @CFLAGS@ $(MORECFLAGS) +CFLAGS += -Wall -Wextra -Wno-unused-parameter -I. +CFLAGS += -DINSTALL_PREFIX=\"$(prefix)\" + +SRCXX = desire.c kernel.c builtins.c builtins_dsp.c s_path.c s_inter.c s_main.c \ + m_sched.c s_loader.c d_soundfile.c d_ugen.c s_audio.c s_midi.c @AUDIOSRC@ @MIDISRC@ +SRC = m_fifo.c m_simd.c d_mayer_fft.c d_fftroutine.c +OBJ = $(SRCXX:.c=.o) $(SRC:.c=.o) +SO = libpd$(LIBSUFFIX) + +# ------------------ targets ------------------------------------ + +.PHONY: bin externs all + +all: bin externs + +bin: pd pd-watchdog pdsend pdreceive + +$(SRCXX:.c=.o): %.o: %.c config.log + $(CXX) $(CFLAGS) -xc++ -c -o $*.o $*.c + +$(SRC:.c=.o): %.o: %.c config.log + $(CC) $(CFLAGS) -Wno-parentheses -Wno-switch -Wstrict-prototypes -c -o $*.o $*.c + +$(SO): $(OBJ) config.log + $(CXX) $(LDSOFLAGS) $(LDFLAGS) $(DBG_CFLAGS) -o $(SO) $(OBJ) + +pd: $(OBJ) config.log $(SO) main.c + $(CXX) $(LDFLAGS) $(DBG_CFLAGS) $$PWD/$(SO) main.c -o pd + +pd-watchdog: s_watchdog.c + $(CC) $(CFLAGS) $(STRIPFLAG) -o pd-watchdog s_watchdog.c + +pdsend: u_pdsend.c + $(CC) $(CFLAGS) $(STRIPFLAG) -o pdsend u_pdsend.c + +pdreceive: u_pdreceive.c + $(CC) $(CFLAGS) $(STRIPFLAG) -o pdreceive u_pdreceive.c + +externs: + cd ../extra; for ext in bonk~ choice expr~ fiddle~ loop~ lrshift~ pique sigmund~; do \ + cd $$ext; make @EXTERNTARGET@ || break; cd ..; done + +install: all + install -d $(DESTDIR)$(bindir) + install -d $(DESTDIR)$(libpdbindir) + for file in defaults.ddrc pkgIndex.tcl pre8.5.tcl poe.tcl bgerror.tcl; do \ + install $$file $(DESTDIR)$(libpdbindir)/$$file; done + cp -r locale $(DESTDIR)$(libpdbindir) + cp -r ../icons $(DESTDIR)$(libpddir) + install $(BINARYMODE) $(SO) $(DESTDIR)$(libdir)/$(SO) + test -w /etc/ld.so.cache && ldconfig || true + $(CXX) $(LDFLAGS) $(DBG_CFLAGS) -lpd main.c -o $(DESTDIR)$(bindir)/pd + install -m755 desire.tk $(DESTDIR)$(bindir)/desire + install -m755 pdsend $(DESTDIR)$(bindir)/pdsend + install -m755 pdreceive $(DESTDIR)$(bindir)/pdreceive + install -m755 pd-watchdog $(DESTDIR)$(libpdbindir)/pd-watchdog + mkdir -p $(DESTDIR)$(pddocdir) + cp -pr ../doc/* $(DESTDIR)$(pddocdir) + cp -pr ../extra/* $(DESTDIR)$(libpddir) + # rm -f $(DESTDIR)$(libpddir)/extra/*/*.o + install -d $(DESTDIR)$(includedir) + for file in m_pd.h desire.h; do install -m644 $$file $(DESTDIR)$(includedir)/$$file; done + install -d $(DESTDIR)$(mandir)/man1 + for page in pd.1 pdsend.1 pdreceive.1; do \ + gzip < ../man/$$page > $(DESTDIR)$(mandir)/man1/$$page.gz; chmod 644 $(DESTDIR)$(mandir)/man1/$$page.gz; done + +local-clean: + -rm -f *.o pd pdsend pdreceive pd-watchdog m_stamp.c + -rm -f *~ + -(cd ../doc/6.externs; rm -f *.pd_linux) + -rm -f makefile.deps + touch makefile.deps + chmod 666 makefile.deps + +extra-clean: + -rm -f `find ../extra/ -name "*.pd_*"` + -rm -f tags + +clean: extra-clean local-clean + +distclean: clean + -rm -f config.cache config.log config.status makefile tags \ + autom4te.cache/output.* autom4te.cache/traces.* autom4te.cache/requests + -rmdir autom4te.cache + -rm -rf autom4te-*.cache + +tags: $(SRC) $(GSRC); ctags *.[ch] + +depend: makefile.deps + +makefile.deps: makefile + $(CXX) $(CPPFLAGS) -MM $(SRC) $(SRCXX) > makefile.deps + +uninstall: + rm -f -r $(DESTDIR)$(libpddir) + rm -f $(DESTDIR)$(libdir)/libpd* + cd $(DESTDIR)$(bindir); rm pd pdsend pdreceive + cd $(DESTDIR)$(includedir); rm m_pd.h desire.h + cd $(DESTDIR)$(mandir)/man1; rm pd.1.gz pdsend.1.gz pdreceive.1.gz + +include makefile.deps + +# echo $$LD_LIBRARY_PATH | sed 's/:/\n/g' | grep -q '^$(DESTDIR)$(libdir)$$' diff --git a/desiredata/src/notes.txt b/desiredata/src/notes.txt new file mode 100644 index 00000000..26393dfb --- /dev/null +++ b/desiredata/src/notes.txt @@ -0,0 +1,300 @@ +---------------- dolist -------------------- +done: +plug-in support +atan2 inlets switched +queued graphics updates for tables, number boxes +cut/paste text (needs more testing) +add standard bindings (ctl-o, etc) to dialogs +separate audio on/off from nchans +setuid flag in configure script +settings saver (registry in Windows; .pdrc in linux; defaults system in OSX?) + audio API + MIDI -- fix to read MIDI on startup (rest works?) + path + startup flags + libs +better params: + extra flag for path + startup flags + startup libraries +printout to pd window +startup from GUI +%x to %lx in all "tags" to make 64-bit safe +portaudio_pd files into src +t_int to int in binbuf_addv +64-bit fix to externs makefiles +new filter objects: cpole~, fpole~, etc. +put in Wini's RME ALSA code; there are still bugs... +portaudio fixed for inchans != outchans, e.g., emi emagic (2/6) +sprout inlets/outlets on objects whose creation failed. +uploaded to CVS +bug fix: click on minaturized subpatch fails to "vis" it +bug fix: CK on Oct. 4 (crash changing font size) +sched_idle hook +fixed startup flags, path, etc. so that spaces, "," chars, etc., are allowed +configure script fixed to handle enable- and disable- correctly +fixed spaces in "startup" dialog + +doc: +document env~ second argument (and why is it no less than 1/10 of first???) +vibrato example + +problems: +'[' in numbox label breaks it (Yury Sept. 3) +soundfiles with 3-byte samples buzz for the first readsf buffer (bug/x.pd) +read xx.txt in "bad" gives warnings +writesf -- "open" without "0" misses closing the previous file. +Also writesf~ acts differently if DSP is off when "open" is sent? +qlist - 'next 1' seems not to work +Krzysztof's qlist_next reentrancy bug +don't draw in/outlets on gui objects in graph-on-parent +font size should depend on subpatch/abstraction +moving a bang toward top of window creates problem +check what happens when going back and forth between graph-on-parent +get rid of messages causing renaming; try to prevent patches closing themselves. +dac~/ adc~/ block~ incompatibility +scofo reports error on reading score1.txt +rfft~ loses nyquist bin -- see "to hell with it" comment in d_fft.c +open_via_path() followed by close() fails in windows? [can't reproduce] +loading e-mailed patches without removing headers crashes pd +pd $1 bug ($1 is saved as it was evaluated, not as '$1') +data copy/paste doesn't check templates aren't changed +figure out why Pd sometimes crashes when you close example after adding fields +check if _vsnprintf with zero argument in windows works any better... + +next release: +update portmusic to latest +IEM guis to use queued updates +pixel font sizes +pd to find running ones (pd -new to defeat) +"enter" into object box to create new one (also, changing borders? forking?) +tab to jump to a connected object (first one?) (shift-tab to back up?) +tables: + if there's just one array, don't do stringent hit check. + array click protection (Krzysztof's suggestion) + make graph labels persistent and add to dialog + object to get/set table size; random; quantile + flag to hide array names +queued graphics updates for IEMGUIs and scalars +document tabwrite~_start +think of a way to embed abstractions in a patch +make watchdog work for MACOSX +GOP bounding box object +IEMGUIs better default font size +search path to include both calling patch and abstraction, if different +abstraction reload shouldn't have to vis everyone +addcomma message to message +pasting should look at current mouse location +delete-in-rectangle message to Pds +put serial object in main dist (see rat@telecoma, Apr. 25; winfried May 22) +open/save panel to take messages to init directory, and to set extent list +flags to defeat pre-loading specified classes +expr to parse exponential notation + + +data: +arrays of non-existent templates crash +vget, vset traversal objects +cursor to show (x, y) location +better hit detection (getrect is too greedy; try tk's "current" tag for canvas) +click on points of plot +typing & dragging 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 + +more features: + +-Wno-unused to -Wno-unused-paramter and clean up unused automatic variables +security module system in 2.6 - see the kernel module replacing jackstart +signal inlets to sense signals; fix +~ etc, vcf~, biquad~, other filters +mess with RME ALSA some more; ALSA readn doesn't work yet; use mmap? +try to reduce startup time +investigate gcc 3.3 warnings; try to reinstate -fstrict-aliasing +message dialog not to disappear +why does changing the name of an explode in jupiter patch take so long? +close-subwindows menu item +show results of opening audio and MIDI on dialogs +windows escape from control-C +settable netsend and netreceive port numbers +new: abs~, nexttick~, extend threshold~ and snapshot~ (vthreshold~ etc) +incorporate pddp doc +try again to fix the font scene +look at prctl(2) for FP exception handling +??? have a way to disambiguate externs from different libs??? +netsend separate thread +netreceive (and netsend?) message to set port number +think about x and y scale preservation when changing between graph and object +show outlines of objects even when graph is "open" +graph_vis() to decorate graphs when they're toplevel (parent_glist == 0) +get graphs to expand to hold their contents +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? +closebang +check that -blocksize really reflects in audiobuf calc for Hammerfall +makefile to have make install depend on make local. +Float method for random +figure out list, message objects +separate control over alsaindev and alsaoutdev +put in something for tilde order forcing +extensible "toolbar" so people can add external GUI objects +allow spaces in paths +variable send and receive -- check how max/MSP does it? +number boxes to darken for typing and/or received messages +dialog to change lib flag and path +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? + +MAX compatibilty: +trigger 1 (on Pd, outputs 0; on Max?) + +LATER +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 +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/desiredata/src/pkgIndex.tcl b/desiredata/src/pkgIndex.tcl new file mode 100644 index 00000000..40115eba --- /dev/null +++ b/desiredata/src/pkgIndex.tcl @@ -0,0 +1,15 @@ +# Tcl package index file, version 1.1 +# This file is generated by the "pkg_mkIndex" command +# and sourced either when an application starts up or +# by a "package unknown" script. It invokes the +# "package ifneeded" command to set up package-related +# information so that packages will be loaded automatically +# in response to "package require" commands. When this +# script is sourced, the variable $dir must contain the +# full path name of this file's directory. + +package ifneeded kb-mode 0.1 [list source [file join $dir kb-mode.tcl]] +package ifneeded poe 0.1 [list source [file join $dir poe.tcl]] +package ifneeded pre8.5 8.4 [list source [file join $dir pre8.5.tcl]] +package ifneeded bgerror 8.4 [list source [file join $dir bgerror.tcl]] +package ifneeded dzinc 0.1 [list source [file join $dir dzinc.tcl]] diff --git a/desiredata/src/plusminus b/desiredata/src/plusminus new file mode 100755 index 00000000..553f84fa --- /dev/null +++ b/desiredata/src/plusminus @@ -0,0 +1,42 @@ +#!/usr/bin/env ruby +# plusminus, Copyright © 2004 by Mathieu Bouchard +# this program makes stats about a unified diff (diff -u) output. +# for example, run this command: cvs diff -u | ./plusminus +# NOTE: the -u option is required! (you can put it in ~/.cvsrc) + +puts "-"*64 + +$plustot=0 +$minustot=0 + +def show + printf "%20s %+5d %+5d (net %+5d)\n", $file, $plus, -$minus, $plus-$minus +end + +loop{ + line = gets + break if not line + if /^diff/.match line then + x = line.split(/\s+/) + $plustot+=$plus if $plus + $minustot+=$minus if $minus + show if $file + $file = x[-1] + $on=false + $plus=0 + $minus=0 + elsif /^\@\@/ =~ line then $on=true + elsif $on and /^\+/ =~ line then $plus+=1 + elsif $on and /^\-/ =~ line then $minus+=1 + end +} + +$plustot+=$plus if $plus +$minustot+=$minus if $minus +show if $file + +$file="total" +$plus=$plustot +$minus=$minustot +puts "-"*64 +show diff --git a/desiredata/src/poe.tcl b/desiredata/src/poe.tcl new file mode 100644 index 00000000..89ddfc78 --- /dev/null +++ b/desiredata/src/poe.tcl @@ -0,0 +1,281 @@ +# $Id: poe.tcl,v 1.1.2.2.2.27 2007-10-15 15:58:13 chunlee Exp $ +#----------------------------------------------------------------# +# POETCL +# +# Copyright (c) 2005,2006 by Mathieu Bouchard +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# See file ../COPYING.desire-client.txt for further informations on licensing terms. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# Note that this is not under the same license as the rest of PureData. +# Even the DesireData server-side modifications stay on the same license +# as the rest of PureData. +# +#-----------------------------------------------------------------------------------# + +# (please distinguish between what this is and what dataflow is) +# note, the toplevel class is called "thing". + +package provide poe 0.1 + +if {$tcl_version < 8.5} {package require pre8.5} +set nextid 0 +set _(Class:_class) Class +set _(Class:_super) {Thing} +set have_expand [expr ![catch {set a {foo bar}; list {expand}$a}]] +proc proc* {name args body} { + set argl {} + foreach arg $args {set arg [lindex $arg 0]; lappend argl "$arg=\$$arg"} + if {[regexp {_unknown$} $name]} { + proc $name $args "upvar 1 selector ___; puts \"\[VTgreen\]CALL TO PROC $name selector=\$___ [join $argl " "]\[VTgrey\]\"; $body" + } else { + if {![regexp "return" $body]} { + set body "time {$body}" + proc $name $args "puts \"\[VTgreen\]CALL TO PROC $name [join $argl " "], \[VTred\]\[lrange \[split \[$body\] \] 0 1\] \[VTgrey\]\"" + } { + proc $name $args "puts \"\[VTgreen\]CALL TO PROC $name [join $argl " "]\[VTgrey\]\"; $body" + } + } +} + +#proc Class_def {self selector args body} { +# global _; if {![info exists _($self:_class)]} {error "unknown class '$self'"} +# proc ${self}_$selector "self $args" "global _; [regsub -all @(\[\\w\\?\]+) $body _(\$self:\\1)]" +#} +#proc def {class selector args body} {$class def $selector $args $body} + +proc expand_macros {body} { + return [regsub -all @(\\\$?\[\\w\\?\]+) $body _(\$self:\\1)] +} + +proc def {self selector argnames body} { + global _ __trace __args + if {![info exists _($self:_class)]} {error "unknown class '$self'"} + set name ${self}_$selector + #if {$name == "Canvas_motion_wrap"} {set body "puts \[time {$body}\]"} + set argnames [concat [list self] $argnames] + if {([info exists __trace($self:$selector)] || [info exists __trace(*:$selector)] + || [info exists __trace($self:*)] || [info exists __trace(*:*)]) + && ![info exists __trace($self:!$selector)] + && ![info exists __trace(*:!$selector)] + } { + proc* $name $argnames "global _; [expand_macros $body]" + } { + proc $name $argnames "global _; [expand_macros $body]" + } + set __args($name) $argnames + #trace add execution ${self}_$selector enter dedebug +} + +proc class_new {self {super {Thing}}} { + global _ + set _($self:_class) Class + set _($self:_super) $super + set _($self:subclasses) {} + foreach sup $super {lappend _($sup:subclasses) $self} + proc ${self}_new {args} "global _ + set self \[format o%07x \$::nextid\] + incr ::nextid + set _(\$self:_class) $self + setup_dispatcher \$self + eval [concat \[list \$self init\] \$args] + return \$self + " + proc ${self}_new_as {self args} "global _ + if {\[info exists _(\$self:_class)\]} {error \"object '\\$self' already exists\" } + set _(\$self:_class) $self + setup_dispatcher \$self + eval [concat \[list \$self init\] \$args] + return \$self + " + setup_dispatcher $self +} + +# TODO: remove duplicates in lookup +proc lookup_method {class selector methodsv ancestorsv} { + global _ + upvar $methodsv methods + upvar $ancestorsv ancestors + set name ${class}_$selector + if {[llength [info procs $name]]} {lappend methods $name} + lappend ancestors $class + foreach super $_($class:_super) {lookup_method $super $selector methods ancestors} +} + +proc cache_method {class selector} { + global _ __ + set methods {}; set ancestors {} + lookup_method $class $selector methods ancestors + if {![llength $methods]} {set methods [cache_method $class unknown]} + set __($class:$selector) $methods + return $methods +} + +if {$have_expand} { + set dispatch { + set i 0; set class $::_($self:_class) + if {[catch {set methods $::__($class:$selector)}]} {set methods [cache_method $class $selector]} + [lindex $methods 0] $self {expand}$args + } +} else { + set dispatch { + set i 0; set class $::_($self:_class) + if {[catch {set methods $::__($class:$selector)}]} {set methods [cache_method $class $selector]} + eval [concat [list [lindex $methods 0] $self] $args] + } +} +proc setup_dispatcher {self} { + if {[llength [info commands $self]]} {rename $self old_$self} + proc $self {selector args} [regsub -all {\$self} $::dispatch $self] +} + +set super { + upvar 1 self self + upvar 2 methods methods i oi + set i [expr {1+$oi}] + if {[llength $methods] < $i} {error "no more supermethods"} +} +if {$have_expand} { + append super {[lindex $methods $i] $self {expand}$args} +} else { + append super {eval [concat [list [lindex $methods $i] $self] $args]} +} +proc super {args} $super + +class_new Thing {} +#set _(Thing:_super) {} +def Thing init {} {} +def Thing == {other} {return [expr ![string compare $self $other]]} + +# virtual destructor +def Thing delete {} { + foreach elem [array names _ $self:*] {array unset _ $elem} + rename $self "" +} + +def Thing vars {} { + set n [string length $self:] + set ks [list] + foreach k [array names _] { + if {0==[string compare -length $n $self: $k]} {lappend ks [string range $k $n end]} + } + return $ks +} + +def Thing inspect {} { + set t [list "#<$self: "] + foreach k [lsort [$self vars]] {lappend t "$k=[list $@$k] "} + lappend t ">" + return [join $t ""] +} + +def Thing class {} {return $@_class} + +def Thing unknown {args} { + upvar 1 selector selector class class + error "no such method '$selector' for object '$self'\nwith ancestors {[Class_ancestors $class]}" +} + +class_new Class + +# those return only the direct neighbours in the hierarchy +def Class superclasses {} {return $@_super} +def Class subclasses {} {return $@subclasses} + +# those look recursively. +def Class ancestors {} { + #if {[info exists @ancestors]} {} + set r [list $self] + foreach super $@_super {eval [concat [list lappend r] [$super ancestors]]} + return $r +} +def Class <= {class} {return [expr [lsearch [$self ancestors] $class]>=0]} + +# note: [luniq] is actually defined in desire.tk +def Class methods {} { + set methods {} + set anc [$self ancestors] + foreach class $anc { + foreach name [info procs ${class}_*] { + lappend methods [join [lrange [split $name _] 1 end] _] + } + } + return [luniq [lsort $methods]] +} + +# those are static methods, and poe.tcl doesn't distinguish them yet. +def Class new { args} {eval [concat [list ${self}_new ] $args]} +def Class new_as {id args} {eval [concat [list ${self}_new_as $id] $args]} + +#-----------------------------------------------------------------------------------# + +# this makes me think of Maximus-CBCS... +proc VTgrey {} {return "\x1b\[0m"} +proc VTred {} {return "\x1b\[0;1;31m"} +proc VTgreen {} {return "\x1b\[0;1;32m"} +proc VTyellow {} {return "\x1b\[0;1;33m"} +proc VTblue {} {return "\x1b\[0;1;34m"} +proc VTmagenta {} {return "\x1b\[0;1;35m"} +proc VTcyan {} {return "\x1b\[0;1;36m"} +proc VTwhite {} {return "\x1b\[0;1;37m"} + +proc error_text {} { + set e $::errorInfo + regsub -all " invoked from within\n" $e "" e + regsub -all "\n \\(" $e " (" e + regsub -all {\n[^\n]*procedure \"(::unknown|super)\"[^\n]*\n} $e "\n" e + regsub -all {\n\"\[lindex \$methods 0\][^\n]*\n} $e "\n" e + regsub -all {\s*while executing\s*\n} $e "\n" e + #regsub {\n$} $e "" e + return $e +} +set suicidal 0 +proc error_dump {} { + puts "[VTred]Exception:[VTgrey] [error_text]" + if {$::suicidal} {exit 1} +} + +proc tracedef {class method {when enter}} { + global __trace + set __trace($class:$method) $when +} + +proc yell {var key args} { + global $key + puts "[VTyellow]HEY! at [info level -1] set $key [list $::_($key)][VTgrey]" +} + +proc object_table {} { + set n 0 + puts "poe.tcl object_table: {" + foreach o [lsort [array names ::_ *:_class]] { + set oo [lindex [split $o :] 0] + set class $::_($o) + incr by_class($class) + puts " $oo is a $class" + incr n + } + puts "} ($n objects)" + set n 0 + puts "poe.tcl class_stats: {" + foreach o [array names by_class] { + puts " class $o has $by_class($o) objects" + incr n + } + puts "} ($n classes)" +} + +proc object_exists {self} {info exists ::_($self:_class)} diff --git a/desiredata/src/pre8.5.tcl b/desiredata/src/pre8.5.tcl new file mode 100644 index 00000000..c4b289c5 --- /dev/null +++ b/desiredata/src/pre8.5.tcl @@ -0,0 +1,209 @@ +package provide pre8.5 8.4 + +proc lremove {args} { + array set opts {-all 0 pattern -exact} + while {[string match -* [lindex $args 0]]} { + switch -glob -- [lindex $args 0] { + -a* { set opts(-all) 1 } + -g* { set opts(pattern) -glob } + -r* { set opts(pattern) -regexp } + -- { set args [lreplace $args 0 0]; break } + default {return -code error "unknown option \"[lindex $args 0]\""} + } + set args [lreplace $args 0 0] + } + set l [lindex $args 0] + foreach i [join [lreplace $args 0 0]] { + if {[set ix [lsearch $opts(pattern) $l $i]] == -1} continue + set l [lreplace $l $ix $ix] + if {$opts(-all)} { + while {[set ix [lsearch $opts(pattern) $l $i]] != -1} { + set l [lreplace $l $ix $ix] + } + } + } + return $l +} +if {![llength [info commands dict]]} { + proc lassign {list args} { + foreach elem $list varName $args { + upvar 1 $varName var + set var $elem + } + } + proc dict {cmd args} { + uplevel 1 [linsert $args 0 _dict_$cmd] + } + proc _dict_get {dv args} { + if {![llength $args]} {return $dv} else { + array set dvx $dv + set key [lindex $args 0] + set dv $dvx($key) + set args [lrange $args 1 end] + return [eval [linsert $args 0 _dict_get $dv]] + } + } + proc _dict_exists {dv key args} { + array set dvx $dv + set r [info exists dvx($key)] + if {!$r} {return 0} + if {[llength $args]} { + return [eval [linsert $args 0 _dict_exists $dvx($key) ]] + } else {return 1} + } + proc _dict_set {dvar key value args } { + upvar 1 $dvar dv + if {![info exists dv]} {set dv [list]} + array set dvx $dv + if {![llength $args]} { + set dvx($key) $value + } else { + eval [linsert $args 0 _dict_set dvx($key) $value] + } + set dv [array get dvx] + } + proc _dict_unset {dvar key args} { + upvar 1 $dvar mydvar + if {![info exists mydvar]} {return} + array set dv $mydvar + if {![llength $args]} { + if {[info exists dv($key)]} { + unset dv($key) + } + } else { + eval [linsert $args 0 _dict_unset dv($key) ] + } + set mydvar [array get dv] + return {} + } + proc _dict_keys {dv {pat *}} { + array set dvx $dv + return [array names dvx $pat] + } + proc _dict_append {dvar key {args}} { + upvar 1 $dvar dv + if {![info exists dv]} {set dv [list]} + array set dvx $dv + eval [linsert $args 0 append dvx($key) ] + set dv [array get dvx] + } + proc _dict_create {args} { + return $args + } + proc _dict_filter {dv ftype args} { + set r [list] + foreach {globpattern} $args {break} + foreach {varlist script} $args {break} + + switch $ftype { + key { + foreach {key value} $dv { + if {[string match $globpattern $key]} { + lappend r $key $value + } + } + } + value { + foreach {key value} $dv { + if {[string match $globpattern $value]} { + lappend r $key $value + } + } + } + script { + foreach {Pkey Pval} $varlist {break} + upvar 1 $Pkey key $Pval value + foreach {key value} $dv { + if {[uplevel 1 $script]} { + lappend r $key $value + } + } + } + default { + error "Wrong filter type" + } + } + return $r + } + proc _dict_for {kv dict body} { + uplevel 1 [list foreach $kv $dict $body] + } + proc _dict_incr {dvar key {incr 1}} { + upvar 1 $dvar dv + if {![info exists dv]} {set dv [list]} + array set dvx $dv + if {![info exists dvx($key)]} {set dvx($key) 0} + incr dvx($key) $incr + set dv [array get dvx] + } + proc _dict_info {dv} { + return "Dictionary is represented as plain list" + } + proc _dict_lappend {dvar key args} { + upvar 1 $dvar dv + if {![info exists dv]} {set dv [list]} + array set dvx $dv + eval [linsert $args 0 lappend dvx($key)] + set dv [array get dvx] + } + proc _dict_merge {args} { + foreach dv $args { + array set dvx $dv + } + array get dvx + } + proc _dict_replace {dv args} { + foreach {k v} $args { + _dict_set dv $k $v + } + return $dv + } + proc _dict_remove {dv args} { + foreach k $args { + _dict_unset dv $k + } + return $dv + } + proc _dict_size {dv} { + return [expr {[llength $dv]/2}] + } + proc _dict_values {dv {gp *}} { + set r [list] + foreach {k v} $dv { + if {[string match $gp $v]} { + lappend r $v + } + } + return $r + } + proc _dict_update {dvar args} { + set name [string map {: {} ( {} ) {}} $dvar] + upvar 1 $dvar dv + upvar 1 _my_dict_array$name local + + array set local $dv + foreach {k v} [lrange $args 0 end-1] { + if {[info exists local($k)]} { + if {![uplevel 1 [list info exists $v]]} { + uplevel 1 [list upvar 0 _my_dict_array${name}($k) $v] + } else { + uplevel 1 [list set $v $local($k)] + } + } + } + set code [catch {uplevel 1 [lindex $args end]} res] + + foreach {k v} [lrange $args 0 end-1] { + if {[uplevel 1 [list info exists $v]]} { + set local($k) [uplevel 1 [list set $v]] + } else { + unset -nocomplain local($k) + } + } + set dv [array get local] + unset local + + return -code $code $res + } + +}
\ No newline at end of file diff --git a/desiredata/src/profile_dd.tcl b/desiredata/src/profile_dd.tcl new file mode 100644 index 00000000..12c98a56 --- /dev/null +++ b/desiredata/src/profile_dd.tcl @@ -0,0 +1,20 @@ + +if 1 { + puts "profiler version [package require profiler]" + profiler::init + # try just: prof + # or try: prof calls + proc prof {{arg totalRuntime}} { + set dump [profiler::dump] + #foreach {a b} $dump {foreach {c d} $b {set prof($a:$c) $d}} + set top [profiler::sortFunctions $arg] + foreach entry $top { + mset {k v} $entry + if {!$v} {continue} + puts [format "%8d %s" $v $k] + } + } +} +if 0 { + load matjuprofiler/matjuprofiler.so +} diff --git a/desiredata/src/rules.txt b/desiredata/src/rules.txt new file mode 100644 index 00000000..ec6c56b3 --- /dev/null +++ b/desiredata/src/rules.txt @@ -0,0 +1,42 @@ +Those rules are in no particular order. +Written by matju, on 2007.07.11 - ... + +#000: Tk-specific Tcl code goes in *.tk files; Tk-independent Tcl code is allowed to go in *.tcl files. + Exceptions: debug.tcl ... + +#001: Long source files are usually better than short files because they are easier to search in, with most editors. + +#002: It's better to make classes/procs/defs smaller but not to the extent that there are too many of them. + +#003: Accessing an object's privates directly, is likely to cause trouble in the future. All @variables are private, but + methods may also be marked as private or protected, by a visible comment where the definition is. (C++ variables are + not necessarily like that, mostly because of compatibility with classic pd) + +#004: Indentation is whatever you like as long as it's locally consistent. Tab stops (of the tab key) are at multiples of 8, + but indentation could be 2, 4, 8. (It's a bad idea to use less than 2 or more than 8). Open-braces don't deserve their + own line, but close-braces do, at least because of how the diff program works. + +#005: Screen width is assumed to be about 125 characters, not 80. This is especially useful for cutting down the need + to wrap lines. Newlines that have to do with linewrap get confused with meaningful newlines. Blank lines should be + used sparsely: the more there are blank lines, the less meaningful they are. If you want blank lines everywhere, + change the spacing of your font. + +#006: Short pieces of code that are quite repetitive but not completely, should be put on one line each and organised into + alternating columns of recurrent and non-recurrent material. This highlights patterns in code. e.g.: + for (int i=0; i<ninlets ; i++) x->inlets [i]->name = gensprintf( "inlet #%d",i); + for (int i=0; i<noutlets; i++) x->outlets[i]->name = gensprintf("outlet #%d",i); + +#007(Tcl): an attribute is a reader method named like "some_noun", which has no args and returns a value and/or a writer + method named like "some_noun=", which takes one arg (or more?) and returns no value. Together they are seen as + manipulating a variable. If the variable directly exists in the object, it should be called "@some_noun". The "=" + suffix should be only used for that purpose. (we should think about whether to accept multiple args, because other + languages with a similar concept only allow one arg in the writer) + +#008(Tcl): Nouns like "visibility" that are simpler as adjectives and whose main purpose is to return a yes/no value, can + be named like "visible?" and are declined in the same way, e.g. "visible?=" and "@visible?". The "?" suffix should be + only used for that purpose. + +#009(Tcl): use :: instead of proc global. + +#010: make variables as local as it makes sense: don't make them global if they'd fit well in an object; don't put them in + an object if they belong inside of a def or proc (fully local). diff --git a/desiredata/src/s_audio.c b/desiredata/src/s_audio.c new file mode 100644 index 00000000..0bcf85f4 --- /dev/null +++ b/desiredata/src/s_audio.c @@ -0,0 +1,724 @@ +/* Copyright (c) 2003, 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. */ + +/* machine-independent (well, mostly!) audio layer. Stores and recalls + audio settings from argparse routine and from dialog window. +*/ + +#define PD_PLUSPLUS_FACE +#include "m_pd.h" +#include "s_stuff.h" +#include "m_simd.h" +#include <stdio.h> +#ifdef UNISTD +#include <unistd.h> +#include <sys/time.h> +#include <sys/resource.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <sstream> + +#define SYS_DEFAULTCH 2 +#define SYS_MAXCH 100 +typedef long t_pa_sample; +#define SYS_SAMPLEWIDTH sizeof(t_pa_sample) +#define SYS_BYTESPERCHAN (sys_dacblocksize * SYS_SAMPLEWIDTH) +#define SYS_XFERSAMPS (SYS_DEFAULTCH*sys_dacblocksize) +#define SYS_XFERSIZE (SYS_SAMPLEWIDTH * SYS_XFERSAMPS) +#define MAXNDEV 100 +#define DEVDESCSIZE 80 + +extern t_audioapi pa_api, jack_api, oss_api, alsa_api, sgi_api, mmio_api, asio_api; + +t_sample *sys_soundin; +t_sample *sys_soundout; +float sys_dacsr; + +using namespace std; + +static t_audioapi *sys_audio() { +#ifdef USEAPI_PORTAUDIO + if (sys_audioapi == API_PORTAUDIO) return &pa_api; +#endif +#ifdef USEAPI_JACK + if (sys_audioapi == API_JACK) return &jack_api; +#endif +#ifdef USEAPI_OSS + if (sys_audioapi == API_OSS) return &oss_api; +#endif +#ifdef USEAPI_ALSA + if (sys_audioapi == API_ALSA) return &alsa_api; +#endif +#ifdef USEAPI_SGI + if (sys_audioapi == API_SGI) return &sgi_api; +#endif +#ifdef USEAPI_MMIO + if (sys_audioapi == API_MMIO) return &mmio_api; +#endif +#ifdef USEAPI_ASIO + if (sys_audioapi == API_ASIO) return &asio_api; +#endif + post("sys_close_audio: unknown API %d", sys_audioapi); + sys_inchannels = sys_outchannels = 0; + sched_set_using_dacs(0); /* tb: dsp is switched off */ + return 0; +} + +static void audio_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize); + +/* these are set in this file when opening audio, but then may be reduced, + even to zero, in the system dependent open_audio routines. */ +int sys_inchannels; +int sys_outchannels; +int sys_advance_samples; /* scheduler advance in samples */ +int sys_blocksize = 0; /* audio I/O block size in sample frames */ +#ifndef API_DEFAULT +#define API_DEFAULT 0 +#endif +int sys_audioapi = API_DEFAULT; +int sys_dacblocksize; +int sys_schedblocksize; +int sys_meters; /* true if we're metering */ +static float sys_inmax; /* max input amplitude */ +static float sys_outmax; /* max output amplitude */ +int sys_schedadvance; /* scheduler advance in microseconds */ + +/* the "state" is normally one if we're open and zero otherwise; but if the state is one, + we still haven't necessarily opened the audio hardware; see audio_isopen() below. */ +static int audio_state; + +/* last requested parameters */ +static t_audiodevs audio_in; +static t_audiodevs audio_out; +static int audio_rate; +static int audio_dacblocksize; +static int audio_advance; +static int audio_scheduler; + +extern int sys_callbackscheduler; + +float peakvec(t_float* vec, t_int n, t_float cur_max); +static float (*peak_fp)(t_float*, t_int, t_float) = peakvec; + +static int audio_isopen() { + return audio_state && ((audio_in.ndev > 0 && audio_in.chdev[0] > 0) + || (audio_out.ndev > 0 && audio_out.chdev[0] > 0)); +} + +extern "C" void sys_get_audio_params(t_audiodevs *in, t_audiodevs *out, int *prate, int *pdacblocksize, int *padvance, int *pscheduler) { + in->ndev = audio_in.ndev; + out->ndev = audio_out.ndev; + for (int i=0; i<MAXAUDIOINDEV; i++) {in ->dev[i] = audio_in.dev[i]; in->chdev[i] = audio_in.chdev[i];} + for (int i=0; i<MAXAUDIOOUTDEV; i++) {out->dev[i] = audio_out.dev[i]; out->chdev[i] = audio_out.chdev[i];} + *prate = audio_rate; + *pdacblocksize = audio_dacblocksize; + *padvance = audio_advance; + *pscheduler = audio_scheduler; +} + +void sys_save_audio_params( +int nindev, int *indev, int *chindev, +int noutdev, int *outdev, int *choutdev, +int rate, int dacblocksize, int advance, int scheduler) { + audio_in.ndev = nindev; + audio_out.ndev = noutdev; + for (int i=0; i<MAXAUDIOINDEV; i++) {audio_in.dev[i] = indev[i]; audio_in.chdev[i] = chindev[i];} + for (int i=0; i<MAXAUDIOOUTDEV; i++) {audio_out.dev[i] = outdev[i]; audio_out.chdev[i] = choutdev[i];} + audio_rate = rate; + audio_dacblocksize = dacblocksize; + audio_advance = advance; + audio_scheduler = scheduler; +} + +extern "C" void sys_open_audio2(t_audiodevs *in, t_audiodevs *out, int rate, int dacblocksize, int advance, int scheduler) { + sys_open_audio(in->ndev, in->dev, in->ndev, in->chdev, + out->ndev, out->dev, out->ndev, out->chdev, rate, dacblocksize, advance, scheduler, 1); +} + +/* init routines for any API which needs to set stuff up before any other API gets used. This is only true of OSS so far. */ +#ifdef USEAPI_OSS +void oss_init(); +#endif + +static void audio_init() { + static int initted = 0; + if (initted) return; + initted = 1; +#ifdef USEAPI_OSS + oss_init(); +#endif +} + +/* set channels and sample rate. */ +void sys_setchsr(int chin, int chout, int sr, int dacblocksize) { + int inbytes = (chin ? chin : 2) * (sys_dacblocksize*sizeof(float)); + int outbytes = (chout ? chout : 2) * (sys_dacblocksize*sizeof(float)); + if (dacblocksize != (1<<ilog2(dacblocksize))) { + dacblocksize = 1<<ilog2(dacblocksize); + post("warning: adjusting dac~blocksize to power of 2: %d", dacblocksize); + } + sys_dacblocksize = dacblocksize; + sys_schedblocksize = dacblocksize; + sys_inchannels = chin; + sys_outchannels = chout; + sys_dacsr = double(sr); + sys_advance_samples = max(int(sys_schedadvance*sys_dacsr/1000000.),sys_dacblocksize); + if (sys_soundin) freealignedbytes(sys_soundin,inbytes); + sys_soundin = (t_float *)getalignedbytes(inbytes); + memset(sys_soundin, 0, inbytes); + if (sys_soundout) freealignedbytes(sys_soundout,outbytes); + sys_soundout = (t_float *)getalignedbytes(outbytes); + memset(sys_soundout, 0, outbytes); + /* tb: modification for simd-optimized peak finding */ + if (SIMD_CHKCNT(sys_inchannels * sys_dacblocksize) && + SIMD_CHKCNT(sys_outchannels * sys_dacblocksize)) + peak_fp = peakvec_simd; + else peak_fp = peakvec; + if (sys_verbose) post("input channels = %d, output channels = %d", sys_inchannels, sys_outchannels); + canvas_resume_dsp(canvas_suspend_dsp()); +} + +/* ----------------------- public routines ----------------------- */ + +/* open audio devices (after cleaning up the specified device and channel vectors). The audio devices are "zero based" + (i.e. "0" means the first one.) We also save the cleaned-up device specification so that we + can later re-open audio and/or show the settings on a dialog window. */ +void sys_open_audio(int nindev, int *indev, int nchindev, int *chindev, int noutdev, int *outdev, int nchoutdev, +int *choutdev, int rate, int dacblocksize, int advance, int schedmode, int enable) { + int defaultchannels = SYS_DEFAULTCH; + int realinchans[MAXAUDIOINDEV], realoutchans[MAXAUDIOOUTDEV]; + char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; + int indevs = 0, outdevs = 0, canmulti = 0; + audio_getdevs(indevlist, &indevs, outdevlist, &outdevs, &canmulti, MAXNDEV, DEVDESCSIZE); + if (sys_externalschedlib) return; + if (sys_inchannels || sys_outchannels) sys_close_audio(); + if (rate < 1) rate = DEFAULTSRATE; + if (dacblocksize < 1) dacblocksize = DEFDACBLKSIZE; + if (advance <= 0) advance = DEFAULTADVANCE; + audio_init(); + /* Since the channel vector might be longer than the audio device vector, or vice versa, we fill the shorter one + in to match the longer one. Also, if both are empty, we fill in one device (the default) and two channels. */ + if (nindev == -1) { /* no input audio devices specified */ + if (nchindev == -1) { + if (indevs >= 1) { + nchindev=1; + chindev[0] = defaultchannels; + nindev = 1; + indev[0] = DEFAULTAUDIODEV; + } else nindev = nchindev = 0; + } else { + for (int i=0; i<MAXAUDIOINDEV; i++) indev[i] = i; + nindev = nchindev; + } + } else { + if (nchindev == -1) { + nchindev = nindev; + for (int i=0; i<nindev; i++) chindev[i] = defaultchannels; + } else if (nchindev > nindev) { + for (int i=nindev; i<nchindev; i++) { + if (i == 0) indev[0] = DEFAULTAUDIODEV; else indev[i] = indev[i-1] + 1; + } + nindev = nchindev; + } else if (nchindev < nindev) { + for (int i=nchindev; i<nindev; i++) { + if (i == 0) chindev[0] = defaultchannels; else chindev[i] = chindev[i-1]; + } + nindev = nchindev; + } + } + if (noutdev == -1) { /* not set */ + if (nchoutdev == -1) { + if (outdevs >= 1) { + nchoutdev=1; + choutdev[0]=defaultchannels; + noutdev=1; + outdev[0] = DEFAULTAUDIODEV; + } else nchoutdev = noutdev = 0; + } else { + for (int i=0; i<MAXAUDIOOUTDEV; i++) outdev[i] = i; + noutdev = nchoutdev; + } + } else { + if (nchoutdev == -1) { + nchoutdev = noutdev; + for (int i=0; i<noutdev; i++) choutdev[i] = defaultchannels; + } else if (nchoutdev > noutdev) { + for (int i=noutdev; i<nchoutdev; i++) { + if (i == 0) outdev[0] = DEFAULTAUDIODEV; else outdev[i] = outdev[i-1] + 1; + } + noutdev = nchoutdev; + } else if (nchoutdev < noutdev) { + for (int i=nchoutdev; i<noutdev; i++) { + if (i == 0) choutdev[0] = defaultchannels; else choutdev[i] = choutdev[i-1]; + } + noutdev = nchoutdev; + } + } + /* count total number of input and output channels */ + int inchans=0, outchans=0; + for (int i=0; i < nindev; i++) inchans += (realinchans[i] = (chindev[i] > 0 ? chindev[i] : 0)); + for (int i=0; i < noutdev; i++) outchans += (realoutchans[i] = (choutdev[i] > 0 ? choutdev[i] : 0)); + /* if no input or output devices seem to have been specified, this really means just disable audio, which we now do. */ + if (!inchans && !outchans) enable = 0; + sys_schedadvance = advance * 1000; + sys_setchsr(inchans, outchans, rate, dacblocksize); + sys_log_error(ERR_NOTHING); + if (enable) { + /* for alsa, only one device is supported; it may be open for both input and output. */ +#ifdef USEAPI_PORTAUDIO + if (sys_audioapi == API_PORTAUDIO) + pa_open_audio(inchans, outchans, rate, advance, + (noutdev > 0 ? indev[0] : 0), + (noutdev > 0 ? outdev[0] : 0), schedmode); + else +#endif +#ifdef USEAPI_JACK + if (sys_audioapi == API_JACK) + jack_open_audio((nindev > 0 ? realinchans[0] : 0), + (noutdev > 0 ? realoutchans[0] : 0), rate, schedmode); + else +#endif + if (sys_audioapi == API_OSS || sys_audioapi == API_ALSA || sys_audioapi == API_MMIO) + sys_audio()->open_audio(nindev, indev, nchindev, realinchans, noutdev, outdev, nchoutdev, realoutchans, rate, -42); + else if (sys_audioapi == API_SGI) + sys_audio()->open_audio(nindev, indev, nchindev, chindev, noutdev, outdev, nchoutdev, choutdev, rate, -42); + else if (sys_audioapi == API_ASIO) + sys_audio()->open_audio(nindev, indev, nchindev, chindev, noutdev, outdev, nchoutdev, choutdev, rate, schedmode); + else post("unknown audio API specified"); + } + sys_save_audio_params(nindev, indev, chindev, noutdev, outdev, choutdev, int(sys_dacsr), sys_dacblocksize, advance, schedmode); + if (sys_inchannels == 0 && sys_outchannels == 0) enable = 0; + audio_state = enable; + sys_vgui("set pd_whichapi %d\n", audio_isopen() ? sys_audioapi : 0); + sched_set_using_dacs(enable); + sys_update_sleepgrain(); + if (enable) { + t_atom argv[1]; + t_symbol *selector = gensym("audio_started"); + t_symbol *pd = gensym("pd"); + SETFLOAT(argv, 1.); + typedmess(pd->s_thing, selector, 1, argv); + } +} + +void sys_close_audio() { + /* jsarlo { (*/ + if (sys_externalschedlib) return; + /* } jsarlo */ + if (!audio_isopen()) return; + if (sys_audio()) sys_audio()->close_audio(); + else post("sys_close_audio: unknown API %d", sys_audioapi); + sys_inchannels = sys_outchannels = 0; + sched_set_using_dacs(0); /* tb: dsp is switched off */ +} + +/* open audio using whatever parameters were last used */ +void sys_reopen_audio() { + t_audiodevs in, out; + int rate, dacblocksize, advance, scheduler; + sys_close_audio(); + sys_get_audio_params(&in,&out,&rate, &dacblocksize, &advance, &scheduler); + sys_open_audio2(&in,&out, rate, dacblocksize, advance, scheduler); +} + +/* tb: default value of peak_fp {*/ +float peakvec(t_float* vec, t_int n, t_float cur_max) { + for (int i=0; i<n; i++) { + float f = *vec++; + if (f > cur_max) cur_max = f; + else if (-f > cur_max) cur_max = -f; + } + return cur_max; +} +/* } */ + +void sys_peakmeters() { + if (sys_inchannels) sys_inmax = peak_fp(sys_soundin, sys_inchannels * sys_dacblocksize, sys_inmax); + if (sys_outchannels) sys_outmax = peak_fp(sys_soundout,sys_outchannels * sys_dacblocksize, sys_outmax); +} + +int sys_send_dacs() { + if (sys_meters) sys_peakmeters(); + if (sys_audio()) return sys_audio()->send_dacs(); + post("unknown API"); + return 0; +} + +float sys_getsr() {return sys_dacsr;} +int sys_get_outchannels() {return sys_outchannels;} +int sys_get_inchannels() {return sys_inchannels;} + +void sys_getmeters(float *inmax, float *outmax) { + if (inmax) { + sys_meters = 1; + *inmax = sys_inmax; + *outmax = sys_outmax; + } else sys_meters = 0; + sys_inmax = sys_outmax = 0; +} + +static void audio_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) { + audio_init(); + if (sys_audio()) sys_audio()->getdevs(indevlist, nindevs, outdevlist, noutdevs, canmulti, maxndev, devdescsize); + else {*nindevs = *noutdevs = 0;} +} + +static void sys_listaudiodevs() { + char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; + int nindevs = 0, noutdevs = 0, canmulti = 0; + audio_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, &canmulti, MAXNDEV, DEVDESCSIZE); + /* To agree with command line flags, normally start at 1; but microsoft "MMIO" device list starts at 0 (the "mapper"). */ + /* (see also sys_mmio variable in s_main.c) */ + if (!nindevs) post("no audio input devices found"); + else { + post("audio input devices:"); + for (int i=0; i<nindevs; i++) post("%d. %s", i + (sys_audioapi != API_MMIO), indevlist + i * DEVDESCSIZE); + } + if (!noutdevs) post("no audio output devices found"); + else { + post("audio output devices:"); + for (int i=0; i<noutdevs; i++) post("%d. %s", i + (sys_audioapi != API_MMIO), outdevlist + i * DEVDESCSIZE); + } + post("API number %d", sys_audioapi); +} + +/* start an audio settings dialog window */ +void glob_audio_properties(t_pd *dummy, t_floatarg flongform) { + /* these are the devices you're using: */ + t_audiodevs in,out; + int rate, dacblocksize, advance, scheduler; + /* these are all the devices on your system: */ + char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; + int nindevs = 0, noutdevs = 0, canmulti = 0; + audio_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, &canmulti, MAXNDEV, DEVDESCSIZE); + ostringstream indevliststring; for (int i=0; i<nindevs; i++) indevliststring << " {" << (indevlist + i*DEVDESCSIZE) << "}"; + ostringstream outdevliststring; for (int i=0; i<noutdevs; i++) outdevliststring << " {" << (outdevlist + i*DEVDESCSIZE) << "}"; + sys_get_audio_params(&in,&out,&rate,&dacblocksize,&advance,&scheduler); + if (in.ndev > 1 || out.ndev > 1) flongform = 1; + ostringstream indevs; for (int i=0; i< in.ndev; i++) indevs << " " << in.dev [i]; + ostringstream outdevs; for (int i=0; i<out.ndev; i++) outdevs << " " << out.dev[i]; + ostringstream inchans; for (int i=0; i< in.ndev; i++) inchans << " " << in.chdev[i]; + ostringstream outchans; for (int i=0; i<out.ndev; i++) outchans << " " << out.chdev[i]; + sys_vgui("pdtk_audio_dialog {%s} {%s} {%s} {%s} {%s} {%s} %d %d %d %d %d\n", + indevliststring .str().data()+1, indevs.str().data()+1, inchans.str().data()+1, + outdevliststring.str().data()+1, outdevs.str().data()+1, outchans.str().data()+1, + rate, dacblocksize, advance, canmulti, flongform!=0); +} + +/* new values from dialog window */ +void glob_audio_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) { + int nindev=0, noutdev=0; + int newindev[4], newinchan[4], newoutdev[4], newoutchan[4]; + /* the new values the dialog came back with: */ + int newrate = atom_getintarg(16, argc, argv); + int newdacblocksize = atom_getintarg(17, argc, argv); + int newadvance = atom_getintarg(18, argc, argv); + int newschedmode = atom_getintarg(19, argc, argv); + for (int i=0; i<4; i++) { + newindev[i] = atom_getintarg(i, argc, argv); + newinchan[i] = atom_getintarg(i+4, argc, argv); + newoutdev[i] = atom_getintarg(i+8, argc, argv); + newoutchan[i] = atom_getintarg(i+12, argc, argv); + } + for (int i=0; i<4; i++) { + if (newinchan[i]) { + newindev[nindev] = newindev[i]; + newinchan[nindev] = newinchan[i]; + nindev++; + } + } + for (int i=0; i<4; i++) { + if (newoutchan[i]) { + newoutdev[noutdev] = newoutdev[i]; + newoutchan[noutdev] = newoutchan[i]; + noutdev++; + } + } + sys_close_audio(); + sys_open_audio(nindev, newindev, nindev, newinchan, + noutdev, newoutdev, noutdev, newoutchan, + newrate, newdacblocksize, newadvance, newschedmode, 1); +} + +extern void sgi_listaudiodevs(); +void sys_listdevs() { + if (sys_audioapi == API_PORTAUDIO) sys_listaudiodevs(); else +#ifdef USEAPI_JACK + if (sys_audioapi == API_JACK) jack_listdevs(); else +#endif + if (sys_audioapi == API_OSS) sys_listaudiodevs(); else + if (sys_audioapi == API_ALSA) sys_listaudiodevs(); else +#ifdef USEAPI_SGI + if (sys_audioapi == API_SGI) sgi_listaudiodevs(); else +#endif + if (sys_audioapi == API_MMIO) sys_listaudiodevs(); else + post("unknown API"); + sys_listmididevs(); +} + +void sys_setblocksize(int n) { + if (n < 1) n = 1; + if (n != (1 << ilog2(n))) post("warning: adjusting blocksize to power of 2: %d", (n = (1 << ilog2(n)))); + sys_blocksize = n; +} + +void sys_set_audio_api(int which) { + sys_audioapi = which; + if (sys_verbose) post("sys_audioapi %d", sys_audioapi); +} + +void glob_audio_setapi(t_pd *dummy, t_floatarg f) { + int newapi = int(f); + if (newapi != sys_audioapi) { + if (newapi != sys_audioapi) { + sys_close_audio(); + sys_audioapi = newapi; + /* bash device params back to default */ + audio_in.ndev = audio_out.ndev = 1; + audio_in.dev[0] = audio_out.dev[0] = DEFAULTAUDIODEV; + audio_in.chdev[0] = audio_out.chdev[0] = SYS_DEFAULTCH; + } + sched_set_using_dacs(0); +/* glob_audio_properties(0, 0); */ + } +} + +/* start or stop the audio hardware */ +void sys_set_audio_state(int onoff) { + if (onoff) { /* start */ + if (!audio_isopen()) sys_reopen_audio(); + } else { + if (audio_isopen()) sys_close_audio(); + } + sched_set_using_dacs(onoff); + sys_setscheduler(sys_getscheduler()); /* tb: reset scheduler */ + audio_state = onoff; +} + +void sys_get_audio_apis(char *buf) { + int n = 0; + strcpy(buf, "{ "); +#ifdef USEAPI_OSS + sprintf(buf + strlen(buf), "{OSS %d} ", API_OSS); n++; +#endif +#ifdef USEAPI_ASIO + sprintf(buf + strlen(buf), "{ASIO %d} ", API_ASIO); n++; +#endif +#ifdef USEAPI_MMIO + sprintf(buf + strlen(buf), "{\"standard (MMIO)\" %d} ", API_MMIO); n++; +#endif +#ifdef USEAPI_ALSA + sprintf(buf + strlen(buf), "{ALSA %d} ", API_ALSA); n++; +#endif +#ifdef USEAPI_PORTAUDIO +#ifdef __APPLE__ + sprintf(buf + strlen(buf), "{\"standard (portaudio)\" %d} ", API_PORTAUDIO); n++; +#else + sprintf(buf + strlen(buf), "{portaudio %d} ", API_PORTAUDIO); n++; +#endif +#endif +#ifdef USEAPI_SGI + sprintf(buf + strlen(buf), "{SGI %d} ", API_SGI); n++; +#endif +#ifdef USEAPI_JACK + sprintf(buf + strlen(buf), "{jack %d} ", API_JACK); n++; +#endif + strcat(buf, "}"); +} + +#ifdef USEAPI_ALSA +void alsa_putzeros(int iodev, int n); +void alsa_getzeros(int iodev, int n); +void alsa_printstate(); +#endif + +/* debugging */ +void glob_foo(void *dummy, t_symbol *s, int argc, t_atom *argv) { + t_symbol *arg = atom_getsymbolarg(0, argc, argv); + if (arg == gensym("restart")) sys_reopen_audio(); +#ifdef USEAPI_ALSA + /* what's the matter here? what should be the value of iodev??? */ + else if (arg == gensym("alsawrite")) alsa_putzeros(0, atom_getintarg(1, argc, argv)); + else if (arg == gensym("alsaread")) alsa_getzeros(0, atom_getintarg(1, argc, argv)); + else if (arg == gensym("print")) alsa_printstate(); +#endif +} + +/* tb: message-based audio configuration + * supported by vibrez.net { */ +void glob_audio_samplerate(t_pd * dummy, t_float f) { + t_audiodevs in, out; + int rate, dacblocksize, advance, scheduler; + if (f == sys_getsr()) return; + sys_get_audio_params(&in,&out,&rate, &dacblocksize, &advance, &scheduler); + sys_close_audio(); + sys_open_audio(in.ndev, in.dev, in.ndev, in.chdev, out.ndev, out.dev, out.ndev, out.chdev, + (int)f, dacblocksize, advance, scheduler, 1); +} + +void glob_audio_api(t_pd *dummy, t_float f) { + int newapi = (int)f; + sys_close_audio(); + sys_audioapi = newapi; +} + +void glob_audio_delay(t_pd *dummy, t_float f) { + t_audiodevs in,out; + int rate, dacblocksize, advance, scheduler; + if ((int)f == audio_advance) return; + sys_get_audio_params(&in,&out, &rate, &dacblocksize, &advance, &scheduler); + sys_close_audio(); + sys_open_audio(in.ndev, in.dev, in.ndev, in.chdev, out.ndev, out.dev, out.ndev, out.chdev, + rate, dacblocksize, (int) f, scheduler, 1); +} + +void glob_audio_dacblocksize(t_pd * dummy, t_float f) { + t_audiodevs in,out; + int rate, dacblocksize, advance, scheduler; + if ((int)f == audio_dacblocksize) return; + sys_get_audio_params(&in,&out, &rate, &dacblocksize, &advance, &scheduler); + sys_close_audio(); + sys_open_audio2(&in,&out, rate, (int)f, advance, scheduler); +} + +void glob_audio_scheduler(t_pd * dummy, t_float f) { + t_audiodevs in,out; + int rate, dacblocksize, advance, scheduler; + if ((int)f == sys_callbackscheduler) return; + scheduler = f!=0; + sys_get_audio_params(&in,&out, &rate, &dacblocksize, &advance, &scheduler); + sys_close_audio(); + sys_open_audio2(&in,&out, rate, dacblocksize, advance, scheduler); + if (scheduler != sys_callbackscheduler) { + if (scheduler == 1) { + post("switched to callback-based scheduler"); + } else { + post("switched to traditional scheduler"); + } + } else post("couldn't change scheduler"); +} + +void glob_audio_device(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) { + t_audiodevs in,out; + int rate, dacblocksize, advance, scheduler; + sys_get_audio_params(&in,&out, &rate, &dacblocksize, &advance, &scheduler); + out.ndev = in.ndev = (int)atom_getfloatarg(0, argc, argv); + for (int i=0; i<MAXAUDIOINDEV; i++) { + out.dev [i] = in.dev [i] = int(atom_getfloatarg(i*2+1, argc, argv)); + out.chdev[i] = in.chdev[i] = int(atom_getfloatarg(i*2+2, argc, argv)); + } + sys_close_audio(); + sys_open_audio2(&in,&out, rate, dacblocksize, advance, scheduler); +} + +void glob_audio_device_in(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) { + t_audiodevs in,out; + int rate, dacblocksize, advance, scheduler; + sys_get_audio_params(&in,&out, &rate, &dacblocksize, &advance, &scheduler); + in.ndev = (int)atom_getfloatarg(0, argc, argv); + for (int i=0; i<MAXAUDIOINDEV; i=i+2) { + in.dev [i] = atom_getintarg(i+1, argc, argv); + in.chdev[i] = atom_getintarg(i+2, argc, argv); + } + sys_close_audio(); + sys_open_audio2(&in,&out,rate, dacblocksize, advance, scheduler); +} + +void glob_audio_device_out(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) { + t_audiodevs in,out; + int rate, dacblocksize, advance, scheduler; + sys_get_audio_params(&in,&out, &rate, &dacblocksize, &advance, &scheduler); + out.ndev = (int)atom_getfloatarg(0, argc, argv); + /* i+=2 ? isn't that a bug??? */ + for (int i=0; i<MAXAUDIOOUTDEV; i+=2) { + out.dev [i] = atom_getintarg(i+1, argc, argv); + out.chdev[i] = atom_getintarg(i+2, argc, argv); + } + sys_close_audio(); + sys_open_audio2(&in,&out, rate, dacblocksize, advance, scheduler); +} + +/* some general helper functions */ +void sys_update_sleepgrain() { + sys_sleepgrain = sys_schedadvance/4; + if (sys_sleepgrain < 1000) sys_sleepgrain = 1000; + else if (sys_sleepgrain > 5000) sys_sleepgrain = 5000; +} + +/* t_audiodevs are the ones you're using; char[] are all the devices available. */ +void glob_audio_getaudioindevices(t_pd * dummy, t_symbol *s, int ac, t_atom *av) { + t_audiodevs in,out; + int rate, dacblocksize, advance, scheduler; + int nindevs = 0, noutdevs = 0, canmulti = 0; + t_atom argv[MAXNDEV]; + int f = ac ? (int)atom_getfloatarg(0,ac,av) : -1; + t_symbol *selector = gensym("audioindev"); + t_symbol *pd = gensym("pd"); + char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; + audio_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, &canmulti, MAXNDEV, DEVDESCSIZE); + sys_get_audio_params(&in,&out, &rate, &dacblocksize, &advance, &scheduler); + if (f < 0) { + for (int i=0; i<nindevs; i++) SETSTRING(argv+i,indevlist+i*DEVDESCSIZE); + typedmess(pd->s_thing, selector, nindevs, argv); + } else if (f < nindevs) { + SETSTRING(argv, indevlist + f * DEVDESCSIZE); + typedmess(pd->s_thing, selector, 1, argv); + } +} +void glob_audio_getaudiooutdevices(t_pd * dummy, t_symbol *s, int ac, t_atom *av) { + t_audiodevs in,out; + int rate, dacblocksize, advance, scheduler; + int nindevs = 0, noutdevs = 0, canmulti = 0; + t_atom argv[MAXNDEV]; + int f = ac ? (int)atom_getfloatarg(0,ac,av) : -1; + t_symbol *selector = gensym("audiooutdev"); + t_symbol *pd = gensym("pd"); + char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; + audio_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, &canmulti, MAXNDEV, DEVDESCSIZE); + sys_get_audio_params(&in,&out, &rate, &dacblocksize, &advance, &scheduler); + if (f < 0) { + for (int i=0; i<noutdevs; i++) SETSYMBOL(argv+i, gensym(outdevlist+i*DEVDESCSIZE)); + typedmess(pd->s_thing, selector, noutdevs, argv); + } else if (f < noutdevs) { + SETSTRING(argv, outdevlist + f * DEVDESCSIZE); + typedmess(pd->s_thing, selector, 1, argv); + } +} + +/* some prototypes from s_audio_portaudio.c */ +extern void pa_getcurrent_devices(); +extern void pa_getaudioininfo(t_float f); +extern void pa_getaudiooutinfo(t_float f); +extern void pa_test_setting (int ac, t_atom *av); +extern void pa_get_asio_latencies(t_float f); + +void glob_audio_getaudioininfo(t_pd * dummy, t_float f) { +#if defined(USEAPI_PORTAUDIO) && !defined(PABLIO) + if (sys_audioapi == API_PORTAUDIO) pa_getaudioininfo(f); +#endif +} +void glob_audio_getaudiooutinfo(t_pd * dummy, t_float f) { +#if defined(USEAPI_PORTAUDIO) && !defined(PABLIO) + if (sys_audioapi == API_PORTAUDIO) pa_getaudiooutinfo(f); +#endif +} +void glob_audio_testaudiosetting(t_pd * dummy, t_symbol *s, int ac, t_atom *av) { +#if defined(USEAPI_PORTAUDIO) && !defined(PABLIO) + if (sys_audioapi == API_PORTAUDIO) pa_test_setting (ac, av); +#endif +} +void glob_audio_getcurrent_devices() { +#if defined(USEAPI_PORTAUDIO) && !defined(PABLIO) + if (sys_audioapi == API_PORTAUDIO) pa_getcurrent_devices(); +#endif +} +void glob_audio_asio_latencies(t_pd * dummy, t_float f) { +#if defined(USEAPI_PORTAUDIO) && !defined(PABLIO) + if (sys_audioapi == API_PORTAUDIO) pa_get_asio_latencies(f); +#endif +} + +/* tb } */ diff --git a/desiredata/src/s_audio_alsa.c b/desiredata/src/s_audio_alsa.c new file mode 100644 index 00000000..d3d1b3f1 --- /dev/null +++ b/desiredata/src/s_audio_alsa.c @@ -0,0 +1,594 @@ +/* Copyright (c) 1997-2003 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 inputs and outputs audio using the ALSA API available on linux. */ + +/* support for ALSA pcmv2 api by Karl MacMillan<karlmac@peabody.jhu.edu> */ +/* support for ALSA MMAP noninterleaved by Winfried Ritsch, IEM */ + +#include <alsa/asoundlib.h> + +#include "m_pd.h" +#include "s_stuff.h" +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <sched.h> +#include <sys/mman.h> +#include "s_audio_alsa.h" + +/* Defines */ +#define DEBUG(x) x +#define DEBUG2(x) {x;} + +/* needed for alsa 0.9 compatibility: */ +#if (SND_LIB_MAJOR < 1) +#define ALSAAPI9 +#endif + +//static void alsa_close_audio(); +static void alsa_checkiosync(); +static void alsa_numbertoname(int iodev, char *devname, int nchar); +static int alsa_jittermax; +static void alsa_close_audio(); +#define ALSA_DEFJITTERMAX 3 + + /* 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)) + +static char *alsa_snd_buf; +static int alsa_snd_bufsize; +static int alsa_buf_samps; +static snd_pcm_status_t *alsa_status; +static int alsa_usemmap; + +t_alsa_dev alsa_indev[ALSA_MAXDEV]; +t_alsa_dev alsa_outdev[ALSA_MAXDEV]; +int alsa_nindev; +int alsa_noutdev; + +static void check_error(int err, const char *why) {if (err<0) error("%s: %s", why, snd_strerror(err));} + +static int alsaio_canmmap(t_alsa_dev *dev) { + snd_pcm_hw_params_t *hw_params; + int err1, err2; + snd_pcm_hw_params_alloca(&hw_params); + err1 = snd_pcm_hw_params_any(dev->a_handle, hw_params); + if (err1 < 0) { + check_error(err1,"Broken configuration: no configurations available"); + return 0; + } + err1 = snd_pcm_hw_params_set_access(dev->a_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); + if (err1 < 0) { + err2 = snd_pcm_hw_params_set_access(dev->a_handle, hw_params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); + } else err2 = -1; +#if 0 + error("err 1 %d (%s), err2 %d (%s)", err1, snd_strerror(err1), err2, snd_strerror(err2)); +#endif + return err1<0 && err2>=0; +} + +static int alsaio_setup(t_alsa_dev *dev, int out, int *channels, int *rate, int nfrags, int frag_size) { + int bufsizeforthis, err; + snd_pcm_hw_params_t* hw_params; + unsigned int tmp_uint; + snd_pcm_uframes_t tmp_snd_pcm_uframes; + if (sys_verbose) { + if (out) post("configuring sound output..."); + else post("configuring sound input..."); + } + /* set hardware parameters... */ + snd_pcm_hw_params_alloca(&hw_params); + /* get the default params */ + err = snd_pcm_hw_params_any(dev->a_handle, hw_params); + check_error(err, "snd_pcm_hw_params_any"); + /* try to set interleaved access */ + err = snd_pcm_hw_params_set_access(dev->a_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); + if (err < 0) return -1; + check_error(err, "snd_pcm_hw_params_set_access"); + /* Try to set 32 bit format first */ + err = snd_pcm_hw_params_set_format(dev->a_handle, hw_params, SND_PCM_FORMAT_S32); + if (err<0) { + error("PD-ALSA: 32 bit format not available - using 16"); + err = snd_pcm_hw_params_set_format(dev->a_handle, hw_params,SND_PCM_FORMAT_S16); + check_error(err, "snd_pcm_hw_params_set_format"); + dev->a_sampwidth = 2; + } else dev->a_sampwidth = 4; + if (sys_verbose) post("Sample width set to %d bytes", dev->a_sampwidth); + /* set the subformat */ + err = snd_pcm_hw_params_set_subformat(dev->a_handle, hw_params, SND_PCM_SUBFORMAT_STD); + check_error(err, "snd_pcm_hw_params_set_subformat"); + /* set the number of channels */ + tmp_uint = *channels; + err = snd_pcm_hw_params_set_channels_min(dev->a_handle, hw_params, &tmp_uint); + check_error(err, "snd_pcm_hw_params_set_channels"); + if (tmp_uint != (unsigned)*channels) post("ALSA: set input channels to %d", tmp_uint); + *channels = tmp_uint; + dev->a_channels = *channels; + /* set the sampling rate */ + err = snd_pcm_hw_params_set_rate_min(dev->a_handle, hw_params, (unsigned int *)rate, 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 */ + /* LATER try this to get a recommended period size... + right now, it trips an assertion failure in ALSA lib */ +#ifdef ALSAAPI9 + err = snd_pcm_hw_params_set_period_size_near(dev->a_handle, hw_params, (snd_pcm_uframes_t)frag_size, 0); +#else + tmp_snd_pcm_uframes = frag_size; + err = snd_pcm_hw_params_set_period_size_near(dev->a_handle, hw_params, &tmp_snd_pcm_uframes, 0); +#endif + check_error(err, "snd_pcm_hw_params_set_period_size_near (input)"); + /* set the number of periods - ie numfrags */ +#ifdef ALSAAPI9 + err = snd_pcm_hw_params_set_periods_near(dev->a_handle, hw_params, nfrags, 0); +#else + tmp_uint = nfrags; + err = snd_pcm_hw_params_set_periods_near(dev->a_handle, hw_params, &tmp_uint, 0); +#endif + check_error(err, "snd_pcm_hw_params_set_periods_near (input)"); + /* set the buffer size */ +#ifdef ALSAAPI9 + err = snd_pcm_hw_params_set_buffer_size_near(dev->a_handle, hw_params, nfrags * frag_size); +#else + tmp_snd_pcm_uframes = nfrags * frag_size; + err = snd_pcm_hw_params_set_buffer_size_near(dev->a_handle, hw_params, &tmp_snd_pcm_uframes); +#endif + check_error(err, "snd_pcm_hw_params_set_buffer_size_near (input)"); + err = snd_pcm_hw_params(dev->a_handle, hw_params); + check_error(err, "snd_pcm_hw_params (input)"); + /* set up the buffer */ + bufsizeforthis = sys_dacblocksize * dev->a_sampwidth * *channels; + if (alsa_snd_buf) { + if (alsa_snd_bufsize < bufsizeforthis) { + if (!(alsa_snd_buf = (char *)realloc(alsa_snd_buf, bufsizeforthis))) {error("out of memory"); return 0;} + memset(alsa_snd_buf, 0, bufsizeforthis); + alsa_snd_bufsize = bufsizeforthis; + } + } else { + if (!(alsa_snd_buf = (char *)malloc(bufsizeforthis))) {error("out of memory"); return 0;} + memset(alsa_snd_buf, 0, bufsizeforthis); + alsa_snd_bufsize = bufsizeforthis; + } + return 1; +} + +/* return 0 on success */ +int alsa_open_audio( +int naudioindev, int * audioindev, int nchindev, int * chindev, +int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, int rate, int dummy) { + int err, inchans = 0, outchans = 0; + char devname[512]; + int frag_size = (sys_blocksize ? sys_blocksize : ALSA_DEFFRAGSIZE); + int nfrags, i, iodev, dev2; + nfrags = int(sys_schedadvance * (float)rate / (1e6 * frag_size)); + /* save our belief as to ALSA's buffer size for later */ + alsa_buf_samps = nfrags * frag_size; + alsa_nindev = alsa_noutdev = 0; + alsa_jittermax = ALSA_DEFJITTERMAX; + if (sys_verbose) post("audio buffer set to %d", (int)(0.001 * sys_schedadvance)); + for (iodev = 0; iodev < naudioindev; iodev++) { + alsa_numbertoname(audioindev[iodev], devname, 512); + err = snd_pcm_open(&alsa_indev[alsa_nindev].a_handle, devname, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); + check_error(err, "snd_pcm_open (input)"); + if (err < 0) + continue; + alsa_indev[alsa_nindev].a_devno = audioindev[iodev]; + snd_pcm_nonblock(alsa_indev[alsa_nindev].a_handle, 1); + if (sys_verbose) + post("opened input device name %s", devname); + alsa_nindev++; + } + for (iodev = 0; iodev < naudiooutdev; iodev++) { + alsa_numbertoname(audiooutdev[iodev], devname, 512); + err = snd_pcm_open(&alsa_outdev[alsa_noutdev].a_handle, devname, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); + check_error(err, "snd_pcm_open (output)"); + if (err < 0) + continue; + alsa_outdev[alsa_noutdev].a_devno = audiooutdev[iodev]; + snd_pcm_nonblock(alsa_outdev[alsa_noutdev].a_handle, 1); + alsa_noutdev++; + } + if (!alsa_nindev && !alsa_noutdev) goto blewit; + /* If all the open devices support mmap_noninterleaved, let's call Wini's code in s_audio_alsamm.c */ + alsa_usemmap = 1; + for (iodev = 0; iodev < alsa_nindev ; iodev++) if (!alsaio_canmmap(&alsa_indev [iodev])) alsa_usemmap = 0; + for (iodev = 0; iodev < alsa_noutdev; iodev++) if (!alsaio_canmmap(&alsa_outdev[iodev])) alsa_usemmap = 0; + if (alsa_usemmap) { + post("using mmap audio interface"); + if (alsamm_open_audio(rate)) goto blewit; else return 0; + } + for (iodev = 0; iodev < alsa_nindev; iodev++) { + int channels = chindev[iodev]; + if (alsaio_setup(&alsa_indev[iodev], 0, &channels, &rate, nfrags, frag_size) < 0) goto blewit; + inchans += channels; + } + for (iodev = 0; iodev < alsa_noutdev; iodev++) { + int channels = choutdev[iodev]; + if (alsaio_setup(&alsa_outdev[iodev], 1, &channels, &rate, nfrags, frag_size) < 0) goto blewit; + outchans += channels; + } + if (!inchans && !outchans) + goto blewit; + for (iodev = 0; iodev < alsa_nindev ; iodev++) snd_pcm_prepare( alsa_indev[iodev].a_handle); + for (iodev = 0; iodev < alsa_noutdev; iodev++) snd_pcm_prepare(alsa_outdev[iodev].a_handle); + /* if duplex we can link the channels so they start together */ + for (iodev = 0; iodev < alsa_nindev; iodev++) { + for (dev2 = 0; dev2 < alsa_noutdev; dev2++) { + if (alsa_indev[iodev].a_devno == alsa_outdev[iodev].a_devno) { + snd_pcm_link(alsa_indev[iodev].a_handle,alsa_outdev[iodev].a_handle); + } + } + } + /* allocate the status variables */ + if (!alsa_status) { + err = snd_pcm_status_malloc(&alsa_status); + check_error(err, "snd_pcm_status_malloc"); + } + /* fill the buffer with silence */ + memset(alsa_snd_buf, 0, alsa_snd_bufsize); + if (outchans) { + i = (frag_size * nfrags)/sys_dacblocksize + 1; + while (i--) { + for (iodev = 0; iodev < alsa_noutdev; iodev++) + snd_pcm_writei(alsa_outdev[iodev].a_handle, alsa_snd_buf, sys_dacblocksize); + } + } else if (inchans) { + for (iodev = 0; iodev < alsa_nindev; iodev++) + if ((err = snd_pcm_start(alsa_indev[iodev].a_handle)) < 0) check_error(err, "input start failed"); + } + return 0; +blewit: + sys_inchannels = 0; + sys_outchannels = 0; + alsa_close_audio(); + return 1; +} + +void alsa_close_audio() { + int err; + if (alsa_usemmap) { + alsamm_close_audio(); + return; + } + for (int iodev=0; iodev<alsa_nindev; iodev++) { + err = snd_pcm_close(alsa_indev[iodev].a_handle); + check_error(err, "snd_pcm_close (input)"); + } + for (int iodev=0; iodev<alsa_noutdev; iodev++) { + err = snd_pcm_close(alsa_outdev[iodev].a_handle); + check_error(err, "snd_pcm_close (output)"); + } + alsa_nindev = alsa_noutdev = 0; +} + +int alsa_send_dacs() { +#ifdef DEBUG_ALSA_XFER + static int xferno = 0; + static int callno = 0; +#endif + static double timenow; + double timelast; + t_sample *fp1, *fp2; + int i, j, k, iodev, result, ch; + int chansintogo, chansouttogo; + unsigned int transfersize; + if (alsa_usemmap) return alsamm_send_dacs(); + if (!alsa_nindev && !alsa_noutdev) return SENDDACS_NO; + chansintogo = sys_inchannels; + chansouttogo = sys_outchannels; + transfersize = sys_dacblocksize; + timelast = timenow; + timenow = sys_getrealtime(); +#ifdef DEBUG_ALSA_XFER + if (timenow - timelast > 0.050) post("(%d)", int(1000 * (timenow - timelast))); + callno++; +#endif + alsa_checkiosync(); /* check I/O are in sync and data not late */ + for (iodev = 0; iodev < alsa_nindev; iodev++) { + snd_pcm_status(alsa_indev[iodev].a_handle, alsa_status); + if (snd_pcm_status_get_avail(alsa_status) < transfersize) return SENDDACS_NO; + } + for (iodev = 0; iodev < alsa_noutdev; iodev++) { + snd_pcm_status(alsa_outdev[iodev].a_handle, alsa_status); + if (snd_pcm_status_get_avail(alsa_status) < transfersize) return SENDDACS_NO; + } + /* do output */ + for (iodev = 0, fp1 = sys_soundout, ch = 0; iodev < alsa_noutdev; iodev++) { + int thisdevchans = alsa_outdev[iodev].a_channels; + int chans = (chansouttogo < thisdevchans ? chansouttogo : thisdevchans); + chansouttogo -= chans; + if (alsa_outdev[iodev].a_sampwidth == 4) { + for (i = 0; i < chans; i++, ch++, fp1 += sys_dacblocksize) + for (j = ch, k = sys_dacblocksize, fp2 = fp1; k--; j += thisdevchans, fp2++) { + float s1 = *fp2 * INT32_MAX; + ((t_alsa_sample32 *)alsa_snd_buf)[j] = CLIP32(int(s1)); + } + for (; i < thisdevchans; i++, ch++) + for (j = ch, k = sys_dacblocksize; k--; j += thisdevchans) ((t_alsa_sample32 *)alsa_snd_buf)[j] = 0; + } else { + for (i = 0; i < chans; i++, ch++, fp1 += sys_dacblocksize) + for (j = ch, k = sys_dacblocksize, fp2 = fp1; k--; j += thisdevchans, fp2++) { + int s = int(*fp2 * 32767.); + if (s > 32767) s = 32767; else if (s < -32767) s = -32767; + ((t_alsa_sample16 *)alsa_snd_buf)[j] = s; + } + for (; i < thisdevchans; i++, ch++) + for (j = ch, k = sys_dacblocksize; k--; j += thisdevchans) ((t_alsa_sample16 *)alsa_snd_buf)[j] = 0; + } + result = snd_pcm_writei(alsa_outdev[iodev].a_handle, alsa_snd_buf, transfersize); + if (result != (int)transfersize) { + #ifdef DEBUG_ALSA_XFER + if (result >= 0 || errno == EAGAIN) post("ALSA: write returned %d of %d", result, transfersize); + else error("ALSA: write: %s", snd_strerror(errno)); + post("inputcount %d, outputcount %d, outbufsize %d", + inputcount, outputcount, (ALSA_EXTRABUFFER + sys_advance_samples) * alsa_outdev[iodev].a_sampwidth * outchannels); + #endif + sys_log_error(ERR_DACSLEPT); + return SENDDACS_NO; + } + + /* zero out the output buffer */ + memset(sys_soundout, 0, sys_dacblocksize * sizeof(*sys_soundout) * + sys_outchannels); + if (sys_getrealtime() - timenow > 0.002) { + #ifdef DEBUG_ALSA_XFER + post("output %d took %d msec", callno, int(1000 * (timenow - timelast))); + #endif + timenow = sys_getrealtime(); + sys_log_error(ERR_DACSLEPT); + } + } + /* do input */ + for (iodev = 0, fp1 = sys_soundin, ch = 0; iodev < alsa_nindev; iodev++) { + int thisdevchans = alsa_indev[iodev].a_channels; + int chans = (chansintogo < thisdevchans ? chansintogo : thisdevchans); + chansouttogo -= chans; + result = snd_pcm_readi(alsa_indev[iodev].a_handle, alsa_snd_buf, transfersize); + if (result < (int)transfersize) { +#ifdef DEBUG_ALSA_XFER + if (result<0) error("snd_pcm_read %d %d: %s", callno, xferno, snd_strerror(errno)); + else post("snd_pcm_read %d %d returned only %d", callno, xferno, result); + post("inputcount %d, outputcount %d, inbufsize %d", + inputcount, outputcount, (ALSA_EXTRABUFFER + sys_advance_samples) * alsa_indev[iodev].a_sampwidth * inchannels); +#endif + sys_log_error(ERR_ADCSLEPT); + return SENDDACS_NO; + } + if (alsa_indev[iodev].a_sampwidth == 4) { + for (int i=0; i<chans; i++, ch++, fp1 += sys_dacblocksize) { + for (j = ch, k = sys_dacblocksize, fp2 = fp1; k--; j += thisdevchans, fp2++) + *fp2 = (float) ((t_alsa_sample32 *)alsa_snd_buf)[j] * (1./ INT32_MAX); + } + } else { + for (int i=0; i<chans; i++, ch++, fp1 += sys_dacblocksize) { + for (j = ch, k = sys_dacblocksize, fp2 = fp1; k--; j += thisdevchans, fp2++) + *fp2 = (float) ((t_alsa_sample16 *)alsa_snd_buf)[j] * 3.051850e-05; + } + } + } +#ifdef DEBUG_ALSA_XFER + xferno++; +#endif + if (sys_getrealtime() - timenow > 0.002) { +#ifdef DEBUG_ALSA_XFER + post("routine took %d msec", int(1000 * (sys_getrealtime() - timenow))); +#endif + sys_log_error(ERR_ADCSLEPT); + } + return SENDDACS_YES; +} + +void alsa_printstate() { + int result, iodev = 0; + snd_pcm_sframes_t indelay, outdelay; + if (sys_audioapi != API_ALSA) { + error("restart-audio: implemented for ALSA only."); + return; + } + if (sys_inchannels) { + result = snd_pcm_delay(alsa_indev[iodev].a_handle, &indelay); + if (result<0) error("snd_pcm_delay 1 failed"); else post( "in delay %d", indelay); + } + if (sys_outchannels) { + result = snd_pcm_delay(alsa_outdev[iodev].a_handle, &outdelay); + if (result<0) error("snd_pcm_delay 2 failed"); else post("out delay %d", outdelay); + } + post("sum %d (%d mod 64)", indelay + outdelay, (indelay+outdelay)%64); + post("buf samples %d", alsa_buf_samps); +} + + +void alsa_resync() { + int i, result, iodev = 0; + if (sys_audioapi != API_ALSA) { + error("restart-audio: implemented for ALSA only."); + return; + } + memset(alsa_snd_buf, 0, alsa_indev[iodev].a_sampwidth * sys_dacblocksize * sys_outchannels); + for (i = 0; i < 1000000; i++) { + result = snd_pcm_writei(alsa_outdev[iodev].a_handle, alsa_snd_buf, sys_dacblocksize); + if (result != (int)sys_dacblocksize) break; + } + post("%d written", i); +} + +void alsa_putzeros(int iodev, int n) { + int result; + memset(alsa_snd_buf, 0, alsa_outdev[iodev].a_sampwidth * sys_dacblocksize * alsa_outdev[iodev].a_channels); + for (int i=0; i<n; i++) { + result = snd_pcm_writei(alsa_outdev[iodev].a_handle, alsa_snd_buf, sys_dacblocksize); +#if 0 + if (result != sys_dacblocksize) post("result %d", result); +#endif + } + /* post ("putzeros %d", n); */ +} + +void alsa_getzeros(int iodev, int n) { + int i, result; + for (i = 0; i < n; i++) { + result = snd_pcm_readi(alsa_indev[iodev].a_handle, alsa_snd_buf, sys_dacblocksize); +#if 0 + if (result != sys_dacblocksize) + post("result %d", result); +#endif + } + /* post ("getzeros %d", n); */ +} + +/* call this only if both input and output are open */ +static void alsa_checkiosync() { + int result, giveup = 1000, alreadylogged = 0; + snd_pcm_sframes_t minphase, maxphase, thisphase, outdelay; + while (1) { + if (giveup-- <= 0) { + post("tried but couldn't sync A/D/A"); + alsa_jittermax += 1; + return; + } + minphase = 0x7fffffff; + maxphase = -0x7fffffff; + for (int iodev=0; iodev<alsa_noutdev; iodev++) { + result = snd_pcm_delay(alsa_outdev[iodev].a_handle, &outdelay); + if (result < 0) { + snd_pcm_prepare(alsa_outdev[iodev].a_handle); + result = snd_pcm_delay(alsa_outdev[iodev].a_handle, &outdelay); + } + if (result<0) { + error("output snd_pcm_delay failed: %s", snd_strerror(result)); + if (snd_pcm_status(alsa_outdev[iodev].a_handle, alsa_status)<0) error("output snd_pcm_status failed"); + else post("astate %d", snd_pcm_status_get_state(alsa_status)); + return; + } + thisphase = alsa_buf_samps - outdelay; + if (thisphase < minphase) minphase = thisphase; + if (thisphase > maxphase) maxphase = thisphase; + if (outdelay < 0) + sys_log_error(ERR_DATALATE), alreadylogged = 1; + } + for (int iodev=0; iodev<alsa_nindev; iodev++) { + result = snd_pcm_delay(alsa_indev[iodev].a_handle, &thisphase); + if (result < 0) { + snd_pcm_prepare(alsa_indev[iodev].a_handle); + result = snd_pcm_delay(alsa_indev[iodev].a_handle, &thisphase); + } + if (result < 0) { + error("output snd_pcm_delay failed: %s", snd_strerror(result)); + if (snd_pcm_status(alsa_outdev[iodev].a_handle, alsa_status) < 0) error("output snd_pcm_status failed"); + else post("astate %d", snd_pcm_status_get_state(alsa_status)); + return; + } + if (thisphase < minphase) minphase = thisphase; + if (thisphase > maxphase) maxphase = thisphase; + } + /* the "correct" position is for all the phases to be exactly equal; + but since we only make corrections sys_dacblocksize samples at a time, + we just ask that the spread be not more than 3/4 of a block. */ + if (maxphase <= minphase + (alsa_jittermax * (sys_dacblocksize / 4))) break; + if (!alreadylogged) sys_log_error(ERR_RESYNC), alreadylogged = 1; + for (int iodev=0; iodev<alsa_noutdev; iodev++) { + result = snd_pcm_delay(alsa_outdev[iodev].a_handle, &outdelay); + if (result < 0) break; + thisphase = alsa_buf_samps - outdelay; + if (thisphase > minphase + sys_dacblocksize) { + alsa_putzeros(iodev, 1); +#if DEBUGSYNC + post("putz %d %d", (int)thisphase, (int)minphase); +#endif + } + } + for (int iodev=0; iodev<alsa_nindev; iodev++) { + result = snd_pcm_delay(alsa_indev[iodev].a_handle, &thisphase); + if (result < 0) break; + if (thisphase > minphase + sys_dacblocksize) { + alsa_getzeros(iodev, 1); +#if DEBUGSYNC + post("getz %d %d", (int)thisphase, (int)minphase); +#endif + } + } + } +#if DEBUGSYNC + if (alreadylogged) post("done"); +#endif +} + +static int alsa_nnames = 0; +static char **alsa_names = 0; + +void alsa_adddev(char *name) { + if (alsa_nnames) alsa_names = (char **)t_resizebytes(alsa_names, alsa_nnames*sizeof(char *), (alsa_nnames+1)*sizeof(char *)); + else alsa_names = (char **)t_getbytes(sizeof(char *)); + alsa_names[alsa_nnames] = gensym(name)->s_name; + alsa_nnames++; +} + +static void alsa_numbertoname(int devno, char *devname, int nchar) { + int ndev = 0, cardno = -1; + while (!snd_card_next(&cardno) && cardno >= 0) ndev++; + if (devno < 2*ndev) { + if (devno & 1) snprintf(devname, nchar, "plughw:%d", devno/2); + else snprintf(devname, nchar, "hw:%d", devno/2); + } else if (devno <2*ndev + alsa_nnames) + snprintf(devname, nchar, "%s", alsa_names[devno - 2*ndev]); + else snprintf(devname, nchar, "???"); +} + +/* For each hardware card found, we list two devices, the "hard" and + "plug" one. The card scan is derived from portaudio code. */ +void alsa_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) { + int ndev = 0, cardno = -1, i, j; + *canmulti = 2; /* supports multiple devices */ + while (!snd_card_next(&cardno) && cardno >= 0) { + snd_ctl_t *ctl; + snd_ctl_card_info_t *info; + char devname[80]; + char *desc; + if (2 * ndev + 2 > maxndev) break; + /* apparently, "cardno" is just a counter; but check that here */ + if (ndev != cardno) post("oops: ALSA cards not reported in order?"); + sprintf(devname, "hw:%d", cardno); + /* post("try %s..", devname); */ + if (snd_ctl_open(&ctl, devname, 0) >= 0) { + snd_ctl_card_info_malloc(&info); + snd_ctl_card_info(ctl, info); + desc = strdup(snd_ctl_card_info_get_name(info)); + snd_ctl_card_info_free(info); + } else { + error("ALSA card scan error"); + desc = strdup("???"); + } + sprintf(indevlist + 2*ndev * devdescsize, "%s (hardware)", desc); + sprintf(indevlist + (2*ndev+1) * devdescsize, "%s (plug-in)", desc); + sprintf(outdevlist + 2*ndev * devdescsize, "%s (hardware)", desc); + sprintf(outdevlist + (2*ndev+1) * devdescsize, "%s (plug-in)", desc); + ndev++; + free(desc); + } + for (i = 0, j = 2*ndev; i < alsa_nnames; i++, j++) { + if (j >= maxndev) break; + snprintf(indevlist + j * devdescsize, devdescsize, "%s", alsa_names[i]); + } + *nindevs = *noutdevs = j; +} + +struct t_audioapi alsa_api = { + alsa_open_audio, + alsa_close_audio, + alsa_send_dacs, + alsa_getdevs, +}; diff --git a/desiredata/src/s_audio_alsa.h b/desiredata/src/s_audio_alsa.h new file mode 100644 index 00000000..986bc1f5 --- /dev/null +++ b/desiredata/src/s_audio_alsa.h @@ -0,0 +1,40 @@ +/* Copyright (c) 1997- 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. */ + + +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) * sys_dacblocksize) +#define ALSA_XFERSIZE32 (signed int)(sizeof(t_alsa_sample32) * sys_dacblocksize) +#define ALSA_MAXDEV 4 +#define ALSA_JITTER 1024 +#define ALSA_EXTRABUFFER 2048 +#define ALSA_DEFFRAGSIZE 64 +#define ALSA_DEFNFRAG 12 + +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffff +#endif + +typedef struct _alsa_dev +{ + snd_pcm_t *a_handle; + int a_devno; + int a_sampwidth; + int a_channels; + char **a_addr; + int a_synced; +} t_alsa_dev; + +extern t_alsa_dev alsa_indev[ALSA_MAXDEV]; +extern t_alsa_dev alsa_outdev[ALSA_MAXDEV]; +extern int alsa_nindev; +extern int alsa_noutdev; + +int alsamm_open_audio(int rate); +void alsamm_close_audio(void); +int alsamm_send_dacs(void); diff --git a/desiredata/src/s_audio_alsamm.c b/desiredata/src/s_audio_alsamm.c new file mode 100644 index 00000000..ef3e28a8 --- /dev/null +++ b/desiredata/src/s_audio_alsamm.c @@ -0,0 +1,889 @@ +/* Copyright (c) 1997-2003 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 audiodriverinterface inputs and outputs audio data using + the ALSA MMAP API available on linux. + this is optimized for hammerfall cards and does not make an attempt to be general + now, please adapt to your needs or let me know ... + constrains now: + - audio Card with ALSA-Driver > 1.0.3, + - alsa-device (preferable hw) with MMAP NONINTERLEAVED SIGNED-32Bit features + - up to 4 cards with has to be hardwaresynced + (winfried) +*/ +#include <alsa/asoundlib.h> +#include "m_pd.h" +#include "s_stuff.h" +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <sched.h> +#include "s_audio_alsa.h" + +/* needed for alsa 0.9 compatibility: */ +#if (SND_LIB_MAJOR < 1) +#define ALSAAPI9 +#endif +/* sample type magic ... + Hammerfall/HDSP/DSPMADI cards always 32Bit where lower 8Bit not used (played) in AD/DA, + but can have some bits set (subchannel coding) +*/ +#define ALSAMM_SAMPLEWIDTH_32 sizeof(t_alsa_sample32) + +#ifndef INT32_MAX +#define INT32_MAX 0x7fffffff +#endif + +/* maybe: + don't assume we can turn all 31 bits when doing float-to-fix; + otherwise some audio drivers (e.g. Midiman/ALSA) wrap around. + but not now on hammerfall (w) +*/ + +/* 24 Bit are used so MAX Samplevalue not INT32_MAX ??? */ +#define F32MAX 0x7fffff00 +#define CLIP32(x) (((x)>F32MAX)?F32MAX:((x) < -F32MAX)?-F32MAX:(x)) + +#define ALSAMM_FORMAT SND_PCM_FORMAT_S32 +/* + maximum of 4 devices + you can mix rme9632,hdsp9632 (18 chans) rme9652,hdsp9652 (26 chans), dsp-madi (64 chans) + if synced +*/ + +/* + we need same samplerate, buffertime and so on for + each card soo we use global vars... + time is in us, size in frames (i hope so ;-) +*/ +static unsigned int alsamm_sr = 0; +static unsigned int alsamm_buffertime = 0; +static unsigned int alsamm_buffersize = 0; + +static bool debug=0; + +/* bad style: we asume all cards give the same answer at init so we make this vars global + to have a faster access in writing reading during send_dacs */ +static snd_pcm_sframes_t alsamm_period_size; +static unsigned int alsamm_periods; +static snd_pcm_sframes_t alsamm_buffer_size; + +/* if more than this sleep detected, should be more than periodsize/samplerate ??? */ +static double sleep_time; + +/* now we just sum all inputs/outputs of used cards to a global count + and use them all + ... later we should just use some channels of each card for pd + so we reduce the overhead of using alsways all channels, + and zero the rest once at start, + because rme9652 and hdsp forces us to use all channels + in mmap mode... + +Note on why: + normally hdsp and dspmadi can handle channel + count from one to all since they can switch on/off + the dma for them to reduce pci load, but this is only + implemented in alsa low level drivers for dspmadi now and maybe fixed for hdsp in future +*/ + +static int alsamm_inchannels = 0; +static int alsamm_outchannels = 0; + +/* Defines */ + #define WATCH_PERIODS 90 + static int in_avail[WATCH_PERIODS]; + static int out_avail[WATCH_PERIODS]; + static int in_offset[WATCH_PERIODS]; + static int out_offset[WATCH_PERIODS]; + static int out_cm[WATCH_PERIODS]; + static char *outaddr[WATCH_PERIODS]; + static char *inaddr[WATCH_PERIODS]; + static int xruns_watch[WATCH_PERIODS]; + static int broken_opipe; + + static int dac_send = 0; + static int alsamm_xruns = 0; + +static void show_availist() { + for(int i=1; i<WATCH_PERIODS; i++){ + post("%2d:avail i=%7d %s o=%7d(%5d), offset i=%7d %s o=%7d, ptr i=%12p o=%12p, %d xruns ", + i,in_avail[i],(out_avail[i] != in_avail[i])? "!=" : "==" , out_avail[i],out_cm[i], + in_offset[i],(out_offset[i] != in_offset[i])? "!=" : "==" , out_offset[i], + inaddr[i], outaddr[i], xruns_watch[i]); + } +} + +/* protos */ +static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, int *chs); +static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int playback); +static int alsamm_start(); +static int alsamm_stop(); + +/* for debugging attach output of alsa mesages to stdout stream */ +snd_output_t* alsa_stdout; + +static void check_error(int err, const char *why) {if (err < 0) error("%s: %s", why, snd_strerror(err));} + +int alsamm_open_audio(int rate) { + int err; + snd_pcm_hw_params_t* hw_params; + snd_pcm_sw_params_t* sw_params; + /* fragsize is an old concept now use periods, used to be called fragments. */ + /* Be aware in ALSA periodsize can be in bytes, where buffersize is in frames, + but sometimes buffersize is in bytes and periods in frames, crazy alsa... + ...we use periodsize and buffersize in frames */ + int i; + snd_pcm_hw_params_alloca(&hw_params); + snd_pcm_sw_params_alloca(&sw_params); + /* see add_devname */ + /* first have a look which cards we can get and set up device infos for them */ + /* init some structures */ + for(i=0;i < ALSA_MAXDEV;i++){ + alsa_indev[i].a_synced=alsa_outdev[i].a_synced=0; + alsa_indev[i].a_channels=alsa_outdev[i].a_channels=-1; /* query defaults */ + } + alsamm_inchannels = 0; + alsamm_outchannels = 0; + /* opening alsa debug channel */ + err = snd_output_stdio_attach(&alsa_stdout, stdout, 0); + if (err < 0) { + check_error(err,"attaching alsa debug Output to stdout failed"); + /* return; no so bad ... and never should happe */ + } + /* + Weak failure prevention: + first card found (out then in) is used as a reference for parameter, + so this set the globals and other cards hopefully dont change them + */ + alsamm_sr = rate; + /* set the asked buffer time (alsa buffertime in us)*/ + alsamm_buffertime = alsamm_buffersize = 0; + if(sys_blocksize == 0) + alsamm_buffertime = sys_schedadvance; + else + alsamm_buffersize = sys_blocksize; + if(sys_verbose) + post("syschedadvance=%d us(%d Samples)so buffertime max should be this=%d" + "or sys_blocksize=%d (samples) to use buffersize=%d", + sys_schedadvance,sys_advance_samples,alsamm_buffertime, + sys_blocksize,alsamm_buffersize); + alsamm_periods = 0; /* no one wants periods setting from command line ;-) */ + for(i=0;i<alsa_noutdev;i++) { + /* post("open audio out %d, of %lx, %d",i,&alsa_device[i], + alsa_outdev[i].a_handle); */ + err = set_hwparams(alsa_outdev[i].a_handle, hw_params, &(alsa_outdev[i].a_channels)); + if (err<0) {check_error(err,"playback device hwparam_set error:"); continue;} + err = set_swparams(alsa_outdev[i].a_handle, sw_params,1); + if (err<0) {check_error(err,"playback device swparam_set error:"); continue;} + alsamm_outchannels += alsa_outdev[i].a_channels; + alsa_outdev[i].a_addr = (char **)malloc(sizeof(char *)*alsa_outdev[i].a_channels); + if(alsa_outdev[i].a_addr == NULL) { + check_error(errno,"playback device outaddr allocation error:"); + continue; + } + memset(alsa_outdev[i].a_addr, 0, sizeof(char*) * alsa_outdev[i].a_channels); + post("playback device with %d channels and buffer_time %d us opened", + alsa_outdev[i].a_channels, alsamm_buffertime); + } + for(i=0;i<alsa_nindev;i++) { + if(sys_verbose) post("capture card %d:--------------------",i); + if((err = set_hwparams(alsa_indev[i].a_handle, hw_params, &(alsa_indev[i].a_channels))) < 0) { + check_error(err,"capture device hwparam_set error:"); + continue; + } + alsamm_inchannels += alsa_indev[i].a_channels; + if((err = set_swparams(alsa_indev[i].a_handle, sw_params,0)) < 0){ + check_error(err,"capture device swparam_set error:"); + continue; + } + alsa_indev[i].a_addr = (char **)malloc(sizeof(char*)*alsa_indev[i].a_channels); + if(alsa_indev[i].a_addr == NULL){ + check_error(errno,"capture device inaddr allocation error:"); + continue; + } + memset(alsa_indev[i].a_addr, 0, sizeof(char*) * alsa_indev[i].a_channels); + if(sys_verbose) post("capture device with %d channels and buffertime %d us opened", alsa_indev[i].a_channels,alsamm_buffertime); + } + /* check for linked handles of input for each output*/ + for(int i=0; i<(alsa_noutdev < alsa_nindev ? alsa_noutdev:alsa_nindev); i++) { + if (alsa_outdev[i].a_devno == alsa_indev[i].a_devno) { + if ((err = snd_pcm_link(alsa_indev[i].a_handle, alsa_outdev[i].a_handle)) == 0) { + alsa_indev[i].a_synced = alsa_outdev[i].a_synced = 1; + if(sys_verbose) post("Linking in and outs of card %d",i); + } else check_error(err,"could not link in and outs"); + } + } + /* some globals */ + sleep_time = (float) alsamm_period_size/ (float) alsamm_sr; + if (debug) { + /* start ---------------------------- */ + if(sys_verbose) post("open_audio: after dacsend=%d (xruns=%d)done",dac_send,alsamm_xruns); + alsamm_xruns = dac_send = 0; /* reset debug */ + /* start alsa in open or better in send_dacs once ??? we will see */ + for(i=0;i<alsa_noutdev;i++) snd_pcm_dump(alsa_outdev[i].a_handle, alsa_stdout); + for(i=0;i<alsa_nindev;i++) snd_pcm_dump( alsa_indev[i].a_handle, alsa_stdout); + fflush(stdout); + } + sys_setchsr(alsamm_inchannels, alsamm_outchannels, alsamm_sr, sys_dacblocksize); + alsamm_start(); + /* report success */ + return 0; +} + +void alsamm_close_audio() { + int i,err; + if(debug&&sys_verbose) post("closing devices"); + alsamm_stop(); + for(i=0;i< alsa_noutdev;i++) { + //if(debug&&sys_verbose) post("unlink audio out %d, of %lx",i,used_outdevice[i]); + if(alsa_outdev[i].a_synced != 0){ + if((err = snd_pcm_unlink(alsa_outdev[i].a_handle)) < 0) check_error(err, "snd_pcm_unlink (output)"); + alsa_outdev[i].a_synced = 0; + } + if((err = snd_pcm_close(alsa_outdev[i].a_handle)) <= 0) check_error(err, "snd_pcm_close (output)"); + if(alsa_outdev[i].a_addr) { + free(alsa_outdev[i].a_addr); + alsa_outdev[i].a_addr = NULL; + } + alsa_outdev[i].a_channels = 0; + } + for(i=0;i< alsa_nindev;i++) { + err = snd_pcm_close(alsa_indev[i].a_handle); + if(sys_verbose) check_error(err, "snd_pcm_close (input)"); + if(alsa_indev[i].a_addr){ + free(alsa_indev[i].a_addr); + alsa_indev[i].a_addr = NULL; + } + alsa_indev[i].a_channels = 0; + } + alsa_nindev = alsa_noutdev = 0; + if(debug) { + if(sys_verbose) post("close_audio: after dacsend=%d (xruns=%d)done",dac_send,alsamm_xruns); + alsamm_xruns = dac_send = 0; + } +} + +/* ------- PCM INITS --------------------------------- */ +static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params,int *chs) { +#ifndef ALSAAPI9 + unsigned int rrate; + int err, dir; + /* choose all parameters */ + err = snd_pcm_hw_params_any(handle, params); + if (err < 0) { + check_error(err,"Broken configuration: no configurations available"); + return err; + } + /* set the nointerleaved read/write format */ + err = snd_pcm_hw_params_set_access(handle, params, + SND_PCM_ACCESS_MMAP_NONINTERLEAVED); + if (err >= 0) { + if(debug&&sys_verbose) post("Access type %s available","SND_PCM_ACCESS_MMAP_NONINTERLEAVED"); + } + else{ + check_error(err,"No Accesstype SND_PCM_ACCESS_MMAP_NONINTERLEAVED"); + return err; + } + /* set the sample format */ + err = snd_pcm_hw_params_set_format(handle, params, ALSAMM_FORMAT); + if (err < 0) { + check_error(err,"Sample format not available for playback"); + return err; + } + if(debug&&sys_verbose) post("Setting format to %s",snd_pcm_format_name(ALSAMM_FORMAT)); + /* first check samplerate since channels numbers are samplerate dependend (double speed) */ + /* set the stream rate */ + rrate = alsamm_sr; + if(debug&&sys_verbose) post("Samplerate request: %i Hz",rrate); + dir=-1; + err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, &dir); + if (err < 0) { + check_error(err,"Rate not available"); + return err; + } + if (rrate != alsamm_sr) { + post("Warning: rate %iHz doesn't match requested %iHz", rrate,alsamm_sr); + alsamm_sr = rrate; + } + else + if(sys_verbose) + post("Samplerate is set to %iHz",alsamm_sr); + /* Info on channels */ + { + int maxchs,minchs,channels = *chs; + if((err = snd_pcm_hw_params_get_channels_max(params, + (unsigned int *)&maxchs)) < 0){ + check_error(err,"Getting channels_max not available"); + return err; + } + if((err = snd_pcm_hw_params_get_channels_min(params, + (unsigned int *)&minchs)) < 0){ + check_error(err,"Getting channels_min not available"); + return err; + } + if(debug&&sys_verbose) post("Getting channels:min=%d, max= %d for request=%d",minchs,maxchs,channels); + if(channels < 0)channels=maxchs; + if(channels > maxchs)channels = maxchs; + if(channels < minchs)channels = minchs; + if(channels != *chs) post("requested channels=%d but used=%d",*chs,channels); + *chs = channels; + if(debug&&sys_verbose) post("trying to use channels: %d",channels); + } + /* set the count of channels */ + err = snd_pcm_hw_params_set_channels(handle, params, *chs); + if (err < 0) { + check_error(err,"Channels count not available"); + return err; + } + /* testing for channels */ + if((err = snd_pcm_hw_params_get_channels(params,(unsigned int *)chs)) < 0) + check_error(err,"Get channels not available"); + else if(debug&&sys_verbose) post("When setting channels count and got %d",*chs); + /* if buffersize is set use this instead buffertime */ + if(alsamm_buffersize > 0) { + if(debug&&sys_verbose) post("hw_params: ask for max buffersize of %d samples", (unsigned int) alsamm_buffersize); + alsamm_buffer_size = alsamm_buffersize; + err = snd_pcm_hw_params_set_buffer_size_near(handle, params, (unsigned long *)&alsamm_buffer_size); + if (err < 0) { + check_error(err,"Unable to set max buffer size"); + return err; + } + } + else { + if(alsamm_buffertime <= 0) /* should never happen, but use 20ms */ + alsamm_buffertime = 20000; + if(debug&&sys_verbose) post("hw_params: ask for max buffertime of %d ms", (unsigned int) (alsamm_buffertime*0.001) ); + err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &alsamm_buffertime, &dir); + if (err < 0) { + check_error(err,"Unable to set max buffer time"); + return err; + } + } + err = snd_pcm_hw_params_get_buffer_time(params, + (unsigned int *)&alsamm_buffertime, &dir); + if (err < 0) { + check_error(err,"Unable to get buffer time"); + return err; + } + if(debug&&sys_verbose) post("hw_params: got buffertime to %f ms", float(alsamm_buffertime*0.001)); + err = snd_pcm_hw_params_get_buffer_size(params, (unsigned long *)&alsamm_buffer_size); + if (err < 0) { + check_error(err,"Unable to get buffer size"); + return err; + } + if(debug&&sys_verbose) post("hw_params: got buffersize to %d samples",(int) alsamm_buffer_size); + err = snd_pcm_hw_params_get_period_size(params, (unsigned long *)&alsamm_period_size, &dir); + if (err > 0) { + check_error(err,"Unable to get period size"); + return err; + } + if(debug&&sys_verbose) post("Got period size of %d", (int) alsamm_period_size); + { + unsigned int pmin,pmax; + err = snd_pcm_hw_params_get_periods_min(params, &pmin, &dir); + if (err > 0) { + check_error(err,"Unable to get period size"); + return err; + } + err = snd_pcm_hw_params_get_periods_min(params, &pmax, &dir); + if (err > 0) { + check_error(err,"Unable to get period size"); + return err; + } + /* use maximum of periods */ + if( alsamm_periods <= 0) + alsamm_periods = pmax; + alsamm_periods = (alsamm_periods > pmax)?pmax:alsamm_periods; + alsamm_periods = (alsamm_periods < pmin)?pmin:alsamm_periods; + err = snd_pcm_hw_params_set_periods(handle, params, alsamm_periods, dir); + if (err > 0) { + check_error(err,"Unable to set periods"); + return err; + } + err = snd_pcm_hw_params_get_periods(params, &pmin, &dir); + if (err > 0) { + check_error(err,"Unable to get periods"); + return err; + } + if(debug&&sys_verbose) post("Got periods of %d, where periodsmin=%d, periodsmax=%d",alsamm_periods,pmin,pmax); + } + /* write the parameters to device */ + err = snd_pcm_hw_params(handle, params); + if (err < 0) { + check_error(err,"Unable to set hw params"); + return err; + } +#endif /* ALSAAPI9 */ + return 0; +} + +static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams, int playback) { +#ifndef ALSAAPI9 + int err; + snd_pcm_uframes_t ps,ops; + snd_pcm_uframes_t bs,obs; + /* get the current swparams */ + err = snd_pcm_sw_params_current(handle, swparams); + if (err < 0) { + check_error(err,"Unable to determine current swparams for playback"); + return err; + } + /* AUTOSTART: start the transfer on each write/commit ??? */ + snd_pcm_sw_params_get_start_threshold(swparams, &obs); + err = snd_pcm_sw_params_set_start_threshold(handle, swparams, 0U); + if (err < 0) { + check_error(err,"Unable to set start threshold mode"); + return err; + } + snd_pcm_sw_params_get_start_threshold(swparams, &bs); + if(debug&&sys_verbose) post("sw_params: got start_thresh_hold= %d (was %d)",(int) bs,(int)obs); + /* AUTOSTOP: never stop the machine */ + snd_pcm_sw_params_get_stop_threshold(swparams, &obs); + err = snd_pcm_sw_params_set_stop_threshold(handle, swparams, (snd_pcm_uframes_t)-1); + if (err < 0) { + check_error(err,"Unable to set stop threshold mode"); + return err; + } + snd_pcm_sw_params_get_stop_threshold(swparams, &bs); + if(debug&&sys_verbose) post("sw_params: set stop_thresh_hold= %d (was %d)", (int) bs,(int)obs); + /* AUTOSILENCE: silence if overrun.... */ + snd_pcm_sw_params_get_silence_threshold (swparams, &ops); + if ((err = snd_pcm_sw_params_set_silence_threshold (handle, swparams, alsamm_period_size)) < 0) { + check_error (err,"cannot set silence threshold for"); + return -1; + } + snd_pcm_sw_params_get_silence_threshold (swparams, &ps); + if(debug&&sys_verbose) post("sw_params: set silence_threshold = %d (was %d)", (int) ps,(int)ops); + snd_pcm_sw_params_get_silence_size (swparams, &ops); + if ((err = snd_pcm_sw_params_set_silence_size(handle, swparams, alsamm_period_size)) < 0) { + check_error (err,"cannot set silence size for"); + return -1; + } + snd_pcm_sw_params_get_silence_size (swparams, &ps); + if(debug&&sys_verbose) post("sw_params: set silence_size = %d (was %d)", (int) ps,(int)ops); + /* AVAIL: allow the transfer when at least period_size samples can be processed */ + snd_pcm_sw_params_get_avail_min(swparams, &ops); + err = snd_pcm_sw_params_set_avail_min(handle, swparams, sys_dacblocksize/2); + if (err < 0) { + check_error(err,"Unable to set avail min for"); + return err; + } + snd_pcm_sw_params_get_avail_min(swparams, &ps); + if(debug&&sys_verbose) post("sw_params: set avail_min= %d (was %d)", (int) ps, (int) ops); + /* ALIGN: align all transfers to 1 sample */ + snd_pcm_sw_params_get_xfer_align(swparams, &ops); + err = snd_pcm_sw_params_set_xfer_align(handle, swparams, 1); + if (err < 0) { + check_error(err,"Unable to set transfer align for playback"); + return err; + } + snd_pcm_sw_params_get_xfer_align(swparams, &ps); + if(debug&&sys_verbose) post("sw_params: set xfer_align = %d (was %d)", (int) ps, (int) ops); + /* write the parameters to the playback device */ + err = snd_pcm_sw_params(handle, swparams); + if (err < 0) { + check_error(err,"Unable to set sw params"); + return err; + } + if(debug&&sys_verbose) post("set sw finished"); +#else + post("alsa: need version 1.0 or above for mmap operation"); +#endif /* ALSAAPI9 */ + return 0; +} + +/* ALSA Transfer helps */ + +/* xrun_recovery is called if time to late or error + Note: use outhandle if synced i/o + the devices are linked so prepare + has only be called on out, + hopefully resume too... +*/ +static int xrun_recovery(snd_pcm_t *handle, int err) { + if (debug) alsamm_xruns++; /* count xruns */ + if (err == -EPIPE) { /* under-run */ + err = snd_pcm_prepare(handle); + if (err < 0) check_error(err,"Can't recovery from underrun, prepare failed."); + err = snd_pcm_start(handle); + if (err < 0) check_error(err,"Can't start when recover from underrun."); + return 0; + } else if (err == -ESTRPIPE) { + while ((err = snd_pcm_resume(handle)) == -EAGAIN) + sleep(1); /* wait until the suspend flag is released */ + if (err < 0) { + err = snd_pcm_prepare(handle); + if (err<0) check_error(err,"Can't recovery from suspend, prepare failed."); + err = snd_pcm_start(handle); + if (err<0) check_error(err,"Can't start when recover from underrun."); + } + return 0; + } + return err; +} + +/* note that snd_pcm_avail has to be called before using this funtion */ +static int alsamm_get_channels(snd_pcm_t *dev, snd_pcm_uframes_t *avail, snd_pcm_uframes_t *offset, int nchns, char **addr) { + int err = 0; + const snd_pcm_channel_area_t *mm_areas; + if (nchns > 0 && avail != NULL && offset != NULL) { + if ((err = snd_pcm_mmap_begin(dev, &mm_areas, offset, avail)) < 0){ + check_error(err,"setmems: begin_mmap failure ???"); + return err; + } + for (int chn = 0; chn < nchns; chn++) { + const snd_pcm_channel_area_t *a = &mm_areas[chn]; + addr[chn] = (char *) a->addr + ((a->first + a->step * *offset) / 8); + } + return err; + } + return -1; +} + +static int alsamm_start() { + int err = 0; + int devno; + int chn; + /* first prepare for in/out */ + for(devno=0; devno<alsa_noutdev; devno++) { + snd_pcm_uframes_t offset, avail; + t_alsa_dev *dev = &alsa_outdev[devno]; + /* snd_pcm_prepare also in xrun, but cannot harm here */ + if ((err = snd_pcm_prepare (dev->a_handle)) < 0) { + check_error (err,"outcard prepare error for playback"); + return err; + } + offset = 0; + avail = snd_pcm_avail_update(dev->a_handle); + if (avail != (snd_pcm_uframes_t) alsamm_buffer_size) { + check_error (avail,"full buffer not available at start"); + } + /* cleaning out mmap buffer before start */ + if(debug&&sys_verbose) post("start: set mems for avail=%d,offset=%d at buffersize=%d",avail,offset,alsamm_buffer_size); + if(avail > 0) { + int comitted = 0; + if ((err = alsamm_get_channels(dev->a_handle, &avail, &offset, dev->a_channels,dev->a_addr)) < 0) { + check_error(err,"setting initial out channelspointer failure ?"); + continue; + } + for (chn = 0; chn < dev->a_channels; chn++) + memset(dev->a_addr[chn],0,avail*ALSAMM_SAMPLEWIDTH_32); + comitted = snd_pcm_mmap_commit (dev->a_handle, offset, avail); + avail = snd_pcm_avail_update(dev->a_handle); + if(debug&&sys_verbose) post("start: now channels cleared, out with avail=%d, offset=%d,comitted=%d",avail,offset,comitted); + } + /* now start, should be autostarted */ + avail = snd_pcm_avail_update(dev->a_handle); + if(debug&&sys_verbose) post("start: finish start, out with avail=%d, offset=%d",avail,offset); + /* we have no autostart so anyway start*/ + if ((err = snd_pcm_start (dev->a_handle)) < 0) { + check_error (err,"could not start playback"); + } + } + for(devno = 0;devno < alsa_nindev;devno++){ + snd_pcm_uframes_t ioffset, iavail; + t_alsa_dev *dev = &alsa_indev[devno]; + /* if devices are synced then dont need to prepare + hopefully dma in aereas allready filled correct by the card */ + if(dev->a_synced == 0) { + if ((err = snd_pcm_prepare (dev->a_handle)) < 0) { + check_error (err,"incard prepare error for capture"); + /* return err;*/ + } + } + ioffset = 0; + iavail = snd_pcm_avail_update (dev->a_handle); + /* cleaning out mmap buffer before start */ + if (debug) post("start in: set in mems for avail=%d,offset=%d at buffersize=%d",iavail,ioffset,alsamm_buffer_size); + if (iavail > (snd_pcm_uframes_t) 0) { + if (debug) post("empty buffer not available at start, since avail %d != %d buffersize", iavail, alsamm_buffer_size); + if ((err = alsamm_get_channels(dev->a_handle, &iavail, &ioffset, dev->a_channels,dev->a_addr)) < 0) { + check_error(err,"getting in channelspointer failure ????"); + continue; + } + snd_pcm_mmap_commit (dev->a_handle, ioffset, iavail); + iavail = snd_pcm_avail_update (dev->a_handle); + if (debug) post("start in now avail=%d",iavail); + } + if (debug) post("start: init inchannels with avail=%d, offset=%d",iavail,ioffset); + /* if devices are synced then dont need to start */ + /* start with autostart , but anyway start */ + if(dev->a_synced == 0) { + if ((err = snd_pcm_start (dev->a_handle)) < 0) { + check_error (err,"could not start capture"); + continue; + } + } + } + return err; +} + +static int alsamm_stop() { + int err = 0; + /* first stop in... */ + for(int devno=0; devno<alsa_nindev; devno++) { + t_alsa_dev *dev = &alsa_indev[devno]; + if(sys_verbose) post("stop in device %d",devno); + int err = snd_pcm_drop(dev->a_handle); + if (err<0) check_error(err,"channel flush for capture failed"); + } + /* then outs */ + for(int devno=0; devno<alsa_noutdev;devno++) { + t_alsa_dev *dev = &alsa_outdev[devno]; + if(sys_verbose) post("stop out device %d",devno); + int err = snd_pcm_drop(dev->a_handle); + if (err<0) check_error(err,"channel flush for playback failed"); + } + if (debug) show_availist(); + return err; +} + +/* ---------- ADC/DAC tranfer in the main loop ------- */ +/* I see: (a guess as a documentation) + all DAC data is in sys_soundout array with + sys_dacblocksize (mostly 64) for each channels which + if we have more channels opened then dac-channels = sys_outchannels + we have to zero (silence them), which should be done once. + +Problems to solve: + + a) Since in ALSA MMAP, the MMAP reagion can change (dont ask me why) + we have to do it each cycle or we say on RME HAMMERFALL/HDSP/DSPMADI + it never changes to it once. so maybe we can do it once in open + + b) we never know if inputs are synced and zero them if not, + except we use the control interface to check for, but this is + a systemcall we cannot afford in RT Loops so we just dont + and if not it will click... users fault ;-))) +*/ + +int alsamm_send_dacs() { + static double timenow,timelast; + t_sample *fpo, *fpi, *fp1, *fp2; + int i, err, devno; + snd_pcm_sframes_t size; + snd_pcm_sframes_t commitres; + snd_pcm_state_t state; + snd_pcm_sframes_t ooffset, oavail; + snd_pcm_sframes_t ioffset, iavail; + /* unused channels should be zeroed out on startup (open) and stay this */ + int inchannels = sys_inchannels; + int outchannels = sys_outchannels; + timelast = sys_getrealtime(); + if (debug) { + if(dac_send++ < 0) + post("dac send called in %d, out %d, xrun %d",inchannels,outchannels, alsamm_xruns); + if(alsamm_xruns && (alsamm_xruns % 1000) == 0) + post("1000 xruns accoured"); + if(dac_send < WATCH_PERIODS){ + out_cm[dac_send] = -1; + in_avail[dac_send] = out_avail[dac_send] = -1; + in_offset[dac_send] = out_offset[dac_send] = -1; + outaddr[dac_send] = inaddr[dac_send] = NULL; + xruns_watch[dac_send] = alsamm_xruns; + } + } + if (!inchannels && !outchannels) return SENDDACS_NO; + /* here we should check if in and out samples are here. + but, the point is if out samples available also in sample should, + so we dont make a precheck of insamples here and let outsample check be the + the first of the forst card. + */ + /* OUTPUT Transfer */ + fpo = sys_soundout; + for(devno = 0;devno < alsa_noutdev;devno++){ + t_alsa_dev *dev = &alsa_outdev[devno]; + snd_pcm_t *out = dev->a_handle; + int ochannels =dev->a_channels; + /* how much samples available ??? */ + oavail = snd_pcm_avail_update(out); + /* only one reason i can think about, + the driver stopped and says broken pipe + so this should not happen if we have enough stopthreshhold + but if try to restart with next commit + */ + if (oavail < 0) { + if (debug) broken_opipe++; + err = xrun_recovery(out, -EPIPE); + if (err < 0) { + check_error(err,"otavail<0 recovery failed"); + return SENDDACS_NO; + } + oavail = snd_pcm_avail_update(out); + } + /* check if we are late and have to (able to) catch up */ + /* xruns will be ignored since you cant do anything since already happend */ + state = snd_pcm_state(out); + if (state == SND_PCM_STATE_XRUN) { + err = xrun_recovery(out, -EPIPE); + if (err < 0) { + check_error(err,"DAC XRUN recovery failed"); + return SENDDACS_NO; + } + oavail = snd_pcm_avail_update(out); + } else if (state == SND_PCM_STATE_SUSPENDED) { + err = xrun_recovery(out, -ESTRPIPE); + if (err < 0) { + check_error(err,"DAC SUSPEND recovery failed"); + return SENDDACS_NO; + } + oavail = snd_pcm_avail_update(out); + } + if(debug && dac_send < WATCH_PERIODS){ + out_avail[dac_send] = oavail; + } + /* we only transfer transfersize of bytes request, + this should only happen on first card otherwise we got a problem :-(()*/ + if(oavail < sys_dacblocksize) return SENDDACS_NO; + /* transfer now */ + size = sys_dacblocksize; + fp1 = fpo; + ooffset = 0; + /* since this can go over a buffer boundery we maybe need two steps to + transfer (normally when buffersize is a multiple of transfersize + this should never happen) */ + while (size > 0) { + int chn; + snd_pcm_sframes_t oframes; + oframes = size; + err = alsamm_get_channels(out, (unsigned long *)&oframes, (unsigned long *)&ooffset,ochannels,dev->a_addr); + if(debug && dac_send < WATCH_PERIODS){ + out_offset[dac_send] = ooffset; + outaddr[dac_send] = (char *) dev->a_addr[0]; + } + if (err < 0) { + if ((err = xrun_recovery(out, err)) < 0) { + check_error(err,"MMAP begins avail error"); + break; /* next card please */ + } + } + /* transfer into memory */ + for (chn = 0; chn < ochannels; chn++) { + t_alsa_sample32 *buf = (t_alsa_sample32 *)dev->a_addr[chn]; + /* osc(buf, oframes, (dac_send%1000 < 500)?-100.0:-10.0,440,&(indexes[chn])); */ + for (i = 0, fp2 = fp1 + chn*sys_dacblocksize; i < oframes; i++,fp2++) { + float s1 = *fp2 * F32MAX; + /* better but slower, better never clip ;-) + buf[i]= CLIP32(s1); */ + buf[i]= ((int) s1 & 0xFFFFFF00); + *fp2 = 0.0; + } + } + commitres = snd_pcm_mmap_commit(out, ooffset, oframes); + if (commitres < 0 || commitres != oframes) { + if ((err = xrun_recovery(out, commitres >= 0 ? -EPIPE : commitres)) < 0) { + check_error(err,"MMAP commit error"); + return SENDDACS_NO; + } + } + if(debug && dac_send < WATCH_PERIODS) out_cm[dac_send] = oframes; + fp1 += oframes; + size -= oframes; + } /* while size */ + fpo += ochannels*sys_dacblocksize; + }/* for devno */ + fpi = sys_soundin; /* star first card first channel */ + for(devno = 0;devno < alsa_nindev;devno++){ + t_alsa_dev *dev = &alsa_indev[devno]; + snd_pcm_t *in = dev->a_handle; + int ichannels = dev->a_channels; + iavail = snd_pcm_avail_update(in); + if (iavail < 0) { + err = xrun_recovery(in, iavail); + if (err < 0) { + check_error(err,"input avail update failed"); + return SENDDACS_NO; + } + iavail=snd_pcm_avail_update(in); + } + state = snd_pcm_state(in); + if (state == SND_PCM_STATE_XRUN) { + err = xrun_recovery(in, -EPIPE); + if (err < 0) { + check_error(err,"ADC XRUN recovery failed"); + return SENDDACS_NO; + } + iavail=snd_pcm_avail_update(in); + } else if (state == SND_PCM_STATE_SUSPENDED) { + err = xrun_recovery(in, -ESTRPIPE); + if (err < 0) { + check_error(err,"ADC SUSPEND recovery failed"); + return SENDDACS_NO; + } + iavail=snd_pcm_avail_update(in); + } + /* only transfer full transfersize or nothing */ + if(iavail < sys_dacblocksize){ + return SENDDACS_NO; + } + size = sys_dacblocksize; + fp1 = fpi; + ioffset = 0; + /* since sysdata can go over a driver buffer boundery we maybe need two steps to + transfer (normally when buffersize is a multiple of transfersize + this should never happen) */ + while(size > 0) { + int chn; + snd_pcm_sframes_t iframes = size; + err = alsamm_get_channels(in, (unsigned long *)&iframes, (unsigned long *)&ioffset,ichannels,dev->a_addr); + if (err < 0){ + if ((err = xrun_recovery(in, err)) < 0) { + check_error(err,"MMAP begins avail error"); + return SENDDACS_NO; + } + } + if(debug && dac_send < WATCH_PERIODS){ + in_avail[dac_send] = iavail; + in_offset[dac_send] = ioffset; + inaddr[dac_send] = dev->a_addr[0]; + } + /* transfer into memory */ + for (chn = 0; chn < ichannels; chn++) { + t_alsa_sample32 *buf = (t_alsa_sample32 *) dev->a_addr[chn]; + for (i = 0, fp2 = fp1 + chn*sys_dacblocksize; i < iframes; i++,fp2++) { + /* mask the lowest bits, since subchannels info can make zero samples nonzero */ + *fp2 = (float) ((t_alsa_sample32) (buf[i] & 0xFFFFFF00)) + * (1.0 / (float) INT32_MAX); + } + } + commitres = snd_pcm_mmap_commit(in, ioffset, iframes); + if (commitres < 0 || commitres != iframes) { + post("please never"); + if ((err = xrun_recovery(in, commitres >= 0 ? -EPIPE : commitres)) < 0) { + check_error(err,"MMAP synced in commit error"); + return SENDDACS_NO; + } + } + fp1 += iframes; + size -= iframes; + } + fpi += ichannels*sys_dacblocksize; + } /* for out devno < alsamm_outcards*/ + if ((timenow = sys_getrealtime()) > (timelast + sleep_time)) { + if(debug && dac_send < 10 && sys_verbose) + post("slept %f > %f + %f (=%f)", timenow,timelast,sleep_time,(timelast + sleep_time)); + return (SENDDACS_SLEPT); + } + return SENDDACS_YES; +} + +/* extra debug info */ +void alsamm_showstat(snd_pcm_t *handle) { + int err; + snd_pcm_status_t *status; + snd_pcm_status_alloca(&status); + if ((err = snd_pcm_status(handle, status)) < 0) { + check_error(err, "Get Stream status error"); + return; + } + snd_pcm_status_dump(status, alsa_stdout); +} diff --git a/desiredata/src/s_audio_asio.cpp b/desiredata/src/s_audio_asio.cpp new file mode 100644 index 00000000..fde2891d --- /dev/null +++ b/desiredata/src/s_audio_asio.cpp @@ -0,0 +1,1014 @@ +/* Copyright (c) 2004, Tim Blechmann and others + * supported by vibrez.net + * For information on usage and redistribution, and for a DISCLAIMER OF ALL + * WARRANTIES, see the file, "LICENSE.txt" in this distribution. */ + +/* native ASIO interface for windows + * adapted from hostsample.cpp (ASIO SDK) + */ + +#ifdef MSW +#include "windows.h" /* for application window handle */ +#define IEEE754_64FLOAT 1 +#else +#error This is for MS Windows (Intel CPU architecture) only!! +#endif + +#ifdef _MSC_VER +#pragma warning( disable : 4091 ) +#endif + +#include "m_pd.h" +extern "C" { +#include "s_stuff.h" +#include "m_simd.h" +} + +#include "asio.h" /* steinberg's header file */ +#include "asiodrivers.h" /* ASIODrivers class */ +#include "asiosys.h" +#include "pthread.h" +#include "stdio.h" /* for sprintf */ + +#include <time.h> +#include <sys/timeb.h> + +#include "assert.h" +#define ASSERT assert + + +/* fast float to integer conversion adapted from Erik de Castro Lopo */ +#define _ISOC9X_SOURCE 1 +#define _ISOC99_SOURCE 1 +#define __USE_ISOC9X 1 +#define __USE_ISOC99 1 +#include "math.h" + +// seconds to wait for driver to respond +#define DRIVERWAIT 1 + +#define ASIODEBUG + +/* public function prototypes */ +// extern "C" void asio_open_audio(int naudioindev, int *audioindev, int nchindev, +// int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, int srate, int scheduler); +extern "C" void asio_close_audio(); +extern "C" void asio_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize); +extern "C" int asio_send_dacs(); + +/* asio callback prototypes for traditional scheduling*/ +static void asio_bufferSwitch(long db_idx, ASIOBool directprocess); +static void asio_sampleRateDidChange(ASIOSampleRate srate); +static long asio_messages(long selector, long value, void* message, double* opt); +static ASIOTime *asio_bufferSwitchTimeInfo(ASIOTime *params, long db_idx, ASIOBool directprocess); + +/* asio callback prototypes for callback-based scheduling*/ +static void asio_bufferSwitch_cb(long db_idx, ASIOBool directprocess); +static void asio_sampleRateDidChange_cb(ASIOSampleRate srate); +static long asio_messages_cb(long selector, long value, void* message, double* opt); +static ASIOTime *asio_bufferSwitchTimeInfo_cb(ASIOTime *params, long db_idx, ASIOBool directprocess); + +static void float32tofloat32(void* inbuffer, void* outbuffer, long frames); +static void float32tofloat32_S(void* inbuffer, void* outbuffer, long frames); +static void float32tofloat64(void* inbuffer, void* outbuffer, long frames); +static void float64tofloat32(void* inbuffer, void* outbuffer, long frames); +static void float32toInt16(void* inbuffer, void* outbuffer, long frames); +static void Int16tofloat32(void* inbuffer, void* outbuffer, long frames); +static void float32toInt24(void* inbuffer, void* outbuffer, long frames); +static void Int24tofloat32(void* inbuffer, void* outbuffer, long frames); +static void float32toInt32(void* inbuffer, void* outbuffer, long frames); +static void Int32tofloat32(void* inbuffer, void* outbuffer, long frames); +static void float32toInt16_S(void* inbuffer, void* outbuffer, long frames); +static void Int16tofloat32_S(void* inbuffer, void* outbuffer, long frames); +static void float32toInt24_S(void* inbuffer, void* outbuffer, long frames); +static void Int24tofloat32_S(void* inbuffer, void* outbuffer, long frames); +static void float32toInt32_S(void* inbuffer, void* outbuffer, long frames); +static void Int32tofloat32_S(void* inbuffer, void* outbuffer, long frames); +void asio_close_audio(void); + +typedef void converter_t(void* inbuffer, void* outbuffer, long frames); + +/* sample converting helper functions: + * - global send / receive functions + * - sample conversion functions (adapted from ASIOConvertSamples.cpp */ +static converter_t *asio_converter_send (ASIOSampleType format); +static converter_t *asio_converter_receive (ASIOSampleType format); + +/* pointers to the converter functions of each channel are stored here */ +static converter_t **asio_converter = NULL; + +/* function to get sample width of data according to ASIOSampleType */ +static int asio_get_samplewidth(ASIOSampleType format); + +/* that's the sample width in bytes (per output channel) - + * it's only for silence when stopping the driver.... (please find a better solution) */ +static int *asio_samplewidth = NULL; + + +/* some local helper functions */ +static void prepare_asio_drivernames(); + +/* system dependent helper functions */ +static unsigned long get_sys_reference_time(); + +/* global storage */ +static ASIODriverInfo * asio_driver = NULL; +static ASIOBufferInfo * asio_bufferinfo = NULL; +static ASIOChannelInfo* asio_channelinfo = NULL; +static AsioTimeInfo * asio_timerinfo = NULL; +static ASIOCallbacks asio_callbacks; +extern AsioDrivers * asioDrivers; /* declared in asiodrivers.cpp */ + +static char ** asio_drivernames = NULL; + +static ASIOSampleRate asio_srate; +static long asio_inchannels; +static long asio_outchannels; + +static long asio_minbufsize; +static long asio_maxbufsize; +static long asio_prefbufsize; +static long asio_granularity; +static unsigned char asio_useoutputready; +static long asio_inputlatency; +static long asio_outputlatency; + +static long asio_bufsize; /* hardware buffer size */ +static long asio_ticks_per_callback; + +unsigned long sys_reftime; + +/* ringbuffer stuff */ +static t_sample ** asio_ringbuffer = NULL; /* ringbuffers */ +static int asio_ringbuffer_inoffset; /* ringbuffer(in) pointer offset for dac */ +static int asio_ringbuffer_outoffset; /* ringbuffer(out) pointer offset */ +static int asio_ringbuffer_length; /* latency - hardware latency in samples*/ + +/* i hope we can remove this to use callback based dsp scheduling */ +static pthread_mutex_t asio_ringbuf_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t asio_ringbuf_cond = PTHREAD_COND_INITIALIZER; + +/* some global definitions: */ +#define ASIOVERSION 2 /* i hope we are compatible with asio 2 */ + +/* definitions from s_audio.c ... it should be save to use them */ +#define DEVDESCSIZE 80 +#define MAXNDEV 20 + +/* from m_sched.c: */ +extern "C" double sys_time_per_dsp_tick; +extern "C" double sys_time; + + +/**************************************************************************/ +/* some function pointers for eventual fast copying when SIMD is possible */ + +static void (*copyblock)(t_sample *dst,t_sample *src,int n); +static void (*zeroblock)(t_sample *dst,int n); +static t_int *(*clipblock)(t_int *w); + +static void copyvec_nrm(t_sample *dst,t_sample *src,int n) { memcpy(dst,src,n*sizeof(t_sample)); } +static void zerovec_nrm(t_sample *dst,int n) { memset(dst,0,n*sizeof(t_sample)); } + +/*************************************************************************/ + + +/* open asio interface */ +/* todo: some more error messages */ +void asio_open_audio(int naudioindev, int *audioindev, int nchindev, +int *chindev, int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, int srate, int scheduler) { + ASIOError status = ASE_OK; + ASIOBufferInfo * buffers = NULL; + int i; + int channels; + +#ifdef IEEE754_64FLOAT + asio_srate=(ASIOSampleRate)srate; +#else + sprintf(asio_srate,"%8d",srate); +#endif + /* check, if the driver is still running */ + if(asio_driver) asio_close_audio(); + /* check, if we use the first asio device */ + prepare_asio_drivernames(); + asioDrivers->getDriverNames(asio_drivernames,MAXNDEV); + + try { + asioDrivers->loadDriver(asio_drivernames[*audioindev]); + } + catch(...) { + error("ASIO: Error loading driver"); + goto bailout; + } + /* initialize ASIO */ + asio_driver = (ASIODriverInfo*) getbytes (sizeof(ASIODriverInfo)); + asio_driver->asioVersion = ASIOVERSION; + asio_driver->sysRef = GetDesktopWindow(); + status = ASIOInit(asio_driver); +#ifdef ASIODEBUG + post("sysRef: %x", asio_driver->sysRef); + post("asioversion: %d", asio_driver->asioVersion); + post("driverversion: %d", asio_driver->driverVersion); + post("name: %s", asio_driver->name); +#endif + + switch (status) { + if(status) post("error: %s", asio_driver->errorMessage); + case ASE_NotPresent: error("ASIO: ASE_NotPresent"); goto bailout; + case ASE_NoMemory: error("ASIO: ASE_NoMemory"); goto bailout; + case ASE_HWMalfunction: error("ASIO: ASE_HWMalfunction"); goto bailout; + } +#ifdef ASIODEBUG + post("ASIO initialized successfully"); +#endif + + + /* query driver */ + status = ASIOGetChannels(&asio_inchannels, &asio_outchannels); + if(status != ASE_OK) { + error("ASIO: Couldn't get channel count"); + goto bailout; + } + +#ifdef ASIODEBUG + post ("ASIOGetChannels\tinputs: %d, outputs: %d", asio_inchannels, + asio_outchannels); +#endif + + sys_inchannels = *chindev <= asio_inchannels ? *chindev : asio_inchannels; + sys_outchannels = *choutdev <= asio_outchannels ? *choutdev : asio_outchannels; + channels = sys_inchannels + sys_outchannels; + status = ASIOGetBufferSize(&asio_minbufsize, &asio_maxbufsize, &asio_prefbufsize, &asio_granularity); + if(status != ASE_OK) { + error("ASIO: Couldn't get buffer size"); + goto bailout; + } +#ifdef ASIODEBUG + post ("ASIOGetBufferSize\tmin: %d, max: %d, preferred: %d, granularity: " + "%d", asio_minbufsize, asio_maxbufsize, asio_prefbufsize, + asio_granularity); +#endif + + /* todo: buffer size hardcoded to asio hardware */ + asio_bufsize = asio_prefbufsize; + if (scheduler) { + if ( asio_bufsize % sys_dacblocksize == 0 ) { + /* use callback scheduler */ + sys_setscheduler(1); + asio_ticks_per_callback = asio_bufsize / sys_dacblocksize; + post("ASIO: using callback-based scheduler"); + } + } else post("ASIO: using traditional scheduler"); + /* try to set sample rate */ + if(ASIOCanSampleRate( asio_srate ) != ASE_OK) { + error ("Samplerate not supported, using default"); +#ifdef IEEE754_64FLOAT + asio_srate = (ASIOSampleRate)44100.0; +#else + sprintf(&asio_srate,"%8d",44100); +#endif + srate=44100; + } + + status = ASIOSetSampleRate( asio_srate ); + if(status != ASE_OK) +#ifdef IEEE754_64FLOAT + post("Setting ASIO sample rate to %lg failed... is the device in slave sync mode?", (double)asio_srate); +#else + post("Setting ASIO sample rate to %s failed... is the device in slave sync mode?", asio_srate); +#endif + + if(ASIOOutputReady() == ASE_OK) + asio_useoutputready = 1; + else + asio_useoutputready = 0; + + + /* set callbacks */ + if(sys_callbackscheduler) { + asio_callbacks.bufferSwitch = &asio_bufferSwitch_cb; + asio_callbacks.sampleRateDidChange = &asio_sampleRateDidChange_cb; + asio_callbacks.asioMessage = &asio_messages_cb; + asio_callbacks.bufferSwitchTimeInfo = &asio_bufferSwitchTimeInfo_cb; + } else { + asio_callbacks.bufferSwitch = &asio_bufferSwitch; + asio_callbacks.sampleRateDidChange = &asio_sampleRateDidChange; + asio_callbacks.asioMessage = &asio_messages; + asio_callbacks.bufferSwitchTimeInfo = &asio_bufferSwitchTimeInfo; + } + /* prepare, create and set up buffers */ + asio_bufferinfo = (ASIOBufferInfo*) getbytes (channels*sizeof(ASIOBufferInfo)); + asio_channelinfo = (ASIOChannelInfo*) getbytes(channels*sizeof(ASIOChannelInfo)); + if (!(asio_bufferinfo && asio_channelinfo)) { + error("ASIO: couldn't allocate buffer or channel info"); + goto bailout; + } + + for (i = 0; i != sys_inchannels + sys_outchannels; ++i) { + if (i < sys_outchannels) { + asio_bufferinfo[i].isInput = ASIOFalse; + asio_bufferinfo[i].channelNum = i; + asio_bufferinfo[i].buffers[0] = asio_bufferinfo[i].buffers[1] = 0; + } else { + asio_bufferinfo[i].isInput = ASIOTrue; + asio_bufferinfo[i].channelNum = i - sys_outchannels; + asio_bufferinfo[i].buffers[0] = asio_bufferinfo[i].buffers[1] = 0; + } + } + status = ASIOCreateBuffers(asio_bufferinfo, sys_inchannels + sys_outchannels, asio_bufsize, &asio_callbacks); + if(status != ASE_OK) { + error("ASIO: couldn't allocate buffers"); + goto bailout; + } +#ifdef ASIODEBUG + post("ASIO: buffers allocated"); +#endif + + asio_converter = (converter_t **)getbytes(channels * sizeof (converter_t *)); + asio_samplewidth = (int *)getbytes((sys_outchannels + sys_inchannels) * sizeof (int)); + for (i = 0; i != sys_outchannels + sys_inchannels; ++i) { + asio_channelinfo[i].channel = asio_bufferinfo[i].channelNum; + asio_channelinfo[i].isInput = asio_bufferinfo[i].isInput; + ASIOGetChannelInfo(&asio_channelinfo[i]); + +#ifdef ASIODEBUG + post("ASIO: channel %d type %d", i, asio_channelinfo[i].type); +#endif + asio_samplewidth[i] = asio_get_samplewidth(asio_channelinfo[i].type); + if (i < sys_outchannels) asio_converter[i] = asio_converter_send( asio_channelinfo[i].type); + else asio_converter[i] = asio_converter_receive(asio_channelinfo[i].type); + } + + /* get latencies */ + ASIOGetLatencies(&asio_inputlatency, &asio_outputlatency); +#ifdef ASIODEBUG + post("ASIO: input latency: %d, output latency: %d",asio_inputlatency, asio_outputlatency); +#endif + + + /* we need a ringbuffer if we use the traditional scheduler */ + if (!sys_callbackscheduler) { + /* a strange way to find the least common multiple, but works, since sys_dacblocksize (expt 2 x) */ + asio_ringbuffer_length = asio_bufsize * sys_dacblocksize; + while ( !(asio_ringbuffer_length % sys_dacblocksize) && !(asio_ringbuffer_length % asio_bufsize)) { + asio_ringbuffer_length /= 2; + } + asio_ringbuffer_length *= 2; +#ifdef ASIODEBUG + post("ASIO: ringbuffer size: %d",asio_ringbuffer_length); +#endif + /* allocate ringbuffer */ + asio_ringbuffer = (t_sample**) getbytes (channels * sizeof (t_sample*)); + for (i = 0; i != channels; ++i) { + asio_ringbuffer[i] = (t_sample*)getalignedbytes(asio_ringbuffer_length * sizeof (t_sample)); + if (!asio_ringbuffer[i]) + error("ASIO: couldn't allocate ASIO ringbuffer"); + memset(asio_ringbuffer[i], 0, asio_ringbuffer_length * sizeof (t_sample)); + } + /* initialize ringbuffer pointers */ + asio_ringbuffer_inoffset = asio_ringbuffer_outoffset = 0; + } + if(ASIOStart() != ASE_OK) goto bailout; + /* set block copy/zero/clip functions */ + if(SIMD_CHKCNT(sys_dacblocksize) && simd_runtime_check()) { + // urgh... ugly cast + copyblock = (void (*)(t_sample *,t_sample *,int))©vec_simd; + zeroblock = &zerovec_simd; + clipblock = &clip_perf_simd; + } else { + copyblock = ©vec_nrm; + zeroblock = &zerovec_nrm; + clipblock = &clip_perform; + } + + post("ASIO: started"); + return; + +bailout: + if(status) post("error: %s", asio_driver->errorMessage); + post("ASIO: couldn't start"); + asio_close_audio(); + return; +} + + + +/* stop asio, free buffers and close asio interface */ +void asio_close_audio() { + if (asio_driver) { + pthread_cond_broadcast(&asio_ringbuf_cond); + + ASIOError status; + int channels = sys_inchannels + sys_outchannels; + int i; + + if(asio_useoutputready) { + // the DMA buffers would be played past ASIOStop + // -> clear output buffers and notify driver +#if 0 + if(asio_ringbuffer) { + // slow, blocking method + for(i = 0; i != sys_outchannels; ++i) + zerovec_simd(asio_ringbuffer[i], asio_ringbuffer_length); + // wait for bufferswitch to process silence (twice) + pthread_cond_wait(&asio_ringbuf_cond, &asio_ringbuf_mutex); + for(i = 0; i != sys_outchannels; ++i) memset(asio_ringbuffer[i], 0, asio_ringbuffer_length * sizeof (t_sample)); + pthread_cond_wait(&asio_ringbuf_cond, &asio_ringbuf_mutex); + } +#else + // direct method - clear both hardware buffers + if(asio_bufferinfo && asio_samplewidth) { + for(i = 0; i < sys_outchannels; ++i) { + long bytes = asio_bufsize*asio_samplewidth[i]; + memset(asio_bufferinfo[i].buffers[0],0,bytes); + memset(asio_bufferinfo[i].buffers[1],0,bytes); + } + } + // notify driver + status = ASIOOutputReady(); +#endif + } + + status = ASIOStop(); + if(status == ASE_OK) post("ASIO: stopped"); + status = ASIODisposeBuffers(); + try { + // ASIOExit can crash if driver not really running + status = ASIOExit(); + } catch(...) {} + // deallocate all memory + if(asio_ringbuffer) { + for(i = 0; i < channels; i++) + if(asio_ringbuffer[i]) freealignedbytes(asio_ringbuffer[i],asio_ringbuffer_length * sizeof (t_sample)); + freebytes(asio_ringbuffer, channels * sizeof (t_sample *)); + asio_ringbuffer = NULL; + } + + if(asio_bufferinfo) { + freebytes(asio_bufferinfo, channels * sizeof (ASIOBufferInfo)); + asio_bufferinfo = NULL; + } + if(asio_channelinfo) { + freebytes(asio_channelinfo, channels * sizeof (ASIOChannelInfo)); + asio_channelinfo = NULL; + } + if(asio_converter) { + freebytes(asio_converter, channels * sizeof (converter_t *)); + asio_converter = NULL; + } + if(asio_samplewidth) { + freebytes(asio_samplewidth, (sys_outchannels + sys_inchannels) * sizeof (int)); + asio_samplewidth = NULL; + } + freebytes(asio_driver, sizeof (ASIODriverInfo)); + asio_driver = NULL; + /* leave the scheduler and return to traditional mode */ + if (sys_callbackscheduler) sys_setscheduler(0); + } +} + +void asio_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) { + prepare_asio_drivernames(); + *canmulti = 0; /* we will only support one asio device */ + *nindevs = *noutdevs = (int)asioDrivers->getDriverNames(asio_drivernames, maxndev); + for(int i = 0; i!= *nindevs; ++i) { + sprintf(indevlist + i * devdescsize, "%s", asio_drivernames[i]); + sprintf(outdevlist + i * devdescsize, "%s", asio_drivernames[i]); + } +} + +/* called on every dac~ send */ +int asio_send_dacs() { + t_sample * sp; /* sample pointer */ + int i, j; + double timenow; + double timeref = sys_getrealtime(); +#ifdef ASIODEBUG + if (!asio_driver) { + static int written = 0; + if(written%100 == 0) error("ASIO not running"); + written++; + return SENDDACS_NO; + } + +#endif + + /* send sound to ringbuffer */ + sp = sys_soundout; + for (i = 0; i < sys_outchannels; i++) { + t_float lo = -1.f; + t_float hi = 1.f; + t_int clipargs[6]; + clipargs[1] = (t_int)sp; + clipargs[2] = (t_int)(asio_ringbuffer[i] + asio_ringbuffer_inoffset); + clipargs[3] = (t_int)&lo; + clipargs[4] = (t_int)&hi; + clipargs[5] = (t_int)sys_dacblocksize; + clipblock(clipargs); + zeroblock(sp,sys_dacblocksize); + sp+=sys_dacblocksize; + } + /* get sound from ringbuffer */ + sp = sys_soundin; + for (j = 0; j < sys_inchannels; j++) { +#if 0 + /* we should be able to read from the ringbuffer on a different position + * to reduce latency for asio buffer sizes that aren't multiples of 64... */ + int offset = asio_bufsize + sys_dacblocksize; + offset += sys_dacblocksize - offset % sys_dacblocksize; + if (asio_ringbuffer_inoffset < offset) { + memcpy(sp, asio_ringbuffer[i+j] + asio_ringbuffer_length + + asio_ringbuffer_inoffset - offset, 64 *sizeof(t_sample)); + } else memcpy(sp, asio_ringbuffer[i+j] + asio_ringbuffer_inoffset - offset, 64*sizeof(t_sample)); +#else + /* working but higher latency */ + copyblock(sp, asio_ringbuffer[i+j] + asio_ringbuffer_inoffset,sys_dacblocksize); +#endif + sp+=sys_dacblocksize; + } + asio_ringbuffer_inoffset += sys_dacblocksize; +#if 1 + // blocking method + if (asio_ringbuffer_inoffset >= asio_ringbuffer_outoffset + asio_bufsize) { + struct timespec tm; + _timeb tmb; + _ftime(&tmb); + tm.tv_nsec = tmb.millitm*1000000; + tm.tv_sec = tmb.time+DRIVERWAIT; // delay + if(pthread_cond_timedwait(&asio_ringbuf_cond, &asio_ringbuf_mutex, &tm) == ETIMEDOUT) { + error("ASIO: ASIO driver non-responsive! - closing"); + asio_close_audio(); + return SENDDACS_SLEPT; + } + if (asio_ringbuffer_inoffset == asio_ringbuffer_length) { + asio_ringbuffer_outoffset = 0; + asio_ringbuffer_inoffset = 0; + } else asio_ringbuffer_outoffset += asio_bufsize; + } + if ((timenow = sys_getrealtime()) - timeref > 0.002) { + return SENDDACS_SLEPT; + } +#else + // non-blocking... let PD wait -> doesn't work! + if (asio_ringbuffer_inoffset >= asio_ringbuffer_outoffset + asio_bufsize) return SENDDACS_NO; + if (asio_ringbuffer_inoffset == asio_ringbuffer_length) { + asio_ringbuffer_outoffset = 0; + asio_ringbuffer_inoffset = 0; + } else asio_ringbuffer_outoffset += asio_bufsize; +#endif + return SENDDACS_YES; +} + +/* buffer switch callback */ +static void asio_bufferSwitch(long db_idx, ASIOBool directprocess) { + ASIOTime time; +#ifdef ASIODEBUG + static int written = 0; + if(written == 0) { + post("ASIO: asio_bufferSwitch_cb"); + written = 1; + } +#endif + memset (&time, 0, sizeof (time)); + /* todo: do we need to syncronize with other media ??? */ + asio_bufferSwitchTimeInfo(&time, db_idx, directprocess); +} + +/* sample rate change callback */ +static void asio_sampleRateDidChange(ASIOSampleRate srate) { + asio_srate = srate; +#ifdef ASIODEBUG + post("sample rate changed"); +#endif +} + +/* asio messaging callback */ +static long asio_messages(long selector, long value, void* message, double* opt) { + switch (selector) { + case kAsioSelectorSupported: + if (value == kAsioResetRequest || value == kAsioSupportsTimeInfo) return 1L; + return 0L; + case kAsioEngineVersion: + return ASIOVERSION; + case kAsioResetRequest: + /* how to handle this without changing the dsp scheduler? */ + post("ASIO: Reset request"); + return 1L; + case kAsioBufferSizeChange: + /* todo */ + post("ASIO: Buffer size changed"); + sys_reopen_audio(); + return 1L; + case kAsioResyncRequest: + post("ASIO: Resync request"); + return 0L; + case kAsioLatenciesChanged: + /* we are not handling the latencies atm */ + return 0L; + case kAsioSupportsTimeInfo: + return 1L; + case kAsioSupportsTimeCode: + /* we don't support that atm */ + return 0L; + } + return 0L; +} + +static ASIOTime *asio_bufferSwitchTimeInfo(ASIOTime *params, long db_idx, ASIOBool directprocess) { + /* todo: i'm not sure if we'll have to synchronize with other media ... probably yes ... */ + /* sys_reftime = get_sys_reference_time(); */ + /* perform the processing */ + int timeout = sys_dacblocksize * (float)asio_ticks_per_callback / (float) sys_dacsr * 1e6; + if (sys_timedlock(timeout) == ETIMEDOUT) /* we're late */ { + post("timeout %d", timeout); + sys_log_error(ERR_SYSLOCK); + return 0; + } + for (long i = 0; i < sys_outchannels + sys_inchannels; i++) { + if(asio_converter[i]) + if (asio_bufferinfo[i].isInput != ASIOTrue) { + asio_converter[i](asio_ringbuffer[i]+asio_ringbuffer_outoffset, + asio_bufferinfo[i].buffers[db_idx], asio_bufsize); + } + else /* these are the input channels */ { + asio_converter[i](asio_bufferinfo[i].buffers[db_idx], + asio_ringbuffer[i]+asio_ringbuffer_outoffset, asio_bufsize); + } + } + pthread_cond_broadcast(&asio_ringbuf_cond); + sys_unlock(); + if(asio_useoutputready) ASIOOutputReady(); + return 0L; /* time info!!! */ +} + +/* get system reference time on both platforms */ +static unsigned long get_sys_reference_time() { +#if WINDOWS + return timeGetTime(); +#elif MAC + static const double twoRaisedTo32 = 4294967296.; + UnsignedWide ys; + Microseconds(&ys); + double r = ((double)ys.hi * twoRaisedTo32 + (double)ys.lo); + return (unsigned long)(r / 1000.); +#endif +} + +/* sample converting helper functions */ +static converter_t *asio_converter_send(ASIOSampleType format) { +#ifdef ASIODEBUG + /* post("ASIO: Sample Type %d", format); */ +#endif + switch (format) { + case ASIOSTInt16LSB: return float32toInt16; + case ASIOSTInt24LSB: return float32toInt24; // used for 20 bits as well + case ASIOSTInt32LSB: return float32toInt32; + case ASIOSTInt16MSB: return float32toInt16_S; + case ASIOSTInt24MSB: return float32toInt24_S; // used for 20 bits as well + case ASIOSTInt32MSB: return float32toInt32_S; + case ASIOSTFloat32LSB:return float32tofloat32; // IEEE 754 32 bit float, as found on Intel x86 architecture + case ASIOSTFloat32MSB:return float32tofloat32_S; + case ASIOSTFloat64LSB:return float32tofloat64; // IEEE 754 64 bit double float, as found on Intel x86 architecture + case ASIOSTFloat64MSB: + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + case ASIOSTInt32LSB16: // 32 bit data with 16 bit alignment + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + case ASIOSTInt32MSB16: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + default: + post("Output sample Type %d not supported, yet!!!",format); + return NULL; + } +} + +static converter_t *asio_converter_receive (ASIOSampleType format) { +#ifdef ASIODEBUG + /* post("ASIO: Sample Type %d", format); */ +#endif + switch (format) { + case ASIOSTInt16LSB: return Int16tofloat32; + case ASIOSTInt24LSB: return Int24tofloat32; // used for 20 bits as well + case ASIOSTInt32LSB: return Int32tofloat32; + case ASIOSTInt16MSB: return Int16tofloat32_S; + case ASIOSTInt24MSB: return Int24tofloat32_S; // used for 20 bits as well + case ASIOSTInt32MSB: return Int32tofloat32_S; + case ASIOSTFloat32MSB:return float32tofloat32_S; // IEEE 754 32 bit float, as found on Intel x86 architecture + case ASIOSTFloat32LSB:return float32tofloat32; // IEEE 754 32 bit float, as found on Intel x86 architecture + case ASIOSTFloat64LSB:return float64tofloat32; // IEEE 754 64 bit double float, as found on Intel x86 architecture + case ASIOSTFloat64MSB: + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + case ASIOSTInt32LSB16: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32LSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32LSB24: // 32 bit data with 24 bit alignment + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + case ASIOSTInt32MSB16: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB18: // 32 bit data with 18 bit alignment + case ASIOSTInt32MSB20: // 32 bit data with 20 bit alignment + case ASIOSTInt32MSB24: // 32 bit data with 24 bit alignment + default: + post("Input sample Type %d not supported, yet!!!",format); + return NULL; + } +} + +static int asio_get_samplewidth(ASIOSampleType format) { + switch (format) { + case ASIOSTInt16LSB: case ASIOSTInt16MSB: return 2; + case ASIOSTInt24LSB: case ASIOSTInt24MSB: return 3; + case ASIOSTFloat32LSB:case ASIOSTFloat32MSB: + case ASIOSTInt32LSB: case ASIOSTInt32MSB: + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + case ASIOSTInt32LSB16: + case ASIOSTInt32LSB18: + case ASIOSTInt32LSB20: + case ASIOSTInt32LSB24: + // these are used for 32 bit data buffer, with different alignment of the data inside + // 32 bit PCI bus systems can more easily used with these + case ASIOSTInt32MSB16: + case ASIOSTInt32MSB18: + case ASIOSTInt32MSB20: + case ASIOSTInt32MSB24: + return 4; + case ASIOSTFloat64MSB: + case ASIOSTFloat64LSB: + return 8; + default: + post("Input sample Type %d not supported, yet!!!",format); + return 0; + } +} + +/* dithering algo taken from Portaudio ASIO implementation */ +/************************************************************* +** Calculate 2 LSB dither signal with a triangular distribution. +** Ranged properly for adding to a 32 bit integer prior to >>15. +*/ +#define DITHER_BITS (15) +#define DITHER_SCALE (1.0f / ((1<<DITHER_BITS)-1)) +inline static long triangulardither() { + static unsigned long previous = 0; + static unsigned long randSeed1 = 22222; + static unsigned long randSeed2 = 5555555; + long current, highPass; +/* Generate two random numbers. */ + randSeed1 = (randSeed1 * 196314165) + 907633515; + randSeed2 = (randSeed2 * 196314165) + 907633515; +/* Generate triangular distribution about 0. */ + current = (((long)randSeed1)>>(32-DITHER_BITS)) + (((long)randSeed2)>>(32-DITHER_BITS)); + /* High pass filter to reduce audibility. */ + highPass = current - previous; + previous = current; + return highPass; +} + +/* sample conversion functions */ + +#define SCALE_INT16 32767.f /* (- (expt 2 15) 1) */ +#define SCALE_INT24 8388607.f /* (- (expt 2 23) 1) */ +#define SCALE_INT32 2147483647.f /* (- (expt 2 31) 1) */ + +/* Swap LSB to MSB and vice versa */ +inline __int32 swaplong(__int32 v) { + return ((v>>24)&0xFF)|((v>>8)&0xFF00)|((v&0xFF00)<<8)|((v&0xFF)<<24); +} + +inline __int16 swapshort(__int16 v) { + return ((v>>8)&0xFF)|((v&0xFF)<<8); +} + +/* todo: check dithering */ + +static void float32tofloat32(void* inbuffer, void* outbuffer, long frames) { + if(SIMD_CHECK2(frames,inbuffer,outbuffer)) copyvec_simd((float *)outbuffer,(float *)inbuffer,frames); + else memcpy (outbuffer, inbuffer, frames* sizeof (float)); +} + +static void float32tofloat32_S(void* inbuffer, void* outbuffer, long frames) { + __int32 *in = (__int32 *)inbuffer; + __int32* out = (__int32*)outbuffer; + while (frames--) *out++ = swaplong(*(in++)); +} + +static void float32tofloat64(void* inbuffer, void* outbuffer, long frames) { + const float *in = (const float *)inbuffer; + double* out = (double*)outbuffer; + while (frames--) *(out++) = *(in++); +} + +static void float64tofloat32(void* inbuffer, void* outbuffer, long frames) { + const double *in = (const double *)inbuffer; + float *out = (float *)outbuffer; + while (frames--) *(out++) = *(in++); +} + +static void float32toInt16(void* inbuffer, void* outbuffer, long frames) { + const float *in = (const float *)inbuffer; + __int16* out = (__int16*)outbuffer; + while (frames--) { + float o = *(in++) * SCALE_INT16 + triangulardither() * DITHER_SCALE; + __int16 lng = lrintf(o); + *out++ = lng ; + } +} + +static void Int16tofloat32(void* inbuffer, void* outbuffer, long frames) { + const __int16* in = (const __int16*)inbuffer; + float *out = (float *)outbuffer; + while (frames--) *(out++) = (float)*(in++) * (1.f / SCALE_INT16); +} + +static void float32toInt24(void* inbuffer, void* outbuffer, long frames) { + const float *in = (const float *)inbuffer; + __int32* out = (__int32*)outbuffer; + while (frames--) { + float o = *(in++) * SCALE_INT24; + __int32 intg = (__int32)lrintf(o); + *(out++) = intg; + } +} + +static void Int24tofloat32(void* inbuffer, void* outbuffer, long frames) { + const __int32* in = (const __int32*)inbuffer; + float *out = (float *)outbuffer; + while (frames--) *(out++) = (float)*(in++) * (1.f / SCALE_INT24); +} + +static void float32toInt32(void* inbuffer, void* outbuffer, long frames) { + const float *in = (const float *)inbuffer; + __int32* out = (__int32*)outbuffer; + while (frames--) { + float o = (float)*(in++) * SCALE_INT32 + triangulardither() * DITHER_SCALE; + *out++ = lrintf(o); + } +} + +static void Int32tofloat32(void* inbuffer, void* outbuffer, long frames) { + const __int32* in = (const __int32*)inbuffer; + float *out = (float *)outbuffer; + while (frames--) *(out++) = (float)*(in++) * (1.f / SCALE_INT32); +} + +static void float32toInt16_S(void* inbuffer, void* outbuffer, long frames) { + const float *in = (const float *)inbuffer; + __int16* out = (__int16*)outbuffer; + while (frames--) { + float o = (float)*(in++) * SCALE_INT16 + triangulardither() * DITHER_SCALE; + __int16 reverse = (__int16)lrintf(o); + *out++ = swapshort(reverse); + } +} + +static void Int16tofloat32_S(void* inbuffer, void* outbuffer, long frames) { + const __int16* in = (const __int16*)inbuffer; + float *out = (float *)outbuffer; + while (frames--) *(out++) = (float)swapshort(*(in++)) * (1.f / SCALE_INT16); +} + +static void float32toInt24_S(void* inbuffer, void* outbuffer, long frames) { + const float *in = (const float *)inbuffer; + char* out = (char*)outbuffer; + while (frames--) { + float o = (float)*(in++) * SCALE_INT24; + __int32 reverse = (__int32)lrintf(o); + out[2] = ((char *)&reverse)[0]; + out[1] = ((char *)&reverse)[1]; + out[0] = ((char *)&reverse)[2]; + out += 3; + } +} + +static void Int24tofloat32_S(void* inbuffer, void* outbuffer, long frames) { + const char* in = (const char*)inbuffer; + float *out = (float *)outbuffer; + __int32 d = 0; + while (frames--) { + ((char *)&d)[1] = in[2]; + ((char *)&d)[2] = in[1]; + ((char *)&d)[3] = in[0]; + *(out++) = (float)d * (1.f / SCALE_INT24); + in += 3; + } +} + +static void float32toInt32_S(void* inbuffer, void* outbuffer, long frames) { + const float *in = (const float *)inbuffer; + __int32* out = (__int32*)outbuffer; + while (frames--) { + float o = (float)*(in++) * SCALE_INT32 + triangulardither() * DITHER_SCALE; + __int32 reverse = (__int32)lrintf(o); + *out++ = swaplong(reverse); + } +} + +static void Int32tofloat32_S(void* inbuffer, void* outbuffer, long frames) { + const __int32* in = (const __int32*)inbuffer; + float *out = (float *)outbuffer; + while (frames--) *(out++) = (float)swaplong(*(in++)) * (1.f / SCALE_INT32); +} + + +/* some local helper functions */ +static void prepare_asio_drivernames() { + if (!asio_drivernames) { + asio_drivernames = (char**)getbytes(MAXNDEV * sizeof(char*)); + for (int i = 0; i!= MAXNDEV; ++i) { + asio_drivernames[i] = (char*)getbytes (32 * sizeof(char)); + } + } + /* load the driver */ + if (!asioDrivers) asioDrivers = new AsioDrivers(); + return; +} + +/* callback-based scheduling callbacks: */ + +/* buffer switch callback */ +static void asio_bufferSwitch_cb(long db_idx, ASIOBool directprocess) { + ASIOTime time; +#ifdef ASIODEBUG + static int written = 0; + if(written == 0) { + post("ASIO: asio_bufferSwitch_cb"); + written = 1; + } +#endif + memset (&time, 0, sizeof (time)); + /* todo: do we need to syncronize with other media ??? */ + asio_bufferSwitchTimeInfo_cb(&time, db_idx, directprocess); +} + +/* sample rate change callback */ +static void asio_sampleRateDidChange_cb(ASIOSampleRate srate) { + asio_sampleRateDidChange(srate); +} + +/* asio messaging callback */ +static long asio_messages_cb(long selector, long value, void* message, double* opt) { + return asio_messages(selector, value, message, opt); +} + +static ASIOTime *asio_bufferSwitchTimeInfo_cb(ASIOTime *params, long db_idx, ASIOBool directprocess) { + /* todo: i'm not sure if we'll have to synchronize with other media ... probably yes ... */ + /* perform the processing */ + int timeout = sys_dacblocksize * (float)asio_ticks_per_callback / (float) sys_dacsr * 1e6; + if (sys_timedlock(timeout) == ETIMEDOUT) + /* we're late ... lets hope that jack doesn't kick us out */ + return 0; + + for (int j = 0; j != asio_ticks_per_callback; j++) { + t_sample * sp = sys_soundin; + /* get sounds from input channels */ + for (long i = 0; i < sys_outchannels + sys_inchannels; i++) { + if(asio_converter[i]) + if (asio_bufferinfo[i].isInput == ASIOTrue) { + asio_converter[i]((char*)asio_bufferinfo[i].buffers[db_idx] + + asio_samplewidth[i] * j *sys_dacblocksize, sp, sys_dacblocksize); + sp += sys_dacblocksize; + } + } + /* run dsp */ + sched_tick(sys_time + sys_time_per_dsp_tick); + sp = sys_soundout; + /* send sound to hardware */ + for (long i = 0; i < sys_outchannels + sys_inchannels; i++) { + if (asio_bufferinfo[i].isInput != ASIOTrue) { + /* clip */ + t_float lo = -1.f; + t_float hi = 1.f; + t_int clipargs[6]; + clipargs[1] = clipargs[2] = (t_int)sp; + clipargs[3] = (t_int)&lo; + clipargs[4] = (t_int)&hi; + clipargs[5] = (t_int)sys_dacblocksize; + clipblock(clipargs); + /* send */ + if(asio_converter[i]) + asio_converter[i](sp, (char*)asio_bufferinfo[i].buffers[db_idx] + + asio_samplewidth[i] * j *sys_dacblocksize, sys_dacblocksize); + zeroblock(sp,sys_dacblocksize); + sp += sys_dacblocksize; + } + } + } + if(asio_useoutputready) ASIOOutputReady(); + sys_unlock(); + return 0L; /* time info!!! */ +} + +t_audioapi asio_api = { + asio_open_audio, + asio_close_audio, + asio_send_dacs, + asio_getdevs, +}; diff --git a/desiredata/src/s_audio_jack.c b/desiredata/src/s_audio_jack.c new file mode 100644 index 00000000..ed37829b --- /dev/null +++ b/desiredata/src/s_audio_jack.c @@ -0,0 +1,381 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#define PD_PLUSPLUS_FACE +#include "desire.h" +#include "m_simd.h" +#include <jack/jack.h> +#include <regex.h> +#include <errno.h> +#define MAX_CLIENTS 100 +#define NUM_JACK_PORTS 32 +#define BUF_JACK 4096 +static jack_nframes_t jack_out_max; +#define JACK_OUT_MAX 64 +static jack_nframes_t jack_filled = 0; +static float jack_outbuf[NUM_JACK_PORTS*BUF_JACK]; +static float jack_inbuf[NUM_JACK_PORTS*BUF_JACK]; +static int jack_started = 0; +static jack_port_t * input_port[NUM_JACK_PORTS]; +static jack_port_t *output_port[NUM_JACK_PORTS]; +static int outport_count = 0; +static jack_client_t *jack_client = 0; +char *jack_client_names[MAX_CLIENTS]; +static int jack_dio_error; +static int jack_scheduler; +pthread_mutex_t jack_mutex; +pthread_cond_t jack_sem; +t_int jack_save_connection_state(t_int* dummy); +static void jack_restore_connection_state(); +void run_all_idle_callbacks(); + +static int process (jack_nframes_t nframes, void *arg) { + jack_out_max = max(int(nframes),JACK_OUT_MAX); + if (jack_filled >= nframes) { + if (jack_filled != nframes) post("Partial read"); + for (int j = 0; j < sys_outchannels; j++) { + float *out = (float *)jack_port_get_buffer(output_port[j], nframes); + memcpy(out, jack_outbuf + (j * BUF_JACK), sizeof(float)*nframes); + } + for (int j = 0; j < sys_inchannels; j++) { + float *in = (float *)jack_port_get_buffer( input_port[j], nframes); + memcpy(jack_inbuf + (j * BUF_JACK), in, sizeof(float)*nframes); + } + jack_filled -= nframes; + } else { /* PD could not keep up ! */ + if (jack_started) jack_dio_error = 1; + for (int j = 0; j < outport_count; j++) { + float *out = (float *)jack_port_get_buffer (output_port[j], nframes); + memset(out, 0, sizeof (float) * nframes); + } + memset(jack_outbuf,0,sizeof(jack_outbuf)); + jack_filled = 0; + } + /* tb: wait in the scheduler */ + /* pthread_cond_broadcast(&jack_sem); */ + return 0; +} + +void sys_peakmeters(); +extern int sys_meters; /* true if we're metering */ +static int dspticks_per_jacktick; +static void (*copyblock)(t_sample *dst,t_sample *src,int n); +static void (*zeroblock)(t_sample *dst,int n); +extern int canvas_dspstate; + +static int cb_process (jack_nframes_t nframes, void *arg) { + int timeout = int(nframes * 1e6 / sys_dacsr); + if (canvas_dspstate == 0) { + /* dsp is switched off, the audio is open ... */ + for (int j=0; j<sys_outchannels; j++) { + t_sample *out = (t_sample *)jack_port_get_buffer (output_port[j], nframes); + zeroblock(out, dspticks_per_jacktick * sys_dacblocksize); + } + return 0; + } + int status = sys_timedlock(timeout); + if (status) + if (status == ETIMEDOUT) { + /* we're late ... lets hope that jack doesn't kick us out */ + error("timeout %d", (timeout)); + sys_log_error(ERR_SYSLOCK); + return 0; + } else { + post("sys_timedlock returned %d", status); + return 0; + } + for (int i = 0; i != dspticks_per_jacktick; ++i) { + for (int j=0; j<sys_inchannels; j++) { + t_sample *in = (t_sample *)jack_port_get_buffer(input_port[j], nframes); + copyblock(sys_soundin + j * sys_dacblocksize, in + i * sys_dacblocksize, sys_dacblocksize); + } + sched_tick(sys_time + sys_time_per_dsp_tick); + for (int j=0; j<sys_outchannels; j++) { + t_sample *out = (t_sample *)jack_port_get_buffer (output_port[j], nframes); + copyblock(out + i * sys_dacblocksize, sys_soundout + j * sys_dacblocksize, sys_dacblocksize); + } + if (sys_meters) sys_peakmeters(); + zeroblock(sys_soundout, sys_outchannels * sys_dacblocksize); + } + run_all_idle_callbacks(); + sys_unlock(); + return 0; +} + +static int jack_srate (jack_nframes_t srate, void *arg) { + sys_dacsr = srate; + return 0; +} + +void jack_close_audio(void); +static int jack_ignore_graph_callback = 0; +static t_int jack_shutdown_handler(t_int* none) { + error("jack kicked us out ... trying to reconnect"); + jack_ignore_graph_callback = 1; + /* clean up */ + jack_close_audio(); + /* try to reconnect to jack server */ + jack_open_audio(sys_inchannels, sys_outchannels, int(sys_dacsr), jack_scheduler); + /* restore last connection state */ + jack_restore_connection_state(); + jack_ignore_graph_callback = 0; + return 0; +} + +/* register idle callback in scheduler */ +static void jack_shutdown (void *arg) {sys_callback(jack_shutdown_handler,0,0);} +static int jack_graph_order_callback(void* arg) {sys_callback(jack_save_connection_state,0,0); return 0;} + +static char** jack_get_clients() { + int num_clients = 0; + regex_t port_regex; + const char **jack_ports = jack_get_ports(jack_client, "", "", 0); + regcomp(&port_regex, "^[^:]*", REG_EXTENDED); + jack_client_names[0] = 0; + /* Build a list of clients from the list of ports */ + for (int i=0; jack_ports[i] != 0; i++) { + regmatch_t match_info; + char tmp_client_name[100]; + /* extract the client name from the port name, using a regex that parses the clientname:portname syntax */ + regexec(&port_regex, jack_ports[i], 1, &match_info, 0); + memcpy(tmp_client_name, &jack_ports[i][match_info.rm_so], match_info.rm_eo-match_info.rm_so); + tmp_client_name[ match_info.rm_eo - match_info.rm_so ] = '\0'; + /* do we know about this port's client yet? */ + int client_seen = 0; + for (int j=0; j<num_clients; j++) if (strcmp(tmp_client_name, jack_client_names[j])==0) client_seen = 1; + if (!client_seen) { + jack_client_names[num_clients] = (char*)getbytes(strlen(tmp_client_name) + 1); + /* The alsa_pcm client should go in spot 0. If this is the alsa_pcm client AND we are NOT about to put + it in spot 0 put it in spot 0 and move whatever was already in spot 0 to the end. */ + if (strcmp("alsa_pcm",tmp_client_name)==0 && num_clients>0) { + /* alsa_pcm goes in spot 0 */ + char* tmp = jack_client_names[ num_clients ]; + jack_client_names[num_clients] = jack_client_names[0]; + jack_client_names[0] = tmp; + strcpy(jack_client_names[0], tmp_client_name); + } else { + /* put the new client at the end of the client list */ + strcpy(jack_client_names[num_clients], tmp_client_name); + } + num_clients++; + } + } + /* for (int i=0; i<num_clients; i++) post("client: %s",jack_client_names[i]); */ + free(jack_ports); + return jack_client_names; +} + +/* Wire up all the ports of one client. */ +static int jack_connect_ports(char *client) { + char regex_pattern[100]; + static int entered = 0; + if (entered) return 0; + entered = 1; + if (strlen(client) > 96) return -1; + sprintf(regex_pattern, "%s:.*", client); + const char **jack_ports = jack_get_ports(jack_client, regex_pattern, 0, JackPortIsOutput); + if (jack_ports) + for (int i=0;jack_ports[i] != 0 && i < sys_inchannels;i++) + if (jack_connect (jack_client, jack_ports[i], jack_port_name (input_port[i]))) + error("cannot connect input ports %s -> %s", jack_ports[i],jack_port_name(input_port[i])); + free(jack_ports); + jack_ports = jack_get_ports(jack_client, regex_pattern, 0, JackPortIsInput); + if (jack_ports) + for (int i=0;jack_ports[i] != 0 && i < sys_outchannels;i++) + if (jack_connect (jack_client, jack_port_name (output_port[i]), jack_ports[i])) + error("cannot connect output ports %s -> %s",jack_port_name(output_port[i]),jack_ports[i]); + free(jack_ports); + return 0; +} + +static void jack_error(const char *desc) {} + +int jack_open_audio_2(int inchans, int outchans, int rate, int scheduler); +int jack_open_audio(int inchans, int outchans, int rate, int scheduler) { + jack_dio_error = 0; + if (inchans==0 && outchans==0) return 0; + int ret = jack_open_audio_2(inchans,outchans,rate,scheduler); + if (ret) sys_setscheduler(0); + return ret; +} + +int jack_open_audio_2(int inchans, int outchans, int rate, int scheduler) { + char port_name[80] = ""; + int new_jack = 0; + if (outchans > NUM_JACK_PORTS) {post("%d output ports not supported, setting to %d",outchans, NUM_JACK_PORTS); outchans = NUM_JACK_PORTS;} + if ( inchans > NUM_JACK_PORTS) {post( "%d input ports not supported, setting to %d", inchans, NUM_JACK_PORTS); inchans = NUM_JACK_PORTS;} + if (jack_client && scheduler != sys_getscheduler()) { + jack_client_close(jack_client); + jack_client = 0; + } + sys_setscheduler(scheduler); + jack_scheduler = scheduler; + /* set block copy/zero functions */ + if(SIMD_CHKCNT(sys_dacblocksize) && simd_runtime_check()) { + copyblock = (void (*)(t_sample *,t_sample *,int))©vec_simd; + zeroblock = &zerovec_simd; + } else { + copyblock = (void (*)(t_sample *,t_sample *,int))©vec; + zeroblock = &zerovec; + } + /* try to become a client of the JACK server (we allow two pd's)*/ + if (!jack_client) { + int client_iterator = 0; + do { + sprintf(port_name,"pure_data_%d",client_iterator); + client_iterator++; + } while (((jack_client = jack_client_new (port_name)) == 0) && client_iterator < 2); + // jack spits out enough messages already, do not warn + if (!jack_client) {sys_inchannels = sys_outchannels = 0; return 1;} + jack_get_clients(); + /* tell the JACK server to call `process()' whenever there is work to be done. + tb: adapted for callback based scheduling */ + if (scheduler == 1) { + dspticks_per_jacktick = jack_get_buffer_size(jack_client) / sys_schedblocksize; + jack_set_process_callback (jack_client, cb_process, 0); + } else jack_set_process_callback (jack_client, process, 0); + jack_set_error_function (jack_error); +#ifdef JACK_XRUN + jack_set_xrun_callback (jack_client, jack_xrun, 0); +#endif + jack_set_graph_order_callback(jack_client, jack_graph_order_callback, 0); + /* tell the JACK server to call `srate()' whenever the sample rate of the system changes. */ + jack_set_sample_rate_callback (jack_client, jack_srate, 0); + /* tell the JACK server to call `jack_shutdown()' if + it ever shuts down, either entirely, or if it just decides to stop calling us. */ + jack_on_shutdown (jack_client, jack_shutdown, 0); + for (int j=0;j<NUM_JACK_PORTS;j++) { + input_port[j]=0; + output_port[j]=0; + } + new_jack = 1; + } + /* display the current sample rate. once the client is activated + (see below), you should rely on your own sample rate callback (see above) for this value. */ + int srate = jack_get_sample_rate (jack_client); + sys_dacsr = srate; + /* create the ports */ + for (int j = 0; j < inchans; j++) { + sprintf(port_name, "input%d", j); + if (!input_port[j]) input_port[j] = jack_port_register(jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); + } + for (int j = 0; j < outchans; j++) { + sprintf(port_name, "output%d", j); + if (!output_port[j]) output_port[j] = jack_port_register(jack_client, port_name, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); + } + outport_count = outchans; + /* tell the JACK server that we are ready to roll */ + if (new_jack) { + if (jack_activate (jack_client)) {error("cannot activate client"); sys_inchannels = sys_outchannels = 0; return 1;} + memset(jack_outbuf,0,sizeof(jack_outbuf)); + if (jack_client_names[0]) jack_connect_ports(jack_client_names[0]); + pthread_mutex_init(&jack_mutex,0); + pthread_cond_init(&jack_sem,0); + } + /* tb: get advance from jack server */ + sys_schedadvance = int((float)jack_port_get_total_latency(jack_client,output_port[0]) * 1000. / sys_dacsr * 1000); + return 0; +} + +void jack_close_audio() { + if (!jack_client) return; + jack_deactivate(jack_client); + jack_started = 0; + jack_client_close(jack_client); + jack_client = 0; + for (int i=0; i<NUM_JACK_PORTS; i++) { + input_port[i] = 0; + output_port[i] = 0; + } +} + +int jack_send_dacs() { + int rtnval = SENDDACS_YES; + int timeref = int(sys_getrealtime()); + if (!jack_client) return SENDDACS_NO; + if (!sys_inchannels && !sys_outchannels) return SENDDACS_NO; + if (jack_dio_error) { + sys_log_error(ERR_RESYNC); + jack_dio_error = 0; + } + if (jack_filled >= jack_out_max) return SENDDACS_NO; + /* tb: wait in the scheduler */ +/* pthread_cond_wait(&jack_sem,&jack_mutex); */ + jack_started = 1; + float *fp = sys_soundout; + for (int j=0; j<sys_outchannels; j++) { + memcpy(jack_outbuf + j*BUF_JACK + jack_filled, fp, sys_dacblocksize*sizeof(float)); + fp += sys_dacblocksize; + } + fp = sys_soundin; + for (int j=0; j<sys_inchannels; j++) { + memcpy(fp, jack_inbuf + j*BUF_JACK + jack_filled, sys_dacblocksize*sizeof(float)); + fp += sys_dacblocksize; + } + int timenow = int(sys_getrealtime()); + if (timenow-timeref > sys_sleepgrain*1e-6) rtnval = SENDDACS_SLEPT; + memset(sys_soundout,0,sys_dacblocksize*sizeof(float)*sys_outchannels); + jack_filled += sys_dacblocksize; + return rtnval; +} + +void jack_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) { + *canmulti = 0; /* supports multiple devices */ + int ndev = 1; + for (int i=0; i<ndev; i++) { + sprintf( indevlist + i * devdescsize, "JACK"); + sprintf(outdevlist + i * devdescsize, "JACK"); + } + *nindevs = *noutdevs = ndev; +} + +void jack_listdevs () {error("device listing not implemented for jack yet");} + +static const char ** jack_in_connections[NUM_JACK_PORTS]; /* ports connected to the inputs */ +static const char **jack_out_connections[NUM_JACK_PORTS]; /* ports connected to the outputs */ + +/* tb: save the current state of pd's jack connections */ +t_int jack_save_connection_state(t_int* dummy) { + if (jack_ignore_graph_callback) return 0; + for (int i=0; i<NUM_JACK_PORTS; i++) { + /* saving the inputs connections */ + if ( jack_in_connections[i]) free( jack_in_connections[i]); + jack_in_connections[i] = i< sys_inchannels ? jack_port_get_all_connections(jack_client, input_port[i]) : 0; + /* saving the outputs connections */ + if (jack_out_connections[i]) free(jack_out_connections[i]); + jack_out_connections[i]= i<sys_outchannels ? jack_port_get_all_connections(jack_client, output_port[i]) : 0; + } + return 0; +} + +/* todo: don't try to connect twice if we're both input and output host */ +static void jack_restore_connection_state() { + post("restoring connections"); + for (int i=0; i<NUM_JACK_PORTS; i++) { + /* restoring the inputs connections */ + if (jack_in_connections[i]) { + for (int j=0;;j++) { + const char *port = jack_in_connections[i][j]; + if (!port) break; /* we've connected all incoming ports */ + int status = jack_connect(jack_client, port, jack_port_name(input_port[i])); + if (status) error("cannot connect input ports %s -> %s", port, jack_port_name (input_port[i])); + } + } + /* restoring the output connections */ + if (jack_out_connections[i]) { + for (int j=0;;j++) { + const char *port = jack_out_connections[i][j]; + if (!port) break; /* we've connected all outgoing ports */ + int status = jack_connect(jack_client, jack_port_name(output_port[i]), port); + if (status) error("cannot connect output ports %s -> %s", jack_port_name(output_port[i]), port); + } + } + } +} + +struct t_audioapi jack_api = { + 0 /*jack_open_audio*/, + jack_close_audio, + jack_send_dacs, + jack_getdevs, +}; diff --git a/desiredata/src/s_audio_mmio.c b/desiredata/src/s_audio_mmio.c new file mode 100644 index 00000000..9165ee93 --- /dev/null +++ b/desiredata/src/s_audio_mmio.c @@ -0,0 +1,571 @@ +/* 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_pd.h" +#include "s_stuff.h" +#include <stdio.h> +#include <windows.h> +#include <MMSYSTEM.H> + +/* ------------------------- audio -------------------------- */ + +static void nt_close_midiin(); +static void nt_noresync(); +static void postflags(); + +#define NAPORTS 16 /* wini hack for multiple ADDA devices */ +#define CHANNELS_PER_DEVICE 2 +#define DEFAULTCHANS 2 +#define DEFAULTSRATE 44100 +#define SAMPSIZE 2 + +int nt_realdacblksize; +#define DEFREALDACBLKSIZE (4 * sys_dacblocksize) /* 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; +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 */ + +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 */ + +static void nt_waveinerror(const char *s, int err) { + char t[256]; + waveInGetErrorText(err, t, 256); + error(s,t); +} + +static void nt_waveouterror(const char *s, int err) { + char t[256]; + waveOutGetErrorText(err, t, 256); + error(s,t); +} + +static void wave_prep(t_sbuf *bp, int setdone) { + 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 * SAMPSIZE * nt_realdacblksize)))) + 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 * nt_realdacblksize, sp = (short *)bp->lpData; i--; ) *sp++ = 0; + wh->lpData = bp->lpData; + wh->dwBufferLength = (CHANNELS_PER_DEVICE * SAMPSIZE * nt_realdacblksize); + wh->dwFlags = 0; + wh->dwLoops = 0L; + wh->lpNext = 0; + wh->reserved = 0; + /* optionally (for writing) set DONE flag as if we had queued them */ + if (setdone) wh->dwFlags = WHDR_DONE; +} + +static UINT nt_whichdac = WAVE_MAPPER, nt_whichadc = WAVE_MAPPER; + +int mmio_do_open_audio() { + PCMWAVEFORMAT form; + int i, j; + UINT mmresult; + int nad, nda; + static int naudioprepped = 0, nindevsprepped = 0, noutdevsprepped = 0; + 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; + if (nt_nwavein <= 1 && nt_nwaveout <= 1) nt_noresync(); + if (nindevsprepped < nt_nwavein) { + for (i = nindevsprepped; i < nt_nwavein; i++) + for (j = 0; j < naudioprepped; j++) + wave_prep(&ntsnd_invec[i][j], 0); + nindevsprepped = nt_nwavein; + } + if (noutdevsprepped < nt_nwaveout) { + for (i = noutdevsprepped; i < nt_nwaveout; i++) + for (j = 0; j < naudioprepped; j++) + wave_prep(&ntsnd_outvec[i][j], 1); + noutdevsprepped = nt_nwaveout; + } + if (naudioprepped < nt_naudiobuffer) { + for (j = naudioprepped; j < nt_naudiobuffer; j++) { + for (i = 0; i < nt_nwavein; i++) wave_prep(&ntsnd_invec [i][j], 0); + for (i = 0; i < nt_nwaveout; i++) wave_prep(&ntsnd_outvec[i][j], 1); + } + naudioprepped = nt_naudiobuffer; + } + 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", mmresult); + nt_nwavein = nad; /* nt_nwavein = 0 wini */ + } else { + 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", mmresult); + mmresult = waveInAddBuffer( ntsnd_indev[nad], ntsnd_invec[nad][i].lpWaveHdr, sizeof(WAVEHDR)); + if (mmresult != MMSYSERR_NOERROR) nt_waveinerror("waveInAddBuffer: %s", 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) post("opened dac device %d, with return %d", nt_whichdac +nda, mmresult); + if (mmresult != MMSYSERR_NOERROR) { + post("Wave out open device %d + %d",nt_whichdac,nda); + nt_waveouterror("waveOutOpen device: %s", mmresult); + nt_nwaveout = nda; + } + } + return 0; +} + +void mmio_close_audio() { + 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", nda, errcode); + errcode = waveOutClose(ntsnd_outdev[nda]); + if (errcode != MMSYSERR_NOERROR) printf("error closing output %d: %d",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", errcode); + errcode = waveInClose(ntsnd_indev[nad]); + if (errcode != MMSYSERR_NOERROR) printf("error closing input: %d", 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() { + 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() { + double jittersec, diff; + if (initsystime == -1) nt_resetmidisync(); + jittersec = (nt_dacjitterbufsallowed > nt_adcjitterbufsallowed ? + nt_dacjitterbufsallowed : nt_adcjitterbufsallowed) + * nt_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() { + 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; + +static void nt_noresync() { + nt_resync_cancelled = 1; +} + +static void nt_resyncaudio() { + 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", 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 * SAMPSIZE * nt_realdacblksize)); + waveOutPrepareHeader( ntsnd_outdev[nda], outwavehdr, sizeof(WAVEHDR)); + mmresult = waveOutWrite(ntsnd_outdev[nda], outwavehdr, sizeof(WAVEHDR)); + if (mmresult != MMSYSERR_NOERROR) nt_waveouterror("waveOutAddBuffer: %s", 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() { + HMMIO hmmio; + UINT mmresult; + HANDLE hFormat; + int i, j; + short *sp1, *sp2; + float *fp1, *fp2; + int nextfill, doxfer = 0; + if (!nt_nwavein && !nt_nwaveout) return 0; + if (nt_meters) { + int i, n; + float maxsamp; + for (i = 0, n = 2 * nt_nwavein * sys_dacblocksize, 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 * sys_dacblocksize, 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 (int 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 (int 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 (int 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 (int 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. */ + fp1 = sys_soundout; + for (int nda=0; 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 += sys_dacblocksize, sp1++) { + for (j = 0, fp2 = fp1, sp2 = sp1; j < sys_dacblocksize; 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, (sys_dacblocksize *sizeof(t_sample)*CHANNELS_PER_DEVICE)*nt_nwaveout); + /* vice versa for the input buffer */ + fp1 = sys_soundin; + for (int nad=0; 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 += sys_dacblocksize, sp1++) { + for (j = 0, fp2 = fp1, sp2 = sp1; j < sys_dacblocksize; j++, fp2++, sp2 += CHANNELS_PER_DEVICE) { + *fp2 = ((float)(1./32767.)) * (float)(*sp2); + } + } + } + nt_fill = nt_fill + sys_dacblocksize; + if (nt_fill == nt_realdacblksize) { + nt_fill = 0; + for (int 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", mmresult); + ntsnd_inphase[nad] = WRAPFWD(phase + 1); + } + for (int 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", mmresult); + ntsnd_outphase[nda] = WRAPFWD(phase + 1); + } + /* check for DAC underflow or ADC overflow. */ + for (int 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 (int 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 (int 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 (int 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; +} + +/* ------------------- public routines -------------------------- */ + +void mmio_open_audio(int naudioindev, int *audioindev, +int nchindev, int *chindev, int naudiooutdev, int *audiooutdev, +int nchoutdev, int *choutdev, int rate) /* IOhannes */ { + int nbuf; + nt_realdacblksize = (sys_blocksize ? sys_blocksize : DEFREALDACBLKSIZE); + nbuf = sys_advance_samples/nt_realdacblksize; + if (nbuf >= MAXBUFFER) { + post("pd: audio buffering maxed out to %d", (int)(MAXBUFFER * ((nt_realdacblksize * 1000.)/44100.))); + nbuf = MAXBUFFER; + } + else if (nbuf < 4) nbuf = 4; + post("%d audio buffers", nbuf); + nt_naudiobuffer = nbuf; + if (nt_adcjitterbufsallowed > nbuf - 2) nt_adcjitterbufsallowed = nbuf - 2; + if (nt_dacjitterbufsallowed > nbuf - 2) nt_dacjitterbufsallowed = nbuf - 2; + nt_nwavein = sys_inchannels / 2; + nt_nwaveout = sys_outchannels / 2; + nt_whichadc = (naudioindev < 1 ? (nt_nwavein > 1 ? WAVE_MAPPER : -1) : audioindev[0]); + nt_whichdac = (naudiooutdev < 1 ? (nt_nwaveout > 1 ? WAVE_MAPPER : -1) : audiooutdev[0]); + if (naudiooutdev > 1 || naudioindev > 1) post("separate audio device choice not supported; using sequential devices."); + mmio_do_open_audio(); +} + +#if 0 +/* list the audio and MIDI device names */ +void mmio_listdevs() { + UINT wRtn, ndevices; + unsigned int i; + ndevices = waveInGetNumDevs(); + for (i = 0; i < ndevices; i++) { + WAVEINCAPS wicap; + wRtn = waveInGetDevCaps(i, (LPWAVEINCAPS) &wicap, sizeof(wicap)); + if (wRtn) nt_waveinerror("waveInGetDevCaps: %s", wRtn); + else post("audio input device #%d: %s", 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", wRtn); + else post("audio output device #%d: %s", i+1, wocap.szPname); + } +} +#endif + +void mmio_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) { + int wRtn, ndev, i; + *canmulti = 2; /* supports multiple devices */ + ndev = waveInGetNumDevs(); + if (ndev > maxndev) ndev = maxndev; + *nindevs = ndev; + for (i = 0; i < ndev; i++) { + WAVEINCAPS wicap; + wRtn = waveInGetDevCaps(i, (LPWAVEINCAPS) &wicap, sizeof(wicap)); + sprintf(indevlist + i * devdescsize, (wRtn ? "???" : wicap.szPname)); + } + ndev = waveOutGetNumDevs(); + if (ndev > maxndev) ndev = maxndev; + *noutdevs = ndev; + for (i = 0; i < ndev; i++) { + WAVEOUTCAPS wocap; + wRtn = waveOutGetDevCaps(i, (LPWAVEOUTCAPS) &wocap, sizeof(wocap)); + sprintf(outdevlist + i * devdescsize, (wRtn ? "???" : wocap.szPname)); + } +} + +struct t_audioapi api_mmio = { + mmio_open_audio, + mmio_close_audio, + mmio_send_dacs, + mmio_getdevs, +}; diff --git a/desiredata/src/s_audio_oss.c b/desiredata/src/s_audio_oss.c new file mode 100644 index 00000000..d38051b7 --- /dev/null +++ b/desiredata/src/s_audio_oss.c @@ -0,0 +1,532 @@ +/* Copyright (c) 1997-2003 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 inputs and outputs audio using the OSS API available on linux. */ +#include <linux/soundcard.h> + +#define PD_PLUSPLUS_FACE +#include "desire.h" +#include "s_stuff.h" +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <sched.h> +#include <sys/mman.h> + +/* 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) (sys_dacblocksize * (width)) +#define OSS_XFERSAMPS(chans) (sys_dacblocksize* (chans)) +#define OSS_XFERSIZE(chans, width) (sys_dacblocksize * (chans) * (width)) + +static int linux_fragsize = 0; /* for block mode; block size (sample frames) */ + +/* our device handles */ +struct t_oss_dev { + int fd; + unsigned int space; /* bytes available for writing/reading */ + int bufsize; /* total buffer size in blocks for this device */ + int dropcount; /* # of buffers to drop for resync (output only) */ + unsigned int nchannels; /* number of channels for this device */ + unsigned int bytespersamp; /* bytes per sample (2 for 16 bit, 4 for 32) */ +}; + +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; + +/* OSS-specific private variables */ +static int oss_blockmode = 0; /* flag to use "blockmode" */ +static int oss_32bit = 0; /* allow 23 bit transfers in OSS */ + +/* 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)) + +/* ---------------- public routines ----------------------- */ +static int oss_ndev = 0; + +/* find out how many OSS devices we have. Since this has to + open the devices to find out if they're there, we have + to be called before audio is actually started up. So we + cache the results, which in effect are the number of available devices. */ +void oss_init() { + static int countedthem = 0; + if (countedthem) return; + for (int i=0; i<10; i++) { + char devname[100]; + if (i == 0) strcpy(devname, "/dev/dsp"); else sprintf(devname, "/dev/dsp%d", i); + int fd = open(devname, O_WRONLY|O_NONBLOCK); + if (fd<0) break; + oss_ndev++; + close(fd); + } + countedthem = 1; +} + +void oss_set32bit() {oss_32bit=1;} + +typedef struct _multidev { + int fd; + int channels; + int format; +} t_multidev; + +int oss_reset(int fd) { + int err = ioctl(fd,SNDCTL_DSP_RESET); + if (err<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, fd = dev->fd, wantformat; + int nchannels = dev->nchannels; + 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->bytespersamp = 4; + } else { + wantformat = AFMT_S16_NE; + dev->bytespersamp = 2; + } + param = wantformat; + + if (sys_verbose) post("bytes per sample = %d", dev->bytespersamp); + if (ioctl(fd, SNDCTL_DSP_SETFMT, ¶m) == -1) error("OSS: Could not set DSP format"); + else if (wantformat != param) error("OSS: DSP format: wanted %d, got %d", wantformat, param); + /* sample rate */ + orig = param = srate; + if (ioctl(fd, SNDCTL_DSP_SPEED, ¶m) == -1) error("OSS: Could not set sampling rate for device"); + else if (orig != param) error("OSS: sampling rate: wanted %d, got %d", orig, param ); + + if (oss_blockmode && !skipblocksize) { + int fragbytes, logfragsize, nfragment; + /* setting fragment count and size. */ + linux_fragsize = sys_blocksize; + if (!linux_fragsize) { + linux_fragsize = OSS_DEFFRAGSIZE; + while (linux_fragsize > sys_dacblocksize && linux_fragsize * 6 > sys_advance_samples) + linux_fragsize = linux_fragsize/2; + } + /* post("adv_samples %d", sys_advance_samples); */ + nfragment = int(sys_schedadvance * 44100.e-6 / linux_fragsize); + fragbytes = linux_fragsize * (dev->bytespersamp * nchannels); + logfragsize = ilog2(fragbytes); + if (fragbytes != (1 << logfragsize)) + post("warning: OSS takes only power of 2 blocksize; using %d", + (1 << logfragsize)/(dev->bytespersamp * nchannels)); + if (sys_verbose) post("setting nfrags = %d, fragsize %d", nfragment, fragbytes); + + param = orig = (nfragment<<16) + logfragsize; + if (ioctl(fd,SNDCTL_DSP_SETFRAGMENT, ¶m) == -1) + error("OSS: Could not set or read fragment size"); + 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) error("OSS: ioctl on output device failed"); + dev->bufsize = ainfo.bytes; + defect = sys_advance_samples * (dev->bytespersamp * nchannels) + - dev->bufsize - OSS_XFERSIZE(nchannels, dev->bytespersamp); + if (defect > 0) { + if (sys_verbose || defect > (dev->bufsize >> 2)) + error("OSS: requested audio buffer size %d limited to %d", + sys_advance_samples * (dev->bytespersamp * nchannels), dev->bufsize); + sys_advance_samples = (dev->bufsize-OSS_XFERSAMPS(nchannels)) / (dev->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 O_NDELAY +/* what's the deal with (!O_NDELAY) ? does it make sense to you? */ + +int oss_open_audio(int nindev, int *indev, int nchin, int *chin, + int noutdev, int *outdev, int nchout, int *chout, int rate, int bogus) +{ /* IOhannes */ + int capabilities = 0; + int inchannels = 0, outchannels = 0; + char devname[20]; + int n, i, fd, flags; + char buf[OSS_MAXSAMPLEWIDTH * sys_dacblocksize * OSS_MAXCHPERDEV]; + int wantmore=0; + + linux_nindevs = linux_noutdevs = 0; + /* mark devices unopened */ + for (int i=0; i<OSS_MAXDEV; i++) linux_adcs[i].fd = linux_dacs[i].fd = -1; + + /* open output devices */ + wantmore=0; + if (noutdev < 0 || nindev < 0) bug("linux_open_audio"); + for (int n=0; n<noutdev; n++) { + int gotchans, j, inindex = -1; + int thisdevice = (outdev[n] >= 0 ? outdev[n] : 0); + int wantchannels = (nchout>n) ? chout[n] : wantmore; + fd = -1; + if (!wantchannels) goto end_out_loop; + if (thisdevice > 0) sprintf(devname, "/dev/dsp%d", thisdevice); 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 (fcntl(fd, F_SETFD, 1) < 0) post("couldn't set close-on-exec flag on audio"); + if ((flags = fcntl(fd, F_GETFL)) < 0) post("couldn't get audio device flags"); + else if (fcntl(fd, F_SETFL, flags & (!O_NDELAY)) < 0) post("couldn't set audio device flags"); + if (sys_verbose) post("opened %s for reading and writing", devname); + linux_adcs[inindex].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 (fcntl(fd, F_SETFD, 1) < 0) post("couldn't set close-on-exec flag on audio"); + if ((flags = fcntl(fd, F_GETFL)) < 0) post("couldn't get audio device flags"); + else if (fcntl(fd, F_SETFL, flags & (!O_NDELAY)) < 0) post("couldn't set audio device flags"); + if (sys_verbose) post("opened %s for writing only", 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) { + close(fd); /* can't even do stereo? just give up. */ + } else { + linux_dacs[linux_noutdevs].nchannels = gotchans; + linux_dacs[linux_noutdevs].fd = fd; + oss_configure(&linux_dacs[linux_noutdevs], rate, 1, 0); + linux_noutdevs++; + outchannels += gotchans; + if (inindex >= 0) { + linux_adcs[inindex].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; + for (n = 0; n < nindev; n++) { + int gotchans=0; + int thisdevice = (indev[n] >= 0 ? indev[n] : 0); + int wantchannels = (nchin>n)?chin[n]:wantmore; + int alreadyopened = 0; + if (!wantchannels) goto end_in_loop; + if (thisdevice > 0) sprintf(devname, "/dev/dsp%d", thisdevice); else sprintf(devname, "/dev/dsp"); + sys_setalarm(1000000); + /* perhaps it's already open from the above? */ + if (linux_dacs[n].fd >= 0) { + fd = linux_adcs[n].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 (fcntl(fd, F_SETFD, 1) < 0) post("couldn't set close-on-exec flag on audio"); + if ((flags = fcntl(fd, F_GETFL)) < 0) post("couldn't get audio device flags"); + else if (fcntl(fd, F_SETFL, flags & (!O_NDELAY)) < 0) post("couldn't set audio device flags"); + if (sys_verbose) post("opened %s for reading only", devname); + } + linux_adcs[linux_nindevs].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].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: ; + } + /* 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) post("OSS: issuing first ADC 'read'..."); + read(linux_adcs[0].fd, buf, linux_adcs[0].bytespersamp * linux_adcs[0].nchannels * sys_dacblocksize); + if (sys_verbose) post("...done."); + } + /* now go and fill all the output buffers. */ + for (i = 0; i < linux_noutdevs; i++) { + t_oss_dev &d = linux_dacs[i]; + memset(buf, 0, d.bytespersamp * d.nchannels * sys_dacblocksize); + for (int j = 0; j < sys_advance_samples/sys_dacblocksize; j++) + write(d.fd, buf, d.bytespersamp * d.nchannels * sys_dacblocksize); + } + sys_setalarm(0); + sys_inchannels = inchannels; + sys_outchannels = outchannels; + return 0; +} + +void oss_close_audio() { + for (int i=0;i<linux_nindevs ;i++) close(linux_adcs[i].fd); + for (int i=0;i<linux_noutdevs;i++) close(linux_dacs[i].fd); + linux_nindevs = linux_noutdevs = 0; +} + +static int linux_dacs_write(int fd,void *buf,long bytes) {return write(fd, buf, bytes);} +static int linux_adcs_read (int fd,void *buf,long bytes) {return read(fd, buf, bytes);} + + /* query audio devices for "available" data size. */ +static void oss_calcspace() { + audio_buf_info ainfo; + for (int dev=0; dev<linux_noutdevs; dev++) { + if (ioctl(linux_dacs[dev].fd, SOUND_PCM_GETOSPACE,&ainfo) < 0) + error("OSS: ioctl on output device %d failed",dev); + linux_dacs[dev].space = ainfo.bytes; + } + for (int dev=0; dev<linux_nindevs; dev++) { + if (ioctl(linux_adcs[dev].fd, SOUND_PCM_GETISPACE,&ainfo) < 0) + error("OSS: ioctl on input device %d, fd %d failed", dev, linux_adcs[dev].fd); + linux_adcs[dev].space = ainfo.bytes; + } +} + +void linux_audiostatus() { + if (!oss_blockmode) { + oss_calcspace(); + for (int dev=0; dev < linux_noutdevs; dev++) post("dac %d space %d", dev, linux_dacs[dev].space); + for (int dev=0; dev < linux_nindevs; dev++) post("adc %d space %d", dev, linux_adcs[dev].space); + } +} + +/* this call resyncs audio output and input which will cause discontinuities +in audio output and/or input. */ + +static void oss_doresync() { + int zeroed = 0; + char buf[OSS_MAXSAMPLEWIDTH * sys_dacblocksize * OSS_MAXCHPERDEV]; + audio_buf_info ainfo; + /* 1. if any input devices are ahead (have more than 1 buffer stored), drop one or more buffers worth */ + for (int dev=0; dev<linux_nindevs; dev++) { + t_oss_dev d = linux_adcs[dev]; + if (d.space == 0) { + linux_adcs_read(d.fd, buf, OSS_XFERSIZE(d.nchannels, d.bytespersamp)); + } else while (d.space > OSS_XFERSIZE(d.nchannels, d.bytespersamp)) { + linux_adcs_read(d.fd, buf, OSS_XFERSIZE(d.nchannels, d.bytespersamp)); + if (ioctl(d.fd, SOUND_PCM_GETISPACE, &ainfo) < 0) { error("OSS: ioctl on input device %d, fd %d failed",dev,d.fd); break;} + d.space = ainfo.bytes; + } + } + /* 2. if any output devices are behind, feed them zeros to catch them up */ + for (int dev=0; dev<linux_noutdevs; dev++) { + t_oss_dev d = linux_dacs[dev]; + while (d.space > d.bufsize - sys_advance_samples*d.nchannels*d.bytespersamp) { + if (!zeroed) { + for (unsigned int i = 0; i < OSS_XFERSAMPS(d.nchannels); i++) buf[i] = 0; + zeroed = 1; + } + linux_dacs_write(d.fd, buf, OSS_XFERSIZE(d.nchannels, d.bytespersamp)); + if (ioctl(d.fd, SOUND_PCM_GETOSPACE, &ainfo) < 0) {error("OSS: ioctl on output device %d, fd %d failed",dev,d.fd); break;} + 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 (int dev=0; dev<linux_noutdevs; dev++) { + t_oss_dev d = linux_dacs[dev]; + if (d.space > d.bufsize - (sys_advance_samples - 1) * d.nchannels * d.bytespersamp) { + d.dropcount = sys_advance_samples - 1 - (d.space - d.bufsize) / (d.nchannels * d.bytespersamp); + } else d.dropcount = 0; + } +} + +int oss_send_dacs() { + float *fp1, *fp2; + int i, j, rtnval = SENDDACS_YES; + char buf[OSS_MAXSAMPLEWIDTH * sys_dacblocksize * 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; + 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 (sys_advance_samples) blocks buffered already. */ + oss_calcspace(); + for (int dev=0; dev<linux_noutdevs; dev++) { + t_oss_dev d = linux_dacs[dev]; + if (d.dropcount || (d.bufsize - d.space > sys_advance_samples * d.bytespersamp * d.nchannels)) idle = 1; + } + for (int dev=0; dev<linux_nindevs; dev++) { + t_oss_dev d = linux_adcs[dev]; + if (d.space < OSS_XFERSIZE(d.nchannels, 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 (int dev=0; dev<linux_nindevs; dev++) if (linux_adcs[dev].space == 0) { + sys_microsleep(sys_sleepgrain); /* tb: changed to sys_sleepgrain */ + oss_calcspace(); + if (linux_adcs[dev].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 sys_advance_samples-1 */ + for (int dev=0; dev<linux_noutdevs; dev++) { + t_oss_dev d = linux_dacs[dev]; + if (!d.dropcount && (d.bufsize - d.space < (sys_advance_samples - 2)*d.bytespersamp*d.nchannels)) + goto badsync; + } + for (int dev=0; dev<linux_nindevs; dev++) + if (linux_adcs[dev].space > 3 * OSS_XFERSIZE(linux_adcs[dev].nchannels, linux_adcs[dev].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 (int dev=0, thischan = 0; dev < linux_noutdevs; dev++) { + t_oss_dev d = linux_dacs[dev]; + int nchannels = d.nchannels; + if (d.dropcount) d.dropcount--; + else { + fp1 = sys_soundout + sys_dacblocksize*thischan; + if (d.bytespersamp == 4) { + for (i = sys_dacblocksize * nchannels, lp = (t_oss_int32 *)buf; i--; fp1++, lp++) { + float f = *fp1 * 2147483648.; + *lp = int(f >= 2147483647. ? 2147483647. : (f < -2147483648. ? -2147483648. : f)); + } + } else { + for (i = sys_dacblocksize, sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) { + for (j=0, fp2 = fp1; j<nchannels; j++, fp2 += sys_dacblocksize) { + int s = int(*fp2 * 32767.); + if (s > 32767) s = 32767; else if (s < -32767) s = -32767; + sp[j] = s; + } + } + } + linux_dacs_write(d.fd, buf, OSS_XFERSIZE(nchannels, 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, sys_outchannels * (sizeof(float) * sys_dacblocksize)); + for (int dev=0, thischan = 0; dev < linux_nindevs; dev++) { + int nchannels = linux_adcs[dev].nchannels; + linux_adcs_read(linux_adcs[dev].fd, buf, OSS_XFERSIZE(nchannels, linux_adcs[dev].bytespersamp)); + if ((timenow = sys_getrealtime()) - timeref > 0.002) { + if (!oss_blockmode) sys_log_error(ERR_ADCSLEPT); else rtnval = SENDDACS_SLEPT; + } + timeref = timenow; + fp1 = sys_soundin + thischan*sys_dacblocksize; + if (linux_adcs[dev].bytespersamp == 4) { + for (i = sys_dacblocksize*nchannels, lp = (t_oss_int32 *)buf; i--; fp1++, lp++) + *fp1 = float(*lp)*float(1./2147483648.); + } else { + for (i = sys_dacblocksize, sp = (t_oss_int16 *)buf; i--; fp1++, sp += nchannels) + for (j=0; j<nchannels; j++) fp1[j*sys_dacblocksize] = (float)sp[j]*(float)3.051850e-05; + } + thischan += nchannels; + } + return rtnval; +} + +void oss_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) { + int ndev = min(oss_ndev,maxndev); + *canmulti = 2; /* supports multiple devices */ + for (int i=0; i<ndev; i++) { + sprintf(indevlist + i*devdescsize, "OSS device #%d", i+1); + sprintf(outdevlist + i*devdescsize, "OSS device #%d", i+1); + } + *nindevs = *noutdevs = ndev; +} + +struct t_audioapi oss_api = { + oss_open_audio, + oss_close_audio, + oss_send_dacs, + oss_getdevs, +}; diff --git a/desiredata/src/s_audio_pa.c b/desiredata/src/s_audio_pa.c new file mode 100644 index 00000000..d1641501 --- /dev/null +++ b/desiredata/src/s_audio_pa.c @@ -0,0 +1,332 @@ +/* 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. */ + +/* tb: requires portaudio >= V19 */ + +#include "m_pd.h" +#include "s_stuff.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <portaudio.h> +#include <errno.h> + +#ifdef MSW +/* jmz thinks that we have to include: + * on Windows: <malloc.h> (probably this is already included?) + * on linux: <alloca.h> (might be true for osX too, no way to check right now...) + */ +#zinclude <malloc.h> +#else +# include <alloca.h> +#endif + +#ifdef MSW +#include "pthread.h" /* for ETIMEDOUT */ +#endif + +#define MAX_PA_CHANS 32 + +/* portaudio's blocking api is not working, yet: */ +/* #define PABLOCKING */ + +//#ifndef PABLOCKING +#include "s_audio_pablio.h" +//#endif + +static int pa_inchans, pa_outchans; + +static PaStream *pa_stream; +static PABLIO_Stream *pablio_stream; +static PaStreamCallback *pa_callback = NULL; + +int process (const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, +PaStreamCallbackFlags statusFlags, void *userData); + +int pa_open_audio(int inchans, int outchans, int rate, int advance, int indeviceno, int outdeviceno, int schedmode) { + PaError err; + int j, devno, pa_indev = -1, pa_outdev = -1; + pa_callback = schedmode==1 ? process : NULL; + sys_setscheduler(schedmode); + /* Initialize PortAudio */ + err = Pa_Initialize(); + if (err != paNoError) { + error("Error number %d occured initializing portaudio: %s", err, Pa_GetErrorText(err)); + return 1; + } + /* post("in %d out %d rate %d device %d", inchans, outchans, rate, deviceno); */ + 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 > 0) { + for (j = 0, devno = 0; j < Pa_GetDeviceCount(); j++) { + const PaDeviceInfo *info = Pa_GetDeviceInfo(j); + int maxchans = info->maxInputChannels; + if (maxchans > 0) { + if (devno == indeviceno) { + if (maxchans < inchans) inchans = maxchans; + pa_indev = j; + break; + } + devno++; + } + } + } + if (outchans > 0) { + for (j = 0, devno = 0; j < Pa_GetDeviceCount(); j++) { + const PaDeviceInfo *info = Pa_GetDeviceInfo(j); + int maxchans = info->maxOutputChannels; + if (maxchans > 0) { + if (devno == outdeviceno) { + if (maxchans < outchans) outchans = maxchans; + pa_outdev = j; + break; + } + devno++; + } + } + } + if (sys_verbose) { + post( "input device %d, channels %d", pa_indev, inchans); + post("output device %d, channels %d", pa_outdev, outchans); + post("latency advance %d", advance); + } + if (inchans || outchans) { +#ifndef PABLOCKING + if (schedmode == 1) { +#endif + PaStreamParameters inputParameters, outputParameters; + /* initialize input */ + inputParameters.device = pa_indev ; + inputParameters.channelCount = inchans; + inputParameters.sampleFormat = paFloat32 | paNonInterleaved; + inputParameters.suggestedLatency = advance * 0.001; + inputParameters.hostApiSpecificStreamInfo = NULL; + /* initialize output */ + outputParameters.device = pa_outdev; + outputParameters.channelCount = outchans; + outputParameters.sampleFormat = paFloat32 | paNonInterleaved; + outputParameters.suggestedLatency = advance * 0.001; + outputParameters.hostApiSpecificStreamInfo = NULL; + /* report to portaudio */ + err = Pa_OpenStream(&pa_stream, + ( pa_indev!=-1 ? &inputParameters : 0), + (pa_outdev!=-1 ? &outputParameters : 0), + rate, sys_dacblocksize, paClipOff, /* tb: we should be faster ;-) */ pa_callback, NULL); + if (err == paNoError) { + const PaStreamInfo * streaminfo = Pa_GetStreamInfo (pa_stream); + sys_schedadvance = 1e6 * streaminfo->outputLatency; + } +#ifndef PABLOCKING + } else { + int nbuffers = sys_advance_samples / sys_dacblocksize; + err = PD_OpenAudioStream( &pablio_stream, rate, paFloat32, + inchans, outchans, sys_dacblocksize, nbuffers, pa_indev, pa_outdev); + } +#endif + } else err = 0; + if (err != paNoError) { + post("Error number %d occured opening portaudio stream", err); + post("Error message: %s", Pa_GetErrorText(err)); + Pa_Terminate(); + sys_inchannels = sys_outchannels = 0; + return 1; + } else if (sys_verbose) post("... opened OK."); + pa_inchans = inchans; + pa_outchans = outchans; +#ifndef PABLOCKING + if (schedmode) +#endif + err = Pa_StartStream(pa_stream); + if (err != paNoError) { + post("Error number %d occured starting portaudio stream", err); + post("Error message: %s", Pa_GetErrorText(err)); + Pa_Terminate(); + sys_inchannels = sys_outchannels = 0; + return 1; + } + post("successfully started"); + return 0; +} + +void sys_peakmeters(void); +extern int sys_meters; /* true if we're metering */ + +int process (const void *input, void *output, unsigned long frameCount, +const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { + int i,j; + int timeout = (float)frameCount / (float) sys_dacsr * 1e6; + if (sys_timedlock(timeout) == ETIMEDOUT) /* we're late */ { + post("timeout %d", timeout); + sys_log_error(ERR_SYSLOCK); + return 0; + } + for (j = 0; j < sys_inchannels; j++) { + t_sample * in = ((t_sample**)input)[j]; + copyvec(sys_soundin + j * sys_dacblocksize,in,sys_dacblocksize); + } + sched_tick(sys_time + sys_time_per_dsp_tick); + for (j = 0; j < sys_outchannels; j++) { + t_sample * out = ((t_sample**)output)[j]; + copyvec(out,sys_soundout + j * sys_dacblocksize,sys_dacblocksize); + } + /* update peak meters */ + if (sys_meters) sys_peakmeters(); + /* clear the output buffer */ + zerovec(sys_soundout, pa_outchans * sys_dacblocksize); + sys_unlock(); + return 0; +} + +void pa_close_audio() { + post("closing portaudio"); + if (pa_inchans || pa_outchans) { + if (pa_stream) { + Pa_StopStream(pa_stream); + Pa_CloseStream(pa_stream); + pa_stream = NULL; + } + if (pablio_stream) { + PD_CloseAudioStream(pablio_stream); + pablio_stream = NULL; + } + } + post("portaudio closed"); + pa_inchans = pa_outchans = 0; +} + +/* for blocked IO */ +int pa_send_dacs() { +#ifdef PABLOCKING /* tb: blocking IO isn't really working for v19 yet */ + double timenow, timebefore; + if (( pa_inchans && Pa_GetStreamReadAvailable(pa_stream) < sys_dacblocksize*0.8) && + (pa_outchans && Pa_GetStreamWriteAvailable(pa_stream) < sys_dacblocksize*0.8)) { + /* we can't transfer data ... wait in the scheduler */ + return SENDDACS_NO; + } + timebefore = sys_getrealtime(); + if (pa_outchans) Pa_WriteStream(pa_stream, &sys_soundout, sys_dacblocksize); + if ( pa_inchans) Pa_ReadStream(pa_stream, &sys_soundin, sys_dacblocksize); + zerovec(sys_soundout, pa_inchans * sys_dacblocksize); + while (( pa_inchans && Pa_GetStreamReadAvailable(pa_stream) < sys_dacblocksize*0.8) && + (pa_outchans && Pa_GetStreamWriteAvailable(pa_stream) < sys_dacblocksize*0.8)) { + if (pa_outchans) Pa_WriteStream(pa_stream, &sys_soundout, sys_dacblocksize); + if ( pa_inchans) Pa_ReadStream(pa_stream, &sys_soundin, sys_dacblocksize); + zerovec(sys_soundout, pa_inchans * sys_dacblocksize); + sched_tick(sys_time + sys_time_per_dsp_tick); + } + if (sys_getrealtime() > timebefore + sys_sleepgrain * 1e-6) { + return SENDDACS_SLEPT; + } else return SENDDACS_YES; +#else /* for now we're using pablio */ + float *samples, *fp1, *fp2; + int i, j; + double timebefore; + samples=(float*)alloca(sizeof(float) * MAX_PA_CHANS * sys_dacblocksize); + timebefore = sys_getrealtime(); + if ((pa_inchans && PD_GetAudioStreamReadable(pablio_stream) < sys_dacblocksize) || + (pa_outchans && PD_GetAudioStreamWriteable(pablio_stream) < sys_dacblocksize)) { + if (pa_inchans && pa_outchans) { + int synced = 0; + while (PD_GetAudioStreamWriteable(pablio_stream) > 2*sys_dacblocksize) { + for (j = 0; j < pa_outchans; j++) + for (i = 0, fp2 = samples + j; i < sys_dacblocksize; i++, fp2 += pa_outchans) + *fp2 = 0; + synced = 1; + PD_WriteAudioStream(pablio_stream, samples, sys_dacblocksize); + } + while (PD_GetAudioStreamReadable(pablio_stream) > 2*sys_dacblocksize) { + synced = 1; + PD_ReadAudioStream(pablio_stream, samples, sys_dacblocksize); + } +/* if (synced) post("sync"); */ + } + return SENDDACS_NO; + } + if (pa_inchans) { + PD_ReadAudioStream(pablio_stream, samples, sys_dacblocksize); + for (j = 0, fp1 = sys_soundin; j < pa_inchans; j++, fp1 += sys_dacblocksize) + for (i = 0, fp2 = samples + j; i < sys_dacblocksize; i++, fp2 += pa_inchans) + fp1[i] = *fp2; + } + if (pa_outchans) { + for (j = 0, fp1 = sys_soundout; j < pa_outchans; j++, fp1 += sys_dacblocksize) + for (i = 0, fp2 = samples + j; i < sys_dacblocksize; i++, fp2 += pa_outchans) { + *fp2 = fp1[i]; + fp1[i] = 0; + } + PD_WriteAudioStream(pablio_stream, samples, sys_dacblocksize); + } + if (sys_getrealtime() > timebefore + sys_sleepgrain * 1e-6) { + /* post("slept"); */ + return SENDDACS_SLEPT; + } else return SENDDACS_YES; +#endif +} + +void pa_listdevs() /* lifted from pa_devs.c in portaudio */ { + int j, numDevices; + const PaDeviceInfo *pdi; + PaError err; + Pa_Initialize(); + numDevices = Pa_GetDeviceCount(); + if (numDevices<0) { + error("ERROR: Pa_GetDeviceCount returned %d", numDevices); + err = numDevices; + goto error; + } + post("Audio Devices:"); + for (int i=0; i<numDevices; i++) { + const PaDeviceInfo *pdi = Pa_GetDeviceInfo(i); + post("device %s", pdi->name); + post("device %d:", i+1); + post(" %s;", pdi->name); + post("%d inputs, ", pdi->maxInputChannels); + post("%d outputs", pdi->maxOutputChannels); +#ifdef PA19 + if (i == Pa_GetDefaultInputDevice ()) post(" (Default Input)"); + if (i == Pa_GetDefaultOutputDevice()) post(" (Default Output)"); +#else + if (i == Pa_GetDefaultInputDeviceID ()) post(" (Default Input)"); + if (i == Pa_GetDefaultOutputDeviceID()) post(" (Default Output)"); +#endif + post(""); + } + post(""); + return; +error: + error("An error occured while using the portaudio stream: #%d: %s",err,Pa_GetErrorText(err)); +} +/* scanning for devices */ +void pa_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) { + int i, nin = 0, nout = 0, ndev; + *canmulti = 1; /* one dev each for input and output */ + Pa_Initialize(); + ndev = Pa_GetDeviceCount(); + for (i = 0; i < ndev; i++) { + const PaDeviceInfo *pdi = Pa_GetDeviceInfo(i); + if (pdi->maxInputChannels > 0 && nin < maxndev) { + sprintf(indevlist + nin * devdescsize, "(%d)%s", pdi->hostApi,pdi->name); + /* strcpy(indevlist + nin * devdescsize, pdi->name); */ + nin++; + } + if (pdi->maxOutputChannels > 0 && nout < maxndev) { + sprintf(outdevlist + nout * devdescsize, "(%d)%s", pdi->hostApi,pdi->name); + /* strcpy(outdevlist + nout * devdescsize, pdi->name); */ + nout++; + } + } + *nindevs = nin; + *noutdevs = nout; +} + +t_audioapi pa_api = { + pa_open_audio, + pa_close_audio, + pa_send_dacs, + pa_getdevs, +}; diff --git a/desiredata/src/s_audio_pablio.c b/desiredata/src/s_audio_pablio.c new file mode 100644 index 00000000..5915250b --- /dev/null +++ b/desiredata/src/s_audio_pablio.c @@ -0,0 +1,304 @@ +/* + * $Id: s_audio_pablio.c,v 1.1.4.2.2.4.2.2 2007-07-30 22:19:11 matju Exp $ + * pablio.c + * Portable Audio Blocking Input/Output utility. + * + * Author: Phil Burk, http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + + /* changes by Miller Puckette to support Pd: device selection, + settable audio buffer size, and settable number of channels. + LATER also fix it to poll for input and output fifo fill points. */ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include "portaudio.h" +#include "s_audio_paring.h" +#include "s_audio_pablio.h" /* MSP */ +#include <string.h> + + /* MSP -- FRAMES_PER_BUFFER constant removed */ +static void NPa_Sleep(int n) { /* MSP wrapper to check we never stall... */ +#if 0 + post("sleep"); +#endif + Pa_Sleep(n); +} + +/************************************************************************/ +/******** Prototypes ****************************************************/ +/************************************************************************/ + +#ifdef PA19 +static int blockingIOCallback( const void *inputBuffer, void *outputBuffer, /*MSP */ + unsigned long framesPerBuffer, + const PaStreamCallbackTimeInfo *outTime, + PaStreamCallbackFlags myflags, + void *userData ); +#else +static int blockingIOCallback( void *inputBuffer, void *outputBuffer, + unsigned long framesPerBuffer, + PaTimestamp outTime, void *userData ); +#endif +static PaError PABLIO_InitFIFO( RingBuffer *rbuf, long numFrames, long bytesPerFrame ); +static PaError PABLIO_TermFIFO( RingBuffer *rbuf ); + +/************************************************************************/ +/******** Functions *****************************************************/ +/************************************************************************/ + +/* Called from PortAudio. + * Read and write data only if there is room in FIFOs. + */ +#ifdef PA19 +static int blockingIOCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, +const PaStreamCallbackTimeInfo *outTime, PaStreamCallbackFlags myflags, void *userData) +#else +static int blockingIOCallback(void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, +PaTimestamp outTime, void *userData ) +#endif +{ + PABLIO_Stream *data = (PABLIO_Stream*)userData; +// (void) outTime; + /* This may get called with NULL inputBuffer during initial setup. */ + if (inputBuffer) PD_RingBuffer_Write( &data->inFIFO, inputBuffer, data->inbytesPerFrame * framesPerBuffer ); + if (outputBuffer) { + int numBytes = data->outbytesPerFrame * framesPerBuffer; + int numRead = PD_RingBuffer_Read( &data->outFIFO, outputBuffer, numBytes); + /* Zero out remainder of buffer if we run out of data. */ + for (int i=numRead; i<numBytes; i++) ((char *)outputBuffer)[i] = 0; + } + return 0; +} + +/* Allocate buffer. */ +static PaError PABLIO_InitFIFO(RingBuffer *rbuf, long numFrames, long bytesPerFrame) { + long numBytes = numFrames * bytesPerFrame; + char *buffer = (char *)malloc(numBytes); + if (!buffer) return paInsufficientMemory; + memset(buffer, 0, numBytes); + return (PaError) PD_RingBuffer_Init(rbuf, numBytes, buffer); +} + +/* Free buffer. */ +static PaError PABLIO_TermFIFO(RingBuffer *rbuf) { + if (rbuf->buffer) free(rbuf->buffer); + rbuf->buffer = NULL; + return paNoError; +} + +/************************************************************ + * Write data to ring buffer. + * Will not return until all the data has been written. + */ +long PD_WriteAudioStream(PABLIO_Stream *aStream, void *data, long numFrames) { + long bytesWritten; + char *p = (char *) data; + long numBytes = aStream->outbytesPerFrame * numFrames; + while (numBytes > 0) { + bytesWritten = PD_RingBuffer_Write(&aStream->outFIFO, p, numBytes); + numBytes -= bytesWritten; + p += bytesWritten; + if (numBytes>0) NPa_Sleep(10); /* MSP */ + } + return numFrames; +} + +/************************************************************ + * Read data from ring buffer. + * Will not return until all the data has been read. + */ +long PD_ReadAudioStream(PABLIO_Stream *aStream, void *data, long numFrames) { + char *p = (char *)data; + long numBytes = aStream->inbytesPerFrame * numFrames; + while (numBytes > 0) { + long bytesRead = PD_RingBuffer_Read(&aStream->inFIFO, p, numBytes); + numBytes -= bytesRead; + p += bytesRead; + if (numBytes > 0) NPa_Sleep(10); /* MSP */ + } + return numFrames; +} + +/* Return the number of frames that could be written to the stream without having to wait. */ +long PD_GetAudioStreamWriteable(PABLIO_Stream *aStream) { + int bytesEmpty = PD_RingBuffer_GetWriteAvailable(&aStream->outFIFO); + return bytesEmpty / aStream->outbytesPerFrame; +} + +/* Return the number of frames that are available to be read from the stream without having to wait. */ +long PD_GetAudioStreamReadable(PABLIO_Stream *aStream) { + int bytesFull = PD_RingBuffer_GetReadAvailable(&aStream->inFIFO); + return bytesFull / aStream->inbytesPerFrame; +} + +static unsigned long RoundUpToNextPowerOf2(unsigned long n) { + long numBits = 0; + if( ((n-1) & n) == 0) return n; /* Already Power of two. */ + while(n > 0) { + n= n>>1; + numBits++; + } + return 1<<numBits; +} + +/************************************************************ + * Opens a PortAudio stream with default characteristics. + * Allocates PABLIO_Stream structure. + * flags parameter can be an ORed combination of: + * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE */ +PaError PD_OpenAudioStream(PABLIO_Stream **rwblPtr, double sampleRate, PaSampleFormat format, int inchannels, +int outchannels, int framesperbuf, int nbuffers, int indeviceno, int outdeviceno) { + long bytesPerSample; + long doRead = 0; + long doWrite = 0; + PaError err; + PABLIO_Stream *aStream; + long minNumBuffers; + long numFrames; +#ifdef PA19 + PaStreamParameters instreamparams, outstreamparams; /* MSP */ +#endif + /* post("open %lf fmt %d flags %d ch: %d fperbuf: %d nbuf: %d devs: %d %d", + sampleRate, format, flags, nchannels, framesperbuf, nbuffers, indeviceno, outdeviceno); */ + if (indeviceno < 0) { +#ifdef PA19 + indeviceno = Pa_GetDefaultInputDevice(); +#else + indeviceno = Pa_GetDefaultInputDeviceID(); +#endif + if (indeviceno == paNoDevice) inchannels = 0; + post("using default input device number: %d", indeviceno); + } + if (outdeviceno<0) { +#ifdef PA19 + outdeviceno = Pa_GetDefaultOutputDevice(); +#else + outdeviceno = Pa_GetDefaultOutputDeviceID(); +#endif + if(outdeviceno == paNoDevice) outchannels = 0; + post("using default output device number: %d", outdeviceno); + } + /* post("nchan %d, flags %d, bufs %d, framesperbuf %d", nchannels, flags, nbuffers, framesperbuf); */ + /* Allocate PABLIO_Stream structure for caller. */ + aStream = (PABLIO_Stream *) malloc(sizeof(PABLIO_Stream)); + if( aStream == NULL ) return paInsufficientMemory; + memset(aStream, 0, sizeof(PABLIO_Stream)); + /* Determine size of a sample. */ + bytesPerSample = Pa_GetSampleSize(format); + if (bytesPerSample<0) {err = (PaError) bytesPerSample; goto error;} + aStream-> insamplesPerFrame = inchannels; aStream-> inbytesPerFrame = bytesPerSample * aStream-> insamplesPerFrame; + aStream->outsamplesPerFrame = outchannels; aStream->outbytesPerFrame = bytesPerSample * aStream->outsamplesPerFrame; + err = Pa_Initialize(); + if (err != paNoError) goto error; +#ifdef PA19 + numFrames = nbuffers * framesperbuf; /* ...MSP */ + instreamparams.device = indeviceno; /* MSP... */ + instreamparams.channelCount = inchannels; + instreamparams.sampleFormat = format; + instreamparams.suggestedLatency = nbuffers*framesperbuf/sampleRate; + instreamparams.hostApiSpecificStreamInfo = 0; + outstreamparams.device = outdeviceno; + outstreamparams.channelCount = outchannels; + outstreamparams.sampleFormat = format; + outstreamparams.suggestedLatency = nbuffers*framesperbuf/sampleRate; + outstreamparams.hostApiSpecificStreamInfo = 0; /* ... MSP */ +#else +/* Warning: numFrames must be larger than amount of data processed per + interrupt inside PA to prevent glitches. */ /* MSP */ + minNumBuffers = Pa_GetMinNumBuffers(framesperbuf, sampleRate); + if (minNumBuffers > nbuffers) + post("warning: number of buffers %d less than recommended minimum %d", (int)nbuffers, (int)minNumBuffers); +#endif + numFrames = nbuffers * framesperbuf; + /* post("numFrames %d", numFrames); */ + /* Initialize Ring Buffers */ + doRead = (inchannels != 0); + doWrite = (outchannels != 0); + if(doRead) { + err = PABLIO_InitFIFO(&aStream->inFIFO, numFrames, aStream->inbytesPerFrame); + if (err != paNoError) goto error; + } + if(doWrite) { + long numBytes; + err = PABLIO_InitFIFO(&aStream->outFIFO, numFrames, aStream->outbytesPerFrame); + if (err != paNoError) goto error; + /* Make Write FIFO appear full initially. */ + numBytes = PD_RingBuffer_GetWriteAvailable( &aStream->outFIFO ); + PD_RingBuffer_AdvanceWriteIndex( &aStream->outFIFO, numBytes ); + } + +/* Open a PortAudio stream that we will use to communicate with the underlying audio drivers. */ +#ifdef PA19 + err = Pa_OpenStream(&aStream->stream, (doRead ? &instreamparams : 0), (doWrite ? &outstreamparams : 0), + sampleRate, framesperbuf, paNoFlag, blockingIOCallback, aStream); +#else + err = Pa_OpenStream(&aStream->stream, + (doRead ? indeviceno : paNoDevice), (doRead ? aStream->insamplesPerFrame : 0 ), format, NULL, + (doWrite ? outdeviceno : paNoDevice), (doWrite ? aStream->outsamplesPerFrame : 0 ), format, NULL, + sampleRate, framesperbuf, nbuffers, paNoFlag, blockingIOCallback, aStream); +#endif + if (err != paNoError) goto error; + err = Pa_StartStream( aStream->stream ); + if (err != paNoError) { + error("Pa_StartStream failed; closing audio stream..."); + PD_CloseAudioStream(aStream); + goto error; + } + *rwblPtr = aStream; + return paNoError; +error: + *rwblPtr = NULL; + return err; +} + +/************************************************************/ +PaError PD_CloseAudioStream(PABLIO_Stream *aStream) { + PaError err; + int bytesEmpty; + int byteSize = aStream->outFIFO.bufferSize; + /* If we are writing data, make sure we play everything written. */ + if (byteSize>0) { + bytesEmpty = PD_RingBuffer_GetWriteAvailable( &aStream->outFIFO ); + while (bytesEmpty<byteSize) { + NPa_Sleep( 10 ); /* MSP */ + bytesEmpty = PD_RingBuffer_GetWriteAvailable( &aStream->outFIFO ); + } + } + err = Pa_StopStream(aStream->stream); if(err != paNoError) goto error; + err = Pa_CloseStream(aStream->stream); if(err != paNoError) goto error; + Pa_Terminate(); +error: + PABLIO_TermFIFO(&aStream->inFIFO); + PABLIO_TermFIFO(&aStream->outFIFO); + free(aStream); + return err; +} diff --git a/desiredata/src/s_audio_pablio.h b/desiredata/src/s_audio_pablio.h new file mode 100644 index 00000000..1a1ecb8d --- /dev/null +++ b/desiredata/src/s_audio_pablio.h @@ -0,0 +1,112 @@ +#ifndef _PD_PABLIO_H +#define _PD_PABLIO_H + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * $Id: s_audio_pablio.h,v 1.1.4.1.2.3 2006-01-24 01:29:54 xovo Exp $ + * PABLIO.h + * Portable Audio Blocking read/write utility. + * + * Author: Phil Burk, http://www.softsynth.com/portaudio/ + * + * Include file for PABLIO, the Portable Audio Blocking I/O Library. + * PABLIO is built on top of PortAudio, the Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <portaudio.h> +#include "s_audio_paring.h" +#include <string.h> + +typedef struct +{ + RingBuffer inFIFO; + RingBuffer outFIFO; + PaStream *stream; + int inbytesPerFrame; + int insamplesPerFrame; + int outbytesPerFrame; + int outsamplesPerFrame; +} +PABLIO_Stream; + +/* Values for flags for OpenAudioStream(). */ +#define PABLIO_READ (1<<0) +#define PABLIO_WRITE (1<<1) +#define PABLIO_READ_WRITE (PABLIO_READ|PABLIO_WRITE) +#define PABLIO_MONO (1<<2) +#define PABLIO_STEREO (1<<3) + +/************************************************************ + * Write data to ring buffer. + * Will not return until all the data has been written. + */ +long PD_WriteAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ); + +/************************************************************ + * Read data from ring buffer. + * Will not return until all the data has been read. + */ +long PD_ReadAudioStream( PABLIO_Stream *aStream, void *data, long numFrames ); + +/************************************************************ + * Return the number of frames that could be written to the stream without + * having to wait. + */ +long PD_GetAudioStreamWriteable( PABLIO_Stream *aStream ); + +/************************************************************ + * Return the number of frames that are available to be read from the + * stream without having to wait. + */ +long PD_GetAudioStreamReadable( PABLIO_Stream *aStream ); + +/************************************************************ + * Opens a PortAudio stream with default characteristics. + * Allocates PABLIO_Stream structure. + * + * flags parameter can be an ORed combination of: + * PABLIO_READ, PABLIO_WRITE, or PABLIO_READ_WRITE, + */ +PaError PD_OpenAudioStream( PABLIO_Stream **rwblPtr, double sampleRate, + PaSampleFormat format, int inchannels, + int outchannels, int framesperbuf, int nbuffers, + int indeviceno, int outdeviceno); /* MSP */ + +PaError PD_CloseAudioStream( PABLIO_Stream *aStream ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _PD_PABLIO_H */ diff --git a/desiredata/src/s_audio_paring.c b/desiredata/src/s_audio_paring.c new file mode 100644 index 00000000..dcdcce0e --- /dev/null +++ b/desiredata/src/s_audio_paring.c @@ -0,0 +1,172 @@ +/* + * $Id: s_audio_paring.c,v 1.1.4.1.2.1.2.1 2007-07-31 00:10:37 matju Exp $ + * ringbuffer.c + * Ring Buffer utility.. + * + * Author: Phil Burk, http://www.softsynth.com + * + * This program uses the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +/* + * modified 2002/07/13 by olaf.matthes@gmx.de to allow any number if channels + */ + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include "s_audio_paring.h" +#include <string.h> + +/* Initialize FIFO. */ +long PD_RingBuffer_Init(RingBuffer *rbuf, long numBytes, void *dataPtr) { + rbuf->bufferSize = numBytes; + rbuf->buffer = (char *)dataPtr; + PD_RingBuffer_Flush( rbuf ); + return 0; +} +/*************************************************************************** +** Return number of bytes available for reading. */ +long PD_RingBuffer_GetReadAvailable(RingBuffer *rbuf) { + long ret = rbuf->writeIndex - rbuf->readIndex; + if (ret < 0) ret += 2 * rbuf->bufferSize; + if (ret < 0 || ret > rbuf->bufferSize) error("consistency check failed: PD_RingBuffer_GetReadAvailable"); + return ret; +} +/* Return number of bytes available for writing. */ +long PD_RingBuffer_GetWriteAvailable(RingBuffer *rbuf) { + return rbuf->bufferSize - PD_RingBuffer_GetReadAvailable(rbuf); +} + +/* Clear buffer. Should only be called when buffer is NOT being read. */ +void PD_RingBuffer_Flush(RingBuffer *rbuf) { + rbuf->writeIndex = rbuf->readIndex = 0; +} + +/* Get address of region(s) to which we can write data. If the region is contiguous, size2 will be zero. + If non-contiguous, size2 will be the size of second region. + Returns room available to be written or numBytes, whichever is smaller. */ +long PD_RingBuffer_GetWriteRegions(RingBuffer *rbuf, long numBytes, +void **dataPtr1, long *sizePtr1, void **dataPtr2, long *sizePtr2) { + long index; + long available = PD_RingBuffer_GetWriteAvailable(rbuf); + if (numBytes > available) numBytes = available; + /* Check to see if write is not contiguous. */ + index = rbuf->writeIndex; + while (index >= rbuf->bufferSize) index -= rbuf->bufferSize; + if ((index + numBytes) > rbuf->bufferSize) { + /* Write data in two blocks that wrap the buffer. */ + long firstHalf = rbuf->bufferSize - index; + *dataPtr1 = &rbuf->buffer[index]; + *sizePtr1 = firstHalf; + *dataPtr2 = &rbuf->buffer[0]; + *sizePtr2 = numBytes - firstHalf; + } else { + *dataPtr1 = &rbuf->buffer[index]; + *sizePtr1 = numBytes; + *dataPtr2 = NULL; + *sizePtr2 = 0; + } + return numBytes; +} + + +/*************************************************************************** +*/ +long PD_RingBuffer_AdvanceWriteIndex(RingBuffer *rbuf, long numBytes) { + long ret = rbuf->writeIndex + numBytes; + if (ret >= 2 * rbuf->bufferSize) ret -= 2 * rbuf->bufferSize; /* check for end of buffer */ + return rbuf->writeIndex = ret; +} + +/*************************************************************************** +** Get address of region(s) from which we can read data. +** If the region is contiguous, size2 will be zero. +** If non-contiguous, size2 will be the size of second region. +** Returns room available to be written or numBytes, whichever is smaller. +*/ +long PD_RingBuffer_GetReadRegions(RingBuffer *rbuf, long numBytes, +void **dataPtr1, long *sizePtr1, void **dataPtr2, long *sizePtr2) { + long index; + long available = PD_RingBuffer_GetReadAvailable(rbuf); + if (numBytes > available) numBytes = available; + /* Check to see if read is not contiguous. */ + index = rbuf->readIndex; + while (index >= rbuf->bufferSize) index -= rbuf->bufferSize; + if ((index + numBytes) > rbuf->bufferSize) { + /* Write data in two blocks that wrap the buffer. */ + long firstHalf = rbuf->bufferSize - index; + *dataPtr1 = &rbuf->buffer[index]; + *sizePtr1 = firstHalf; + *dataPtr2 = &rbuf->buffer[0]; + *sizePtr2 = numBytes - firstHalf; + } else { + *dataPtr1 = &rbuf->buffer[index]; + *sizePtr1 = numBytes; + *dataPtr2 = NULL; + *sizePtr2 = 0; + } + return numBytes; +} + +long PD_RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes ) { + long ret = (rbuf->readIndex + numBytes); + if( ret >= 2 * rbuf->bufferSize) ret -= 2 * rbuf->bufferSize; + return rbuf->readIndex = ret; +} + +/* Return bytes written. */ +long PD_RingBuffer_Write(RingBuffer *rbuf, const void *data, long numBytes) { + long size1, size2, numWritten; + void *data1, *data2; + numWritten = PD_RingBuffer_GetWriteRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 ); + if (size2 > 0) { + memcpy(data1, data, size1); + data = ((char *)data) + size1; + memcpy(data2, data, size2); + } else { + memcpy(data1, data, size1); + } + PD_RingBuffer_AdvanceWriteIndex( rbuf, numWritten ); + return numWritten; +} + +/* Return bytes read. */ +long PD_RingBuffer_Read(RingBuffer *rbuf, void *data, long numBytes) { + long size1, size2, numRead; + void *data1, *data2; + numRead = PD_RingBuffer_GetReadRegions( rbuf, numBytes, &data1, &size1, &data2, &size2 ); + if (size2 > 0) { + memcpy(data, data1, size1); + data = ((char *)data) + size1; + memcpy(data, data2, size2); + } else { + memcpy(data, data1, size1); + } + PD_RingBuffer_AdvanceReadIndex( rbuf, numRead ); + return numRead; +} diff --git a/desiredata/src/s_audio_paring.h b/desiredata/src/s_audio_paring.h new file mode 100644 index 00000000..5415f64a --- /dev/null +++ b/desiredata/src/s_audio_paring.h @@ -0,0 +1,101 @@ +#ifndef _PD_RINGBUFFER_H +#define _PD_RINGBUFFER_H +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +/* + * $Id: s_audio_paring.h,v 1.1.4.1.2.3 2006-01-24 01:29:54 xovo Exp $ + * ringbuffer.h + * Ring Buffer utility.. + * + * Author: Phil Burk, http://www.softsynth.com + * + * This program is distributed with the PortAudio Portable Audio Library. + * For more information see: http://www.audiomulch.com/portaudio/ + * Copyright (c) 1999-2000 Ross Bencina and Phil Burk + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files + * (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * Any person wishing to distribute modifications to the Software is + * requested to send the modifications to the original developer so that + * they can be incorporated into the canonical version. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> + +typedef struct +{ + long bufferSize; /* Number of bytes in FIFO. Power of 2. Set by RingBuffer_Init. */ +/* These are declared volatile because they are written by a different thread than the reader. */ + volatile long writeIndex; /* Index of next writable byte. Set by RingBuffer_AdvanceWriteIndex. */ + volatile long readIndex; /* Index of next readable byte. Set by RingBuffer_AdvanceReadIndex. */ + long bigMask; /* Used for wrapping indices with extra bit to distinguish full/empty. */ + long smallMask; /* Used for fitting indices to buffer. */ + char *buffer; +} +RingBuffer; +/* + * Initialize Ring Buffer. + * numBytes must be power of 2, returns -1 if not. + */ +long PD_RingBuffer_Init( RingBuffer *rbuf, long numBytes, void *dataPtr ); + +/* Clear buffer. Should only be called when buffer is NOT being read. */ +void PD_RingBuffer_Flush( RingBuffer *rbuf ); + +/* Return number of bytes available for writing. */ +long PD_RingBuffer_GetWriteAvailable( RingBuffer *rbuf ); +/* Return number of bytes available for read. */ +long PD_RingBuffer_GetReadAvailable( RingBuffer *rbuf ); +/* Return bytes written. */ +long PD_RingBuffer_Write( RingBuffer *rbuf, const void *data, long numBytes ); +/* Return bytes read. */ +long PD_RingBuffer_Read( RingBuffer *rbuf, void *data, long numBytes ); + +/* Get address of region(s) to which we can write data. +** If the region is contiguous, size2 will be zero. +** If non-contiguous, size2 will be the size of second region. +** Returns room available to be written or numBytes, whichever is smaller. +*/ +long PD_RingBuffer_GetWriteRegions( RingBuffer *rbuf, long numBytes, + void **dataPtr1, long *sizePtr1, + void **dataPtr2, long *sizePtr2 ); +long PD_RingBuffer_AdvanceWriteIndex( RingBuffer *rbuf, long numBytes ); + +/* Get address of region(s) from which we can read data. +** If the region is contiguous, size2 will be zero. +** If non-contiguous, size2 will be the size of second region. +** Returns room available to be read or numBytes, whichever is smaller. +*/ +long PD_RingBuffer_GetReadRegions( RingBuffer *rbuf, long numBytes, + void **dataPtr1, long *sizePtr1, + void **dataPtr2, long *sizePtr2 ); + +long PD_RingBuffer_AdvanceReadIndex( RingBuffer *rbuf, long numBytes ); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* _PD_RINGBUFFER_H */ diff --git a/desiredata/src/s_audio_portaudio.c b/desiredata/src/s_audio_portaudio.c new file mode 100755 index 00000000..05b11643 --- /dev/null +++ b/desiredata/src/s_audio_portaudio.c @@ -0,0 +1,416 @@ +/* Copyright (c) 2001 Miller Puckette and others. + * Copyright (c) 2005-2006 Tim Blechmann + * supported by vibrez.net + * 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. */ + +/* tb: requires portaudio >= V19 */ + +#include "m_pd.h" +#include "s_stuff.h" +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <portaudio.h> +#include <errno.h> +#include "assert.h" +#ifdef MSW +# include <malloc.h> +# include <pa_asio.h> +#else +# include <alloca.h> +#endif +#include "pthread.h" +/* for M_PI */ +#if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES) +#define _USE_MATH_DEFINES +#endif +#include <math.h> +#define MAX_PA_CHANS 32 + +static int pa_inchans, pa_outchans; +static int pa_blocksize; + +static PaStream *pa_stream; +/* Initialize PortAudio */ +PaError pa_status = -1; +int pa_initialized = 0; + +void pa_initialize() { +// if (pa_initialized) return; + pa_status = Pa_Initialize(); + if (pa_status!=paNoError) { + error("Error number %d occured initializing portaudio: %s", pa_status, Pa_GetErrorText(pa_status)); + return; + } + pa_initialized = 1; +} + +static float* pa_inbuffer[MAX_PA_CHANS]; +static float* pa_outbuffer[MAX_PA_CHANS]; +static int pa_bufferpos; +static int pddev2padev(int pdindev,int isinput); +static int padev2pddev(int padev,int isinput); +int process (const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, + PaStreamCallbackFlags statusFlags, void *userData); + +static int pa_indev = -1, pa_outdev = -1; + +int pa_open_audio(int inchans, int outchans, int rate, int advance, +int indeviceno, int outdeviceno, int schedmode) { + PaError err; + const PaDeviceInfo *pdi,*pdo; + schedmode = 1; /* we don't support blocking io */ + pa_initialize(); + sys_setscheduler(schedmode); + /* post("in %d out %d rate %d device %d", inchans, outchans, rate, deviceno); */ + 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;} + pdi = NULL; + if (inchans > 0) { + pa_indev = pddev2padev(indeviceno,1); + if(pa_indev >= 0) { + pdi = Pa_GetDeviceInfo(pa_indev); + if(pdi->maxInputChannels < inchans) inchans = pdi->maxInputChannels; + } + } + pdo = NULL; + if (outchans > 0) { + pa_outdev = pddev2padev(outdeviceno,0); + if(pa_outdev >= 0) { + pdo = Pa_GetDeviceInfo(pa_outdev); + if(pdo->maxOutputChannels < outchans) outchans = pdo->maxOutputChannels; + } + } + if (sys_verbose) { + post("input device %d, channels %d", pa_indev, inchans); + post("output device %d, channels %d", pa_outdev, outchans); + post("latency advance %d", advance); + } + if (inchans || outchans) { + int blocksize; + PaStreamParameters iparam,oparam; + /* initialize input */ + iparam.device = pa_indev; + iparam.channelCount = inchans; + iparam.sampleFormat = paFloat32 | paNonInterleaved; + iparam.suggestedLatency = advance * 0.001; + iparam.hostApiSpecificStreamInfo = NULL; + /* initialize output */ + oparam.device = pa_outdev; + oparam.channelCount = outchans; + oparam.sampleFormat = paFloat32 | paNonInterleaved; + oparam.suggestedLatency = advance * 0.001; + oparam.hostApiSpecificStreamInfo = NULL; + /* set block size */ + blocksize=64; + while ((float)blocksize/(float)rate*1000*2 < advance && blocksize==1024) blocksize *= 2; + pa_blocksize = blocksize; + /* initialize io buffer */ + for (int j=0; j != MAX_PA_CHANS;++j) { + if (pa_inbuffer[j]) freealignedbytes(pa_inbuffer[j], 0); + if (pa_outbuffer[j]) freealignedbytes(pa_outbuffer[j], 0); + pa_inbuffer[j] = (float *)getalignedbytes((blocksize + sys_dacblocksize)*sizeof(float)); + pa_outbuffer[j] = (float *)getalignedbytes((blocksize + sys_dacblocksize)*sizeof(float)); + } + pa_bufferpos = 0; + /* report to portaudio */ + err = Pa_OpenStream(&pa_stream, + (( pa_indev!=-1) ? &iparam : 0), + ((pa_outdev!=-1) ? &oparam : 0), + rate, pa_blocksize, paClipOff, /* tb: we should be faster ;-) */ process /* patestCallback */, NULL); + if (err == paNoError) { + const PaStreamInfo *streaminfo = Pa_GetStreamInfo(pa_stream); + t_atom atoms[4]; + t_symbol *pd = gensym("pd"); + t_symbol *selector1 = gensym("audiocurrentininfo"); + t_symbol *selector2 = gensym("audiocurrentoutinfo"); + sys_schedadvance = int(1e-6 * streaminfo->outputLatency); + + SETFLOAT(atoms, (float)indeviceno); + SETFLOAT(atoms+1, (float)inchans); + SETFLOAT(atoms+2, (float)rate); + SETFLOAT(atoms+3, (float)streaminfo->inputLatency * 1000.f); + typedmess(pd->s_thing, selector1, 4, atoms); + + SETFLOAT(atoms, (float)outdeviceno); + SETFLOAT(atoms+1, (float)outchans); + SETFLOAT(atoms+2, (float)rate); + SETFLOAT(atoms+3, (float)streaminfo->outputLatency * 1000.f); + typedmess(pd->s_thing, selector2, 4, atoms); + } + } else err = 0; + + if (err != paNoError) { + error("Error number %d occured opening portaudio stream: %s", err, Pa_GetErrorText(err)); + sys_inchannels = sys_outchannels = 0; + pa_indev = pa_outdev = -1; + pa_inchans = pa_outchans = 0; + return 1; + } else if (sys_verbose) post("... opened OK."); + pa_inchans = inchans; + pa_outchans = outchans; + + /* we might have adapted the channel count */ + sys_setchsr(inchans, outchans, rate, sys_dacblocksize); + err = Pa_StartStream(pa_stream); + if (err!=paNoError) { + post("Error number %d occured starting portaudio stream: %s", err, Pa_GetErrorText(err)); + sys_inchannels = sys_outchannels = 0; + return 1; + } + if(sys_verbose) post("successfully started"); + return 0; +} + +void sys_peakmeters(); +extern int sys_meters; /* true if we're metering */ + +void run_all_idle_callbacks(); +void sys_xrun_notification(); /* in m_sched.c */ +void sys_lock_timeout_notification(); + +int process (const void *input, void *output, unsigned long frameCount, const PaStreamCallbackTimeInfo* timeInfo, +PaStreamCallbackFlags statusFlags, void *userData) { + int timeout = int((float)frameCount / (float) sys_dacsr * 1e6); + if (statusFlags) sys_xrun_notification(); + if (sys_timedlock(timeout) == ETIMEDOUT) /* we're late */ { + sys_lock_timeout_notification(); + return 0; + } + for (int i=0; (unsigned)i < frameCount / sys_dacblocksize; ++i) { + for (int j=0; j < sys_inchannels; j++) { + t_sample *in = ((t_sample**)input)[j] + i * sys_dacblocksize; + copyvec(sys_soundin + j * sys_dacblocksize, in, sys_dacblocksize); + } + sched_tick(sys_time + sys_time_per_dsp_tick); + for (int j=0; j < sys_outchannels; j++) { + t_sample *out = ((t_sample**)output)[j] + i * sys_dacblocksize; + copyvec(out, sys_soundout + j * sys_dacblocksize, sys_dacblocksize); + } + if (sys_meters) sys_peakmeters(); + zerovec(sys_soundout, pa_outchans * sys_dacblocksize); + } + run_all_idle_callbacks(); + sys_unlock(); + return 0; +} + +void pa_close_audio() { + if(sys_verbose) post("closing portaudio"); + if (pa_inchans || pa_outchans) { + if (pa_stream) { + int status = Pa_StopStream(pa_stream); + if (status) post("error closing audio: %d", status); + Pa_CloseStream(pa_stream); + pa_stream = NULL; + } + } + sys_setscheduler(0); + if(sys_verbose) post("portaudio closed"); + pa_inchans = pa_outchans = 0; + pa_indev = pa_outdev = -1; +} + +/* for blocked IO */ +int pa_send_dacs() { + /* we don't support blocking i/o */ + return SENDDACS_NO; +} + +/* lifted from pa_devs.c in portaudio */ +void pa_listdevs() { + PaError err; + pa_initialize(); + int numDevices = Pa_GetDeviceCount(); + if(numDevices < 0) { + error("ERROR: Pa_GetDeviceCount returned %d", numDevices); + err = numDevices; + goto error; + } + post("Audio Devices:"); + for(int i=0; i<numDevices; i++) { + const PaDeviceInfo *pdi = Pa_GetDeviceInfo(i); + post ("device %s", pdi->name); + post("device %d:", i+1); + post(" %s;", pdi->name); + post("%d inputs, ", pdi->maxInputChannels); + post("%d outputs ", pdi->maxOutputChannels); + if (i == Pa_GetDefaultInputDevice()) post(" (Default Input)"); + if (i == Pa_GetDefaultOutputDevice()) post(" (Default Output)"); + post(""); + } + post(""); + return; + error: + error("Error #%d occurred while using the portaudio stream: %s\n", err, Pa_GetErrorText(err)); +} + +/* scanning for devices */ +void pa_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) { + int nin = 0, nout = 0, ndev; + *canmulti = 1; /* one dev each for input and output */ + pa_initialize(); + ndev = Pa_GetDeviceCount(); + for (int i=0; i<ndev; i++) { + const PaDeviceInfo *pdi = Pa_GetDeviceInfo(i); + if (pdi->maxInputChannels > 0 && nin < maxndev) { + PaHostApiIndex api = pdi->hostApi; + const PaHostApiInfo *info = Pa_GetHostApiInfo(api); + const char *apiName = info->name; + unsigned int apiNameLen = strlen(apiName); + strcpy(indevlist + nin * devdescsize, apiName); + indevlist[nin * devdescsize + apiNameLen] = '/'; + strcpy(indevlist + nin * devdescsize + apiNameLen + 1, pdi->name); + nin++; + } + if (pdi->maxOutputChannels > 0 && nout < maxndev) { + PaHostApiIndex api = pdi->hostApi; + const PaHostApiInfo *info = Pa_GetHostApiInfo(api); + const char *apiName = info->name; + unsigned int apiNameLen = strlen(apiName); + strcpy(outdevlist + nout * devdescsize, apiName); + outdevlist[nout * devdescsize + apiNameLen] = '/'; + strcpy(outdevlist + nout * devdescsize + apiNameLen + 1, pdi->name); + nout++; + } + } + *nindevs = nin; + *noutdevs = nout; +} + +void pa_getaudioininfo(t_float f) { + int i = pddev2padev((int)f,1); + const PaDeviceInfo *pdi; + pa_initialize(); + pdi = Pa_GetDeviceInfo(i); + if (pdi) { + t_symbol *selector = gensym("audioininfo"); + t_symbol *pd = gensym("pd"); + t_atom argv[4]; + SETFLOAT(argv, pdi->maxInputChannels); + SETFLOAT(argv+1, pdi->defaultSampleRate); + SETFLOAT(argv+2, pdi->defaultLowInputLatency*1000.f); + SETFLOAT(argv+3, pdi->defaultHighInputLatency*1000.f); + typedmess(pd->s_thing, selector, 4, argv); + } +} + +void pa_getaudiooutinfo(t_float f) { + int i = pddev2padev((int)f,0); + const PaDeviceInfo *pdi; + pa_initialize(); + pdi = Pa_GetDeviceInfo(i); + if (pdi) { + t_symbol *selector = gensym("audiooutinfo"); + t_symbol *pd = gensym("pd"); + t_atom argv[4]; + SETFLOAT(argv, pdi->maxOutputChannels); + SETFLOAT(argv+1, pdi->defaultSampleRate); + SETFLOAT(argv+2, pdi->defaultLowOutputLatency*1000.f); + SETFLOAT(argv+3, pdi->defaultHighOutputLatency*1000.f); + typedmess(pd->s_thing, selector, 4, argv); + } +} + +void pa_getcurrent_devices() { + t_symbol *pd = gensym("pd"); + t_symbol *selector = gensym("audiodevice"); + t_atom argv[2]; + SETFLOAT(argv, padev2pddev(pa_indev,1)); + SETFLOAT(argv+1, padev2pddev(pa_outdev,0)); + typedmess(pd->s_thing, selector, 2, argv); +} + +void pa_test_setting (int ac, t_atom *av) { + int indev = atom_getintarg(0, ac, av); + int outdev = atom_getintarg(1, ac, av); + int samplerate = atom_getintarg(2, ac, av); + int inchans = atom_getintarg(3, ac, av); + int outchans = atom_getintarg(4, ac, av); + int advance = atom_getintarg(5, ac, av); + t_symbol *pd = gensym("pd"); + t_symbol *selector = gensym("testaudiosettingresult"); + t_atom argv[1]; + pa_initialize(); + indev = pddev2padev(indev,1); + outdev = pddev2padev(outdev,0); + if (pa_indev==-1 && pa_outdev==-1) { + int ret; + PaStreamParameters iparam, oparam; + iparam.device = indev; + iparam.channelCount = inchans; + iparam.sampleFormat = paFloat32 | paNonInterleaved; + iparam.suggestedLatency = advance * 0.001; + iparam.hostApiSpecificStreamInfo = NULL; + oparam.device = outdev; + oparam.channelCount = outchans; + oparam.sampleFormat = paFloat32 | paNonInterleaved; + oparam.suggestedLatency = advance * 0.001; + oparam.hostApiSpecificStreamInfo = NULL; + ret = Pa_IsFormatSupported(&iparam, &oparam, samplerate); + SETFLOAT(argv, ret == paNoError?1:0); + typedmess(pd->s_thing, selector, 1, argv); + } +} + +static int pddev2padev(int pddev,int input) { + pa_initialize(); + for (int j=0, devno=0; j < Pa_GetDeviceCount(); j++) { + const PaDeviceInfo *info = Pa_GetDeviceInfo(j); + int maxchans = input?info->maxInputChannels:info->maxOutputChannels; + if (maxchans > 0) { + if (devno == pddev) return j; + devno++; + } + } + return -1; +} + +static int padev2pddev(int padev,int input) { + int count = Pa_GetDeviceCount(); + for (int j=0, devno=0; j < count; j++) { + const PaDeviceInfo *info = Pa_GetDeviceInfo(j); + int chans = input?info->maxInputChannels:info->maxOutputChannels; + if (chans > 0) { + if(j == padev) return devno; + devno++; + } + } + return -1; // no found +} + +void pa_get_asio_latencies(t_float f) { + int index = pddev2padev((int)f,0); + const PaDeviceInfo *pdi = Pa_GetDeviceInfo(index); + const PaHostApiInfo *phi = Pa_GetHostApiInfo(pdi->hostApi); + if (phi->type != paASIO) { + post("device not an asio device"); + return; + } +#ifdef WIN32 + else { + long minlat, maxlat, preflat, gran; + t_atom argv[4]; + t_symbol *selector = gensym("asiolatency"); + t_symbol *pd = gensym("pd"); + PaAsio_GetAvailableLatencyValues(index, &minlat, &maxlat, &preflat, &gran); + SETFLOAT(argv, (float) minlat); + SETFLOAT(argv + 1, (float) maxlat); + SETFLOAT(argv + 2, (float) preflat); + SETFLOAT(argv + 3, (float) gran); + typedmess(pd->s_thing, selector, 4, argv); + } +#endif +} + +t_audioapi pa_api = { + 0 /* pa_open_audio */, + pa_close_audio, + pa_send_dacs, + pa_getdevs, +}; diff --git a/desiredata/src/s_audio_sgi.c b/desiredata/src/s_audio_sgi.c new file mode 100644 index 00000000..878cf255 --- /dev/null +++ b/desiredata/src/s_audio_sgi.c @@ -0,0 +1,313 @@ +/* ----------------------- Experimental routines for SGI -------------- */ +/* written by Olaf Matthes <olaf.matthes@gmx.de> */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <m_pd.h> +#include <s_stuff.h> + +#include <dmedia/audio.h> +#include <dmedia/midi.h> +#include <sys/fpu.h> +#include <errno.h> + +#define SGI_MAXDEV 4 /* it's just 3, but default counts as well... */ +#define SGI_MAXCH 12 /* max. number of channels - is this enough? */ + +static ALport sgi_iport[SGI_MAXDEV]; +static ALport sgi_oport[SGI_MAXDEV]; +static ALconfig sgi_inconfig; +static ALconfig sgi_outconfig; +static int sgi_nindevs, sgi_noutdevs; +static int sgi_inchannels, sgi_outchannels; +static int sgi_ninchans[SGI_MAXDEV], sgi_noutchans[SGI_MAXDEV]; + +/* 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 () { + union fpc_csr f; + f.fc_word = get_fpc_csr(); + f.fc_struct.flush = 1; + set_fpc_csr(f.fc_word); +} + +/* convert the most common errors into readable strings */ +static char *sgi_get_error_message(int err) { + switch (err) { + case AL_BAD_CONFIG: return "Invalid config"; + case AL_BAD_DIRECTION: return "Invalid direction (neither \"r\" nor \"w\")"; + case AL_BAD_OUT_OF_MEM: return "Not enough memory"; + case AL_BAD_DEVICE_ACCESS: return "Audio hardware is not available or is improperly configured"; + case AL_BAD_DEVICE: return "Invalid device"; + case AL_BAD_NO_PORTS: return "No audio ports available"; + case AL_BAD_QSIZE: return "Invalid fifo size"; + case AL_BAD_SAMPFMT: return "Invalid sample format"; + case AL_BAD_FLOATMAX: return "Invalid float maximum"; + case AL_BAD_WIDTH: return "Invalid sample width"; + default: return "Unknown error"; + } +} + +int sgi_open_audio(int nindev, int *indev, int nchin, int *chin, +int noutdev, int *outdev, int nchout, int *chout, int rate/*, int dummy*/) { + ALpv pvbuf[2]; + int num_devs = 0; + int in_dev = 0; + int out_dev = 0; + int inchans = 0; + int outchans = 0; + int err, n, gotchannels; + char *indevnames[4] = {"DefaultIn", "AnalogIn", "AESIn", "ADATIn"}; + char *outdevnames[4] = {"DefaultOut", "AnalogOut", "AESOut", "ADATOut"}; + sgi_flush_all_underflows_to_zero(); + if (sys_verbose) post("opening sound input..."); + for (n = 0; n < nindev; n++) { + int gotchannels = 0; + if (indev[n] >= 0 && indev[n] < SGI_MAXDEV) { + if (sys_verbose) post("opening %s", indevnames[indev[n]]); + in_dev = alGetResourceByName(AL_SYSTEM, indevnames[indev[n]], AL_DEVICE_TYPE); + } else { + if(sys_verbose)post("opening %s", indevnames[0]); + in_dev = AL_DEFAULT_INPUT; + } + if (!in_dev) { + error("%s", sgi_get_error_message(in_dev)); + continue; /* try next device, if any */ + } + sgi_inconfig = alNewConfig(); + alSetSampFmt(sgi_inconfig, AL_SAMPFMT_FLOAT); + alSetFloatMax(sgi_outconfig, 1.1f); + alSetChannels(sgi_outconfig, chin[n]); + alSetQueueSize(sgi_inconfig, sys_advance_samples * chin[n]); + alSetDevice(sgi_inconfig, in_dev); + sgi_iport[n] = alOpenPort("Pd input port", "r", sgi_inconfig); + if (!sgi_iport[n]) error("failed to open audio read port"); + /* try to set samplerate */ + pvbuf[0].param = AL_RATE; + pvbuf[0].value.ll = alDoubleToFixed(rate); + if ((err = alSetParams(in_dev, pvbuf, 1)) < 0) { + post("could not set specified sample rate for input (%s)", sgi_get_error_message(err)); + if(pvbuf[0].sizeOut < 0) post("rate was invalid"); + } + /* check how many channels we actually got */ + pvbuf[0].param = AL_CHANNELS; + if (alGetParams(in_dev, pvbuf, 1) < 0) { + post("could not figure out how many input channels we got"); + gotchannels = chin[n]; /* assume we got them all */ + } else { + gotchannels = pvbuf[0].value.i; + } + inchans += gotchannels; /* count total number of channels */ + sgi_ninchans[n] = gotchannels; /* remember channels for this device */ + } + if (sys_verbose) post("opening sound output..."); + for (n = 0; n < noutdev; n++) { + if (outdev[n] >= 0 && outdev[n] < SGI_MAXDEV) { + if(sys_verbose)post("opening %s", outdevnames[outdev[n]]); + out_dev = alGetResourceByName(AL_SYSTEM, outdevnames[outdev[n]], AL_DEVICE_TYPE); + } else { + if (sys_verbose) post("opening %s", outdevnames[0]); + out_dev = AL_DEFAULT_OUTPUT; + } + if (!out_dev) { + error("%s", sgi_get_error_message(out_dev)); + continue; /* try next device, if any */ + } + /* configure the port before opening it */ + sgi_outconfig = alNewConfig(); + alSetSampFmt(sgi_outconfig, AL_SAMPFMT_FLOAT); + alSetFloatMax(sgi_outconfig, 1.1f); + alSetChannels(sgi_outconfig, chout[n]); + alSetQueueSize(sgi_outconfig, sys_advance_samples * chout[n]); + alSetDevice(sgi_outconfig, out_dev); + /* open the port */ + sgi_oport[n] = alOpenPort("Pd ouput port", "w", sgi_outconfig); + if (!sgi_oport[n]) error("failed to open audio write port"); + /* now try to set sample rate */ + pvbuf[0].param = AL_RATE; + pvbuf[0].value.ll = alDoubleToFixed(rate); + if ((err = alSetParams(out_dev, pvbuf, 1)) < 0) { + post("could not set specified sample rate for output (%s)", sgi_get_error_message(err)); + if(pvbuf[0].sizeOut < 0) post("rate was invalid"); + } + /* check how many channels we actually got */ + pvbuf[0].param = AL_CHANNELS; + if (alGetParams(out_dev, pvbuf, 1) < 0) { + post("could not figure out how many output channels we got"); + gotchannels = chout[n]; + } else { + gotchannels = pvbuf[0].value.i; + } + outchans += gotchannels; + sgi_noutchans[n] = gotchannels; + } + sgi_noutdevs = noutdev; + sgi_nindevs = nindev; + sgi_inchannels = inchans; + sgi_outchannels = outchans; + return !(inchans || outchans); +} + +void sgi_close_audio() { + int err, n; + for (n = 0; n < sgi_nindevs; n++) { + if (sgi_iport[n]) { + err = alClosePort(sgi_iport[n]); + if (err < 0) error("closing input %d: %s (%d)", n + 1, alGetErrorString(oserror()), err); + } + } + for (n = 0; n < sgi_noutdevs; n++) { + if (sgi_oport[n]) { + err = alClosePort(sgi_oport[n]); + if (err < 0) error("closing output %d: %s (%d)", n + 1, alGetErrorString(oserror()), err); + } + } +} + +/* call this only if both input and output are open */ +static void sgi_checkiosync() { +// int i, result, checkit = 1, giveup = 1000, alreadylogged = 0; +// long indelay, outdelay, defect; +// if (!(sgi_outchannels && sgi_inchannels)) return; +} + +int sgi_send_dacs() { + float buf[SGI_MAXCH * sys_dacblocksize], *fp1, *fp2, *fp3, *fp4; + static int xferno = 0; + static int callno = 0; + static double timenow; + double timelast; + int inchannels = min(sys_inchannels, sgi_inchannels); + int outchannels = min(sys_outchannels,sgi_outchannels); + long outfill[SGI_MAXDEV], infill[SGI_MAXDEV]; + int outdevchannels, indevchannels; + int i, n, channel; + int result; + unsigned int outtransfersize = sys_dacblocksize; + unsigned int intransfersize = sys_dacblocksize; + /* no audio channels open, return */ + if (!inchannels && !outchannels) return SENDDACS_NO; + timelast = timenow; + timenow = sys_getrealtime(); +#ifdef DEBUG_SGI_XFER + if (timenow - timelast > 0.050) post("(%d)", (int)(1000 * (timenow - timelast))); +#endif + callno++; + sgi_checkiosync(); /* check I/O are in sync and data not late */ + /* check whether there is enough space in buffers */ + if (sgi_nindevs) { + for (n = 0; n < sgi_nindevs; n++) { + if (alGetFilled(sgi_iport[n]) < intransfersize) + return SENDDACS_NO; + } + } + if (sgi_noutdevs) { + for(n = 0; n < sgi_noutdevs; n++) { + if (alGetFillable(sgi_oport[n]) < outtransfersize) + return SENDDACS_NO; + } + } + /* output audio data, if we use audio out */ + if (sgi_noutdevs) { + fp2 = sys_soundout; /* point to current output position in buffer */ + for(n = 0; n < sgi_noutdevs; n++) { + outdevchannels = sgi_noutchans[n]; /* channels supported by this device */ + for (channel = 0, fp1 = buf; channel < outdevchannels; channel++, fp1++, fp2 += sys_dacblocksize) { + for (i = 0, fp3 = fp1, fp4 = fp2; i < sys_dacblocksize; i++, fp3 += outdevchannels, fp4++) *fp3 = *fp4, *fp4 = 0; + } + alWriteFrames(sgi_oport[n], buf, sys_dacblocksize); + } + } + /* zero out the output buffer */ + memset(sys_soundout, 0, sys_dacblocksize * sizeof(*sys_soundout) * sys_outchannels); + if (sys_getrealtime() - timenow > 0.002) { + #ifdef DEBUG_SGI_XFER + post("output %d took %d msec", callno, (int)(1000 * (timenow - timelast))); + #endif + timenow = sys_getrealtime(); + sys_log_error(ERR_DACSLEPT); + } + /* get audio data from input, if we use audio in */ + if (sgi_nindevs) { + fp2 = sys_soundin; /* point to current input position in buffer */ + for (n = 0; n < sgi_nindevs; n++) { + indevchannels = sgi_ninchans[n]; /* channels supported by this device */ + if (alGetFilled(sgi_iport[n]) > sys_dacblocksize) { + alReadFrames(sgi_iport[n], buf, sys_dacblocksize); + } else /* have to read but nothing's there... */ { + // if (sys_verbose) post("extra ADC buf"); + /* set buffer to silence */ + memset(buf, 0, intransfersize * sizeof(*sys_soundout) * sgi_ninchans[n]); + } + for (channel = 0, fp1 = buf; channel < indevchannels; channel++, fp1++, fp2 += sys_dacblocksize) { + for (i = 0, fp3 = fp1, fp4 = fp2; i < sys_dacblocksize; i++, fp3 += indevchannels, fp4++) *fp4 = *fp3; + } + } + } + xferno++; + if (sys_getrealtime() - timenow > 0.002) { +#ifdef DEBUG_SGI_XFER + post("routine took %d msec", int(1000 * (sys_getrealtime() - timenow))); +#endif + sys_log_error(ERR_ADCSLEPT); + } + return SENDDACS_YES; +} + +void sgi_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize) { +#if 0 + ALpv pvs[1]; + char name[32]; + int i, ndev, err; + *canmulti = 3; /* supports multiple devices */ + /* get max. number of audio ports from system */ + pvs[0].param = AL_DEVICES; + err = alGetParams(AL_SYSTEM, pvs, 1); + if (err < 0) { + sprintf("alGetParams failed: %s\n", alGetErrorString(oserror())); + } + ndev = pvs[0].value.i; + for (i = 0; i < ndev; i++) { + pvs[0].param = AL_NAME; /* a string parameter */ + pvs[0].value.ptr = name; /* the string we've allocated */ + pvs[0].sizeIn = 32; /* the array size, in characters, including space for NULL */ + if (alGetParams(i, pvs, 1) < 0) { + sprintf("alGetParams failed: %s\n", alGetErrorString(oserror())); + } + sprintf(indevlist + i * devdescsize, "%d: %s", i + 1, name); + sprintf(outdevlist + i * devdescsize, "%d: %s", i + 1, name); + } + *nindevs = ndev; + *noutdevs = ndev; +#else + sprintf( indevlist + 0 * devdescsize, "Default In"); + sprintf(outdevlist + 0 * devdescsize, "Default Out"); + sprintf( indevlist + 1 * devdescsize, "Analog In"); + sprintf(outdevlist + 1 * devdescsize, "Analog Out"); + sprintf( indevlist + 2 * devdescsize, "AES In"); + sprintf(outdevlist + 2 * devdescsize, "AES Out"); + sprintf( indevlist + 3 * devdescsize, "ADAT In"); + sprintf(outdevlist + 3 * devdescsize, "ADAT Out"); + *nindevs = 4; + *noutdevs = 4; + *canmulti = 3; /* supports multiple devices */ +#endif +} + +/* list devices: only reflect the most common setup (Octane) */ +void sgi_listaudiodevs() { + post("common devices on SGI machines:"); + post("#-1 - Default In/Out selected in Audio Panel"); + post("#1 - Analog In/Out"); + post("#2 - AES In/Out"); + post("#3 - ADAT I/O"); +} + +struct t_audioapi sgi_api = { + sgi_open_audio, + sgi_close_audio, + sgi_send_dacs, + sgi_getdevs, +}; diff --git a/desiredata/src/s_inter.c b/desiredata/src/s_inter.c new file mode 100644 index 00000000..ad19ceb1 --- /dev/null +++ b/desiredata/src/s_inter.c @@ -0,0 +1,732 @@ +/* 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. Also, some system interface routines +that didn't really belong anywhere. */ + +#define WATCHDOGTHREAD + +#define PD_PLUSPLUS_FACE +#include "desire.h" +#include "pthread.h" +#include <sstream> +#ifdef UNISTD +#include <unistd.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/mman.h> +#include <sys/resource.h> +#endif +#ifdef HAVE_BSTRING_H +#include <bstring.h> +#endif +#if defined(_WIN32) && !defined(__CYGWIN__) +#include <io.h> +#include <fcntl.h> +#include <process.h> +#include <winsock.h> +#include <windows.h> +#ifdef _MSC_VER +typedef int pid_t; +#endif +typedef int socklen_t; +#define EADDRINUSE WSAEADDRINUSE +#endif + +#include <stdarg.h> +#include <signal.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <stdio.h> + +#ifndef SIGIOT +#define SIGIOT SIGABRT +#endif + +#ifdef __APPLE__ +#include <sys/types.h> +#include <sys/stat.h> +#include <pthread.h> +#else +#include <stdlib.h> +#endif + +#define DEBUG_MESSUP 1 /* messages up from pd to pd-gui */ +#define DEBUG_MESSDOWN 2 /* messages down from pd-gui to pd */ + +/* T.Grill - make it a _little_ more adaptable... */ +#ifndef PDBINDIR +#define PDBINDIR "bin/" +#endif + +#ifndef WISHAPP +#define WISHAPP "wish84.exe" +#endif + +#ifdef __linux__ +#define LOCALHOST "127.0.0.1" +#else +#define LOCALHOST "localhost" +#endif + +struct t_fdpoll { + int fdp_fd; + t_fdpollfn fdp_fn; + void *fdp_ptr; +}; + +#define INBUFSIZE 16384 + +extern int sys_guisetportnumber; +static int sys_nfdpoll; +static t_fdpoll *sys_fdpoll; +static int sys_maxfd; +t_text *sys_netreceive; +static t_binbuf *inbinbuf; +t_socketreceiver *sys_socketreceiver; +extern int sys_addhist(int phase); + +/* ----------- functions for timing, signals, priorities, etc --------- */ + +#ifdef _WIN32 +static LARGE_INTEGER nt_inittime; +static double nt_freq = 0; + +static void sys_initntclock() { + 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 /* _WIN32 */ + + /* get "real time" in seconds; take the + first time we get called as a reference time of zero. */ +double sys_getrealtime() { +#ifndef _WIN32 + 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); +#else + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + if (nt_freq == 0) sys_initntclock(); + return double(now.QuadPart - nt_inittime.QuadPart) / nt_freq; +#endif +} + +int sys_pollsockets () { + struct timeval timout; + int didsomething = 0; + fd_set readset, writeset, exceptset; + timout.tv_sec = 0; + timout.tv_usec = 0; + FD_ZERO(&writeset); + FD_ZERO(&readset); + FD_ZERO(&exceptset); + t_fdpoll *fp = sys_fdpoll; + for (int i=sys_nfdpoll; i--; fp++) FD_SET(fp->fdp_fd, &readset); + select(sys_maxfd+1, &readset, &writeset, &exceptset, &timout); + for (int 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; +} + +void sys_microsleep(int microsec) { + /* Tim says: sleep granularity on "modern" operating systems is only 1ms??? + - linux: we might be better with the high precision posix timer kernel patches ... + - windows: win9x doesn't implement a SwitchToThread function, so we can't sleep for small timeslices + - osx: ??? + */ + if (!sys_callbackscheduler && microsec < 1000) { + if (500 < microsec) microsec = 1000; else return; + } +#ifndef MSW + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = microsec; + select(0,0,0,0,&timeout); + +#else + Sleep(microsec/1000); +#endif + /* a solution for lower timeslices might be a busysleep but this might + block a low-priority thread and won't work for win9x +#define _WIN32_WINNT 0x0400 + double end = sys_getrealtime() + (double)microsec * 1e-6; + do { +#ifdef MSW + SwitchToThread(); +#else + sched_yield(); +#endif + } + while(sys_getrealtime() < end); + */ +} + +#ifdef UNISTD +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)); +#if 0 /* GG says: don't use that */ + 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 sys_bail(0); +} + +static void sys_alarmhandler(int n) { + fprintf(stderr, "Pd: system call timed out\n"); +} + +/* what is this for?? */ +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 it; + it.it_interval.tv_sec = 0; + it.it_interval.tv_usec = 0; + it.it_value.tv_sec = microsec/1000000; + it.it_value.tv_usec = microsec%1000000; + sys_signal(SIGALRM, microsec ? sys_alarmhandler : SIG_IGN); + setitimer(ITIMER_REAL, &it, 0); +} + +#endif + +#if defined(__linux) || defined(__APPLE__) +#if (_POSIX_PRIORITY_SCHEDULING - 0) >= 200112L || (_POSIX_MEMLOCK - 0) >= 200112L +#include <sched.h> +#endif +#if (_POSIX_MEMLOCK - 0) >= 200112L +#include <sys/resource.h> +#endif +void sys_set_priority(int higher) { +#if (_POSIX_PRIORITY_SCHEDULING - 0) >= 200112L + struct sched_param par; +#ifdef USEAPI_JACK + int p1 = sched_get_priority_min(SCHED_FIFO); + int p3 = (higher ? p1 + 7 : p1 + 5); +#else + int p2 = sched_get_priority_max(SCHED_FIFO); + int p3 = (higher ? p2 - 1 : p2 - 3); +#endif + par.sched_priority = p3; + if (sched_setscheduler(0,SCHED_FIFO,&par) != -1) + fprintf(stderr, "priority %d scheduling enabled.\n", p3); +#endif +#if (_POSIX_MEMLOCK - 0) >= 200112L + /* tb: force memlock to physical memory { */ + struct rlimit mlock_limit; + mlock_limit.rlim_cur=0; + /* tb: only if we are really root we can set the hard limit */ + mlock_limit.rlim_max = getuid() ? 100 : 0; + setrlimit(RLIMIT_MEMLOCK,&mlock_limit); + /* } tb */ + if (mlockall(MCL_FUTURE) != -1) fprintf(stderr, "memory locking enabled.\n"); +#endif +} +#endif /* __linux__ */ + +#ifdef IRIX /* hack by <olaf.matthes@gmx.de> at 2003/09/21 */ + +#if (_POSIX_PRIORITY_SCHEDULING - 0) >= 200112L || (_POSIX_MEMLOCK - 0) >= 200112L +#include <sched.h> +#endif + +void sys_set_priority(int higher) { +#if (_POSIX_PRIORITY_SCHEDULING - 0) >= 200112L + struct sched_param par; + /* Bearing the table found in 'man realtime' in mind, I found it a */ + /* good idea to use 192 as the priority setting for Pd. Any thoughts? */ + if (higher) par.sched_priority = 250; /* priority for watchdog */ + else par.sched_priority = 192; /* priority for pd (DSP) */ + if (sched_setscheduler(0, SCHED_FIFO, &par) != -1) + fprintf(stderr, "priority %d scheduling enabled.\n", par.sched_priority); +#endif + +#if (_POSIX_MEMLOCK - 0) >= 200112L + if (mlockall(MCL_FUTURE) != -1) fprintf(stderr, "memory locking enabled.\n"); +#endif +} +/* end of hack */ +#endif /* IRIX */ + +/* ------------------ receiving incoming messages over sockets ------------- */ + +void sys_sockerror(char *s) { +#ifdef _WIN32 + 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"); + } +#else + int err = errno; +#endif /* _WIN32 */ + 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); + sys_fdpoll = (t_fdpoll *)t_resizebytes(sys_fdpoll, size, size + sizeof(t_fdpoll)); + 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 size = nfd * sizeof(t_fdpoll); + t_fdpoll *fp = sys_fdpoll; + for (int i=nfd; 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); +} + +t_socketreceiver *socketreceiver_new(t_pd *owner, int fd, t_socketnotifier notifier, +t_socketreceivefn socketreceivefn, int udp) { + t_socketreceiver *x = (t_socketreceiver *)getbytes(sizeof(*x)); + x->inhead = x->intail = 0; + x->owner = owner; + x->notifier = notifier; + x->socketreceivefn = socketreceivefn; + x->udp = udp; + x->fd = fd; + x->obuf = 0; + x->next = 0; + x->inbuf = (char *)malloc(INBUFSIZE); + if (!x->inbuf) bug("t_socketreceiver"); + return x; +} + +void socketreceiver_free(t_socketreceiver *x) {free(x->inbuf); free(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 inhead = x->inhead; + int intail = x->intail; + char *inbuf = x->inbuf; + if (intail == inhead) return 0; + for (int i=intail; i!=inhead; i=(i+1)&(INBUFSIZE-1)) { + /* ";" not preceded by "\" is a message boundary in current syntax. + in future syntax it might become more complex. */ + char c = *bp++ = inbuf[i]; + if (c == ';' && (!i || inbuf[i-1] != '\\')) { + intail = (i+1)&(INBUFSIZE-1); + binbuf_text(inbinbuf, messbuf, bp - messbuf); + x->inhead = inhead; + x->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 (buf[ret-1] == '\n') { + char *semi = strchr(buf, ';'); + if (semi) *semi = 0; + binbuf_text(inbinbuf, buf, strlen(buf)); + outlet_setstacklim(); + x->socketreceivefn(x->owner, inbinbuf); + } else {/*bad buffer ignored */} + } +} + +void sys_exit(); + +void socketreceiver_read(t_socketreceiver *x, int fd) { + if (x->udp) {socketreceiver_getudp(x, fd); return;} + /* else TCP */ + int readto = x->inhead >= x->intail ? INBUFSIZE : x->intail-1; + /* the input buffer might be full. If so, drop the whole thing */ + if (readto == x->inhead) { + fprintf(stderr, "pd: dropped message from gui\n"); + x->inhead = x->intail = 0; + readto = INBUFSIZE; + } else { + int ret = recv(fd, x->inbuf+x->inhead, readto-x->inhead, 0); + if (ret<=0) { + if (ret<0) sys_sockerror("recv"); else post("EOF on socket %d", fd); + if (x->notifier) x->notifier(x->owner); + sys_rmpollfn(fd); + sys_closesocket(fd); + return; + } + x->inhead += ret; + if (x->inhead >= INBUFSIZE) x->inhead = 0; + while (socketreceiver_doread(x)) { + outlet_setstacklim(); + x->socketreceivefn(x->owner, inbinbuf); + } + } +} + +void sys_closesocket(int fd) { +#ifdef UNISTD + close(fd); +#endif +#ifdef _WIN32 + closesocket(fd); +#endif /* _WIN32 */ +} + +/* ---------------------- sending messages to the GUI ------------------ */ +#define GUI_ALLOCCHUNK 8192 +#define GUI_UPDATESLICE 512 /* how much we try to do in one idle period */ +#define GUI_BYTESPERPING 1024 /* how much we send up per ping */ + +static void sys_trytogetmoreguibuf(t_socketreceiver *self, int newsize) { + self->osize = newsize; + self->obuf = (char *)realloc(self->obuf, newsize); +} + +#undef max /* for msys compat */ +int max(int a, int b) { return ((a)>(b)?(a):(b)); } + +std::ostringstream lost_posts; + +void sys_vgui(char *fmt, ...) { + t_socketreceiver *self = sys_socketreceiver; + va_list ap; + va_start(ap, fmt); + if (!self) {voprintf(lost_posts,fmt,ap); va_end(ap); return;} + if (!self->obuf) { + self->obuf = (char *)malloc(GUI_ALLOCCHUNK); + self->osize = GUI_ALLOCCHUNK; + self->ohead = self->otail = 0; + } + if (self->ohead > self->osize - GUI_ALLOCCHUNK/2) + sys_trytogetmoreguibuf(self,self->osize + GUI_ALLOCCHUNK); + int msglen = vsnprintf(self->obuf+self->ohead, self->osize-self->ohead, fmt, ap); + va_end(ap); + if(msglen < 0) {fprintf(stderr, "Pd: buffer space wasn't sufficient for long GUI string\n"); return;} + if (msglen >= self->osize - self->ohead) { + int msglen2, newsize = self->osize+1+max(msglen,GUI_ALLOCCHUNK); + sys_trytogetmoreguibuf(self,newsize); + va_start(ap, fmt); + msglen2 = vsnprintf(self->obuf+self->ohead, self->osize-self->ohead, fmt, ap); + va_end(ap); + if (msglen2 != msglen) bug("sys_vgui"); + if (msglen >= self->osize-self->ohead) msglen = self->osize-self->ohead; + } + self->ohead += msglen; + self->bytessincelastping += msglen; +} + +void sys_gui(char *s) {sys_vgui("%s", s);} + +static int sys_flushtogui(t_socketreceiver *self) { + int writesize = self->ohead-self->otail; + if (!writesize) return 0; + int nwrote = send(self->fd, self->obuf+self->otail, writesize, 0); + if (nwrote < 0) { + perror("pd-to-gui socket"); + sys_bail(1); + } else if (!nwrote) { + return 0; + } else if (nwrote >= self->ohead-self->otail) { + self->ohead = self->otail = 0; + } else if (nwrote) { + self->otail += nwrote; + if (self->otail > self->osize>>2) { + memmove(self->obuf, self->obuf+self->otail, self->ohead-self->otail); + self->ohead -= self->otail; + self->otail = 0; + } + } + return 1; +} + +void glob_ping(t_pd *dummy) {t_socketreceiver *self = sys_socketreceiver; self->waitingforping = 0;} + +int sys_pollgui() { + if (sys_socketreceiver) sys_flushtogui(sys_socketreceiver); + return sys_pollsockets(); +} + +/* --------------------- starting up the GUI connection ------------- */ + +#ifdef __linux__ +void glob_watchdog(t_pd *dummy) { +#ifndef WATCHDOGTHREAD + if (write(sys_watchfd, "\n", 1) < 1) { + fprintf(stderr, "pd: watchdog process died\n"); + sys_bail(1); + } +#endif +} +#endif + +static void sys_setsignals() { +#ifdef UNISTD + 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, SIG_IGN); + signal(SIGALRM, SIG_IGN); +/* GG says: don't set SIGSTKFLT */ +#endif +} + +//t_pd *pd_new3(const char *s); + +extern "C" t_text *netreceive_new(t_symbol *compatflag, t_floatarg fportno, t_floatarg udpflag); +static int sys_start_watchdog_thread(); +static void sys_setpriority(); +extern t_text *manager; + +int sys_startgui() { +#ifdef _WIN32 + short version = MAKEWORD(2, 0); + WSADATA nobby; + if (WSAStartup(version, &nobby)) sys_sockerror("WSAstartup"); +#endif /* _WIN32 */ + /* create an empty FD poll list */ + sys_fdpoll = (t_fdpoll *)t_getbytes(0); + sys_nfdpoll = 0; + inbinbuf = binbuf_new(); + sys_setsignals(); + sys_netreceive = netreceive_new(&s_,sys_guisetportnumber,0); +// fprintf(stderr,"sys_netreceive=%p\n",sys_netreceive); + if (!sys_netreceive) return 0; +// obj_connect(sys_netreceive,0,(t_text *)pd_new3("print left"),0); +// obj_connect(sys_netreceive,1,(t_text *)pd_new3("print right"),0); + obj_connect(sys_netreceive,0,manager,0); +#if defined(__linux__) || defined(IRIX) +/* now that we've spun off the child process we can promote our process's + priority, if we can and want to. If not specfied (-1), we check root + status. This misses the case where we might have permission from a + "security module" (linux 2.6) -- I don't know how to test for that. + The "-rt" flag must be set in that case. */ + if (sys_hipriority == -1) sys_hipriority = !getuid() || !geteuid(); +#endif + if (sys_hipriority) sys_setpriority(); + setuid(getuid()); + return 0; +} + +/* 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.) */ +void sys_setpriority() { +#if defined(__linux__) || defined(IRIX) +#ifndef WATCHDOGTHREAD + int pipe9[2]; + if (pipe(pipe9) < 0) { + setuid(getuid()); + sys_sockerror("pipe"); + return 1; + } + int watchpid = fork(); + if (watchpid < 0) { + setuid(getuid()); + 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); + setuid(getuid()); + if (pipe9[1]) { + 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, (char*)0); + perror("pd: exec"); + _exit(1); + } else { /* we're the parent */ + sys_set_priority(0); + setuid(getuid()); + 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 + sys_start_watchdog_thread(); + sys_set_priority(0); +#endif +#endif /* __linux__ */ +#ifdef MSW + if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) + fprintf(stderr, "pd: couldn't set high priority class\n"); +#endif +#ifdef __APPLE__ + if (sys_hipriority) { + struct sched_param param; + int policy = SCHED_RR; + param.sched_priority = 80; /* adjust 0 : 100 */ + int err = pthread_setschedparam(pthread_self(), policy, ¶m); + if (err) post("warning: high priority scheduling failed"); + } +#endif /* __APPLE__ */ + if (!sys_nogui) { + /* here is where we start the pinging. */ +#if defined(__linux__) || defined(IRIX) +#ifndef WATCHDOGTHREAD + if (sys_hipriority) sys_gui("pdtk_watchdog\n"); +#endif +#endif + } +} + +/* This is called when something bad has happened, like a segfault. +Call glob_quit() below to exit cleanly. +LATER try to save dirty documents even in the bad case. */ +void sys_bail(int n) { + static int reentered = 0; + if (reentered) _exit(1); + reentered = 1; + fprintf(stderr, "closing audio...\n"); + sys_close_audio(); + fprintf(stderr, "closing MIDI...\n"); + sys_close_midi(); + fprintf(stderr, "... done.\n"); + exit(n); +} + +extern "C" void glob_closeall(void *dummy, t_floatarg fforce); + +void glob_quit(void *dummy) { + glob_closeall(0, 1); + sys_bail(0); +} + +static pthread_t watchdog_id; +static pthread_t main_pd_thread; +static pthread_mutex_t watchdog_mutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t watchdog_cond = PTHREAD_COND_INITIALIZER; +static void *watchdog_thread(void *); + +/* start a high priority watchdog thread */ +static int sys_start_watchdog_thread() { + pthread_attr_t w_attr; + main_pd_thread = pthread_self(); + pthread_attr_init(&w_attr); + pthread_attr_setschedpolicy(&w_attr, SCHED_FIFO); /* use rt scheduling */ + int status = pthread_create(&watchdog_id, &w_attr, (void*(*)(void*)) watchdog_thread, NULL); + return status; /* what is this function supposed to return anyway? it tried returning void though declared as int */ +} + +#ifdef MSW +int gettimeofday (struct timeval *tv, void *tz); +#endif + +static t_int* watchdog_callback(t_int *dummy) { + /* signal the condition */ + pthread_cond_signal(&watchdog_cond); + return 0; +} + +/* this watchdog thread registers an idle callback once a minute + if the idle callback isn't excecuted within one minute, we're probably + blocking the system. + kill the whole process! +*/ + +static void *watchdog_thread(void *dummy) { + sys_set_priority(1); + post("watchdog thread started"); + sys_microsleep(60*1000*1000); /* wait 60 seconds ... hoping that everything is set up */ + post("watchdog thread active"); + while (1) { + struct timespec timeout; + struct timeval now; + int status; + gettimeofday(&now,0); + timeout.tv_sec = now.tv_sec + 15; /* timeout: 15 seconds */ + timeout.tv_nsec = now.tv_usec * 1000; + sys_callback((t_int(*)(t_int*))watchdog_callback, 0, 0); + status = pthread_cond_timedwait(&watchdog_cond, &watchdog_mutex, &timeout); + if (status) { +#if defined(__linux__) || defined(IRIX) + fprintf(stderr, "watchdog killing"); + kill(0,9); /* kill parent thread */ +#endif + } + sys_microsleep(15*1000*1000); /* and sleep for another 15 seconds */ + } + return 0; /* avoid warning */ +} diff --git a/desiredata/src/s_loader.c b/desiredata/src/s_loader.c new file mode 100644 index 00000000..aa4d81a0 --- /dev/null +++ b/desiredata/src/s_loader.c @@ -0,0 +1,196 @@ +/* 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 <dlfcn.h> +#endif +#ifdef UNISTD +#include <stdlib.h> +#include <unistd.h> +#endif +#ifdef MSW +#include <io.h> +#include <windows.h> +#endif +#ifdef __APPLE__ +#include <mach-o/dyld.h> +#endif +#include <string.h> +#define PD_PLUSPLUS_FACE +#include "desire.h" +#include "s_stuff.h" +#include <stdio.h> +#include <sstream> +using namespace std; + +typedef void (*t_xxx)(); + +/* naming convention for externs. The names are kept distinct for those +who wich to make "fat" externs compiled for many platforms. Less specific +fallbacks are provided, primarily for back-compatibility; these suffice if +you are building a package which will run with a single set of compiled +objects. The specific name is the letter b, l, d, or m for BSD, linux, +darwin, or microsoft, followed by a more specific string, either "fat" for +a fat binary or an indication of the instruction set. */ + +#ifdef __FreeBSD__ +static char sys_dllextent[] = ".b_i386", sys_dllextent2[] = ".pd_freebsd"; +#endif +#ifdef __linux__ +#ifdef __ia64__ +static char sys_dllextent[] = ".l_ia64", sys_dllextent2[] = ".pd_linux"; +#else +static char sys_dllextent[] = ".l_i386", sys_dllextent2[] = ".pd_linux"; +#endif +#endif +#ifdef __APPLE__ +#ifndef MACOSX3 +static char sys_dllextent[] = ".d_fat", sys_dllextent2[] = ".pd_darwin"; +#else +static char sys_dllextent[] = ".d_ppc", sys_dllextent2[] = ".pd_darwin"; +#endif +#endif +#ifdef MSW +static char sys_dllextent[] = ".m_i386", sys_dllextent2[] = ".dll"; +#endif + +/* maintain list of loaded modules to avoid repeating loads */ +struct t_loadlist { + t_loadlist *ll_next; + t_symbol *ll_name; +}; + +static t_loadlist *sys_loaded; +/* return true if already loaded */ +int sys_onloadlist(char *classname) { + t_symbol *s = gensym(classname); + t_loadlist *ll; + for (ll = sys_loaded; ll; ll = ll->ll_next) + if (ll->ll_name == s) + return 1; + return 0; +} + +/* add to list of loaded modules */ +void sys_putonloadlist(char *classname) { + t_loadlist *ll = (t_loadlist *)getbytes(sizeof(*ll)); + ll->ll_name = gensym(classname); + ll->ll_next = sys_loaded; + sys_loaded = ll; + /* post("put on list %s", classname); */ +} + +static char *make_setup_name(const char *s) { + bool hexmunge=0; + ostringstream buf; + for (; *s; s++) { + char c = *s; + if ((c>='0' && c<='9') || (c>='A' && c<='Z') || (c>='a' && c<='z' )|| c == '_') { + buf << c; + } else if (c=='~' && s[1]==0) { /* trailing tilde becomes "_tilde" */ + buf << "_tilde"; + } else { /* anything you can't put in a C symbol is sprintf'ed in hex */ + // load_object: symbol "setup_0xbf861ee4" not found + oprintf(buf,"0x%02x",c); + hexmunge = 1; + } + } + char *r; + if (hexmunge) asprintf(&r,"setup_%s",buf.str().data()); + else asprintf(&r,"%s_setup",buf.str().data()); + return r; +} + +static int sys_do_load_lib(t_canvas *canvas, char *objectname) { + char *filename=0, *dirbuf, *classname, *nameptr; + t_xxx makeout = NULL; + int fd; + if ((classname = strrchr(objectname, '/'))) classname++; + else classname = objectname; + if (sys_onloadlist(objectname)) { + post("%s: already loaded", objectname); + return 1; + } + char *symname = make_setup_name(classname); + /* try looking in the path for (objectname).(sys_dllextent) ... */ + if ((fd = canvas_open2(canvas, objectname, sys_dllextent , &dirbuf, &nameptr, 1)) >= 0) goto gotone; + /* same, with the more generic sys_dllextent2 */ + if ((fd = canvas_open2(canvas, objectname, sys_dllextent2, &dirbuf, &nameptr, 1)) >= 0) goto gotone; + /* next try (objectname)/(classname).(sys_dllextent) ... */ + asprintf(&filename,"%s/%s",objectname,classname); + if ((fd = canvas_open2(canvas, filename, sys_dllextent , &dirbuf, &nameptr, 1)) >= 0) goto gotone; + if ((fd = canvas_open2(canvas, filename, sys_dllextent2, &dirbuf, &nameptr, 1)) >= 0) goto gotone; + return 0; +gotone: + close(fd); + class_set_extern_dir(gensym(dirbuf)); + /* rebuild the absolute pathname */ + free(filename); + /* extra nulls are a workaround for a dlopen bug */ + asprintf(&filename,"%s/%s%c%c%c%c",dirbuf,nameptr,0,0,0,0); +// filename = realloc(filename,); +#ifdef DL_OPEN + void *dlobj = dlopen(filename, RTLD_NOW | RTLD_GLOBAL); + if (!dlobj) { + post("%s: %s", filename, dlerror()); + goto forgetit; + } + makeout = (t_xxx)dlsym(dlobj, symname); +#endif +#ifdef MSW + sys_bashfilename(filename, filename); + HINSTANCE ntdll = LoadLibrary(filename); + if (!ntdll) { + post("%s: couldn't load", filename); + goto forgetit; + } + makeout = (t_xxx)GetProcAddress(ntdll); +#endif + if (!makeout) { + post("%s: can't find symbol '%s' in library", filename, symname); + goto forgetit; + } + makeout(); + class_set_extern_dir(&s_); + sys_putonloadlist(objectname); + free(filename); free(symname); + return 1; +forgetit: + class_set_extern_dir(&s_); + free(filename); free(symname); free(dirbuf); + return 0; +} + +/* callback type definition */ +typedef int (*t_loader)(t_canvas *canvas, char *classname); + +/* linked list of loaders */ +typedef struct t_loader_queue { + t_loader loader; + t_loader_queue *next; +}; + +static t_loader_queue loaders = {sys_do_load_lib, NULL}; + +/* register class loader function */ +void sys_register_loader(t_loader loader) { + t_loader_queue *q = &loaders; + while (1) { + if (q->next) q = q->next; + else { + q->next = (t_loader_queue *)getbytes(sizeof(t_loader_queue)); + q->next->loader = loader; + q->next->next = NULL; + break; + } + } +} + +int sys_load_lib(t_canvas *canvas, char *classname) { + int dspstate = canvas_suspend_dsp(); + int ok = 0; + for(t_loader_queue *q = &loaders; q; q = q->next) if ((ok = q->loader(canvas, classname))) break; + canvas_resume_dsp(dspstate); + return ok; +} diff --git a/desiredata/src/s_main.c b/desiredata/src/s_main.c new file mode 100644 index 00000000..70cc7a9b --- /dev/null +++ b/desiredata/src/s_main.c @@ -0,0 +1,632 @@ +/* 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. */ + +/* Zmlnig added advanced multidevice-support (2001) */ + +#include "desire.h" +#include "s_stuff.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <limits.h> +#include <string.h> +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <sstream> + +#ifdef __CYGWIN__ +#define UNISTD +#endif +#ifdef UNISTD +#include <unistd.h> +#endif +#ifdef MSW +#include <io.h> +#include <windows.h> +#include <winbase.h> +#endif + +#ifdef DAZ +#define _MM_DENORM_ZERO_ON 0x0040 +#include <xmmintrin.h> +#endif + +extern "C" void pd_init(); +extern "C" int sys_argparse(int argc, char **argv); +void sys_findprogdir(char *progname); +int sys_startgui(); +void sys_init_idle_callbacks(); +extern "C" int sys_rcfile(); +extern int m_scheduler(); +void sys_addhelppath(char *p); +void alsa_adddev(char *name); +extern "C" int sys_parsercfile(char*filename); + +#ifdef THREADED_SF +void sys_start_sfthread(); +#endif /* THREDED_SF */ + +char pd_version[] = "DesireData 2007.08.22"; +char pd_compiletime[] = __TIME__; +char pd_compiledate[] = __DATE__; + +int sys_verbose; +int sys_noloadbang; +int sys_hipriority = -1; /* -1 = don't care; 0 = no; 1 = yes */ +int sys_guisetportnumber = 5400; + +int sys_defeatrt; +t_symbol *sys_flags; + +char *sys_guicmd; +t_symbol *sys_libdir; +t_namelist *sys_externlist =0; +t_namelist *sys_openlist =0; +t_namelist *sys_messagelist=0; +static int sys_version; + +int sys_nmidiout = 1; +#ifdef MSW +int sys_nmidiin = 0; +#else +int sys_nmidiin = 1; +#endif +int sys_midiindevlist[MAXMIDIINDEV] = {1}; +int sys_midioutdevlist[MAXMIDIOUTDEV] = {1}; + +static int sys_main_srate; +static int sys_main_dacblocksize = DEFDACBLKSIZE; +static int sys_main_advance; +static int sys_listplease; + +/* jsarlo { */ +int sys_externalschedlib; +char *sys_externalschedlibname; +int sys_extraflags; +char *sys_extraflagsstring; +/* } jsarlo */ + +/* IOhannes { */ +/* here the "-1" counts signify that the corresponding vector hasn't been + specified in command line arguments; sys_open_audio will detect this and fill things in. */ +static int sys_nsoundin = -1; +static int sys_nsoundout = -1; +static int sys_soundindevlist[MAXAUDIOINDEV]; +static int sys_soundoutdevlist[MAXAUDIOOUTDEV]; +static int sys_nchin = -1; +static int sys_nchout = -1; +static int sys_chinlist[MAXAUDIOINDEV]; +static int sys_choutlist[MAXAUDIOOUTDEV]; +/* } IOhannes */ + +/* jsarlo { */ +t_sample* get_sys_soundout() { return sys_soundout; } +t_sample* get_sys_soundin() { return sys_soundin; } +int* get_sys_main_advance() { return &sys_main_advance; } +double* get_sys_time_per_dsp_tick() { return &sys_time_per_dsp_tick; } +int* get_sys_schedblocksize() { return &sys_schedblocksize; } +double* get_sys_time() { return &sys_time; } +float* get_sys_dacsr() { return &sys_dacsr; } +int* get_sys_sleepgrain() { return &sys_sleepgrain; } +int* get_sys_schedadvance() { return &sys_schedadvance; } +/* } jsarlo */ + +static void sys_afterargparse(); + +/* this is called from main() in s_entry.c */ +extern "C" int sys_main(int argc, char **argv) { + int noprefs = 0; + class_table = new t_hash<t_symbol*,t_class*>(127); + /* jsarlo { */ + sys_externalschedlib = 0; + sys_extraflags = 0; + /* } jsarlo */ +#ifdef DEBUG + fprintf(stderr, "Pd: COMPILED FOR DEBUGGING\n"); +#endif + pd_init(); /* start the message system */ + sys_findprogdir(argv[0]); /* set sys_progname, guipath */ + /* tb: command line flag to defeat preset loading */ + for (int i=0; i<argc; i++) if (!strcmp(argv[i],"-noprefs") || !strcmp(argv[i],"-rcfile")) noprefs = 1; + if (!noprefs) sys_rcfile(); /* parse the startup file */ + /* initalize idle callbacks before starting the gui */ + sys_init_idle_callbacks(); + /* } tb */ + if (sys_argparse(argc-1, argv+1)) return 1; /* parse cmd line */ + sys_afterargparse(); /* post-argparse settings */ + if (sys_verbose || sys_version) fprintf(stderr, "%s, compiled %s %s\n", pd_version, pd_compiletime, pd_compiledate); + if (sys_version) return 0; /* if we were just asked our version, exit here. */ + if (sys_startgui()) return 1; /* start the gui */ + /* tb: { start the soundfiler helper thread */ +#ifdef THREADED_SF + sys_start_sfthread(); +#endif /* THREDED_SF */ + /* try to set ftz and daz */ +#ifdef DAZ + _mm_setcsr(_MM_FLUSH_ZERO_ON | _MM_MASK_UNDERFLOW | _mm_getcsr()); + _mm_setcsr(_MM_DENORM_ZERO_ON | _mm_getcsr()); +#endif /* DAZ */ + /* } tb */ + /* jsarlo { */ + if (sys_externalschedlib) { +#ifdef MSW + typedef int (*t_externalschedlibmain)(char *); + t_externalschedlibmain externalmainfunc; + HINSTANCE ntdll; + char *filename; + asprintf(&filename,"%s.dll", sys_externalschedlibname); + sys_bashfilename(filename, filename); + ntdll = LoadLibrary(filename); + if (!ntdll) { + post("%s: couldn't load external scheduler lib ", filename); + free(filename); + return 0; + } + free(filename); + externalmainfunc = (t_externalschedlibmain)GetProcAddress(ntdll,"main"); + return externalmainfunc(sys_extraflagsstring); +#else + return 0; +#endif + } + /* open audio and MIDI */ + sys_reopen_midi(); + sys_reopen_audio(); + /* run scheduler until it quits */ + int r = m_scheduler(); + fprintf(stderr,"%s",lost_posts.str().data()); // this could be useful if anyone ever called sys_exit + return r; +} + +static char *(usagemessage[]) = { +"usage: pd [-flags] [file]...","", +"audio configuration flags:", +"-r <n> -- specify sample rate", +"-audioindev ... -- audio in devices; e.g., \"1,3\" for first and third", +"-audiooutdev ... -- audio out devices (same)", +"-audiodev ... -- specify input and output together", +"-inchannels ... -- audio input channels (by device, like \"2\" or \"16,8\")", +"-outchannels ... -- number of audio out channels (same)", +"-channels ... -- specify both input and output channels", +"-audiobuf <n> -- specify size of audio buffer in msec", +"-blocksize <n> -- specify audio I/O block size in sample frames", +"-dacblocksize <n>-- specify audio dac~block size in samples", +"-sleepgrain <n> -- specify number of milliseconds to sleep when idle", +"-cb_scheduler -- use callback-based scheduler (jack and native asio only)", +"-nodac -- suppress audio output", +"-noadc -- suppress audio input", +"-noaudio -- suppress audio input and output (-nosound is synonym)", +"-listdev -- list audio and MIDI devices", + +#define NOT_HERE "(support not compiled in)" + +#ifdef USEAPI_OSS +"-oss -- use OSS audio API", +"-32bit ---- allow 32 bit OSS audio (for RME Hammerfall)", +#else +"-oss -- OSS "NOT_HERE, +"-32bit ---- allow 32 bit OSS audio "NOT_HERE, +#endif + +#ifdef USEAPI_ALSA +"-alsa -- use ALSA audio API", +"-alsaadd <name> -- add an ALSA device name to list", +#else +"-alsa -- use ALSA audio API "NOT_HERE, +"-alsaadd <name> -- add an ALSA device name to list "NOT_HERE, +#endif + +#ifdef USEAPI_JACK +"-jack -- use JACK audio API", +#else +"-jack -- use JACK audio API "NOT_HERE, +#endif + +#ifdef USEAPI_ASIO + "-asio_native -- use native ASIO API", +#else + "-asio_native -- use native ASIO API "NOT_HERE, +#endif + +#ifdef USEAPI_PORTAUDIO +"-pa -- use Portaudio API", +"-asio -- synonym for -pa - use ASIO via Portaudio", +#else +"-pa -- use Portaudio API "NOT_HERE, +"-asio -- synonym for -pa - use ASIO via Portaudio "NOT_HERE, +#endif + +#ifdef USEAPI_MMIO +"-mmio -- use MMIO audio API", +#else +"-mmio -- use MMIO audio API "NOT_HERE, +#endif +#ifdef API_DEFSTRING +" (default audio API for this platform: "API_DEFSTRING")","", +#endif + +"MIDI configuration flags:", +"-midiindev ... -- midi in device list; e.g., \"1,3\" for first and third", +"-midioutdev ... -- midi out device list, same format", +"-mididev ... -- specify -midioutdev and -midiindev together", +"-nomidiin -- suppress MIDI input", +"-nomidiout -- suppress MIDI output", +"-nomidi -- suppress MIDI input and output", +#ifdef USEAPI_ALSA +"-alsamidi -- use ALSA midi API", +#else +"-alsamidi -- use ALSA midi API "NOT_HERE, +#endif + +"","other flags:", +"-rcfile <file> -- read this rcfile instead of default", +"-noprefs -- suppress loading preferences on startup", +"-path <path> -- add to file search path", +"-nostdpath -- don't search standard (\"extra\") directory", +"-stdpath -- search standard directory (true by default)", +"-helppath <path> -- add to help file search path", +"-open <file> -- open file(s) on startup", +"-lib <file> -- load object library(s)", +"-verbose -- extra printout on startup and when searching for files", +"-version -- don't run Pd; just print out which version it is", +"-d <n> -- specify debug level", +"-noloadbang -- suppress all loadbangs", +"-stderr -- send printout to standard error instead of GUI", +"-guiport <n> -- set port that the GUI should connect to", +"-send \"msg...\" -- send a message at startup, after patches are loaded", +#ifdef UNISTD +"-rt or -realtime -- use real-time priority", +"-nrt -- don't use real-time priority", +#else +"-rt or -realtime -- use real-time priority "NOT_HERE, +"-nrt -- don't use real-time priority "NOT_HERE, +#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; +} + +#ifndef INSTALL_PREFIX +#define INSTALL_PREFIX "." +#endif + +/* 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 that fails, + by consulting the variable INSTALL_PREFIX. In MSW, we don't try to use INSTALL_PREFIX. */ +void sys_findprogdir(char *progname) { + char *lastslash; +#ifdef UNISTD + struct stat statbuf; +#endif + /* find out by what string Pd was invoked; put answer in "sbuf". */ + char *sbuf; +#ifdef MSW + int len = GetModuleFileName(NULL, sbuf, 0); + sbuf = malloc(len+1); + GetModuleFileName(NULL, sbuf, len); + sbuf[len] = 0; + sys_unbashfilename(sbuf,sbuf); +#endif /* MSW */ +#ifdef UNISTD + sbuf = strdup(progname); +#endif + lastslash = strrchr(sbuf, '/'); + char *sbuf2; + 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) { + asprintf(&sbuf2, "%.*s", lastslash-sbuf, sbuf); + } else sbuf2 = strdup(".."); + } else { /* no slashes found. Try INSTALL_PREFIX. */ + sbuf2 = strdup(INSTALL_PREFIX); + } +#ifdef MSW + sys_libdir = gensym(sbuf2); +#else + sys_libdir = stat(sbuf, &statbuf)>=0 ? symprintf("%s/lib/pd",sbuf2) : gensym(sbuf2); +#endif + free(sbuf); + post("sys_libdir = %s",sys_libdir->name); +} + +#ifdef MSW +static int sys_mmio = 1; +#else +static int sys_mmio = 0; +#endif + +#undef NOT_HERE +#define NOT_HERE fprintf(stderr,"option %s not compiled into this pd\n", *argv) + +#define ARG(name,count) (!strcmp(*argv,(name)) && argc>=(count)) +#define NEXT(count) argc -= (count), argv += (count); continue; + +int sys_argparse(int argc, char **argv) { + while ((argc > 0) && **argv == '-') { + if (ARG("-r",2)) { + if (sscanf(argv[1], "%d", &sys_main_srate)<1) goto usage; + NEXT(2); + } + if (ARG("-inchannels",2)) { /* IOhannes */ + sys_parsedevlist(&sys_nchin, sys_chinlist, MAXAUDIOINDEV, argv[1]); + if (!sys_nchin) goto usage; + NEXT(2); + } + if (ARG("-outchannels",2)) { /* IOhannes */ + sys_parsedevlist(&sys_nchout, sys_choutlist, MAXAUDIOOUTDEV, argv[1]); + if (!sys_nchout) goto usage; + NEXT(2); + } + if (ARG("-channels",2)) { + sys_parsedevlist(&sys_nchin, sys_chinlist,MAXAUDIOINDEV, argv[1]); + sys_parsedevlist(&sys_nchout, sys_choutlist,MAXAUDIOOUTDEV, argv[1]); + if (!sys_nchout) goto usage; + NEXT(2); + } + if (ARG("-soundbuf",2) || ARG("-audiobuf",2)) { + sys_main_advance = atoi(argv[1]); + NEXT(2); + } + if (ARG("-blocksize",2)) {sys_setblocksize(atoi(argv[1])); NEXT(2);} + if (ARG("-dacblocksize",2)) { + if (sscanf(argv[1], "%d", &sys_main_dacblocksize)<1) goto usage; + NEXT(2); + } + if (ARG("-sleepgrain",2)) {sys_sleepgrain = int(1000 * atof(argv[1])); NEXT(2);} + + /* from tim */ + if (ARG("-cb_scheduler",1)) {sys_setscheduler(1); NEXT(1);} + + /* IOhannes */ + if (ARG("-nodac",1)) {sys_nsoundout= sys_nchout = 0; NEXT(1);} + if (ARG("-noadc",1)) {sys_nsoundin = sys_nchin = 0; NEXT(1);} + if (ARG("-nosound",1) || ARG("-noaudio",1)) { + sys_nsoundin=sys_nsoundout = 0; + sys_nchin = sys_nchout = 0; + NEXT(1); + } + + /* many options for selecting audio api */ + if (ARG("-oss",1)) { +#ifdef USEAPI_OSS + sys_set_audio_api(API_OSS); +#else + NOT_HERE; +#endif + NEXT(1); + } + if (ARG("-32bit",1)) { +#ifdef USEAPI_OSS + sys_set_audio_api(API_OSS); + oss_set32bit(); +#else + NOT_HERE; +#endif + NEXT(1); + } + if (ARG("-alsa",1)) { +#ifdef USEAPI_ALSA + sys_set_audio_api(API_ALSA); +#else + NOT_HERE; +#endif + NEXT(1); + } + if (ARG("-alsaadd",2)) { +#ifdef USEAPI_ALSA + if (argc > 1) + alsa_adddev(argv[1]); + else goto usage; +#else + NOT_HERE; +#endif + NEXT(2); + } + if (ARG("-alsamidi",1)) { +#ifdef USEAPI_ALSA + sys_set_midi_api(API_ALSA); +#else + NOT_HERE; +#endif + NEXT(1); + } + if (ARG("-jack",1)) { +#ifdef USEAPI_JACK + sys_set_audio_api(API_JACK); +#else + NOT_HERE; +#endif + NEXT(1); + } + if (ARG("-pa",1) || ARG("-portaudio",1) || ARG("-asio",1)) { +#ifdef USEAPI_PORTAUDIO + sys_set_audio_api(API_PORTAUDIO); +#else + NOT_HERE; +#endif + sys_mmio = 0; + argc--; argv++; + } + if (ARG("-asio_native",1)) { +#ifdef USEAPI_ASIO + sys_set_audio_api(API_ASIO); +#else + NOT_HERE; +#endif + sys_mmio = 0; + argc--; argv++; + } + if (ARG("-mmio",1)) { +#ifdef USEAPI_MMIO + sys_set_audio_api(API_MMIO); +#else + NOT_HERE; +#endif + sys_mmio = 1; + NEXT(1); + } + + if (ARG("-nomidiin", 1)) {sys_nmidiin = 0; NEXT(1);} + if (ARG("-nomidiout",1)) {sys_nmidiout = 0; NEXT(1);} + if (ARG("-nomidi",1)) {sys_nmidiin = sys_nmidiout = 0; NEXT(1);} + if (ARG("-midiindev",2)) { + sys_parsedevlist(&sys_nmidiin, sys_midiindevlist, MAXMIDIINDEV, argv[1]); + if (!sys_nmidiin) goto usage; + NEXT(2); + } + if (ARG("-midioutdev",2)) { + sys_parsedevlist(&sys_nmidiout, sys_midioutdevlist, MAXMIDIOUTDEV, argv[1]); + if (!sys_nmidiout) goto usage; + NEXT(2); + } + if (ARG("-mididev",2)) { + sys_parsedevlist(&sys_nmidiin, sys_midiindevlist, MAXMIDIINDEV, argv[1]); + sys_parsedevlist(&sys_nmidiout, sys_midioutdevlist, MAXMIDIOUTDEV, argv[1]); + if (!sys_nmidiout) goto usage; + NEXT(2); + } + if (ARG("-nostdpath",1)) {sys_usestdpath = 0; NEXT(1);} + if (ARG("-stdpath",1)) {sys_usestdpath = 1; NEXT(1);} + if (ARG("-path",2)) {sys_searchpath = namelist_append_files(sys_searchpath,argv[1]); NEXT(2);} + if (ARG("-helppath",2)) {sys_helppath = namelist_append_files(sys_helppath, argv[1]); NEXT(2);} + if (ARG("-open",2)) {sys_openlist = namelist_append_files(sys_openlist, argv[1]); NEXT(2);} + if (ARG("-lib",2)) {sys_externlist = namelist_append_files(sys_externlist,argv[1]); NEXT(2);} + if (ARG("-font",2)) { + fprintf(stderr,"Warning: -font ignored by DesireData; use .ddrc instead\n"); + //sys_defaultfont = sys_nearestfontsize(atoi(argv[1])); + NEXT(2); + } + if (ARG("-typeface",2)) { /* tim */ + fprintf(stderr,"Warning: -typeface ignored by DesireData; use .ddrc instead\n"); + NEXT(2); + } + if (ARG("-noprefs",1)) {NEXT(1);} /* tim: skip flag, we already parsed it */ + /* jmz: read an alternative rcfile { */ + if (ARG("-rcfile",2)) {sys_parsercfile(argv[1]); NEXT(2);} /* recursively */ + /* } jmz */ + if (ARG("-verbose",1)) {sys_verbose++; NEXT(1);} + if (ARG("-version",1)) {sys_version = 1; NEXT(1);} + if (ARG("-noloadbang",1)) {sys_noloadbang = 1; NEXT(1);} + if (ARG("-nogui",1)) { + sys_printtostderr = 1; + fprintf(stderr,"Warning: -nogui is obsolete: nowadays it does just like -stderr instead\n"); + NEXT(1); + } + if (ARG("-guiport",2)) { + if (sscanf(argv[1], "%d", &sys_guisetportnumber)<1) goto usage; + NEXT(2); + } + if (ARG("-stderr",1)) {sys_printtostderr = 1; NEXT(1);} + if (ARG("-guicmd",2)) { + fprintf(stderr,"Warning: -guicmd ignored"); + NEXT(2); + } + if (ARG("-send",2)) { + sys_messagelist = namelist_append(sys_messagelist, argv[1], 1); + NEXT(2); + } + if (ARG("-listdev",1)) {sys_listplease=1; NEXT(1);} + /* jsarlo { */ + if (ARG("-schedlib",2)) { + sys_externalschedlib = 1; + sys_externalschedlibname = strdup(argv[1]); + NEXT(2); + } + if (ARG("-extraflags",2)) { + sys_extraflags = 1; + sys_extraflagsstring = strdup(argv[1]); + NEXT(2); + } + /* } jsarlo */ +#ifdef UNISTD + if (ARG("-rt",1) || ARG("-realtime",1)) {sys_hipriority = 1; NEXT(1);} + if (ARG("-nrt",1)) {sys_hipriority = 0; NEXT(1);} +#endif + if (ARG("-soundindev",2) || ARG("-audioindev",2)) { /* IOhannes */ + sys_parsedevlist(&sys_nsoundin, sys_soundindevlist, MAXAUDIOINDEV, argv[1]); + if (!sys_nsoundin) goto usage; + NEXT(2); + } + if (ARG("-soundoutdev",2) || ARG("-audiooutdev",2)) { /* IOhannes */ + sys_parsedevlist(&sys_nsoundout, sys_soundoutdevlist, MAXAUDIOOUTDEV, argv[1]); + if (!sys_nsoundout) goto usage; + NEXT(2); + } + if (ARG("-sounddev",2) || ARG("-audiodev",2)) { + sys_parsedevlist(&sys_nsoundin, sys_soundindevlist, MAXAUDIOINDEV, argv[1]); + sys_parsedevlist(&sys_nsoundout, sys_soundoutdevlist, MAXAUDIOOUTDEV, argv[1]); + if (!sys_nsoundout) goto usage; + NEXT(2); + } + usage: + if (argc) fprintf(stderr, "Can't handle option '%s'.\n",*argv); + for (size_t i=0; i < sizeof(usagemessage)/sizeof(*usagemessage); i++) + fprintf(stderr, "%s\n", usagemessage[i]); + return 1; + } + for (; argc > 0; argc--, argv++) sys_openlist = namelist_append_files(sys_openlist, *argv); + return 0; +} + +int sys_getblksize() {return sys_dacblocksize;} + +/* stuff to do, once, after calling sys_argparse() -- which may itself + be called more than once (first from "settings, second from .pdrc, then + from command-line arguments */ +static void sys_afterargparse() { + char *sbuf; + t_audiodevs audio_in, audio_out; + int nchindev, nchoutdev, rate, dacblksize, advance, scheduler; + int nmidiindev = 0, midiindev[MAXMIDIINDEV]; + int nmidioutdev = 0, midioutdev[MAXMIDIOUTDEV]; + /* add "extra" library to path */ + asprintf(&sbuf,"%s/extra",sys_libdir->name); + sys_setextrapath(sbuf); + free(sbuf); + asprintf(&sbuf,"%s/doc/5.reference",sys_libdir->name); + sys_helppath = namelist_append_files(sys_helppath, sbuf); + free(sbuf); + /* correct to make audio and MIDI device lists zero based. On MMIO, however, "1" really means the second device + the first one is "mapper" which is was not included when the command args were set up, so we leave it that way for compatibility. */ + if (!sys_mmio) { + for (int i=0; i<sys_nsoundin ; i++) sys_soundindevlist[i]--; + for (int i=0; i<sys_nsoundout; i++) sys_soundoutdevlist[i]--; + } + for (int i=0; i<sys_nmidiin; i++) sys_midiindevlist[i]--; + for (int i=0; i<sys_nmidiout; i++) sys_midioutdevlist[i]--; + if (sys_listplease) sys_listdevs(); + /* get the current audio parameters. These are set by the preferences mechanism (sys_loadpreferences()) or + else are the default. Overwrite them with any results of argument parsing, and store them again. */ + sys_get_audio_params(&audio_in, &audio_out, &rate, &dacblksize, &advance, &scheduler); + nchindev = sys_nchin>=0 ? sys_nchin : audio_in.ndev; + nchoutdev = sys_nchout>=0 ? sys_nchout : audio_out.ndev; + if (sys_nchin >=0) {for (int i=0; i< nchindev; i++) audio_in.chdev[i] = sys_chinlist[i];} + if (sys_nchout>=0) {for (int i=0; i<nchoutdev; i++) audio_out.chdev[i] = sys_choutlist[i];} + if (sys_nsoundin>=0) {audio_in.ndev = sys_nsoundin; for (int i=0; i< audio_in.ndev; i++) audio_in.dev[i] = sys_soundindevlist[i];} + if (sys_nsoundout>=0) {audio_out.ndev = sys_nsoundout;for (int i=0; i<audio_out.ndev; i++) audio_out.dev[i] = sys_soundoutdevlist[i];} + if (sys_nmidiin >=0) {nmidiindev = sys_nmidiin; for (int i=0; i< nmidiindev; i++) midiindev[i] = sys_midiindevlist[i];} + if (sys_nmidiout>=0) {nmidioutdev = sys_nmidiout; for (int i=0; i< nmidioutdev; i++) midioutdev[i] = sys_midioutdevlist[i];} + if (sys_main_advance) advance = sys_main_advance; + if (sys_main_srate) rate = sys_main_srate; + if (sys_main_dacblocksize) dacblksize = sys_main_dacblocksize; + sys_open_audio(audio_in.ndev, audio_in.dev, nchindev, audio_in.chdev, + audio_out.ndev, audio_out.dev, nchoutdev, audio_out.chdev, rate, dacblksize, advance, scheduler, 0); + sys_open_midi(nmidiindev, midiindev, nmidioutdev, midioutdev, 0); +} diff --git a/desiredata/src/s_midi.c b/desiredata/src/s_midi.c new file mode 100644 index 00000000..f8ee0fd9 --- /dev/null +++ b/desiredata/src/s_midi.c @@ -0,0 +1,619 @@ +/* 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. */ + +/* Clock functions (which should move, but where?) and MIDI queueing */ + +#define PD_PLUSPLUS_FACE +#include "desire.h" +#include "s_stuff.h" +#ifdef UNISTD +#include <unistd.h> +#include <sys/time.h> +#ifdef HAVE_BSTRING_H +#include <bstring.h> +#endif +#endif +#ifdef MSW +#include <winsock.h> +#include <sys/types.h> +#include <sys/timeb.h> +#include <wtypes.h> +#endif +#include <string.h> +#include <stdio.h> +#include <signal.h> + +struct t_midiqelem { + double time; + int portno; + unsigned char onebyte; + unsigned char byte1; + unsigned char byte2; + unsigned char byte3; +}; + +#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; + +#ifndef API_DEFAULT +#define API_DEFAULT 0 +#endif +int sys_midiapi = API_DEFAULT; + +/* 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() { + 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() {return sys_getrealtime() + sys_dactimeminusrealtime;} +static double sys_getmidiinrealtime () {return sys_getrealtime() + sys_adctimeminusrealtime;} + +static void sys_putnext () { + t_midiqelem &m = midi_outqueue[midi_outtail]; + int portno = m.portno; +#ifdef USEAPI_ALSA + if (sys_midiapi == API_ALSA) { + if (m.onebyte) sys_alsa_putmidibyte(portno, m.byte1); + else sys_alsa_putmidimess(portno, m.byte1, m.byte2, m.byte3); + } else +#endif /* ALSA */ + { + if (m.onebyte) sys_putmidibyte(portno, m.byte1); + else sys_putmidimess(portno, m.byte1, m.byte2, m.byte3); + } + midi_outtail = (midi_outtail + 1 == MIDIQSIZE ? 0 : midi_outtail + 1); +} + +/* #define TEST_DEJITTER */ + +void sys_pollmidioutqueue() { +#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].time - midirealtime), + midirealtime, .001 * clock_gettimesince(sys_midiinittime), + sys_getrealtime(), sys_dactimeminusrealtime); + db = 1; + } +#endif + if (midi_outqueue[midi_outtail].time <= midirealtime) + sys_putnext(); + else break; + } +} + +static void sys_queuemidimess(int portno, int onebyte, int a, int b, int c) { + 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(); + t_midiqelem m = midi_outqueue[midi_outhead]; + m.portno = portno; + m.onebyte = onebyte; + m.byte1 = a; + m.byte2 = b; + m.byte3 = c; + m.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_byte(int portno, int value) { +#ifdef USEAPI_ALSA + if (sys_midiapi == API_ALSA) sys_alsa_putmidibyte(portno, value); else +#endif + sys_putmidibyte(portno, 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_gotbyte1; + int mp_byte1; +} t_midiparser; + +#define MIDINOTEOFF 0x80 /* 2 following 'data bytes' */ +#define MIDINOTEON 0x90 /* 2 */ +#define MIDIPOLYTOUCH 0xa0 /* 2 */ +#define MIDICONTROLCHANGE 0xb0 /* 2 */ +#define MIDIPROGRAMCHANGE 0xc0 /* 1 */ +#define MIDICHANNELTOUCH 0xd0 /* 1 */ +#define MIDIPITCHBEND 0xe0 /* 2 */ +#define MIDISTARTSYSEX 0xf0 /* (until F7) */ +#define MIDITIMECODE 0xf1 /* 1 */ +#define MIDISONGPOS 0xf2 /* 2 */ +#define MIDISONGSELECT 0xf3 /* 1 */ +#define MIDIRESERVED1 0xf4 /* ? */ +#define MIDIRESERVED2 0xf5 /* ? */ +#define MIDITUNEREQUEST 0xf6 /* 0 */ +#define MIDIENDSYSEX 0xf7 /* 0 */ +#define MIDICLOCK 0xf8 /* 0 */ +#define MIDITICK 0xf9 /* 0 */ +#define MIDISTART 0xfa /* 0 */ +#define MIDICONT 0xfb /* 0 */ +#define MIDISTOP 0xfc /* 0 */ +#define MIDIACTIVESENSE 0xfe /* 0 */ +#define MIDIRESET 0xff /* 0 */ + +static void sys_dispatchnextmidiin() { + static t_midiparser parser[MAXMIDIINDEV], *parserp; + int portno = midi_inqueue[midi_intail].portno, byte = midi_inqueue[midi_intail].byte1; + if (!midi_inqueue[midi_intail].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 & 0x80) { + if (byte == MIDITUNEREQUEST || byte == MIDIRESERVED1 || byte == MIDIRESERVED2) { + parserp->mp_status = 0; + } else if (byte == MIDISTARTSYSEX) { + inmidi_sysex(portno, byte); + parserp->mp_status = byte; + } else if (byte == MIDIENDSYSEX) { + inmidi_sysex(portno, byte); + parserp->mp_status = 0; + } else { + parserp->mp_status = byte; + } + parserp->mp_gotbyte1 = 0; + } else { + int cmd = (parserp->mp_status >= 0xf0 ? parserp->mp_status : + (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; + case MIDISTARTSYSEX: + inmidi_sysex(portno, byte); + break; + /* other kinds of messages are just dropped here. We'll + need another status byte before we start letting MIDI in + again (no running status across "system" messages). */ + case MIDITIMECODE: break; /* 1 data byte*/ + case MIDISONGPOS: break; /* 2 */ + case MIDISONGSELECT: break; /* 1 */ + } + } + } + midi_intail = (midi_intail + 1 == MIDIQSIZE ? 0 : midi_intail + 1); +} + +void sys_pollmidiinqueue() { +#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].time - logicaltime), + logicaltime, sys_getrealtime(), sys_adctimeminusrealtime); + db = 1; + } +#endif +#if 0 + if (midi_inqueue[midi_intail].time <= logicaltime - 0.007) + post("late %f", 1000 * (logicaltime - midi_inqueue[midi_intail].time)); +#endif + if (midi_inqueue[midi_intail].time <= logicaltime) { +#if 0 + post("diff %f", 1000* (logicaltime - midi_inqueue[midi_intail].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) { + static int warned = 0; + int newhead = midi_inhead+1; + if (newhead == MIDIQSIZE) newhead = 0; + /* if FIFO is full flush an element to make room */ + if (newhead == midi_intail) { + if (!warned) { + post("warning: MIDI timing FIFO overflowed"); + warned = 1; + } + sys_dispatchnextmidiin(); + } + midi_inqueue[midi_inhead].portno = portno; + midi_inqueue[midi_inhead].onebyte = 1; + midi_inqueue[midi_inhead].byte1 = byte; + midi_inqueue[midi_inhead].time = sys_getmidiinrealtime(); + midi_inhead = newhead; + sys_pollmidiinqueue(); +} + +void sys_pollmidiqueue() { +#if 0 + static double lasttime; + double newtime = sys_getrealtime(); + if (newtime - lasttime > 0.007) + post("delay %d", (int)(1000 * (newtime - lasttime))); + lasttime = newtime; +#endif +#ifdef USEAPI_ALSA + if (sys_midiapi == API_ALSA) sys_alsa_poll_midi(); + else +#endif /* ALSA */ + sys_poll_midi(); /* OS dependent poll for MIDI input */ + sys_pollmidioutqueue(); + sys_pollmidiinqueue(); +} + +/******************** dialog window and device listing ********************/ + +#ifdef USEAPI_ALSA +void midi_alsa_init(); +#endif +#ifndef USEAPI_PORTMIDI +#ifdef USEAPI_OSS +void midi_oss_init(); +#endif +#endif + +/* last requested parameters */ +static int midi_nmidiindev; +static int midi_midiindev[MAXMIDIINDEV]; +static int midi_nmidioutdev; +static int midi_midioutdev[MAXMIDIOUTDEV]; + +void sys_get_midi_apis(char *buf) { + int n = 0; + strcpy(buf, "{ "); + sprintf(buf + strlen(buf), "{default-MIDI %d} ", API_DEFAULT); n++; +#ifdef USEAPI_ALSA + sprintf(buf + strlen(buf), "{ALSA-MIDI %d} ", API_ALSA); n++; +#endif + strcat(buf, "}"); +} +void sys_get_midi_params(int *pnmidiindev, int *pmidiindev, int *pnmidioutdev, int *pmidioutdev) { + *pnmidiindev = midi_nmidiindev; for (int i=0; i<MAXMIDIINDEV; i++) pmidiindev [i] = midi_midiindev[i]; + *pnmidioutdev = midi_nmidioutdev; for (int i=0; i<MAXMIDIOUTDEV; i++) pmidioutdev[i] = midi_midioutdev[i]; +} + +static void sys_save_midi_params(int nmidiindev, int *midiindev, int nmidioutdev, int *midioutdev) { + midi_nmidiindev = nmidiindev; for (int i=0; i<MAXMIDIINDEV; i++) midi_midiindev [i] = midiindev[i]; + midi_nmidioutdev = nmidioutdev; for (int i=0; i<MAXMIDIOUTDEV; i++) midi_midioutdev[i] = midioutdev[i]; +} + +void sys_open_midi(int nmidiindev, int *midiindev, int nmidioutdev, int *midioutdev, int enable) { +#ifdef USEAPI_ALSA + midi_alsa_init(); +#endif +#ifndef USEAPI_PORTMIDI +#ifdef USEAPI_OSS + midi_oss_init(); +#endif +#endif + if (enable) +#ifdef USEAPI_ALSA + if (sys_midiapi == API_ALSA) + sys_alsa_do_open_midi(nmidiindev, midiindev, nmidioutdev, midioutdev); + else +#endif /* ALSA */ + sys_do_open_midi(nmidiindev, midiindev, nmidioutdev, midioutdev); + sys_save_midi_params(nmidiindev, midiindev, nmidioutdev, midioutdev); + sys_vgui("set pd_whichmidiapi %d\n", sys_midiapi); +} + +/* open midi using whatever parameters were last used */ +void sys_reopen_midi() { + int nmidiindev, midiindev[MAXMIDIINDEV]; + int nmidioutdev, midioutdev[MAXMIDIOUTDEV]; + sys_get_midi_params(&nmidiindev, midiindev, &nmidioutdev, midioutdev); + sys_open_midi(nmidiindev, midiindev, nmidioutdev, midioutdev, 1); +} + +#define MAXNDEV 50 +#define DEVDESCSIZE 80 +#define DEVONSET 1 /* To agree with command line flags, normally start at 1 */ + +void sys_listmididevs() { + char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; + int nindevs = 0, noutdevs = 0; +#ifdef USEAPI_ALSA + if (sys_midiapi == API_ALSA) + midi_alsa_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, MAXNDEV, DEVDESCSIZE); + else +#endif /* ALSA */ + midi_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, MAXNDEV, DEVDESCSIZE); + if (!nindevs) post("no midi input devices found"); + else { + post("MIDI input devices:"); + for (int i=0; i<nindevs; i++) post("%d. %s", i+1, indevlist + i * DEVDESCSIZE); + } + if (!noutdevs) post("no midi output devices found"); + else { + post("MIDI output devices:"); + for (int i=0; i<noutdevs; i++) post("%d. %s", i+DEVONSET, outdevlist + i * DEVDESCSIZE); + } +} + +void sys_set_midi_api(int which) { + sys_midiapi = which; + if (sys_verbose) post("sys_midiapi %d", sys_midiapi); +} + +void glob_midi_properties(t_pd *dummy, t_floatarg flongform); + +void glob_midi_setapi(t_pd *dummy, t_floatarg f) { + int newapi = int(f); + if (newapi) { + if (newapi == sys_midiapi) { + //if (!midi_isopen()) s_reopen_midi(); + } else { +#ifdef USEAPI_ALSA + if (sys_midiapi == API_ALSA) sys_alsa_close_midi(); + else +#endif + sys_close_midi(); + sys_midiapi = newapi; + /* bash device params back to default */ + midi_nmidiindev = midi_nmidioutdev = 1; + //midi_midiindev[0] = midi_midioutdev[0] = DEFAULTMIDIDEV; + //midi_midichindev[0] = midi_midichoutdev[0] = SYS_DEFAULTCH; + sys_reopen_midi(); + } + glob_midi_properties(0, 0); + } else /* if (midi_isopen()) */ { + sys_close_midi(); + //midi_state = 0; + } +} + +extern t_class *glob_pdobject; + +/* start an midi settings dialog window */ +void glob_midi_properties(t_pd *dummy, t_floatarg flongform) { + /* these are the devices you're using: */ + int nindev, midiindev[MAXMIDIINDEV]; + int noutdev, midioutdev[MAXMIDIOUTDEV]; + char midiinstr[16*4+1],midioutstr[16*4+1],*strptr; + /* these are all the devices on your system: */ + char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; + int nindevs = 0, noutdevs = 0, i; + char indevliststring[MAXNDEV*(DEVDESCSIZE+4)+80], + outdevliststring[MAXNDEV*(DEVDESCSIZE+4)+80]; + midi_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, MAXNDEV, DEVDESCSIZE); + strcpy(indevliststring, "{"); + for (i = 0; i < nindevs; i++) { + strcat(indevliststring, "\""); + strcat(indevliststring, indevlist + i * DEVDESCSIZE); + strcat(indevliststring, "\" "); + } + strcat(indevliststring, "}"); + strcpy(outdevliststring, "{"); + for (i = 0; i < noutdevs; i++) { + strcat(outdevliststring, "\""); + strcat(outdevliststring, outdevlist + i * DEVDESCSIZE); + strcat(outdevliststring, "\" "); + } + strcat(outdevliststring, "}"); + sys_get_midi_params(&nindev, midiindev, &noutdev, midioutdev); + if (nindev > 1 || noutdev > 1) flongform = 1; + *(strptr = midiinstr) = 0; + for(i = 0; i < 16; ++i) { + sprintf(strptr,"%3d ",nindev > i && midiindev[i]>= 0 ? midiindev[i] : -1); + strptr += strlen(strptr); + } + *(strptr = midioutstr) = 0; + for(i = 0; i < 16; ++i) { + sprintf(strptr,"%3d ",noutdev > i && midioutdev[i]>= 0 ? midioutdev[i] : -1); + strptr += strlen(strptr); + } + sys_vgui("pdtk_midi_dialog %%s %s %s %s %s %d\n", + indevliststring,midiinstr,outdevliststring,midioutstr,!!flongform); +} + +/* new values from dialog window */ +void glob_midi_dialog(t_pd *dummy, t_symbol *s, int argc, t_atom *argv) { + int i, nindev, noutdev; + int newmidiindev[16], newmidioutdev[16]; + int alsadevin, alsadevout; + for (i = 0; i < 16; i++) { + newmidiindev[i] = atom_getintarg(i, argc, argv); + newmidioutdev[i] = atom_getintarg(i+16, argc, argv); + } + for (i = 0, nindev = 0; i < 16; i++) { + if (newmidiindev[i] >= 0) {newmidiindev[nindev] = newmidiindev[i]; nindev++;} + } + for (i = 0, noutdev = 0; i < 16; i++) { + if (newmidioutdev[i] >= 0) {newmidioutdev[noutdev] = newmidioutdev[i]; noutdev++;} + } + alsadevin = atom_getintarg(32, argc, argv); + alsadevout = atom_getintarg(33, argc, argv); +#ifdef USEAPI_ALSA + if (sys_midiapi == API_ALSA) { + sys_alsa_close_midi(); + sys_open_midi(alsadevin, newmidiindev, alsadevout, newmidioutdev, 1); + } else +#endif + { + sys_close_midi(); + sys_open_midi(nindev, newmidiindev, noutdev, newmidioutdev, 1); + } +} + +/* tb { */ + +void glob_midi_getindevs(t_pd *dummy, t_symbol *s, int ac, t_atom *av) { + /* these are all the devices on your system: */ + char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; + int nindevs = 0, noutdevs = 0; + t_atom argv[MAXNDEV]; + int f = ac ? (int)atom_getfloatarg(0,ac,av) : -1; + t_symbol *selector = gensym("midiindev"); + t_symbol *pd = gensym("pd"); + midi_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, MAXNDEV, DEVDESCSIZE); + if (f < 0) { + for (int i=0; i<nindevs; i++) SETSYMBOL(argv+i, gensym(indevlist + i * DEVDESCSIZE)); + typedmess(pd->s_thing, selector, nindevs, argv); + } else if (f < nindevs) { + SETSYMBOL(argv, gensym(indevlist + f * DEVDESCSIZE)); + typedmess(pd->s_thing, selector, 1, argv); + } +} + +void glob_midi_getoutdevs(t_pd *dummy, t_symbol *s, int ac, t_atom *av) { + /* these are all the devices on your system: */ + char indevlist[MAXNDEV*DEVDESCSIZE], outdevlist[MAXNDEV*DEVDESCSIZE]; + int nindevs = 0, noutdevs = 0; + t_atom argv[MAXNDEV]; + int f = ac ? (int)atom_getfloatarg(0,ac,av) : -1; + t_symbol *selector = gensym("midioutdev"); + t_symbol *pd = gensym("pd"); + midi_getdevs(indevlist, &nindevs, outdevlist, &noutdevs, MAXNDEV, DEVDESCSIZE); + if (f < 0) { + for (int i=0; i<noutdevs; i++) SETSYMBOL(argv+i, gensym(outdevlist + i*DEVDESCSIZE)); + typedmess(pd->s_thing, selector, noutdevs, argv); + } else if (f < noutdevs) { + SETSYMBOL(argv, gensym(outdevlist + f*DEVDESCSIZE)); + typedmess(pd->s_thing, selector, 1, argv); + } +} + +void glob_midi_getcurrentindevs(t_pd *dummy) { + /* these are the devices you're using: */ + int nindev, midiindev[MAXMIDIINDEV]; + int noutdev, midioutdev[MAXMIDIOUTDEV]; + t_atom argv[MAXNDEV]; + sys_get_midi_params(&nindev, midiindev, &noutdev, midioutdev); + for (int i=0; i<nindev; i++) SETFLOAT(argv+i, midiindev[i]); + typedmess(gensym("pd")->s_thing, gensym("midicurrentindev"), nindev, argv); +} + +void glob_midi_getcurrentoutdevs(t_pd *dummy) { + /* these are the devices you're using: */ + int nindev, midiindev[MAXMIDIINDEV]; + int noutdev, midioutdev[MAXMIDIOUTDEV]; + t_atom argv[MAXNDEV]; + sys_get_midi_params(&nindev, midiindev, &noutdev, midioutdev); + for (int i=0; i<noutdev; i++) SETFLOAT(argv+i, midioutdev[i]); + typedmess(gensym("pd")->s_thing, gensym("midicurrentoutdev"), noutdev, argv); +} diff --git a/desiredata/src/s_midi_alsa.c b/desiredata/src/s_midi_alsa.c new file mode 100644 index 00000000..894d7200 --- /dev/null +++ b/desiredata/src/s_midi_alsa.c @@ -0,0 +1,209 @@ +/* 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. */ + +/* MIDI I/O for Linux using ALSA */ + +#include <stdio.h> +#ifdef UNISTD +#include <unistd.h> +#endif +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include <alsa/asoundlib.h> +#include "desire.h" +//#include "s_stuff.h" + +#define MAX_EVENT_SIZE 256 + +static int alsa_nmidiin; +static int alsa_midiinfd[MAXMIDIINDEV]; +static int alsa_nmidiout; +static int alsa_midioutfd[MAXMIDIOUTDEV]; +static snd_seq_t *midi_handle; +static snd_midi_event_t *midiev; + +static unsigned short CombineBytes(unsigned char First, unsigned char Second) { + return ((unsigned short)Second << 7) | (unsigned short)First; +} + +void sys_alsa_do_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec) { + char portname[50]; + int err = 0; + snd_seq_client_info_t *alsainfo; + /* do we want to connect pd automatically with other devices ?; see below! */ + /* LATER: think about a flag to enable/disable automatic connection (sometimes it could be a pain) */ + int autoconnect = 1; + alsa_nmidiin = 0; + alsa_nmidiout = 0; + if (nmidiout == 0 && nmidiin == 0) return; + if (nmidiin>MAXMIDIINDEV) {post( "midi input ports reduced to maximum %d", MAXMIDIINDEV); nmidiin =MAXMIDIINDEV;} + if (nmidiout>MAXMIDIOUTDEV) {post("midi output ports reduced to maximum %d", MAXMIDIOUTDEV); nmidiout=MAXMIDIOUTDEV;} + if (nmidiin>0 && nmidiout>0) err = snd_seq_open(&midi_handle,"default",SND_SEQ_OPEN_DUPLEX,0); + else if (nmidiin > 0) err = snd_seq_open(&midi_handle,"default",SND_SEQ_OPEN_INPUT,0); + else if (nmidiout > 0) err = snd_seq_open(&midi_handle,"default",SND_SEQ_OPEN_OUTPUT,0); + if (err!=0) { + sys_setalarm(1000000); + post("couldn't open alsa sequencer"); + return; + } + int client; + for (int i=0; i<nmidiin; i++) { + sprintf(portname,"Pure Data Midi-In %d",i+1); + int port = snd_seq_create_simple_port(midi_handle,portname, + SND_SEQ_PORT_CAP_WRITE |SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_APPLICATION); + if (port < 0) goto error; + alsa_midiinfd[i] = port; + } + for (int i=0; i<nmidiout; i++) { + sprintf(portname,"Pure Data Midi-Out %d",i+1); + int port = snd_seq_create_simple_port(midi_handle,portname, + SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, SND_SEQ_PORT_TYPE_APPLICATION); + if (port < 0) goto error; + alsa_midioutfd[i] = port; + } + snd_seq_client_info_malloc(&alsainfo); + snd_seq_get_client_info(midi_handle,alsainfo); + snd_seq_client_info_set_name(alsainfo,"Pure Data"); + client = snd_seq_client_info_get_client(alsainfo); + snd_seq_set_client_info(midi_handle,alsainfo); + snd_seq_client_info_free(alsainfo); + post("Opened Alsa Client %d in:%d out:%d",client,nmidiin,nmidiout); + sys_setalarm(0); + snd_midi_event_new(MAX_EVENT_SIZE,&midiev); + alsa_nmidiout = nmidiout; + alsa_nmidiin = nmidiin; + /* JMZ: connect all available devices to pd */ + if (autoconnect) { + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + snd_seq_port_subscribe_t *subs; + snd_seq_addr_t other, topd, frompd; + /* since i don't know how to connect multiple ports (connect everything to each port, modulo,...), + * i only fully connect where we have only one single port */ + if(alsa_nmidiin ) { topd.client = client; topd.port = alsa_midiinfd [0];} + if(alsa_nmidiout) {frompd.client = client; frompd.port = alsa_midioutfd[0];} + snd_seq_port_subscribe_alloca(&subs); + snd_seq_client_info_alloca(&cinfo); + snd_seq_port_info_alloca(&pinfo); + snd_seq_client_info_set_client(cinfo, -1); + while (snd_seq_query_next_client(midi_handle, cinfo) >= 0) { + /* reset query info */ + int client_id=snd_seq_client_info_get_client(cinfo); + if((SND_SEQ_CLIENT_SYSTEM != client_id)&&(client != client_id)) { /* skipping port 0 and ourself */ + snd_seq_port_info_set_client(pinfo, client_id); + snd_seq_port_info_set_port(pinfo, -1); + while (snd_seq_query_next_port(midi_handle, pinfo) >= 0) { + other.client=client_id; + other.port =snd_seq_port_info_get_port(pinfo); + if(1==alsa_nmidiin) /* only autoconnect 1st port */ { + snd_seq_port_subscribe_set_sender(subs, &other); + snd_seq_port_subscribe_set_dest(subs, &topd); + snd_seq_subscribe_port(midi_handle, subs); + } + if(1==alsa_nmidiout) /* only autoconnect 1st port */ { + snd_seq_port_subscribe_set_sender(subs, &frompd); + snd_seq_port_subscribe_set_dest(subs, &other); + snd_seq_subscribe_port(midi_handle, subs); + } + } + } + } + } + return; + error: + sys_setalarm(1000000); + post("couldn't open alsa MIDI output device"); + return; +} + +#define md_msglen(x) (((x)<0xC0)?2:((x)<0xE0)?1:((x)<0xF0)?2:\ + ((x)==0xF2)?2:((x)<0xF4)?1:0) + +void sys_alsa_putmidimess(int portno, int a, int b, int c) { + int channel = a&15; + snd_seq_event_t ev; + snd_seq_ev_clear(&ev); + if (portno >= 0 && portno < alsa_nmidiout) { + if (a >= 224) { // pitchbend + snd_seq_ev_set_pitchbend(&ev,channel,CombineBytes(b,c)); + } else if (a >= 208) { // touch + snd_seq_ev_set_chanpress(&ev,channel,b); + } else if (a >= 192) { // program + snd_seq_ev_set_pgmchange(&ev,channel,b); + } else if (a >= 176) { // controller + snd_seq_ev_set_controller(&ev,channel,b,c); + } else if (a >= 160) { // polytouch + snd_seq_ev_set_keypress(&ev,channel,b,c); + } else if (a >= 144) { // note + channel = a-144; + if (c) snd_seq_ev_set_noteon(&ev,channel,b,c); + else snd_seq_ev_set_noteoff(&ev,channel,b,c); + } + snd_seq_ev_set_direct(&ev); + snd_seq_ev_set_subs(&ev); + snd_seq_ev_set_source(&ev,alsa_midioutfd[portno]); + snd_seq_event_output_direct(midi_handle,&ev); + } +} + +void sys_alsa_putmidibyte(int portno, int byte) { + snd_seq_event_t ev; + snd_seq_ev_clear(&ev); + if (portno >= 0 && portno < alsa_nmidiout) { + // repack into 1 byte char and put somewhere to point at + unsigned char data = (unsigned char)byte; + snd_seq_ev_set_sysex(&ev,1,&data); //...set_variable *should* have worked but didn't + snd_seq_ev_set_direct(&ev); + snd_seq_ev_set_subs(&ev); + snd_seq_ev_set_source(&ev,alsa_midioutfd[portno]); + snd_seq_event_output_direct(midi_handle,&ev); + } +} + +/* this version uses the asynchronous "read()" ... */ +void sys_alsa_poll_midi() { + unsigned char buf[MAX_EVENT_SIZE]; + int count, alsa_source; + snd_seq_event_t *midievent = NULL; + if (alsa_nmidiout == 0 && alsa_nmidiin == 0) return; + snd_midi_event_init(midiev); + if (!alsa_nmidiout && !alsa_nmidiin) return; + count = snd_seq_event_input_pending(midi_handle,1); + if (count != 0) count = snd_seq_event_input(midi_handle,&midievent); + if (midievent != NULL) { + count = snd_midi_event_decode(midiev,buf,sizeof(buf),midievent); + alsa_source = midievent->dest.port; + for(int i=0; i<count; i++) sys_midibytein(alsa_source, (buf[i] & 0xff)); + //post("received %d midi bytes",count); + } +} + +void sys_alsa_close_midi() { + alsa_nmidiin = alsa_nmidiout = 0; + if(midi_handle) { + snd_seq_close(midi_handle); + if(midiev) snd_midi_event_free(midiev); + } +} + +#define NSEARCH 10 +static int alsa_nmidiindevs, alsa_nmidioutdevs, alsa_initted; + +void midi_alsa_init() { + if (alsa_initted) return; + alsa_initted = 1; +} + +void midi_alsa_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int maxndev, int devdescsize) { + int ndev = min(maxndev,alsa_nmidiindevs); + for (int i=0; i<ndev; i++) sprintf(indevlist + i * devdescsize, "ALSA MIDI device #%d", i+1); + *nindevs = ndev; + ndev = min(maxndev,alsa_nmidioutdevs); + for (int i=0; i<ndev; i++) sprintf(outdevlist + i * devdescsize, "ALSA MIDI device #%d", i+1); + *noutdevs = ndev; +} diff --git a/desiredata/src/s_midi_mmio.c b/desiredata/src/s_midi_mmio.c new file mode 100644 index 00000000..95c95d76 --- /dev/null +++ b/desiredata/src/s_midi_mmio.c @@ -0,0 +1,505 @@ +/* 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 "s_stuff.h" +#include <stdio.h> +#include <windows.h> +#include <MMSYSTEM.H> + +/* ------------- MIDI time stamping from audio clock ------------ */ +#ifdef MIDI_TIMESTAMP + +static double msw_hibuftime; +static double initsystime = -1; + +/* call this whenever we reset audio */ +static void msw_resetmidisync() { + initsystime = clock_getsystime(); + msw_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 msw_midisync() { + if (initsystime == -1) msw_resetmidisync(); + double jittersec = max(msw_dacjitterbufsallowed,msw_adcjitterbufsallowed) * REALDACBLKSIZE / sys_getsr(); + double diff = sys_getrealtime() - 0.001 * clock_gettimesince(initsystime); + if (diff > msw_hibuftime) msw_hibuftime = diff; + if (diff < msw_hibuftime - jittersec) { + post("jitter excess %d %f", dac, diff); + msw_resetmidisync(); + } +} + +static double msw_midigettimefor(LARGE_INTEGER timestamp) { + /* this is broken now... used to work when "timestamp" was derived from QueryPerformanceCounter() instead of + the MS-approved timeGetSystemTime() call in the MIDI callback routine below. */ + return msw_tixtotime(timestamp) - msw_hibuftime; +} +#endif /* MIDI_TIMESTAMP */ + +/* ------------------------- MIDI output -------------------------- */ +static void msw_midiouterror(char *s, int err) { + char t[256]; + midiOutGetErrorText(err, t, 256); + error(s,t); +} + +static HMIDIOUT hMidiOut[MAXMIDIOUTDEV]; /* output device */ +static int msw_nmidiout; /* number of devices */ + +static void msw_open_midiout(int nmidiout, int *midioutvec) { + UINT result, wRtn; + MIDIOUTCAPS midioutcaps; + if (nmidiout > MAXMIDIOUTDEV) nmidiout = MAXMIDIOUTDEV; + int dev = 0; + for (int i=0; i<nmidiout; i++) { + MIDIOUTCAPS mocap; + int devno = midioutvec[i]; + result = midiOutOpen(&hMidiOut[dev], devno, 0, 0, CALLBACK_NULL); + wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap, sizeof(mocap)); + if (result != MMSYSERR_NOERROR) { + error("midiOutOpen: %s",midioutcaps.szPname); + msw_midiouterror("midiOutOpen: %s", result); + } else { + if (sys_verbose) error("midiOutOpen: Open %s as Port %d", midioutcaps.szPname, dev); + dev++; + } + } + msw_nmidiout = dev; +} + +static void msw_close_midiout() { + for (int i=0; i<msw_nmidiout; i++) { + midiOutReset(hMidiOut[i]); + midiOutClose(hMidiOut[i]); + } + msw_nmidiout = 0; +} + +/* -------------------------- MIDI input ---------------------------- */ + +#define INPUT_BUFFER_SIZE 1000 // size of input buffer in events + +static void msw_midiinerror(char *s, int err) { + char t[256]; + midiInGetErrorText(err, t, 256); + error(s,t); +} + +/* Structure to represent a single MIDI event. */ +#define EVNT_F_ERROR 0x00000001L +typedef struct evemsw_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 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(). + Return: A pointer to the allocated CALLBACKINSTANCE data structure. */ +LPCALLBACKINSTANCEDATA FAR PASCAL AllocCallbackInstanceData() { + HANDLE hMem; + LPCALLBACKINSTANCEDATA lpBuf; + /* Allocate and lock global memory. */ + hMem = GlobalAlloc(GMEM_SHARE | GMEM_MOVEABLE, (DWORD)sizeof(CALLBACKINSTANCEDATA)); + if(!hMem) return 0; + lpBuf = (LPCALLBACKINSTANCEDATA)GlobalLock(hMem); + if(!lpBuf) {GlobalFree(hMem); return 0;} + /* 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. */ +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) return 0; + lpBuf = (LPCIRCULARBUFFER)GlobalLock(hMem); + if(!lpBuf) {GlobalFree(hMem); return 0;} + /* 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) { +#ifndef _WIN32 + GlobalSmartPageUnlock((HGLOBAL)HIWORD(lpBuf)); +#endif + GlobalUnlock(lpBuf->hSelf); + GlobalFree(lpBuf->hSelf); + return 0; + } + lpMem = (LPEVENT)GlobalLock(hMem); + if(!lpMem) { + 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. */ +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. */ + +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) */ +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 msw_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) { + 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) && (i<MAXMIDIINDEV); i++) { + if ((lpCallbackInstanceData[ndev] = AllocCallbackInstanceData()) == NULL) { + printf("Not enough memory available.\n"); + FreeCircularBuffer(lpInputBuffer); + return; + } + lpCallbackInstanceData[i]->dwDevice = i; + lpCallbackInstanceData[i]->lpBuf = lpInputBuffer; + wRtn = midiInOpen((LPHMIDIIN)&hMidiIn[ndev], midiinvec[i], (DWORD)midiInputHandler, + (DWORD)lpCallbackInstanceData[ndev], CALLBACK_FUNCTION); + if (wRtn) { + FreeCallbackInstanceData(lpCallbackInstanceData[ndev]); + msw_midiinerror("midiInOpen: %s", wRtn); + } else ndev++; + } + /* Start MIDI input. */ + for (i=0; i<ndev; i++) { + if (hMidiIn[i]) midiInStart(hMidiIn[i]); + } + wNumDevices = ndev; +} + +static void msw_close_midiin() { + unsigned int i; + /* Stop, reset, close MIDI input. Free callback instance data. */ + for (i=0; (i<wNumDevices) && (i<MAXMIDIINDEV); i++) { + if (hMidiIn[i]) { + if (sys_verbose) post("closing MIDI input %d...", i); + midiInStop(hMidiIn[i]); + midiInReset(hMidiIn[i]); + midiInClose(hMidiIn[i]); + FreeCallbackInstanceData(lpCallbackInstanceData[i]); + } + } + /* Free input buffer. */ + if (lpInputBuffer) FreeCircularBuffer(lpInputBuffer); + if (sys_verbose) post("...done"); + wNumDevices = 0; +} + +/* ------------------- public routines -------------------------- */ + +void sys_putmidimess(int portno, int a, int b, int c) { + DWORD foo; + MMRESULT res; + if (portno >= 0 && portno < msw_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 < msw_nmidiout) { + res = midiOutShortMsg(hMidiOut[portno], byte); + if (res != MMSYSERR_NOERROR) post("MIDI out error %d", res); + } +} + +void sys_poll_midi() { + static EVENT msw_nextevent; + static int msw_isnextevent; + static double msw_nexteventtime; + while (1) { + if (!msw_isnextevent) { + if (!GetEvent(lpInputBuffer, &msw_nextevent)) break; + msw_isnextevent = 1; +#ifdef MIDI_TIMESTAMP + msw_nexteventtime = msw_midigettimefor(&foo.timestamp); +#endif + } +#ifdef MIDI_TIMESTAMP + if (0.001 * clock_gettimesince(initsystime) >= msw_nexteventtime) +#endif + { + int msgtype = ((msw_nextevent.data & 0xf0) >> 4) - 8; + int commandbyte = msw_nextevent.data & 0xff; + int byte1 = (msw_nextevent.data >> 8) & 0xff; + int byte2 = (msw_nextevent.data >> 16) & 0xff; + int portno = msw_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; + } + msw_isnextevent = 0; + } + } +} + +void sys_do_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec) { + if (nmidiout) msw_open_midiout(nmidiout, midioutvec); + if (nmidiin) { + post("Warning: midi input is dangerous in Microsoft Windows; see Pd manual)"); + msw_open_midiin(nmidiin, midiinvec); + } +} + +void sys_close_midi() { + msw_close_midiin(); + msw_close_midiout(); +} + +#if 0 +/* list the audio and MIDI device names */ +void sys_listmididevs() { + 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) msw_midiinerror("midiInGetDevCaps: %s", wRtn); + else error("MIDI input device #%d: %s", i+1, micap.szPname); + } + ndevices = midiOutGetNumDevs(); + for (i = 0; i < ndevices; i++) { + MIDIOUTCAPS mocap; + wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap, sizeof(mocap)); + if (wRtn) msw_midiouterror("midiOutGetDevCaps: %s", wRtn); + else error("MIDI output device #%d: %s", i+1, mocap.szPname); + } +} +#endif + +void midi_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int maxndev, int devdescsize) { + int i, nin = midiInGetNumDevs(), nout = midiOutGetNumDevs(); + UINT wRtn; + if (nin > maxndev) nin = maxndev; + for (i = 0; i < nin; i++) { + MIDIINCAPS micap; + wRtn = midiInGetDevCaps(i, (LPMIDIINCAPS) &micap, sizeof(micap)); + strncpy(indevlist + i * devdescsize, (wRtn ? "???" : micap.szPname), devdescsize); + indevlist[(i+1) * devdescsize - 1] = 0; + } + if (nout > maxndev) nout = maxndev; + for (i = 0; i < nout; i++) { + MIDIOUTCAPS mocap; + wRtn = midiOutGetDevCaps(i, (LPMIDIOUTCAPS) &mocap, sizeof(mocap)); + strncpy(outdevlist + i * devdescsize, (wRtn ? "???" : mocap.szPname), devdescsize); + outdevlist[(i+1) * devdescsize - 1] = 0; + } + *nindevs = nin; + *noutdevs = nout; +} diff --git a/desiredata/src/s_midi_none.c b/desiredata/src/s_midi_none.c new file mode 100644 index 00000000..9d83545e --- /dev/null +++ b/desiredata/src/s_midi_none.c @@ -0,0 +1,16 @@ +/* This is for compiling pd without any midi support. by matju, 2006.11.21 */ + +#include "m_pd.h" +#include "s_stuff.h" + +void sys_do_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec) {} +void sys_close_midi(void) {} +void sys_putmidimess(int portno, int a, int b, int c) {} +void sys_putmidibyte(int portno, int byte) {} +void sys_poll_midi(void) {} +void midi_getdevs(char *indevlist, int *nindevs, + char *outdevlist, int *noutdevs, int maxndev, int devdescsize) +{ + *nindevs = 0; + *noutdevs = 0; +} diff --git a/desiredata/src/s_midi_oss.c b/desiredata/src/s_midi_oss.c new file mode 100644 index 00000000..1cfeae7c --- /dev/null +++ b/desiredata/src/s_midi_oss.c @@ -0,0 +1,234 @@ +/* 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. */ + +/* MIDI I/O for Linux using OSS */ + +#include <stdio.h> +#ifdef UNISTD +#include <unistd.h> +#endif +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <errno.h> +#include "m_pd.h" +#include "s_stuff.h" + +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 sys_do_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 == 0 && fd < 0 && outdevindex >= 0) { + sys_setalarm(1000000); fd = open("/dev/midi", O_RDWR | O_MIDIFLAG); + if (sys_verbose) error("device 1: tried /dev/midi READ/WRITE; returned %d", fd); + if (outdevindex >= 0 && fd >= 0) oss_midioutfd[outdevindex] = fd; + } + if (fd < 0 && outdevindex >= 0) { + sprintf(namebuf, "/dev/midi%2.2d", devno); + sys_setalarm(1000000); fd = open(namebuf, O_RDWR | O_MIDIFLAG); + if (sys_verbose) error("device %d: tried %s READ/WRITE; returned %d", devno, namebuf, fd); + if (outdevindex >= 0 && fd >= 0) oss_midioutfd[outdevindex] = fd; + } + if (fd < 0 && outdevindex >= 0) { + sprintf(namebuf, "/dev/midi%d", devno); + sys_setalarm(1000000); fd = open(namebuf, O_RDWR | O_MIDIFLAG); + if (sys_verbose) error("device %d: tried %s READ/WRITE; returned %d", 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) error("device 1: tried /dev/midi READONLY; returned %d", fd); + } + if (fd < 0) { + sprintf(namebuf, "/dev/midi%2.2d", devno); + sys_setalarm(1000000); fd = open(namebuf, O_RDONLY | O_MIDIFLAG); + if (sys_verbose) error("device %d: tried %s READONLY; returned %d", devno, namebuf, fd); + } + if (fd < 0) { + sprintf(namebuf, "/dev/midi%d", devno); + sys_setalarm(1000000); fd = open(namebuf, O_RDONLY | O_MIDIFLAG); + if (sys_verbose) error("device %d: tried %s READONLY; returned %d", 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) error("device 1: tried /dev/midi WRITEONLY; returned %d", fd); + } + if (fd < 0) { + sprintf(namebuf, "/dev/midi%2.2d", devno); + sys_setalarm(1000000); fd = open(namebuf, O_WRONLY | O_MIDIFLAG); + if (sys_verbose) error("device %d: tried %s WRITEONLY; returned %d", devno, namebuf, fd); + } + if (fd < 0) { + sprintf(namebuf, "/dev/midi%d", devno); + sys_setalarm(1000000); fd = open(namebuf, O_WRONLY | O_MIDIFLAG); + if (sys_verbose) error("device %d: tried %s WRITEONLY; returned %d", 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); + sys_setalarm(0); +} + +#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() { + int 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 (int 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 (int 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) error("Midi read error"); + else sys_midibytein(i, (c & 0xff)); + did = 1; + } + } +} +#else +/* this version uses the asynchronous "read()" ... */ +void sys_poll_midi() { + int throttle = 100; + struct timeval timout; + int did = 1, maxfd = 0; + while (did) { + fd_set readset, writeset, exceptset; + did = 0; + if (throttle-- < 0) break; + for (int 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 sys_close_midi() { + for (int i=0; i<oss_nmidiin ; i++) close(oss_midiinfd [i]); + for (int i=0; i<oss_nmidiout; i++) close(oss_midioutfd[i]); + oss_nmidiin = oss_nmidiout = 0; +} + +#define NSEARCH 10 +static int oss_nmidiindevs, oss_nmidioutdevs, oss_initted; + +void midi_oss_init() { + if (oss_initted) return; + oss_initted = 1; + for (int i=0; i<NSEARCH; i++) { + int fd; + char namebuf[80]; + oss_nmidiindevs = i; + /* try to open the device for reading */ + if (i == 0) { + fd = open("/dev/midi", O_RDONLY | O_NDELAY); + if (fd >= 0) {close(fd); continue;} + } + sprintf(namebuf, "/dev/midi%2.2d", i); + fd = open(namebuf, O_RDONLY | O_NDELAY); + if (fd >= 0) {close(fd); continue;} + sprintf(namebuf, "/dev/midi%d", i); + fd = open(namebuf, O_RDONLY | O_NDELAY); + if (fd >= 0) {close(fd); continue;} + break; + } + for (int i=0; i<NSEARCH; i++) { + int fd; + char namebuf[80]; + oss_nmidioutdevs = i; + /* try to open the device for writing */ + if (i == 0) { + fd = open("/dev/midi", O_WRONLY | O_NDELAY); + if (fd >= 0) {close(fd); continue;} + } + sprintf(namebuf, "/dev/midi%2.2d", i); + fd = open(namebuf, O_WRONLY | O_NDELAY); + if (fd >= 0) {close(fd); continue;} + sprintf(namebuf, "/dev/midi%d", i); + fd = open(namebuf, O_WRONLY | O_NDELAY); + if (fd >= 0) {close(fd); continue;} + break; + } +} + +void midi_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int maxndev, int devdescsize) { + int i, ndev; + if ((ndev = oss_nmidiindevs) > maxndev) ndev = maxndev; + for (i = 0; i < ndev; i++) sprintf(indevlist + i * devdescsize, "OSS MIDI device #%d", i+1); + *nindevs = ndev; + if ((ndev = oss_nmidioutdevs) > maxndev) ndev = maxndev; + for (i = 0; i < ndev; i++) sprintf(outdevlist + i * devdescsize, "OSS MIDI device #%d", i+1); + *noutdevs = ndev; +} diff --git a/desiredata/src/s_midi_pm.c b/desiredata/src/s_midi_pm.c new file mode 100644 index 00000000..d51badf7 --- /dev/null +++ b/desiredata/src/s_midi_pm.c @@ -0,0 +1,239 @@ +/* Copyright (c) 1997-2003 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 calls portmidi to do MIDI I/O for MSW and Mac OSX. + applied sysexin/midiin patch by Nathaniel Dose, july 2007. + +*/ + + +#include "m_pd.h" +#include "s_stuff.h" +#include <stdio.h> +#ifdef UNISTD +#include <unistd.h> +#include <sys/time.h> +#include <sys/resource.h> +#endif +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <portaudio.h> +#include <portmidi.h> +#include <porttime.h> + +static PmStream *mac_midiindevlist[MAXMIDIINDEV]; +static PmStream *mac_midioutdevlist[MAXMIDIOUTDEV]; +static int mac_nmidiindev; +static int mac_nmidioutdev; + +void sys_do_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec) { + PmError err; + Pt_Start(1, 0, 0); /* start a timer with millisecond accuracy */ + mac_nmidiindev = 0; + for (int i=0; i<nmidiin; i++) { + int found = 0,count = Pm_CountDevices(); + for (int j=0, devno=0; j<count && !found; j++) { + const PmDeviceInfo *info = Pm_GetDeviceInfo(j); + if (info->input) { + if (devno == midiinvec[i]) { + err = Pm_OpenInput(&mac_midiindevlist[mac_nmidiindev],j,NULL,100,NULL,NULL); + if (err != pmNoError) post("could not open midi input %d (%s): %s", j, info->name, Pm_GetErrorText(err)); + else {mac_nmidiindev++; if (sys_verbose) post("Midi Input (%s) opened.", info->name);} + found = 1; + } + devno++; + } + } + if (!found) post("could not find midi device %d",midiinvec[i]); + } + mac_nmidioutdev = 0; + for (int i=0; i<nmidiout; i++) { + int found = 0,count = Pm_CountDevices(); + for (int j=0, devno=0; j<count && !found; j++) { + const PmDeviceInfo *info = Pm_GetDeviceInfo(j); + if (info->output) { + if (devno == midioutvec[i]) { + err = Pm_OpenOutput(&mac_midioutdevlist[mac_nmidioutdev],j,NULL,0,NULL,NULL,0); + if (err != pmNoError) post("could not open midi output %d (%s): %s",j,info->name,Pm_GetErrorText(err)); + else {mac_nmidioutdev++; if (sys_verbose) post("Midi Output (%s) opened.",info->name);} + found = 1; + } + devno++; + } + } + if (!found) post("could not find midi device %d",midioutvec[i]); + } +} + +void sys_close_midi () { + for (int i=0; i< mac_nmidiindev; i++) Pm_Close(mac_midiindevlist[i]); + mac_nmidiindev = 0; + for (int i=0; i<mac_nmidioutdev; i++) Pm_Close(mac_midioutdevlist[i]); + mac_nmidioutdev = 0; +} + +void sys_putmidimess(int portno, int a, int b, int c) { + PmEvent buffer; + /* post("put 1 msg %d %d", portno, mac_nmidioutdev); */ + if (portno >= 0 && portno < mac_nmidioutdev) { + buffer.message = Pm_Message(a, b, c); + buffer.timestamp = 0; + /* post("put msg"); */ + Pm_Write(mac_midioutdevlist[portno], &buffer, 1); + } +} + +static void writemidi4(PortMidiStream* stream, int a, int b, int c, int d) { + PmEvent buffer; + buffer.timestamp = 0; + buffer.message = ((a & 0xff) | ((b & 0xff) << 8) | ((c & 0xff) << 16) | ((d & 0xff) << 24)); + Pm_Write(stream, &buffer, 1); +} + +void sys_putmidibyte(int portno, int byte) { + /* try to parse the bytes into MIDI messages so they can fit into PortMidi buffers. */ + static int mess[4]; + static int nbytes = 0, sysex = 0, i; + if (byte >= 0xf8) /* MIDI real time */ + writemidi4(mac_midioutdevlist[portno], byte, 0, 0, 0); + else if (byte == 0xf0) { + mess[0] = 0xf0; + nbytes = 1; + sysex = 1; + } else if (byte == 0xf7) { + mess[nbytes] = byte; + for (i = nbytes+1; i<4; i++) mess[i] = 0; + writemidi4(mac_midioutdevlist[portno], mess[0], mess[1], mess[2], mess[3]); + sysex = 0; + nbytes = 0; + } else if (byte >= 0x80) { + sysex = 0; + if (byte == 0xf4 || byte == 0xf5 || byte == 0xf6) { + writemidi4(mac_midioutdevlist[portno], byte, 0, 0, 0); + nbytes = 0; + } else { + mess[0] = byte; + nbytes = 1; + } + } else if (sysex) { + mess[nbytes] = byte; + nbytes++; + if (nbytes == 4) { + writemidi4(mac_midioutdevlist[portno], mess[0], mess[1], mess[2], mess[3]); + nbytes = 0; + } + } else if (nbytes) { + int status = mess[0]; + if (status < 0xf0) status &= 0xf0; + /* 2 byte messages: */ + if (status == 0xc0 || status == 0xd0 || status == 0xf1 || status == 0xf3) { + writemidi4(mac_midioutdevlist[portno], mess[0], byte, 0, 0); + nbytes = (status < 0xf0 ? 1 : 0); + } else { + if (nbytes == 1) { + mess[1] = byte; + nbytes = 2; + } else { + writemidi4(mac_midioutdevlist[portno], + mess[0], mess[1], byte, 0); + nbytes = (status < 0xf0 ? 1 : 0); + } + } + } +} + +/* this is non-zero if we are in the middle of transmitting sysex */ +int nd_sysex_mode=0; + +/* send in 4 bytes of sysex data. if one of the bytes is 0xF7 (sysex end) stop and unset nd_sysex_mode */ +void nd_sysex_inword(int midiindev, int status, int data1, int data2, int data3) { + if (nd_sysex_mode) {sys_midibytein(midiindev, status); if (status == 0xF7) nd_sysex_mode = 0;} + if (nd_sysex_mode) {sys_midibytein(midiindev, data1); if (data1 == 0xF7) nd_sysex_mode = 0;} + if (nd_sysex_mode) {sys_midibytein(midiindev, data2); if (data2 == 0xF7) nd_sysex_mode = 0;} + if (nd_sysex_mode) {sys_midibytein(midiindev, data3); if (data3 == 0xF7) nd_sysex_mode = 0;} +} + +void sys_poll_midi() { + PmEvent buffer; + for (int i=0; i<mac_nmidiindev; i++) { + int nmess = Pm_Read(mac_midiindevlist[i], &buffer, 1); + if (nmess > 0) { + PmMessage msg = buffer.message; + int status = Pm_MessageStatus(msg); + if(status == 0xf0 || !(status&0x80)) { + /* sysex header or data */ + for(int j=0; j<4; ++j,msg >>= 8) { + int data = msg&0xff; + sys_midibytein(i, data); + if(data == 0xf7) break; /* sysex end */ + } + } else { + int data1 = Pm_MessageData1(msg); + int data2 = Pm_MessageData2(msg); + /* non-sysex */ + sys_midibytein(i, status); + switch(status>>4) { + case 0x8: /* note off */ + case 0x9: /* note on */ + case 0xa: /* poly pressure */ + case 0xb: /* control change */ + case 0xe: /* pitch bend */ + sys_midibytein(i,data1); + sys_midibytein(i,data2); + break; + case 0xc: /* program change */ + case 0xd: /* channel pressure */ + sys_midibytein(i,data1); + break; + case 0xf: /* system common/realtime messages */ + switch(status) { + case 0xf1: /* time code */ + case 0xf3: /* song select */ + case 0xf6: /* tune request */ + sys_midibytein(i,data1); + break; + case 0xf2: /* song position pointer */ + sys_midibytein(i,data1); + sys_midibytein(i,data2); + break; + case 0xf7: // from Nathaniel; don't know whether it'll work in this context. + nd_sysex_mode=1; + nd_sysex_inword(i,status,data1,data2,((msg>>24)&0xFF)); + break; + default: // from Nathaniel too. + if (nd_sysex_mode) nd_sysex_inword(i,status,data1,data2,((msg>>24)&0xFF)); + break; + } + } + } + } + } +} + +#if 0 +/* lifted from pa_devs.c in portaudio */ +void sys_listmididevs() { + for (int 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 + +void midi_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int maxndev, int devdescsize) { + int nindev=0, noutdev=0; + for (int i=0; i<Pm_CountDevices(); i++) { + const PmDeviceInfo *info = Pm_GetDeviceInfo(i); + /* post("%d: %s, %s (%d,%d)", i, info->interf, info->name,info->input, info->output); */ + if (info->input && nindev < maxndev) {strcpy(indevlist + nindev * devdescsize, info->name); nindev++;} + if (info->output && noutdev < maxndev) {strcpy(outdevlist + noutdev * devdescsize, info->name); noutdev++;} + } + *nindevs = nindev; + *noutdevs = noutdev; +} diff --git a/desiredata/src/s_midi_sgi.c b/desiredata/src/s_midi_sgi.c new file mode 100644 index 00000000..8a0895ba --- /dev/null +++ b/desiredata/src/s_midi_sgi.c @@ -0,0 +1,143 @@ +/* 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 "s_stuff.h" +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#ifdef HAVE_BSTRING_H +#include <bstring.h> +#endif +#include <sys/types.h> +#include <sys/time.h> +#include <dmedia/audio.h> +#include <sys/fpu.h> +#include <dmedia/midi.h> +int mdInit(); /* prototype was messed up in midi.h */ +//#include "sys/select.h" + +/* 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() { + union fpc_csr f; + f.fc_word = get_fpc_csr(); + f.fc_struct.flush = 1; + set_fpc_csr(f.fc_word); +} + +#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"); + 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() { + 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"); + 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; + } + } + } + } +} + +void sys_do_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec) { + sgi_open_midi(nmidiin!=0, nmidiout!=0); +} +void sys_close_midi() {/* ??? */} + +void midi_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int maxndev, int devdescsize) { + int i, nindev = 0, noutdev = 0; + for (i = 0; i < mdInit(); i++) { + if (nindev < maxndev) { + strcpy(indevlist + nindev * devdescsize, mdGetName(i)); + nindev++; + strcpy(outdevlist + noutdev * devdescsize, mdGetName(i)); + noutdev++; + } + } + *nindevs = nindev; + *noutdevs = noutdev; +} diff --git a/desiredata/src/s_path.c b/desiredata/src/s_path.c new file mode 100644 index 00000000..06a37c1a --- /dev/null +++ b/desiredata/src/s_path.c @@ -0,0 +1,350 @@ +/* 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) + +#include <stdlib.h> +#ifdef UNISTD +#include <unistd.h> +#include <sys/stat.h> +#endif +#ifdef MSW +#include <io.h> +#endif + +#include <string.h> +#include "desire.h" +#include <stdio.h> +#include <fcntl.h> +#include <ctype.h> + +extern t_namelist *sys_externlist; +t_namelist *sys_searchpath; +t_namelist *sys_helppath; + +/* change '/' characters to the system's native file separator */ +void sys_bashfilename(const char *from, char *to) { + char c; + while ((c = *from++)) { +#ifdef MSW + 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 MSW + if (c == '\\') c = '/'; +#endif + *to++ = c; + } + *to = 0; +} + +/******************* Utility functions used below ******************/ + +/* 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++; + to = (char *)malloc(size+1); + strncpy(to,from,size); + to[size] = '\0'; + if (from[size] == '\0') return NULL; + return size ? from+size+1 : 0; +} + +/* add a single item to a namelist. If "allowdup" is true, duplicates +may be added; othewise they're dropped. */ +t_namelist *namelist_append(t_namelist *listwas, const char *s, int allowdup) { + t_namelist *nl, *nl2 = (t_namelist *)getbytes(sizeof(*nl)); + nl2->nl_next = 0; + nl2->nl_string = strdup(s); + sys_unbashfilename(nl2->nl_string, nl2->nl_string); + if (!listwas) return nl2; + for (nl = listwas; ;) { + if (!allowdup && !strcmp(nl->nl_string, s)) return listwas; + if (!nl->nl_next) break; + nl = nl->nl_next; + } + nl->nl_next = nl2; + return listwas; +} + +/* add a colon-separated list of names to a namelist */ + +#ifdef MSW +#define SEPARATOR ';' /* in MSW the natural separator is semicolon instead */ +#else +#define SEPARATOR ':' +#endif + +t_namelist *namelist_append_files(t_namelist *listwas, const char *s) { + const char *npos = s; + t_namelist *nl = listwas; + do { + char *temp; + npos = strtokcpy(temp, npos, SEPARATOR); + if (!*temp) continue; + nl = namelist_append(nl, temp, 0); + free(temp); + } while (npos); + return nl; +} + +void namelist_free(t_namelist *listwas) { + t_namelist *nl2; + for (t_namelist *nl = listwas; nl; nl = nl2) { + nl2 = nl->nl_next; + free(nl->nl_string); + free(nl); + } +} + +char *namelist_get(t_namelist *namelist, int n) { + int i=0; + for (t_namelist *nl = namelist; i < n && nl; nl = nl->nl_next) {if (i==n) return nl->nl_string; else i++;} + return 0; +} + +static t_namelist *pd_extrapath; + +int sys_usestdpath = 1; + +void sys_setextrapath(const char *p) { + namelist_free(pd_extrapath); + pd_extrapath = namelist_append(0, p, 0); +} + +#ifdef MSW +#define MSWOPENFLAG(bin) (bin ? _O_BINARY : _O_TEXT) +#else +#define MSWOPENFLAG(bin) 0 +#endif + +/* try to open a file in the directory "dir", named "name""ext", for reading. "Name" may have slashes. + The directory is copied to "dirresult" which must be at least "size" bytes. "nameresult" is set + to point to the filename (copied elsewhere into the same buffer). The "bin" flag requests opening + for binary (which only makes a difference on Windows). */ +int sys_trytoopenone(const char *dir, const char *name, const char* ext, char **dirresult, char **nameresult, int bin) { + bool needslash = (*dir && dir[strlen(dir)-1] != '/'); + asprintf(dirresult,"%s%s%s%s", dir, needslash ? "/" : "", name, ext); + sys_bashfilename(*dirresult, *dirresult); + DEBUG(post("looking for %s",*dirresult)); + /* see if we can open the file for reading */ + int fd = open(*dirresult,O_RDONLY | MSWOPENFLAG(bin)); + if (fd<0) { + if (sys_verbose) post("tried %s and failed", *dirresult); + return -1; + } +#ifdef UNISTD /* in unix, further check that it's not a directory */ + 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); + return -1; + } +#endif + if (sys_verbose) post("tried %s and succeeded", *dirresult); + sys_unbashfilename(*dirresult, *dirresult); + char *slash = strrchr(*dirresult, '/'); + if (slash) { + *slash = 0; + *nameresult = slash + 1; + } else *nameresult = *dirresult; + return fd; +} + +/* check if we were given an absolute pathname, if so try to open it and return 1 to signal the caller to cancel any path searches */ +int sys_open_absolute(const char *name, const char* ext, char **dirresult, char **nameresult, int bin, int *fdp) { + if (name[0] == '/' +#ifdef MSW + || (name[1] == ':' && name[2] == '/') +#endif + ) { + int dirlen = strrchr(name, '/') - name; + char *dirbuf = new char[dirlen+1]; + *fdp = sys_trytoopenone(name, name+dirlen+1, ext, dirresult, nameresult, bin); + delete[] dirbuf; + return 1; + } else return 0; +} + +/* search for a file in a specified directory, then along the globally +defined search path, using ext as filename extension. 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. Exception: +if the 'name' starts with a slash or a letter, colon, and slash in MSW, +there is no search and instead we just try to open the file literally. */ + +/* see also canvas_openfile() which, in addition, searches down the +canvas-specific path. */ + +static int do_open_via_path( +const char *dir, const char *name, const char *ext, char **dirresult, char **nameresult, int bin, t_namelist *searchpath) { + int fd = -1; + /* first check if "name" is absolute (and if so, try to open) */ + if (sys_open_absolute(name, ext, dirresult, nameresult, bin, &fd)) return fd; + /* otherwise "name" is relative; try the directory "dir" first. */ + if ((fd = sys_trytoopenone(dir, name, ext, dirresult, nameresult, bin)) >= 0) return fd; + /* next go through the search path */ + for (t_namelist *nl=searchpath; nl; nl=nl->nl_next) + if ((fd = sys_trytoopenone(nl->nl_string, name, ext, dirresult, nameresult, bin)) >= 0) return fd; + /* next look in "extra" */ + if (sys_usestdpath && (fd = sys_trytoopenone(pd_extrapath->nl_string, name, ext, dirresult, nameresult, bin)) >= 0) + return fd; + *dirresult = 0; + *nameresult = *dirresult; + return -1; +} + +extern "C" int open_via_path2(const char *dir, const char *name, const char *ext, char **dirresult, char **nameresult, int bin) { + return do_open_via_path(dir, name, ext, dirresult, nameresult, bin, sys_searchpath); +} + +/* open via path, using the global search path. */ +extern "C" int open_via_path(const char *dir, const char *name, const char *ext, +char *dirresult, char **nameresult, unsigned int size, int bin) { + char *dirr; + int r = do_open_via_path(dir, name, ext, &dirr, nameresult, bin, sys_searchpath); + if (dirr) {strncpy(dirresult,dirr,size); dirresult[size-1]=0; free(dirr);} + return r; +} + +/* Open a help file using the help search path. We expect the ".pd" suffix here, + even though we have to tear it back off for one of the search attempts. */ +extern "C" void open_via_helppath(const char *name, const char *dir) { + char *realname=0, *dirbuf, *basename; + int suffixed = strlen(name) > 3 && !strcmp(name+strlen(name)-3, ".pd"); + asprintf(&realname,"%.*s-help.pd",strlen(name)-3*suffixed,name); + int fd; + if ((fd = do_open_via_path(dir,realname,"",&dirbuf,&basename,0,sys_helppath))>=0) goto gotone; + free(realname); + asprintf(&realname,"help-%s",name); + if ((fd = do_open_via_path(dir,realname,"",&dirbuf,&basename,0,sys_helppath))>=0) goto gotone; + free(realname); + if ((fd = do_open_via_path(dir, name,"",&dirbuf,&basename,0,sys_helppath))>=0) goto gotone; + post("sorry, couldn't find help patch for \"%s\"", name); + return; +gotone: + close(fd); if (realname) free(realname); + glob_evalfile(0, gensym((char*)basename), gensym(dirbuf)); +} + +extern "C" int sys_argparse(int argc, char **argv); + +#define NUMARGS 1000 + +extern "C" int sys_parsercfile(char *filename) { + int rcargc; + char* rcargv[NUMARGS]; + char buf[1000]; + char c[MAXPDSTRING]; + int retval = 1; /* that's what we will return at the end; for now, let's think it'll be an error */ + /* initialize rc-arg-array so we can safely clean up at the end */ + for (int i=1; i<NUMARGS-1; i++) rcargv[i]=0; + /* parse a startup file */ + FILE* file = fopen(filename, "r"); + if (!file) return 1; + post("reading startup file: %s", filename); + rcargv[0] = "."; /* this no longer matters to sys_argparse() */ + /* tb: comments in pdrc file { */ + int i=1; + while ((fgets(c,MAXPDSTRING,file)) != 0) { + if (c[strlen(c)-1] !='\n') { + //it is unlikely that this is ever the case + error("startup file contains a line that's too long"); + while(fgetc(file) != '\n') {} + } + if (c[0] != '#') { + long n; + while (sscanf(c,"%999s%ln",buf,&n) != EOF) { + buf[999] = 0; + if (!(rcargv[i] = (char *)malloc(strlen(buf) + 1))) goto cleanup; + strcpy(rcargv[i], buf); + strcpy(buf,c+n); + strcpy(c,buf); + ++i; + } + } + } + /* } tb */ + if (i >= NUMARGS-1) error("startup file too long; extra args dropped"); + 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-1, rcargv+1)) { + post("error parsing RC arguments"); + goto cleanup; + } + retval=0; /* we made it without an error */ + cleanup: /* prevent memleak */ + for (i=1; i<NUMARGS-1; i++) if (rcargv[i]) free(rcargv[i]); + return retval; +} + +#ifndef MSW +#define STARTUPNAME ".pdrc" +extern "C" int sys_rcfile () { + char *fname, *home = getenv("HOME"); + asprintf(&fname,"%s/%s",home? home : ".",STARTUPNAME); + int r = sys_parsercfile(fname); + free(fname); + return r; +} +#endif /* MSW */ + +void sys_doflags() { + int beginstring = 0, state = 0, len = strlen(sys_flags->s_name); + int rcargc = 0; + char *rcargv[MAXPDSTRING]; + if (len > MAXPDSTRING) {post("flags: %s: too long", sys_flags->s_name); return;} + for (int i=0; i<len+1; i++) { + int c = sys_flags->s_name[i]; + if (state == 0) { + if (c && !isspace(c)) { + beginstring = i; + state = 1; + } + } else { + if (!c || isspace(c)) { + char *foo = (char *)malloc(i - beginstring + 1); + if (!foo) return; + strncpy(foo, sys_flags->s_name + beginstring, i - beginstring); + foo[i - beginstring] = 0; + rcargv[rcargc] = foo; + rcargc++; + if (rcargc >= MAXPDSTRING) break; + state = 0; + } + } + } + if (sys_argparse(rcargc, rcargv)) post("error parsing startup arguments"); +} + +extern "C" void glob_update_path () { + t_namelist *nl; + sys_vgui("global pd_path; set pd_path {"); + for (nl=sys_searchpath; nl; nl=nl->nl_next) sys_vgui("%s ",nl->nl_string); + sys_vgui("}\n"); +} diff --git a/desiredata/src/s_stuff.h b/desiredata/src/s_stuff.h new file mode 100644 index 00000000..8edf5982 --- /dev/null +++ b/desiredata/src/s_stuff.h @@ -0,0 +1,321 @@ +#ifndef __STUFF_H +#define __STUFF_H +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +extern "C" { +#endif + +/* Copyright (c) 1997-1999 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in this distribution. */ + +/* Audio and MIDI I/O, and other scheduling and system stuff. */ + +/* NOTE: this file describes Pd implementation details which may change +in future releases. The public (stable) API is in m_pd.h. */ + +/* in s_path.c */ + +typedef struct _namelist { /* element in a linked list of stored strings */ + struct _namelist *nl_next; /* next in list */ + char *nl_string; /* the string */ +} t_namelist; + +t_namelist *namelist_append(t_namelist *listwas, const char *s, int allowdup); +t_namelist *namelist_append_files(t_namelist *listwas, const char *s); +void namelist_free(t_namelist *listwas); +char *namelist_get(t_namelist *namelist, int n); +void sys_setextrapath(const char *p); +extern int sys_usestdpath; +extern t_namelist *sys_externlist; +extern t_namelist *sys_searchpath; +extern t_namelist *sys_helppath; + +// IOhannes : added namespace support for libraries +// the define QUALIFIED_NAME both turns on namespaces and sets the library-object delimiter +#define QUALIFIED_NAME "/" +#ifdef QUALIFIED_NAME +void pd_set_library_name(char *libname); +#endif + +int sys_open_absolute( const char *name, const char* ext, char **dirresult, char **nameresult, int bin, int *fdp); +int sys_trytoopenone(const char *dir, const char *name, const char* ext, char **dirresult, char **nameresult, int bin); + +/* s_main.c */ +extern int sys_debuglevel; +extern int sys_verbose; +extern int sys_noloadbang; +extern int sys_nogui; +extern char *sys_guicmd; +extern int sys_tooltips; +extern int sys_defeatrt; +extern t_symbol *sys_flags; +extern t_symbol *sys_libdir; /* library directory for auxilliary files */ + +/* s_loader.c */ +int sys_load_lib(t_canvas *canvas, char *filename); + +/* s_audio.c */ +#define SENDDACS_NO 0 /* return values for sys_send_dacs() */ +#define SENDDACS_YES 1 +#define SENDDACS_SLEPT 2 +#define DEFDACBLKSIZE 64 /* the default dac~blocksize */ +extern int sys_dacblocksize; /* the real dac~blocksize */ +extern int sys_schedblocksize; /* audio block size for scheduler */ +extern int sys_hipriority; /* real-time flag, true if priority boosted */ +extern t_sample *sys_soundout; +extern t_sample *sys_soundin; +extern int sys_inchannels; +extern int sys_outchannels; +extern int sys_advance_samples; /* scheduler advance in samples */ +extern int sys_blocksize; /* audio I/O block size in sample frames */ +extern float sys_dacsr; +extern int sys_schedadvance; +extern int sys_sleepgrain; +extern int sys_callbackscheduler; /* tb: scheduler to use (0: traditional, 1: callback) */ +void sys_open_audio( +int naudioindev, int * audioindev, int nchindev, int * chindev, +int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, +int srate, int dacblocksize, int advance, int schedmode, int enable); +void sys_reopen_audio(void); +void sys_close_audio(void); +int sys_send_dacs(void); +void sys_set_priority(int higher); +void sys_audiobuf(int nbufs); +void sys_getmeters(float *inmax, float *outmax); +void sys_listdevs(void); +void sys_setblocksize(int n); +void sys_update_sleepgrain(void); //tb + +/* s_midi.c */ +#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[]; +void sys_open_midi(int nmidiin, int *midiinvec, int nmidiout, int *midioutvec, int enable); +void sys_get_midi_params(int *pnmidiindev, int *pmidiindev, int *pnmidioutdev, int *pmidioutdev); +void sys_get_midi_apis(char *buf); +void sys_reopen_midi( void); +void sys_close_midi( void); +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); +/* implemented in the system dependent MIDI code (s_midi_pm.c, etc.) */ +void midi_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int maxndev, int devdescsize); +void sys_do_open_midi(int nmidiindev, int *midiindev, int nmidioutdev, int *midioutdev); + +#ifdef USEAPI_ALSA +EXTERN void sys_alsa_putmidimess(int portno, int a, int b, int c); +EXTERN void sys_alsa_putmidibyte(int portno, int a); +EXTERN void sys_alsa_poll_midi(void); +EXTERN void sys_alsa_setmiditimediff(double inbuftime, double outbuftime); +EXTERN void sys_alsa_midibytein(int portno, int byte); +EXTERN void sys_alsa_close_midi( void); +/* implemented in the system dependent MIDI code (s_midi_pm.c, etc. ) */ +void midi_alsa_getdevs(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int maxndev, int devdescsize); +void sys_alsa_do_open_midi(int nmidiindev, int *midiindev, int nmidioutdev, int *midioutdev); +#endif + +/* 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 +#define ERR_XRUN 5 +#define ERR_SYSLOCK 6 +void sched_set_using_dacs(int flag); +void sys_setscheduler(int scheduler); //tb +int sys_getscheduler(void); //tb + +/* s_inter.c */ +EXTERN void sys_microsleep(int microsec); +EXTERN void sys_bail(int exitcode); +EXTERN int sys_pollgui(void); +typedef void (*t_socketnotifier)(void *x); +typedef void (*t_socketreceivefn)(void *x, t_binbuf *b); + +typedef struct _socketreceiver { + char *inbuf; + int inhead; + int intail; + void *owner; + int udp; + t_socketnotifier notifier; + t_socketreceivefn socketreceivefn; +/* for sending only: */ + int fd; + struct _socketreceiver *next; + char *obuf; + int ohead, otail, osize; + int waitingforping; + int bytessincelastping; +} t_socketreceiver; + +EXTERN char pd_version[]; +EXTERN t_text *sys_netreceive; +EXTERN t_socketreceiver *sys_socketreceiver; +//EXTERN t_socketreceiver *netreceive_newest_receiver(t_text *x); + +EXTERN t_namelist *sys_externlist; +EXTERN t_namelist *sys_openlist; +EXTERN t_namelist *sys_messagelist; + +EXTERN t_socketreceiver *socketreceiver_new(t_pd *owner, int fd, + 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 + +#define API_NONE 0 +#define API_ALSA 1 +#define API_OSS 2 +#define API_MMIO 3 +#define API_PORTAUDIO 4 +#define API_JACK 5 +#define API_SGI 6 +#define API_ASIO 7 + +#if defined(USEAPI_OSS) +#define API_DEFAULT API_OSS +#define API_DEFSTRING "oss" +#elif defined(USEAPI_ALSA) +#define API_DEFAULT API_ALSA +#define API_DEFSTRING "alsa" +#elif defined(USEAPI_JACK) +#define API_DEFAULT API_JACK +#define API_DEFSTRING "jack" +#elif defined(USEAPI_MMIO) +#define API_DEFAULT API_MMIO +#define API_DEFSTRING "mmio" +#elif defined(USEAPI_PORTAUDIO) +#define API_DEFAULT API_PORTAUDIO +#define API_DEFSTRING "portaudio" +#else +#define API_DEFAULT 0 +#define API_DEFSTRING "none" +#endif + +#define DEFAULTAUDIODEV 0 +#define MAXAUDIOINDEV 4 +#define MAXAUDIOOUTDEV 4 +#define DEFMIDIDEV 0 +#define DEFAULTSRATE 44100 +#ifdef MSW +#define DEFAULTADVANCE 70 +#else +#define DEFAULTADVANCE 50 +#endif + +struct t_audiodevs { + int ndev; + int dev[MAXAUDIOINDEV]; + int chdev[MAXAUDIOINDEV]; +#ifdef __cplusplus + t_audiodevs() : ndev(-1) {} +#endif +}; + +/* new audio api interface */ +typedef struct t_audioapi { + int (*open_audio)( + int naudioindev, int *audioindev, int nchindev, int *chindev, + int naudiooutdev, int *audiooutdev, int nchoutdev, int *choutdev, + int rate, int schedmode); + void (*close_audio)(void); + int (*send_dacs)(void); + void (*getdevs)(char *indevlist, int *nindevs, char *outdevlist, int *noutdevs, int *canmulti, int maxndev, int devdescsize); +} t_audioapi; + +int pa_open_audio(int inchans, int outchans, int rate, int advance, int indeviceno, int outdeviceno, int schedmode); + +/* tb { */ +void pa_test_setting (int ac, t_atom *av); +void pa_getcurrent_devices(void); +void pa_getaudiooutdevinfo(t_float f); +void pa_getaudioindevinfo(t_float f); +/* } tb */ + +int jack_open_audio(int wantinchans, int wantoutchans, int rate, int scheduler); +void jack_listdevs(void); +void sys_listmididevs(void); +void sys_set_midi_api(int whichapi); +void sys_set_audio_api(int whichapi); +void sys_get_audio_apis(char *buf); +extern int sys_audioapi; +void sys_set_audio_state(int onoff); + +/* API dependent audio flags and settings */ +void oss_set32bit(void); +void linux_alsa_devname(char *devname); + +void sys_get_audio_params(t_audiodevs *in, t_audiodevs *out, int *prate, int *dacblocksize, int *padvance, int *pscheduler); +void sys_save_audio_params(t_audiodevs *in, t_audiodevs *out, int rate, int dacblocksize, int advance, int scheduler); + +/* s_file.c */ +typedef void (*t_printhook)(const char *s); +extern t_printhook sys_printhook; /* set this to override printing */ +extern int sys_printtostderr; +#ifdef MSW +#define vsnprintf _vsnprintf /* jsarlo -- alias this name for msw */ +#endif + +/* jsarlo { */ +EXTERN double sys_time; +EXTERN double sys_time_per_dsp_tick; +EXTERN int sys_externalschedlib; + +EXTERN t_sample* get_sys_soundout(void); +EXTERN t_sample* get_sys_soundin(void); +EXTERN int* get_sys_main_advance(void); +EXTERN double* get_sys_time_per_dsp_tick(void); +EXTERN int* get_sys_schedblocksize(void); +EXTERN double* get_sys_time(void); +EXTERN float* get_sys_dacsr(void); +EXTERN int* get_sys_sleepgrain(void); +EXTERN int* get_sys_schedadvance(void); + +EXTERN void sys_clearhist(void); +EXTERN void sys_initmidiqueue(void); +EXTERN int sys_addhist(int phase); +EXTERN void sys_setmiditimediff(double inbuftime, double outbuftime); +EXTERN void sched_tick(double next_sys_time); +EXTERN void sys_pollmidiqueue(void); +EXTERN int sys_pollgui(void); +EXTERN void sys_setchsr(int chin, int chout, int sr, int dacblocksize); + +EXTERN void inmidi_noteon(int portno, int channel, int pitch, int velo); +EXTERN void inmidi_controlchange(int portno, int channel, int ctlnumber, int value); +EXTERN void inmidi_programchange(int portno, int channel, int value); +EXTERN void inmidi_pitchbend(int portno, int channel, int value); +EXTERN void inmidi_aftertouch(int portno, int channel, int value); +EXTERN void inmidi_polyaftertouch(int portno, int channel, int pitch, int value); +/* } jsarlo */ + +/* 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); + +#if defined(_LANGUAGE_C_PLUS_PLUS) || defined(__cplusplus) +} +#endif +#endif /* __STUFF_H */ diff --git a/desiredata/src/s_watchdog.c b/desiredata/src/s_watchdog.c new file mode 100644 index 00000000..14089eeb --- /dev/null +++ b/desiredata/src/s_watchdog.c @@ -0,0 +1,40 @@ +/* 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 <sys/time.h> +#include <sys/types.h> +#include <unistd.h> +#include <signal.h> +#include <stdio.h> + +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; + continue; + } + happy = 0; + kill(getppid(), SIGHUP); + fprintf(stderr, "watchdog: signaling pd...\n"); + } +} diff --git a/desiredata/src/tests/abstr-test.pd b/desiredata/src/tests/abstr-test.pd new file mode 100644 index 00000000..dce22c6b --- /dev/null +++ b/desiredata/src/tests/abstr-test.pd @@ -0,0 +1,3 @@ +#N canvas 0 0 450 300 10; +#X obj 30 36 abstr; +#X obj 101 36 abstr2; diff --git a/desiredata/src/tests/abstr.pd b/desiredata/src/tests/abstr.pd new file mode 100644 index 00000000..91250635 --- /dev/null +++ b/desiredata/src/tests/abstr.pd @@ -0,0 +1,11 @@ +#N canvas 0 0 450 300 10; +#X obj 77 5 inlet; +#X obj 189 6 inlet; +#X obj 171 256 outlet; +#X obj 133 6 inlet; +#X obj 245 7 inlet; +#X connect 0 0 2 0; +#X connect 1 0 2 0; +#X connect 3 0 2 0; +#X connect 4 0 2 0; +#X coords 0 0 1 1 0 0 0; diff --git a/desiredata/src/tests/abstr2.pd b/desiredata/src/tests/abstr2.pd new file mode 100644 index 00000000..c3a9639c --- /dev/null +++ b/desiredata/src/tests/abstr2.pd @@ -0,0 +1,15 @@ +#N canvas 0 0 450 300 10; +#X obj 19 29 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X obj 39 29 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1 +; +#X obj 59 29 nbx 8 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 0 8 -262144 +-1 -1 0 256; +#X obj 19 52 vsl 15 128 0 127 0 0 empty empty empty 0 -6 0 8 -262144 +-1 -1 0 1; +#X obj 19 11 hsl 128 15 0 127 0 0 empty empty empty 0 -6 0 8 -262144 +-1 -1 0 1; +#X obj 136 29 vradio 15 1 0 8 empty empty empty 0 -6 0 8 -262144 -1 +-1 0; +#X obj 38 156 hradio 15 1 0 8 empty empty empty 0 -6 0 8 -262144 -1 +-1 6; diff --git a/desiredata/src/tests/all_guis_and_gop.pd b/desiredata/src/tests/all_guis_and_gop.pd new file mode 100644 index 00000000..2be10062 --- /dev/null +++ b/desiredata/src/tests/all_guis_and_gop.pd @@ -0,0 +1,116 @@ +#N canvas 339 27 590 690 10; +#X obj 98 29 +; +#X floatatom 99 83 5 0 0 0 - - -; +#X symbolatom 99 108 10 0 0 0 - - -; +#X obj 158 27 bng 42 250 50 0 empty empty button-label 50 8 0 8 -258699 +-1 -1; +#X obj 158 87 tgl 15 0 empty empty hello? 20 8 0 9 -24198 -1 -1 0 1 +; +#X obj 227 140 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 0 10 +-262144 -1 -1 0 256; +#X obj 5 13 vsl 25 150 0 127 0 0 empty empty vslider-hello -2 -6 0 +9 -241291 -1 -1 0 0; +#X obj 43 201 hsl 100 10 0 127 0 0 empty empty hello-hslider 10 6 0 +9 -262144 -1 -258699 0 0; +#X obj 67 159 hradio 15 1 0 8 empty empty hello-hradio 0 -6 0 9 -261681 +-1 -1 0; +#X obj 68 24 vradio 15 1 0 8 empty empty hello-vradio 0 -6 0 9 -262144 +-1 -1 0; +#X obj 435 73 vu 15 120 empty vumeter-label -1 -8 0 8 -166441 -1 1 +0; +#X obj 306 9 cnv 15 100 60 empty empty empty 20 12 0 14 -233017 -66577 +0; +#N canvas 0 0 450 300 graph2 0; +#X array array1 100 float 1; +#A 0 -0.0571427 -0.0428571 -0.0857141 -0.171428 -0.185714 -0.199999 +-0.206428 -0.212856 -0.219285 -0.225714 -0.232142 -0.238571 -0.244999 +-0.251428 -0.257856 -0.264285 -0.270713 -0.277142 -0.28357 -0.289999 +-0.296427 -0.302856 -0.309285 -0.315713 -0.322142 -0.32857 -0.331427 +-0.334285 -0.337142 -0.339999 -0.342856 -0.345713 -0.34857 -0.351427 +-0.354284 -0.357141 -0.359999 -0.362856 -0.365713 -0.35857 -0.351427 +-0.329999 -0.30857 -0.279999 -0.25619 -0.23238 -0.208571 -0.18 0.162856 +0.305712 0.419998 0.448569 0.491426 0.57714 0.591425 0.605711 0.305712 +0.14857 0.105713 -0.00857189 -0.122857 -0.237142 -0.322856 -0.40857 +-0.479998 -0.50857 -0.565712 -0.594283 -0.608569 -0.608569 -0.608569 +-0.594283 -0.565712 -0.546664 -0.527617 -0.494284 -0.460951 0.0342851 +-0.0514288 -0.165714 -0.201428 -0.251428 -0.265714 -0.294285 -0.337142 +-0.337142 -0.365713 -0.365713 -0.40857 -0.437141 -0.451427 -0.451427 +-0.451427 -0.479998 -0.494284 -0.477141 -0.482855 -0.48857 -0.494284 +-0.499998; +#X coords 0 1 99 -1 200 140 1; +#X restore 199 178 graph; +#X floatatom 432 22 5 0 0 0 - - -; +#X floatatom 479 23 5 -99 42 2 floatatom-label - -; +#X obj 225 74 nbx 5 14 -1e+37 1e+37 0 0 empty empty numbox2-label 60 +8 0 9 -260818 -62784 -1 0 256; +#X floatatom 237 48 5 0 0 0 - - -; +#X msg 98 57; +#N canvas 0 0 450 300 foo 0; +#X obj 148 99 +; +#X floatatom 149 153 5 0 0 0 - - -; +#X symbolatom 149 178 10 0 0 0 - - -; +#X obj 208 97 bng 42 250 50 0 empty empty button-label 50 8 0 8 -258699 +-1 -1; +#X obj 208 157 tgl 15 0 empty empty hello? 20 8 0 9 -24198 -1 -1 0 +1; +#X obj 277 210 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 0 10 +-262144 -1 -1 0 256; +#X obj 55 83 vsl 25 150 0 127 0 0 empty empty vslider-hello -2 -6 0 +9 -241291 -1 -1 0 0; +#X obj 93 271 hsl 100 10 0 127 0 0 empty empty hello-hslider 10 6 0 +9 -262144 -1 -258699 0 0; +#X obj 117 229 hradio 15 1 0 8 empty empty hello-hradio 0 -6 0 9 -261681 +-1 -1 0; +#X obj 118 94 vradio 15 1 0 8 empty empty hello-vradio 0 -6 0 9 -262144 +-1 -1 0; +#X obj 485 143 vu 15 120 empty vumeter-label -1 -8 0 8 -166441 -1 1 +0; +#X obj 356 79 cnv 15 100 60 empty empty empty 20 12 0 14 -233017 -66577 +0; +#N canvas 0 0 450 300 graph2 0; +#X array array1 100 float 1; +#A 0 -0.0571427 -0.0428571 -0.0857141 -0.171428 -0.185714 -0.199999 +-0.206428 -0.212856 -0.219285 -0.225714 -0.232142 -0.238571 -0.244999 +-0.251428 -0.257856 -0.264285 -0.270713 -0.277142 -0.28357 -0.289999 +-0.296427 -0.302856 -0.309285 -0.315713 -0.322142 -0.32857 -0.331427 +-0.334285 -0.337142 -0.339999 -0.342856 -0.345713 -0.34857 -0.351427 +-0.354284 -0.357141 -0.359999 -0.362856 -0.365713 -0.35857 -0.351427 +-0.329999 -0.30857 -0.279999 -0.25619 -0.23238 -0.208571 -0.18 0.162856 +0.305712 0.419998 0.448569 0.491426 0.57714 0.591425 0.605711 0.305712 +0.14857 0.105713 -0.00857189 -0.122857 -0.237142 -0.322856 -0.40857 +-0.479998 -0.50857 -0.565712 -0.594283 -0.608569 -0.608569 -0.608569 +-0.594283 -0.565712 -0.546664 -0.527617 -0.494284 -0.460951 0.0342851 +-0.0514288 -0.165714 -0.201428 -0.251428 -0.265714 -0.294285 -0.337142 +-0.337142 -0.365713 -0.365713 -0.40857 -0.437141 -0.451427 -0.451427 +-0.451427 -0.479998 -0.494284 -0.477141 -0.482855 -0.48857 -0.494284 +-0.499998; +#X coords 0 1 99 -1 200 140 1; +#X restore 249 248 graph; +#X floatatom 482 92 5 0 0 0 - - -; +#X floatatom 529 93 5 -99 42 2 floatatom-label - -; +#X obj 275 144 nbx 5 14 -1e+37 1e+37 0 0 empty empty numbox2-label +60 8 0 9 -260818 -62784 -1 0 256; +#X floatatom 287 118 5 0 0 0 - - -; +#X msg 148 127; +#X obj 15 17 bng 15 250 50 0 empty empty i_shouldn't_be_visible_in_parent +0 -6 0 9 -262144 -1 -1; +#X connect 3 0 4 0; +#X connect 4 0 5 0; +#X connect 6 0 7 0; +#X connect 8 0 7 0; +#X connect 9 0 8 0; +#X connect 13 0 10 0; +#X connect 14 0 10 1; +#X connect 15 0 5 0; +#X connect 16 0 15 0; +#X coords 0 -1 1 1 400 300 1 50 50; +#X restore 99 336 pd foo; +#X connect 3 0 4 0; +#X connect 4 0 5 0; +#X connect 6 0 7 0; +#X connect 8 0 7 0; +#X connect 9 0 8 0; +#X connect 13 0 10 0; +#X connect 14 0 10 1; +#X connect 15 0 5 0; +#X connect 16 0 15 0; diff --git a/desiredata/src/tests/all_guis_and_gop.pd.gif b/desiredata/src/tests/all_guis_and_gop.pd.gif Binary files differnew file mode 100644 index 00000000..288c6902 --- /dev/null +++ b/desiredata/src/tests/all_guis_and_gop.pd.gif diff --git a/desiredata/src/tests/bof.pd b/desiredata/src/tests/bof.pd new file mode 100644 index 00000000..706950cf --- /dev/null +++ b/desiredata/src/tests/bof.pd @@ -0,0 +1,27 @@ +#N canvas 0 0 675 450 10; +#X obj 23 28 +; +#X obj 35 206 adc~; +#X obj 99 124 -; +#X obj 142 214 * 42; +#X msg 130 47 foo; +#X msg 280 91 0; +#X msg 280 70 set \$1; +#X obj 280 46 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 0 +0; +#X obj 228 54 vsl 15 128 0 127 0 0 empty empty empty 0 -6 0 8 -262144 +-1 -1 0 0 0; +#X obj 254 64 vradio 15 1 0 8 empty empty empty 0 -6 0 8 -262144 -1 +-1 0; +#X floatatom 381 79 5 0 0 0 - - -; +#X obj 372 109 nbx 5 14 -1e+37 1e+37 0 0 empty empty empty 0 -6 0 8 +-262144 -1 -1 0 0 256; +#X symbolatom 352 42 10 0 0 0 - - -; +#X obj -43 -41 lop~; +#X text 11 3 there's a [lop~] top left \, but negative coords are buggy. +; +#X connect 0 0 1 0; +#X connect 0 0 2 1; +#X connect 2 0 3 0; +#X connect 4 0 2 0; +#X connect 6 0 5 0; +#X connect 7 0 6 0; diff --git a/desiredata/src/tests/chun.pd b/desiredata/src/tests/chun.pd new file mode 100644 index 00000000..0ee7d48a --- /dev/null +++ b/desiredata/src/tests/chun.pd @@ -0,0 +1,36 @@ +#N canvas 385 550 450 300 10; +#X obj 151 212 dac~; +#X obj 132 139 *~ 0.02; +#X msg 231 120 0; +#X obj 252 95 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X msg 235 154 0.2; +#X obj 274 140 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X obj 45 105 delread~ down; +#X obj 21 26 delread~ up; +#X obj 15 132 *~ 0.8; +#X obj 126 41 osc~ 500; +#X obj 133 11 osc~ 40; +#X obj 225 19 +~ 1; +#X obj 219 46 *~ 500; +#X floatatom 337 69 5 0 0 0 - - -; +#X obj 46 78 delwrite~ down 20; +#X obj 15 156 delwrite~ up 20; +#X text 323 50 tweak me; +#X connect 1 0 0 0; +#X connect 1 0 0 1; +#X connect 2 0 1 1; +#X connect 3 0 2 0; +#X connect 4 0 1 1; +#X connect 5 0 4 0; +#X connect 6 0 8 0; +#X connect 6 0 1 0; +#X connect 7 0 14 0; +#X connect 8 0 15 0; +#X connect 9 0 14 0; +#X connect 10 0 11 0; +#X connect 11 0 12 0; +#X connect 12 0 9 0; +#X connect 13 0 6 0; +#X connect 13 0 7 0; diff --git a/desiredata/src/tests/city.pd b/desiredata/src/tests/city.pd new file mode 100644 index 00000000..9b30647c --- /dev/null +++ b/desiredata/src/tests/city.pd @@ -0,0 +1,128 @@ +#N canvas 0 0 450 300 10; +#X obj 23 20 +; +#X obj 53 20 +; +#X obj 23 50 +; +#X obj 53 50 +; +#X obj 83 20 +; +#X obj 113 20 +; +#X obj 83 50 +; +#X obj 113 50 +; +#X obj 23 80 +; +#X obj 53 80 +; +#X obj 23 110 +; +#X obj 53 110 +; +#X obj 83 80 +; +#X obj 113 80 +; +#X obj 83 110 +; +#X obj 113 110 +; +#X obj 143 20 +; +#X obj 173 20 +; +#X obj 143 50 +; +#X obj 173 50 +; +#X obj 203 20 +; +#X obj 233 20 +; +#X obj 203 50 +; +#X obj 233 50 +; +#X obj 143 80 +; +#X obj 173 80 +; +#X obj 143 110 +; +#X obj 173 110 +; +#X obj 203 80 +; +#X obj 233 80 +; +#X obj 203 110 +; +#X obj 233 110 +; +#X obj 23 140 +; +#X obj 53 140 +; +#X obj 23 170 +; +#X obj 53 170 +; +#X obj 83 140 +; +#X obj 113 140 +; +#X obj 83 170 +; +#X obj 113 170 +; +#X obj 23 200 +; +#X obj 53 200 +; +#X obj 23 230 +; +#X obj 53 230 +; +#X obj 83 200 +; +#X obj 113 200 +; +#X obj 83 230 +; +#X obj 113 230 +; +#X obj 143 140 +; +#X obj 173 140 +; +#X obj 143 170 +; +#X obj 173 170 +; +#X obj 203 140 +; +#X obj 233 140 +; +#X obj 203 170 +; +#X obj 233 170 +; +#X obj 143 200 +; +#X obj 173 200 +; +#X obj 143 230 +; +#X obj 173 230 +; +#X obj 203 200 +; +#X obj 233 200 +; +#X obj 203 230 +; +#X obj 233 230 +; +#X connect 0 0 1 0; +#X connect 0 0 2 0; +#X connect 1 0 4 0; +#X connect 2 0 3 0; +#X connect 2 0 8 0; +#X connect 4 0 5 0; +#X connect 4 0 6 0; +#X connect 5 0 16 0; +#X connect 6 0 7 0; +#X connect 8 0 9 0; +#X connect 8 0 10 0; +#X connect 9 0 12 0; +#X connect 10 0 11 0; +#X connect 10 0 32 0; +#X connect 12 0 13 0; +#X connect 12 0 14 0; +#X connect 14 0 15 0; +#X connect 16 0 17 0; +#X connect 16 0 18 0; +#X connect 17 0 20 0; +#X connect 18 0 19 0; +#X connect 18 0 24 0; +#X connect 20 0 21 0; +#X connect 20 0 22 0; +#X connect 22 0 23 0; +#X connect 24 0 25 0; +#X connect 24 0 26 0; +#X connect 25 0 28 0; +#X connect 26 0 27 0; +#X connect 28 0 29 0; +#X connect 28 0 30 0; +#X connect 30 0 31 0; +#X connect 32 0 33 0; +#X connect 32 0 34 0; +#X connect 33 0 36 0; +#X connect 34 0 35 0; +#X connect 34 0 40 0; +#X connect 36 0 37 0; +#X connect 36 0 38 0; +#X connect 37 0 48 0; +#X connect 38 0 39 0; +#X connect 40 0 41 0; +#X connect 40 0 42 0; +#X connect 41 0 44 0; +#X connect 42 0 43 0; +#X connect 44 0 45 0; +#X connect 44 0 46 0; +#X connect 46 0 47 0; +#X connect 48 0 49 0; +#X connect 48 0 50 0; +#X connect 49 0 52 0; +#X connect 50 0 51 0; +#X connect 50 0 56 0; +#X connect 52 0 53 0; +#X connect 52 0 54 0; +#X connect 54 0 55 0; +#X connect 56 0 57 0; +#X connect 56 0 58 0; +#X connect 57 0 60 0; +#X connect 58 0 59 0; +#X connect 60 0 61 0; +#X connect 60 0 62 0; +#X connect 62 0 63 0; diff --git a/desiredata/src/tests/desiredata-presentation-piksel06.pd b/desiredata/src/tests/desiredata-presentation-piksel06.pd new file mode 100644 index 00000000..fee64d2d --- /dev/null +++ b/desiredata/src/tests/desiredata-presentation-piksel06.pd @@ -0,0 +1,26 @@ +#N canvas 0 0 640 480 10; +#X text 100 140 Zoomable patches (and def Canvas item); +#X text 100 160 Client-side selection and clipboard; +#X text 100 180 Client-side undo and redo: multiple \, atomic \, labeled +\, history; +#X text 100 200 Class browser and class name completions; +#X text 100 220 Internationalization (the Patching-in-Tongues project) +; +#X text 100 240 Keyboard-based Navigation and Edition; +#X text 100 260 Server Preferences (.pdrc Editor); +#X text 100 280 Client Preferences (.ddrc Editor); +#X text 100 300 Canvas Actions; +#X text 100 320 #V for visual attributes; +#V bg 255 255 0; +#X text 100 340 Dialog autogeneration with def Dialog add; +#X text 100 360 ClientClassTreeDialog; +#X text 100 380 Deconstructors; +#X text 100 400 desire.h; +#X text 100 420 what is happening to desire.c these days; +#X obj 94 31 bng 32 250 50 0 empty empty DesireData-0.39.A 40 15 1 +19 -262088 -1 -1; +#X text 295 270 tab support in all dialogs; +#X obj 0 0 + 242; +#V pretty 1; +#X text 99 440 scaling of selection or complete patch; +#X coords 0 0 1 1 0 0 0; diff --git a/desiredata/src/tests/gop-one.pd b/desiredata/src/tests/gop-one.pd new file mode 100644 index 00000000..b1414997 --- /dev/null +++ b/desiredata/src/tests/gop-one.pd @@ -0,0 +1,18 @@ +#N canvas 447 533 552 376 10; +#X obj 5 24 vsl 10 50 0 127 0 0 empty empty empty 0 -6 0 8 -262144 +-1 -1 0 1; +#X obj 20 21 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X floatatom 20 58 5 0 0 0 - - -; +#X obj 41 21 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1 +; +#X obj 6 124 outlet; +#X obj 80 0 inlet; +#X obj 136 8 inlet; +#X connect 0 0 2 0; +#X connect 1 0 4 0; +#X connect 2 0 4 0; +#X connect 3 0 4 0; +#X connect 5 0 0 0; +#X connect 6 0 1 0; +#X coords 0 0 1 1 60 85 1 0 0; diff --git a/desiredata/src/tests/gop-three.pd b/desiredata/src/tests/gop-three.pd new file mode 100644 index 00000000..7ab5cb13 --- /dev/null +++ b/desiredata/src/tests/gop-three.pd @@ -0,0 +1,41 @@ +#N canvas 238 407 695 410 10; +#N canvas 0 0 450 300 three 0; +#X obj 89 21 gop-two; +#X obj 22 65 vsl 10 50 0 127 0 0 empty empty empty 0 -6 0 8 -262144 +-1 -1 0 1; +#X obj 41 61 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X obj 63 61 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1 +; +#X floatatom 41 101 5 0 0 0 - - -; +#X obj 259 3 inlet; +#X obj 339 4 inlet; +#X obj 51 237 outlet; +#X connect 0 0 7 0; +#X connect 1 0 4 0; +#X connect 2 0 0 1; +#X connect 4 0 0 0; +#X connect 5 0 1 0; +#X connect 6 0 2 0; +#X coords 0 0 1 1 250 150 1 0 0; +#X restore 182 100 pd three; +#X obj 100 165 vsl 10 50 0 127 0 0 empty empty empty 0 -6 0 8 -262144 +-1 -1 0 1; +#X floatatom 121 202 5 0 0 0 - - -; +#X obj 121 262 print A; +#X obj 182 262 print B; +#X obj 100 43 random 127; +#X obj 100 21 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X obj 100 68 t f f; +#X obj 425 62 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X obj 460 120 gop-two; +#X connect 0 0 4 0; +#X connect 1 0 2 0; +#X connect 2 0 3 0; +#X connect 5 0 7 0; +#X connect 6 0 5 0; +#X connect 7 0 1 0; +#X connect 7 1 0 0; +#X connect 8 0 0 1; diff --git a/desiredata/src/tests/gop-two.pd b/desiredata/src/tests/gop-two.pd new file mode 100644 index 00000000..080cf53f --- /dev/null +++ b/desiredata/src/tests/gop-two.pd @@ -0,0 +1,19 @@ +#N canvas 373 135 592 432 10; +#X obj 80 20 gop-one; +#X obj 9 44 vsl 10 50 0 127 0 0 empty empty empty 0 -6 0 8 -262144 +-1 -1 0 1; +#X obj 25 40 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X obj 50 40 tgl 15 0 empty empty empty 0 -6 0 8 -262144 -1 -1 0 1 +; +#X floatatom 29 82 5 0 0 0 - - -; +#X obj 81 162 outlet; +#X obj 168 11 inlet; +#X obj 237 12 inlet; +#X connect 0 0 5 0; +#X connect 1 0 4 0; +#X connect 2 0 0 1; +#X connect 4 0 0 0; +#X connect 6 0 1 0; +#X connect 7 0 2 0; +#X coords 0 0 1 1 150 120 1 0 0; diff --git a/desiredata/src/tests/sub.pd b/desiredata/src/tests/sub.pd new file mode 100644 index 00000000..1a9e570b --- /dev/null +++ b/desiredata/src/tests/sub.pd @@ -0,0 +1,21 @@ +#N canvas 586 146 668 714 10; +#X obj 86 58 +; +#X obj 107 130 -; +#N canvas 0 0 450 300 foo 0; +#X obj 110 30 inlet; +#X obj 110 100 + 1.618; +#X obj 110 130 * 3.14159; +#X obj 110 180 outlet; +#X connect 0 0 1 0; +#X connect 1 0 2 0; +#X connect 2 0 3 0; +#X restore 224 153 pd foo; +#X obj 101 73 +; +#X obj 122 145 -; +#X msg 220 120 42; +#X floatatom 220 180 8 0 0 0 - - -; +#X text 220 200 137.03; +#X connect 0 0 1 0; +#X connect 2 0 6 0; +#X connect 3 0 4 0; +#X connect 5 0 2 0; diff --git a/desiredata/src/tests/subgop-test.pd b/desiredata/src/tests/subgop-test.pd new file mode 100644 index 00000000..48fb0bb1 --- /dev/null +++ b/desiredata/src/tests/subgop-test.pd @@ -0,0 +1,8 @@ +#N canvas 0 0 450 300 10; +#N canvas 0 0 450 300 foo 1; +#X obj 10 24 bng 15 250 50 0 empty empty empty 0 -6 0 8 -262144 -1 +-1; +#X obj 276 183 print; +#X coords 0 -1 1 1 85 60 1 0 0; +#X restore 145 113 pd foo; +#X obj 319 154 pack; diff --git a/desiredata/src/u_pdreceive.c b/desiredata/src/u_pdreceive.c new file mode 100644 index 00000000..8898e3a3 --- /dev/null +++ b/desiredata/src/u_pdreceive.c @@ -0,0 +1,321 @@ +/* Copyright (c) 2000 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in the Pd distribution. */ + +/* the "pdreceive" command. This is a standalone program that receives messages +from Pd via the netsend/netreceive ("FUDI") protocol, and copies them to +standard output. */ + +#include <sys/types.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#ifdef MSW +#include <winsock.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <stdio.h> +#include <unistd.h> +#define SOCKET_ERROR -1 +#endif + +typedef struct _fdpoll +{ + int fdp_fd; + char *fdp_inbuf; + int fdp_inhead; + int fdp_intail; + int fdp_udp; +} t_fdpoll; + +static int nfdpoll; +static t_fdpoll *fdpoll; +static int maxfd; +static int sockfd; +static int protocol; + +static void sockerror(char *s); +static void x_closesocket(int fd); +static void dopoll(void); +#define BUFSIZE 4096 + +int main(int argc, char **argv) +{ + int portno; + struct sockaddr_in server; +#ifdef MSW + short version = MAKEWORD(2, 0); + WSADATA nobby; +#endif + if (argc < 2 || sscanf(argv[1], "%d", &portno) < 1 || portno <= 0) + goto usage; + if (argc >= 3) + { + if (!strcmp(argv[2], "tcp")) + protocol = SOCK_STREAM; + else if (!strcmp(argv[2], "udp")) + protocol = SOCK_DGRAM; + else goto usage; + } + else protocol = SOCK_STREAM; +#ifdef MSW + if (WSAStartup(version, &nobby)) sockerror("WSAstartup"); +#endif + sockfd = socket(AF_INET, protocol, 0); + if (sockfd < 0) + { + sockerror("socket()"); + exit(1); + } + maxfd = sockfd + 1; + server.sin_family = AF_INET; + server.sin_addr.s_addr = INADDR_ANY; + +#ifdef IRIX + /* this seems to work only in IRIX but is unnecessary in + Linux. Not sure what MSW needs in place of this. */ + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 0, 0) < 0) + fprintf(stderr, "setsockopt failed\n"); +#endif + + /* assign client port number */ + server.sin_port = htons((unsigned short)portno); + + /* name the socket */ + if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0) + { + sockerror("bind"); + x_closesocket(sockfd); + return (0); + } + if (protocol == SOCK_STREAM) + { + if (listen(sockfd, 5) < 0) + { + sockerror("listen"); + x_closesocket(sockfd); + exit(1); + } + } + /* now loop forever selecting on sockets */ + while (1) + dopoll(); + +usage: + fprintf(stderr, "usage: pdreceive <portnumber> [udp|tcp]\n"); + fprintf(stderr, "(default is tcp)\n"); + exit(1); +} + +static void addport(int fd) +{ + t_fdpoll *fp; + fdpoll = (t_fdpoll *)realloc(fdpoll, + (nfdpoll+1) * sizeof(t_fdpoll)); + fp = fdpoll + nfdpoll; + fp->fdp_fd = fd; + nfdpoll++; + if (fd >= maxfd) maxfd = fd + 1; + fp->fdp_inhead = fp->fdp_intail = 0; + if (!(fp->fdp_inbuf = (char *) malloc(BUFSIZE))) + { + fprintf(stderr, "out of memory"); + exit(1); + } + printf("number_connected %d;\n", nfdpoll); +} + +static void rmport(t_fdpoll *x) +{ + int i; + t_fdpoll *fp; + for (i = nfdpoll, fp = fdpoll; i--; fp++) + { + if (fp == x) + { + x_closesocket(fp->fdp_fd); + free(fp->fdp_inbuf); + while (i--) + { + fp[0] = fp[1]; + fp++; + } + fdpoll = (t_fdpoll *)realloc(fdpoll, + (nfdpoll-1) * sizeof(t_fdpoll)); + nfdpoll--; + printf("number_connected %d;\n", nfdpoll); + return; + } + } + fprintf(stderr, "warning: item removed from poll list but not found"); +} + +static void doconnect(void) +{ + int fd = accept(sockfd, 0, 0); + if (fd < 0) + perror("accept"); + else addport(fd); +} + +static void udpread(void) +{ + char buf[BUFSIZE]; + int ret = recv(sockfd, buf, BUFSIZE, 0); + if (ret < 0) + { + sockerror("recv (udp)"); + x_closesocket(sockfd); + exit(1); + } + else if (ret > 0) + { +#ifdef MSW + int j; + for (j = 0; j < ret; j++) + putchar(buf[j]); +#else + if (write(1, buf, ret) < ret) + { + perror("write"); + exit(1); + } +#endif + } +} + +static int tcpmakeoutput(t_fdpoll *x) +{ + char messbuf[BUFSIZE+1], *bp = messbuf; + int indx; + int inhead = x->fdp_inhead; + int intail = x->fdp_intail; + char *inbuf = x->fdp_inbuf; + if (intail == inhead) + return (0); + for (indx = intail; indx != inhead; indx = (indx+1)&(BUFSIZE-1)) + { + /* search for a semicolon. */ + char c = *bp++ = inbuf[indx]; + if (c == ';') + { + intail = (indx+1)&(BUFSIZE-1); + if (inbuf[intail] == '\n') + intail = (intail+1)&(BUFSIZE-1); + *bp++ = '\n'; +#ifdef MSW + { + int j; + for (j = 0; j < bp - messbuf; j++) + putchar(messbuf[j]); + } +#else + write(1, messbuf, bp - messbuf); +#endif + x->fdp_inhead = inhead; + x->fdp_intail = intail; + return (1); + } + } + return (0); +} + +static void tcpread(t_fdpoll *x) +{ + int readto = + (x->fdp_inhead >= x->fdp_intail ? BUFSIZE : x->fdp_intail-1); + int ret; + + /* the input buffer might be full. If so, drop the whole thing */ + if (readto == x->fdp_inhead) + { + fprintf(stderr, "pd: dropped message from gui\n"); + x->fdp_inhead = x->fdp_intail = 0; + readto = BUFSIZE; + } + else + { + ret = recv(x->fdp_fd, x->fdp_inbuf + x->fdp_inhead, + readto - x->fdp_inhead, 0); + if (ret < 0) + { + sockerror("recv (tcp)"); + rmport(x); + } + else if (ret == 0) + rmport(x); + else + { + x->fdp_inhead += ret; + if (x->fdp_inhead >= BUFSIZE) + x->fdp_inhead = 0; + while (tcpmakeoutput(x)) + ; + } + } +} + +static void dopoll(void) +{ + int i; + t_fdpoll *fp; + fd_set readset, writeset, exceptset; + FD_ZERO(&writeset); + FD_ZERO(&readset); + FD_ZERO(&exceptset); + + FD_SET(sockfd, &readset); + if (protocol == SOCK_STREAM) + { + for (fp = fdpoll, i = nfdpoll; i--; fp++) + FD_SET(fp->fdp_fd, &readset); + } + if (select(maxfd+1, &readset, &writeset, &exceptset, 0) < 0) + { + perror("select"); + exit(1); + } + if (protocol == SOCK_STREAM) + { + for (i = 0; i < nfdpoll; i++) + if (FD_ISSET(fdpoll[i].fdp_fd, &readset)) + tcpread(&fdpoll[i]); + if (FD_ISSET(sockfd, &readset)) + doconnect(); + } + else + { + if (FD_ISSET(sockfd, &readset)) + udpread(); + } +} + + +static void sockerror(char *s) +{ +#ifdef MSW + int err = WSAGetLastError(); + if (err == 10054) return; + else if (err == 10044) + { + fprintf(stderr, + "Warning: you might not have TCP/IP \"networking\" turned on\n"); + } +#else + int err = errno; +#endif + fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err); +} + +static void x_closesocket(int fd) +{ +#ifdef MSW + closesocket(fd); +#else + close(fd); +#endif +} diff --git a/desiredata/src/u_pdsend.c b/desiredata/src/u_pdsend.c new file mode 100644 index 00000000..3efa1479 --- /dev/null +++ b/desiredata/src/u_pdsend.c @@ -0,0 +1,138 @@ +/* Copyright (c) 2000 Miller Puckette. +* For information on usage and redistribution, and for a DISCLAIMER OF ALL +* WARRANTIES, see the file, "LICENSE.txt," in the Pd distribution. */ + +/* the "pdsend" command. This is a standalone program that forwards messages +from its standard input to Pd via the netsend/netreceive ("FUDI") protocol. */ + +#include <sys/types.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#ifdef MSW +#include <winsock.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <stdio.h> +#include <unistd.h> +#define SOCKET_ERROR -1 +#endif + +void sockerror(char *s); +void x_closesocket(int fd); +#define BUFSIZE 4096 + +int main(int argc, char **argv) +{ + int sockfd, portno, protocol; + struct sockaddr_in server; + struct hostent *hp; + char *hostname; +#ifdef MSW + short version = MAKEWORD(2, 0); + WSADATA nobby; +#endif + if (argc < 2 || sscanf(argv[1], "%d", &portno) < 1 || portno <= 0) + goto usage; + if (argc >= 3) + hostname = argv[2]; + else hostname = "127.0.0.1"; + if (argc >= 4) + { + if (!strcmp(argv[3], "tcp")) + protocol = SOCK_STREAM; + else if (!strcmp(argv[3], "udp")) + protocol = SOCK_DGRAM; + else goto usage; + } + else protocol = SOCK_STREAM; +#ifdef MSW + if (WSAStartup(version, &nobby)) sockerror("WSAstartup"); +#endif + + sockfd = socket(AF_INET, protocol, 0); + if (sockfd < 0) + { + sockerror("socket()"); + exit(1); + } + /* connect socket using hostname provided in command line */ + server.sin_family = AF_INET; + hp = gethostbyname(hostname); + if (hp == 0) + { + fprintf(stderr, "%s: unknown host\n", hostname); + x_closesocket(sockfd); + 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) + { + sockerror("connect"); + x_closesocket(sockfd); + exit(1); + } + + /* now loop reading stdin and sending it to socket */ + while (1) + { + char buf[BUFSIZE], *bp; + unsigned int nsent, nsend; + if (!fgets(buf, BUFSIZE, stdin)) + break; + nsend = strlen(buf); + for (bp = buf, nsent = 0; nsent < nsend;) + { + int res = send(sockfd, buf, nsend-nsent, 0); + if (res < 0) + { + sockerror("send"); + goto done; + } + nsent += res; + bp += res; + } + } +done: + if (ferror(stdin)) + perror("stdin"); + exit (0); +usage: + fprintf(stderr, "usage: pdsend <portnumber> [host] [udp|tcp]\n"); + fprintf(stderr, "(default is localhost and tcp)\n"); + exit(1); +} + +void sockerror(char *s) +{ +#ifdef MSW + int err = WSAGetLastError(); + if (err == 10054) return; + else if (err == 10044) + { + fprintf(stderr, + "Warning: you might not have TCP/IP \"networking\" turned on\n"); + } +#else + int err = errno; +#endif + fprintf(stderr, "%s: %s (%d)\n", s, strerror(err), err); +} + +void x_closesocket(int fd) +{ +#ifdef MSW + closesocket(fd); +#else + close(fd); +#endif +} diff --git a/desiredata/src/valgrind3.supp b/desiredata/src/valgrind3.supp new file mode 100644 index 00000000..7ec16dbb --- /dev/null +++ b/desiredata/src/valgrind3.supp @@ -0,0 +1,89 @@ +{ + supp01 + Memcheck:Param + ioctl(arg) + obj:/lib/ld-2.3.6.so + fun:snd_pcm_prepare + fun:snd_pcm_hw_params + fun:_Z12alsaio_setupP9_alsa_deviPiS1_ii + fun:_Z15alsa_open_audioiPiiS_iS_iS_ii + fun:sys_open_audio +} +{ + supp02 + Memcheck:Param + ioctl(arg) + obj:/lib/ld-2.3.6.so + fun:snd_pcm_prepare + fun:_Z15alsa_open_audioiPiiS_iS_iS_ii + fun:sys_open_audio +} +{ + supp03 + Memcheck:Param + ioctl(generic) + obj:/lib/ld-2.3.6.so + fun:snd_pcm_link + fun:_Z15alsa_open_audioiPiiS_iS_iS_ii + fun:sys_open_audio +} +{ + supp04 + Memcheck:Cond + obj:/lib/ld-2.3.6.so + obj:/lib/ld-2.3.6.so + obj:/lib/ld-2.3.6.so + obj:/lib/tls/i686/cmov/libc-2.3.6.so + obj:/lib/ld-2.3.6.so + fun:_dl_open + obj:/lib/tls/i686/cmov/libdl-2.3.6.so + obj:/lib/ld-2.3.6.so + obj:/lib/tls/i686/cmov/libdl-2.3.6.so + fun:dlopen + fun:_Z15sys_do_load_libP6_glistPc + fun:sys_load_lib +} +{ + supp05 + Memcheck:Cond + obj:/lib/ld-2.3.6.so + obj:/lib/tls/i686/cmov/libc-2.3.6.so + obj:/lib/ld-2.3.6.so + fun:_dl_open + obj:/lib/tls/i686/cmov/libdl-2.3.6.so + obj:/lib/ld-2.3.6.so + obj:/lib/tls/i686/cmov/libdl-2.3.6.so + fun:dlopen + fun:_Z15sys_do_load_libP6_glistPc + fun:sys_load_lib +} +{ + supp06 + Memcheck:Param + ioctl(arg) + obj:/lib/ld-2.3.6.so + fun:snd_pcm_start + fun:_Z15alsa_open_audioiPiiS_iS_iS_ii + fun:sys_open_audio +} +{ + supp07 + Memcheck:Param + ioctl(arg) + obj:/lib/ld-2.3.6.so + fun:snd_pcm_drop + fun:snd_pcm_close + fun:_Z16alsa_close_audiov + fun:sys_close_audio +} +{ + supp08 + Memcheck:Param + ioctl(arg) + obj:/lib/ld-2.3.6.so + fun:snd_pcm_hw_free + fun:snd_pcm_close + fun:_Z16alsa_close_audiov + fun:sys_close_audio +} + |